文章目录
1. equals与hashcode的关系
- 基类Object中的equals方法比较两个对象时就是比较两个对象的内存地址是否相等,与==的作用相同。
- hashcode方法的作用是获取对象的哈希码。
- 如果重写了一个类中的equals方法之后,应该也把hashcode方法重写,否则创建了类对应的散列表以后,将对象添加到散列表中时,会无法识别对象是否为相等,因为将元素加入到散列表时,散列表默认会以元素的哈希码判断是否已存在,如果改写了equals方法后,那么我们认为的相等的元素的其实哈希码可能是不想等的,所以如果重写了equals方法,hashcode方法也应该重写。
2. String, StringBuffer, StringBuilder
-
String对象是不可变的,类使用final关键字修饰,线程安全,适合操作少量数据。
-
AbstractStringBuilder 是StringBuilder 与 StringBuffer 的公共⽗类,StringBuffer 对⽅法加了同步锁或者对调⽤的⽅法加了同步锁,所以是线程安全的。StringBuilder 并没有对⽅法进⾏加同步锁,所以是⾮线程安全的。
-
单线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuilder,多线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuffer。
-
每次对 String 类型进⾏改变的时候,都会⽣成⼀个新的 String 对象,然后将指针指向新的 String对象。StringBuffer 每次都会对 StringBuffer 对象本身进⾏操作,⽽不是⽣成新的对象并改变对象
引⽤。
3. Java异常
-
Throwable是java异常的基类,Java异常主要分成两类,Exception与Error,Exception是可以通过程序来处理的,Error是无法通过程序处理的,只能尽量避免
-
Error是程序无法处理的错误,如Java 虚拟机运⾏错误( Virtual MachineError )、虚拟机内存不够错误
( OutOfMemoryError )、类定义错误( NoClassDefFoundError )等 。这些异常发⽣时,Java虚拟机(JVM)⼀般会选择线程终⽌。
-
Exception又可以分为受检异常和非受检异常,受检异常必须在代码中进行显式的try catch处理,或者是throws出去,常见的受检异常有 IO 相关的异常、 ClassNotFoundException 、 SQLException;非受检异常即在代码编译过程中不做处理也能通过编译,RuntimeException 及其⼦类都统称为⾮受检查异常,如NullPointExecrption 、 NumberFormatException (字符串转换为数字)、 ArrayIndexOutOfBoundsException (数组越界)、 ClassCastException (类型转换错误)、 ArithmeticException (算术错误)等。
4. Java I/O流
-
Java中的IO流可分为字节流
InputStream/OutputStream
与字符流Reader/Writer
-
音频文件、图片等媒体文件使用字节流较好,涉及字符相关的使用字符流较好,避免出现乱码的问题
-
BIO是同步阻塞I/O模式,数据的读取写入必须阻塞在同一个线程内等待其完成
-
NIO是同步非阻塞的I/O模式,支持面向缓冲的,基于通道的I/O操作方法
-
AIO是异步非阻塞的I/O模式,异步 IO 是基于事件和回调机制实现的,也就是应⽤操作之后会直
接返回,不会堵塞在那⾥,当后台处理完成,操作系统会通知相应的线程进⾏后续的操作。
5. 浅拷贝、深拷贝
- 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝
- 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新对象,并复制其内容
6. ArrayList, LinkedList
- Object数组
- 双向链表
7. HashMap, ConcurrentHashMap
- HashMap底层实现采用数组加链表/红黑树的形式,数组默认大小为16,链表默认长度为8;产生哈希冲突以后,如果链表长度大于8,且数组大小不超过64,则会进行2倍扩容,否则会将链表转成红黑树
- hashmap线程不安全,concurrenthashmap线程安全
8. 进程与线程
- 进程是运行中的程序,线程是比线程更小的执行单元,一个进程可以创建多个线程,进程间的资源是相互独立的,运行开销较大,隶属于同一进程的线程共享父进程的堆与方法区的资源,但是每个线程会有自己独立的虚拟机栈、本地方法栈、程序计数器,线程运行的开销较小,但不利于资源的管理和保护,会涉及到资源竞争,也就是线程安全的问题
- 线程的程序计数器私有主要是为了线程切换之后能恢复到正确的执行位置
- 虚拟机栈与本地方法栈私有是为了保护线程中的局部变量不被其它线程访问
- 堆和⽅法区是所有线程共享的资源,其中堆是进程中最⼤的⼀块内存,主要⽤于存放新创建的对象 (所有对象都在这⾥分配内存),⽅法区主要⽤于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
9. Atomic原子类
10. AQS
- AQS是一个用来构建锁和同步器的框架
- 核心思想是如果请求的共享资源是空闲的,则将当前请求资源的线程设置为有效的工作线程,并把共享资源设置为锁定状态;如果请求的共享资源被占用,那么就需要一套线程阻塞等待以及唤醒时锁分配的机制,AQS使用CLH队列锁实现,将暂时获取不到锁的队列加入队列中
11. Java 中垃圾回收机制中如何判断对象需要回收?常见的 GC 回收算法有哪些?
- 判断对象是否要回收,先判断对象是否已经死亡,能再被任何途径使用的对象,使用的方法有两种,一个是
引用计数法
,一个是可达性分析算法
引用计数法
即是给对象添加一个引用计数器,每当有一个地方引用他,计数器就加一;引用失效,计数器减一;计数器为0的对象就是不可再被使用的可达性分析算法
的基本思想就是通过一系列的称为GC Roots
的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的话,则证明此对象是不可用的- 常见的垃圾回收算法有
标记-清除算法
、复制算法
、标记-整理算法
、分代收集算法
标记-清除算法
可分为标记和清除阶段,首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象,这是最基础的收集算法。主要存在效率以及空间上的问题,标记清除后会产生大量不连续的碎片复制算法
主要是为了解决效率问题,算法思想是将内存分为大小相同的两块,每次使用其中的一块。当这一块内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉,这样每次的内存回收就是对一半的内存空间进行回收标记-整理算法
是根据老年代的特点提出的一种标记算法,标记过程与标记-清除算法一样,但标记以后不是直接对可回收对象进行回收,而是让所有存货的对象向一端移动,然后直接清理掉边界以外的内存分代收集算法
的思想是根据对象存活周期不同,将内存分为几块,然后根据各个年代的特点选择合适的垃圾回收算法。比如在新生代,每次收集都会有大量的对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次的垃圾回收;而老年代的对象存活率较高,而且没有额外的空间对他进行分配担保,所以我们必须选择标记-清除或者标记-整理的方法进行垃圾回收