基础知识
基础知识主要涉及java语法及常见知识点。包括IO、类加载,反射、注解、异常,集合类等。下面以这几个大类分别做汇总,并引出其中若干小问题。
知识梳理
- java的集合体系及继承关系,以及主要类的用法及原理。
- java类加载机制
- BIO与NIO、AIO区别及联系,什么是Reactor模型。
- 异常类继承关系,受检异常和非受检异常。
- 反射知识梳理。
- 其他java特性(枚举,注解,synchronized和volatile)
- 序列化相关。
- 多线程相关
以下就分别从这几个方面来讲讲java基础知识。
java集合
上图是java Collection主要类成员。基本上Collection
为集合框架的最抽象接口。其下根据功能需要又分为Set
、List
和Queue
3个主要接口。分别表示无序不重复集合(Set)、有序集合和队列。
- HashSet底层实现由
HashMap
完成。HashMap的key即为HashSet的值。value则为一固定的Object对象。
// 部分HashSet源码
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
LinkedHashSet
实现由LinkedHashMap
完成。而LinkedHashMap
是HashMap和LinkedList的结合体。由HashMap保证Map属性,再由链表将Map中的元素以插入顺序链起来。LinkedHashSet调用了HashSet的构造函数。
// 此构造函数专为LinkedHashSet使用
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
TreeSet
由TreeMap
实现,同上一样,TreeSet调用了TreeMap的方法。
// TreeSet部分源码
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>());
}
可以看到,和之前HashSet之于HashMap是类似的处理。TreeMap是一个有序的Map结构,底层通过红黑树实现。
Queue
队列数据结构可简单分为双端队列(Deque)和阻塞队列(BlockingQueue)和优先级队列(PriorityQueue)。主要类结构图已如上图所示。- 队列可用于生产者、消费者模型,尤其阻塞队列,需掌握。
- List的实现主要分为数组和链表实现两种。按线程安全又分为线程安全和线程非安全。
CopyOnWriteArrayList
写时复制数组。 - 有关队列的入队出队方法及解释
操作 | 返回特殊值 | 抛出异常 |
---|---|---|
插入(队尾) | offer(e) | add(e) |
删除(队首) | poll() | remove() |
查询(获取队首元素) | peek() | element() |
对于阻塞队列(BlockingQueue
)而言,又多了阻塞和超时的方法。
操作 | 返回特殊值 | 抛出异常 | 阻塞 | 超时退出 |
---|---|---|---|---|
插入(队尾) | offer(e) | add(e) | put(e) | offer(e,timeout,unit) |
删除(队首) | poll() | remove() | take() | poll(timeout,unit) |
查询(获取队首元素) | peek() | element() | 无 | 无 |
java Map结构
以上是Map的类结构图。除了现已不再推荐使用的Hashtable
以外,主要有3类Map。ConcurrentHashMap
基于Hash表和分段锁实现,线程安全,在多线程环境最常使用的Map。HashMap
基于Hash表实现,日常使用最广的Map。TreeMap
,基于红黑树实现。插入元素排序。
集合常见问题
有关java集合的问题挺多,常见如HashMap底层数据结构和实现原理,ConcurrentHashMap原理等。这里总结下以备忘,常看常新。
- HashMap在高并发会出现什么问题,能否画出来?此问题在java8还有吗?
- TreeMap原理?有关红黑树的左旋和右旋?
- java集合类继承关系都是怎样的,能否画一下?
- 阻塞队列原理?
- 手写实现生产者消费者模型
- 优先级队列和延时队列实现原理
java类加载机制
类加载顺序
类的加载过程是: 加载->连接(验证,准备,解析)->初始化->使用->卸载。具体解析如下:
加载 : 将class二进制字节流(文件或jar包或字节流等各种形式)加载到内存中。
验证 :包括文件格式、元数据,以及字节码等验证,看是否合法。
准备 : 为类变量分配内存,设初始值(零值)等。
解析 : 符号引用替换为直接引用。加载这个类所引用的其他所有类。
初始化: 初始化类变量或其他资源。是执行方法的过程。JVM会保证在执行时,其父类的被执行(接口则不会执行父类cinit,除非被用到)。是线程安全的,由JVM保证。
注意:
- 类加载阶段不会涉及类构造器的调用,显然那时还不一定有对象实例的产生。
- 类变量在准备阶段分配内存并赋零值,在初始化阶段赋真正的值。
- 方法实际上是JVM收集所有类变量的赋值语句和静态语句块形成的。
- 两个类是否相等的前提是,都由一个类加载器加载。若不是,则一定不相等。(即使是同一个类加载器的两个实例加载出来的类,也不相等)。
- 初始化某类数组不会导致该类初始化。
- 访问经static和final共同修饰的字段为编译期常量,不会导致该类初始化
双亲委派模型
三种类加载器,分别是BootstrapClassLoader
、ExtClassLoader
和AppClassLoader
加载器。从顶到下排列。当前类加载器会先将加载任务委托给父类加载,父类一级一级加载,直到BootstrapClassLoader
加载不了(加载不了的意思是指其classpath中没有要加载的类),则向下流转。直到其父类都不能加载,自己才尝试加载。
常见问题
- 实现自己的类加载器
- 哪些情况会导致类初始化
java IO
常见问题
- BIO、NIO的理解?BIO的阻塞理解和NIO的非阻塞理解?
- 阻塞模型
- AIO的概念
- IO涉及到的设计模式
- 手写文件读写、缓冲区读写等
java主要IO类
字节流、字符流四个主要接口。为InputStream、 OutputStream、Writer和Reader。前2个为字节流,后2个为字符流。主要方法为write(int b)
和read()
。
对于字节流InputStream,read()
方法返回int值。
public abstract int read() throws IOException;
返回的int值范围在-1 - 255
之间,-1表示流结束。
同理,OutputStream , write(int b)
方法参数为int,
public abstract void write(int b) throws IOException;
传入的参数值只有低8位有效,其余16为会被忽略,也即不能超过255。
为什么BIO会阻塞
讨论为什么传统IO会阻塞?
在系统内核,有关于socket的2个缓存区比较重要。分别是socket的读缓存区(read buffer)和写缓冲区(write buffer)。
流程如下:首先通过操作socket写入数据,先写入写缓冲区(write buffer)系统有一个专门的线程用于检查写缓冲区,有数据后将数据送入网卡设备经网络传送出去。
同时,系统内核有线程会将收到的数据拷贝到socket读缓冲区供用户空间使用。
两个缓冲区的大小是有限的,当写缓冲区写满后,会阻塞写操作,当读缓冲区为空后,会阻塞读操作。
所以,当有线程专门用于读取时,若读不到指定大小的字节,会阻塞该线程。
所以总结来说:
- 在我们使用中调用的read/write方法实际上只是将数据写到缓存区或从缓存区拿数据,对于写。它只是将数据写到缓冲区去了,至于什么时发送,由操作系统决定。对于读,只是从系统缓存区读取数据而已。
- 对于阻塞读而言,read的原理是 没有数据会一直等待,有数据则会返回(若多于指定大小,会返回指定大小,若小于指定大小,则能返回多少返回多少)。
- 对于非阻塞读,没有数据立即返回,有数据则有多少读多少。所以,对于读,阻塞和非阻塞的区别在于,没有数据时是否会立即返回。
当使用IO读取数据时,实际上底层使用的是socket操作的数据。读取时读取socket的 read buffer。若socket读缓冲区没有足够的数据时,读取操作就会阻塞,直到有了足够数据。
其他IO参考文章
异常体系
java的异常分为错误和异常。
错误(Error)指发生非常严重的异常,通常是JVM发生的。如
内存溢出(OutOfMemoryError)、线程死亡(ThreadDeath)等。这些异常发生时,JVM一般会终止程序,所以一般也不捕获。
Exception指程序发生的异常。分为运行时异常(RuntimeException及其子类)和非运行时异常(其他Exception子类)。运行时异常有空指针异常(NullPointerException)、下标越界异常(IndexOutOfBoundsException)、类型转换异常(ClassCastException) 等。一般是程序错误引起,在运行时才能发现的异常。
非运行时异常也称为受检异常。指必须捕获的异常,如IOException和SQLException等。
反射
- 了解反射知识,熟练引用发射方法。
序列化
- transient作用
- readObject和writeObject
- 序列化id
- Serializable
多线程相关
- synchronized和lock锁区别
- AQS实现原理
- lock锁实现原理,与AQS关系(线程被阻塞后放在哪里,被await住又放在哪里)
- CAS什么意思
- 有几种线程池,分别是什么意思
- 线程池参数含义
- 线程池工具类(CountDownLatch等原理)
- ThreadLocal原理及场景,它如何处理垃圾回收
- 阻塞队列及原理
其他知识点
- synchronized底层原理
- volatile作用及原理
- Java内存模型介绍
- 偏向锁、共享锁概念及理解
- wait、notify通知机制
- StringBuffer和StringBuilder