1、面向对象有哪些特征?
面向对象特征封装、继承、多态。
封装:隐藏对象的属性,将这些属性进行封装隐藏
/**
* 封装
*/
public Student(String name, int age) {
this.name = name;
this.age = age;
}
继承:通过extends子类继承父类
public class Student extends People {
public Student(String name, int age) {
super(name, age);
}
}
多态:多态就是多种状态,就是一个方法在这个类中是这个状态,在另一个类中是另一种状态。
/**
* 多态1
*/
public class User implements Action {
@Override
public void eat() {
System.out.println("吃的状态一");
}
}
/**
* 多态2
*/
public class Student implements Action{
@Override
public void eat() {
System.out.println("吃的状态二");
}
}
扩展:编程语言分为面向机器、面向过程、面向对象;面向过程:按解决问题的步骤,然后一步一步的实现,按本意就是按问题的过程顺序执行。
2、Arraylist与Linkedlist的区别?
1、Arraylist与Linkedlist都实现List接口。
2、Arraylist是基于数组、Linkedlist是基于双向链表。
3、对于随机访问:Arraylist优于Linkedlist,根据索引访问。
4、对新增和删除:Linkedlist代码Arraylist,Linkedlist只需要更换指针指向,Arraylist会移动数据与重新计算长度。
5、Linkedlist比Arraylist更占内存
3、什么是线程不安全?
先看如下例子
// 例子一
static int runNum = 0;
static final int TOTAL = 10000000;
for (int i = 0; i < TOTAL; i++) {
runNum++;
}
for (int i = 0; i < TOTAL; i++) {
runNum--;
}
System.out.println("输出结果为="+runNum);
输出结果为:
输出结果为=0
// 例子二
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < TOTAL; i++) {
runNum++;
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < TOTAL; i++) {
runNum--;
}
}
});
thread.start();
thread2.start();
try {
thread.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("输出结果为="+runNum);
输出结果为:
输出结果为=5835732
通过上面的例子你会发现,“例一”结果是唯一的,但是“例二”得到的结果每次都不一样,这个是什么原因造成的呢?这就是线程不安全。那这个代码如何调整呢?看如下,其它就是增加一个“synchronized”
public class Student {
static int runNum = 0;
static final int TOTAL = 10000000;
static synchronized void add() {
runNum++;
}
static synchronized void reduce() {
runNum--;
}
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < TOTAL; i++) {
add();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < TOTAL; i++) {
reduce();
}
}
});
thread.start();
thread2.start();
try {
thread.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("输出结果为=" + runNum);
}
}
4、影响线程不安全的原因?
线程不安全主要有三个原因,原子性、可见性、指令重排。
原子性:对于高级程序而言,对于一个简单的语句如a++,都会涉及多个指令(读取a的值->a+1的操作->赋值)才能完成a++的操作。但是CPU的执行是抢占时间片,如果a++在执行到一半时,CPU时间片被另外一个线程抢占了,这时候就没有原子性,需要给执行操作增加一个锁,类似于地铁站卫生间小门一样,一次只能进一个人。
可见性:在 聊可见性之前,我们先聊一下CPU的缓存机制,CPU为了提高计算的速度,CPU会从主存中复制一份数据到工作内存(有直接使用,没有直接复制份到缓存),计算的时候直接取工作内存中的数据,计算完成后再将工作内存中的数据更新回主存中。当多个线程同时操作一个变量b时,第一个线程变量b未完成从工作内存更新回主存中时,第二个线程已经开始,重新从主存中将b变量复制一份数据到自己工作内存中,此时第一个线程工作内存中的b变量与第二个线程中的b变量已经不一致。这样就造成了数据的不可见性。
指令重排:JVM为优化代码执行效率,在执行中会对代码顺序进行重新排序(不会影响代码的执行结果),这个情况在单线程中不会出现执行结果错误,但是在多线程中会出现错误,指令重排会造成可见性的问题。
public class User {
static int a = 0;
static int b = 0;
static int c = 0;
static int num = 1000000000;
static boolean bStatu;
public static void main(String[] args) {
Thread read = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < num; i++) {
if (bStatu) {
System.out.println("结果=" + c);
a = 0;
b = 0;
c = 0;
bStatu = false;
}
}
}
});
Thread write = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < num; i++) {
a = 1;
b = 1;
c = a + b;
bStatu = true;
}
}
});
write.start();
read.start();
}
}
执行上面这段程序,逻辑上应该全为结果=2,但是执行过程中会出现结果=0的情况,这就是指令重排造成的
5、java有那几种方式创建线程?
public class ThreadMain {
public static void main(String[] args) {
/**
* 方式一
*/
ThreadTest threadWork = new ThreadTest();
threadWork.start();
/**
* 方式二
*/
Thread runnableTest = new Thread(new RunnableTest());
runnableTest.start();
/**
* 方式三
*/
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread方式三");
}
});
thread3.start();
/**
* 方式四
*/
FutureTask futureTask = new FutureTask(new CallabeTest());
Thread thread4 = new Thread(futureTask);
thread4.start();
try {
System.out.println("线程输出返回结果=" + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
/**
* 方式五
*/
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new ThreadTest());
}
}
class ThreadTest extends Thread {
@Override
public void run() {
System.out.println("ThreadTest");
}
}
class RunnableTest implements Runnable {
@Override
public void run() {
System.out.println("RunnableTest");
}
}
class CallabeTest implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "线程返回成功啦!";
}
}