Java相关
引用拷贝、深拷贝、浅拷贝
引用拷贝:不会生成新的对象,只会在原对象上增加了一个新的对象引用,两个引用指向的对象还是同一个
浅拷贝:会生成新的对象,但是对象中的引用类型并不会重新生成(修改该类属性仍然会连环修改)
- 实现方式:对象实现cloneable接口,在直接调用clone方法
深拷贝:会生成新的对象,且对象中的引用类型也会重新生成对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35package JavaGuide;
public class Teacher implements Cloneable{
public int num;
public Student s;
protected Object clone() throws CloneNotSupportedException {
Teacher teacher = (Teacher) super.clone();
teacher.s = (Student) this.s.clone();
return teacher;
}
public static void main(String[] args) throws CloneNotSupportedException {
Student stu = new Student();
stu.num = 1111;
Teacher t1 = new Teacher();
t1.num = 1;
t1.s = stu;
// Teacher t2 = t1;
Teacher t2 = (Teacher) t1.clone();
t2.s.num = 2222;
System.out.println(t1.s.num);
}
}
class Student implements Cloneable{
public int num;
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
JDK和JRE
JDK 是 Java Development Kit 缩写,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。
JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。
如果你只是为了运行一下 Java 程序的话,那么你只需要安装 JRE 就可以了。如果你需要进行一些 Java 编程方面的工作,那么你就需要安装 JDK 了。
有哪些集合是线程不安全的?怎么解决呢?
我们常用的 Arraylist
,LinkedList
,Hashmap
,HashSet
,TreeSet
,TreeMap
,PriorityQueue
都不是线程安全的。解决办法很简单,可以使用线程安全的集合来代替。
如果你要使用线程安全的集合的话, java.util.concurrent
包中提供了很多并发容器供你使用:
ConcurrentHashMap
: 可以看作是线程安全的HashMap
CopyOnWriteArrayList
:可以看作是线程安全的ArrayList
,在读多写少的场合性能非常好,远远好于Vector
.ConcurrentLinkedQueue
:高效的并发队列,使用链表实现。可以看做一个线程安全的LinkedList
,这是一个非阻塞队列。BlockingQueue
: 这是一个接口,JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列,非常适合用于作为数据共享的通道。ConcurrentSkipListMap
:跳表的实现。这是一个Map
,使用跳表的数据结构进行快速查找。
Arraylist 与 LinkedList 区别?
- 是否保证线程安全:
ArrayList
和LinkedList
都是不同步的,也就是不保证线程安全; - 底层数据结构:
Arraylist
底层使用的是Object
数组;LinkedList
底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!) - 插入和删除是否受元素位置的影响: ①
ArrayList
采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)
方法的时候,ArrayList
会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element)
)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ②LinkedList
采用链表存储,所以对于add(E e)
方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置i
插入和删除元素的话((add(int index, E element)
) 时间复杂度近似为o(n))
因为需要先移动到指定位置再插入。 - 是否支持快速随机访问:
LinkedList
不支持高效的随机元素访问,而ArrayList
支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)
方法)。 - 内存空间占用: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。
多线程相关
什么是线程和进程? 线程与进程的关系,区别及优缺点?
- 进程是具有一定独立功能的程序,关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位
- 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
使用多线程可能带来什么问题?
内存泄漏、死锁、线程不安全等等
创建线程有哪几种方式?
a.继承 Thread 类;b.实现 Runnable 接口;c. 使用 Executor 框架;d.使用 FutureTask
线程的生命周期和状态?
什么是上下文切换
CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态,从任务保存到再加载的过程就是一次上下文切换。
synchronized加锁方式
1、修饰一个代码块,作用的范围是大括号括起来的对象,作用的对象是调用这个代码块的对象
2、修饰一个方法,被修饰的方法称为同步方法,作用的范围是整个方法,作用的对象是调用这个 方法的对象
3、修饰一个静态的方法,其作用的范围是整个静态方法,租用的对象是这个类的所有对象
4、修饰一个类,起作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象
什么是线程死锁?如何避免死锁?
当线程A持有独占锁a,并尝试去获取独占锁b的同时,线程B持有独占锁b,并尝试获取独占锁a的情况下,就会发生AB两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁
破坏死锁的四个必要条件:互斥条件、请求与保持条件、不剥夺条件、循环等待条件。其中前三个称为独占锁。
synchronized 关键字和 volatile 关键字的区别
volatile能够保证可见性和有序性,但无法保证原子性;synchronized则都能保证