NingG +

Java 剖析:基础汇总(1)

0.概要

整体几个方面:

  1. Object 和 Class
  2. Enum 枚举的实现
  3. String 的实现
  4. 泛型:底层实现
  5. 异常处理
  6. 集合类:HashMap、HashTable、ConcurrentHashMap、LinkedHashMap
  7. 线程池

1. Object 和 Class

Object 自带的方法:

  1. getClass():获取运行时类
  2. clone():完成对象的复制
  3. equals():默认判断==,基本类型的值、对象的引用地址,
  4. hashCode():获取对象hashcode,方便 HashMap 和 HashTable
  5. toString():对象的字符串表示
  6. wait():当前线程,挂起等待
  7. notify():唤醒其他在当前对象上等待的一个线程
  8. notifyAll():唤醒其他在当前对象上等待的所有线程,此时,多线程并发访问,不一定是线程安全的;

获取 Class 的方法:

  1. Object.getClass()
  2. Class.forName(“class 的全限定名”)
  3. 类名.class

拓展equals() 方法,重写注意事项

更多细节:

2. Enum 枚举的实现

特性:静态常量单例

  1. 构造方法private
  2. 内部属性 name 和 ordinal 都是 final常量,无法修改

作用:相比于「静态常量」,枚举作为「静态常量类」,优点

更多细节:

3. String 的实现

StringStringBufferStringBuilder

更多细节:

4. 泛型:底层实现

不用泛型:可以直接用 Object 替代,然后,使用时,类型强制转换

泛型的作用

  1. 安全性:在编译期,进行类型匹配检查
  2. 可读性:避免手动的强制类型转换,强制类型转换是强制的、自动的,代码可读性好
  3. 代码重用:安全性和可读性更高

底层实现,细节:

  1. 类型检查:在编译期,进行类型检查
  2. 类型擦除:在编译期,自动转换为「强制类型转换」,同时,擦除泛型
  3. 泛型类:泛型类,只会生成一个 class 字节码文件,其中不包含泛型概念,底层是 Object 类型
    1. 一个泛型类:底层是同一个 class 文件,泛型参数底层对应的是 Object 类型
    2. 调用类:生成的字节码中,
      1. 编译时,强制类型转换,插入字节码中
      2. 运行时,进行强制类型转换

更多细节:

5. 异常处理

关于 Error、Exception、RuntimeException:

  1. 所有类的共同父类:Throwable 类
  2. Error,不可恢复的错误,OutOfMemoryError、StackOverflowError、NoClassDefFoundError
  3. Exception,受检异常,需要程序主动捕获,IOException,FileNotFoundException
  4. RuntimeException,运行时异常,不强制程序捕获,NPE、数组越界 IndexOutOfBoundsException

关于异常处理机制,沉淀一套统一异常处理的经验:

  1. 对内,异常栈、错误日志
  2. 对外,用户展示的引导信息,一般需要定制
  3. 转换:统一异常处理机制 ControllerAdvice、ExceptionHandler,统一进行异常的转换,将「内部异常」转换为「对外提示」同时,记录错误日志

更多细节:

6. 集合类

几个方面:

6.1. HashMap

底层数组链表

扩容

Hash 冲突时

线程安全问题:非线程安全

更多细节:

6.2. HashTable

底层:数组链表

几个细节:

6.3. ConcurrentHashMap

概要:JDK1.7 和 JDK 1.8 之间的实现细节,差异非常大

实现细节:

更多细节:

6.4. LinkedHashMap

底层原理:

继承 HashMap,同时,内部 Entry 节点,包含 2 个指针,构成双向链表

7. 线程池

几个参数:

  1. 核心线程数
  2. 最大线程数
  3. 空闲线程最大存活时间
  4. 线程排队队列
  5. 队列拒绝策略
  6. 线程工厂:生成线程池里的 worker 线程

更多细节:

8. ThreadLocal

ThreadLocal 几点:

  1. 作用:线程本地变量,线程之间资源隔离,解决线程并发时,资源共享的问题;
  2. 实现:
    1. 每个 Thread 都绑定了一个 ThreadLocalMap
    2. ThreadLocal 的 set、get,都是针对 Thread 的 ThreadLocalMap 进行的
    3. ThreadLocalMap 中,Entry[] table
      1. ThreadLocal 作为 key,定位到 Entry
      2. ThreadLocal 存储的 value 作为 value
      3. Entry 中,同时存储了 keyvalue
      4. 数据存储时, Entry 数组,出现Hash,采取避让(开放寻址)策略,而非数组拉链(开放链路)策略
      5. Entry[] 数组,初始长度为 16;大于 threshold 时,2 倍扩容。
      6. Entry[] 数组中,对 key弱引用(WeakReference),key 就是 ThreadLocal 对象自身
        • ThreadLocal 变量被回收后,Entry 和 Value 并未被回收;
        • ThreadLocalMap 只是用于存储的,供其他地方使用;
        • 如果其他地方不再使用这个 ThreadLocal 对象了,由于其为弱引用,因此,其弱引用被自动置为 null,即,key 被置为 null;
        • 但,Value 是强引用,仍然没有被回收,存在内存泄露问题;
        • Key 由于为弱引用,被置为 null 后,在 ThreadLocal 的 get、set 方法调用时,会消除 key 为 null 对应的 value 的强引用,避免内存泄露;
      7. 上述弱引用对应的 Entry,什么时候回收?get()、set() 会回收 Entry;
      8. 内存泄漏问题:如果 ThreadLocal 不再使用了,但一直未调用 get、set 方法,则,内存泄漏;当然,如果线程彻底销毁,对应 ThreadLocal 会被回收,但在此之前,内存泄露;
      9. 线程池问题:线程一直存活,下一次使用的时候,获取上一次使用时,设置的 threadLocal 变量,建议:使用之前先清理一次 threadLocal 变量;
    4. 每个 ThreadLocal 都用于存储一个变量,ThreadLocalMap 中,可以存储多个变量

ThreadLocal 在内存中的存储关系:

关于强引用(Strong)、软引用(Soft)、弱引用(Weak)、虚引用(Phantom):

  1. 强引用:我们平时使用的引用,Object obj = new Object(); 只要引用存在,GC 时,就不会回收对象;
  2. 软引用:还有一些用,但非必需的对象,系统发生内存溢出之前,会回收软引用指向的对象;
  3. 弱引用:非必需的对象,每次 GC 时,都会回收弱引用指向的对象;
  4. 虚引用:不影响对象的生命周期,每次 GC 时,都会回收,虚引用用于在 GC 回收对象时,获取一个系统通知

更多细节,参考: Java并发:ThreadLocal

9. 多线程

Java 下,多线程协作,常用的 2 个类:

  1. CountDownLatch:主线程,等待子线程
  2. CyclicBarrier:子线程之间,相互等待

CountDownLatch 被用于:

因此,具体使用过程中:

  1. 主线程:定义 CountDownLatch 需要等待的子线程个数
  2. 子线程:调整 CountDownLatch 的剩余线程数
  3. 主线程countDownLatch.await() 阻塞等待子线程执行结束

使用 CyclicBarrier,多个子线程之间相互等待,具体操作:

  1. 主线程:定义 CyclicBarrier 需要等待的子线程个数
  2. 子线程:调用 CyclicBarrier.await() 等待其他线程

直译为循环栅栏,通过它可以让一组线程全部到达某个状态后再同时执行,也就是说假如有5个线程协作完成一个任务,那么只有当每个线程都完成了各自的任务(都到达终点),才能继续运行(开始领奖)。循环的意思是当所有等待线程都被释放(也就是所有线程完成各自的任务,整个程序开始继续执行)以后,CyclicBarrier 可以被重用。而上面的 CountDownLatch 只能用一次。

更多细节,参考:Java并发:concurrent 包

同类文章:

微信搜索: 公众号 ningg ,即可联系我

Top