Java基础

三大特性

  • 封装
  • 继承
  • 多态
    • 多态分为编译时多态(方法重载)和运行时多态(对象引用所指向的具体类型在运行期间才确定
    • 运行时多态有三个条件:继承覆盖(重写)向上转型

方法分类

https://www.runoob.com/note/40084

  • 虚方法:可以被覆写的方法都可以称作虚方法,也可以理解为除了用static、final、private修饰之外的所有方法都是虚方法。
  • 纯虚方法:abstract修饰的方法

解析与分派

  • 解析是指方法在编译期就可知的,有一个确定的版本且运行期不可变。是静态的,在**类加载时**解析阶段会将其符号应用解析从直接引用。
    • invokestatic:静态方法
    • invokespecial:构造器、私有方法、父类方法、final方法
  • 分派又分为静态分派、动态分派。
    • 重载属于静态分派(invokevirtual),在**编译期间**能根据参数的静态类型决定使用哪一版本的方法。
    • 重写属于动态分派(invokevirtual),在运行期间进行符号引用解析,根据参数的实际类型决定调用方法的版本。
  • 解析和分派时多态的一个实现,这种多态是方法的多态。但是字段不支持多态,父类字段名和子类字段名相同时,子类对象会存在两个字段,但是他们会被分开在所属类的空间中,父类方法访问的时父类的字段,子类方法访问的是子类的字段。

面向对象设计原则(单开里依接米)

  • 单一职责:一个类应该只负责一个职责
  • 开闭原则:一个类当对扩展开放,对修改关闭
  • 里氏替换原则:系统中所有用到某个类的地方都替换成其子类,系统应该仍然可以正常工作。子类与父类的含义与功能一致
  • 依赖倒置原则:抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
  • 接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。让调用者依赖的接口尽可能的小。
  • 米迪特原则:一个类应该对自己需要调用的类知道得最少,类的内部如何实现与调用者没关系,调用者只需知道他需要的方法即可,其他的一概不关心。

八个基本类型:

  • boolean/1
  • byte/8:-2^7 ~ 2^7 - 1,即 -128 ~ 127
  • char/16: 0 ~ 2^16 - 1,即 0 ~ 65535
  • short/16: -2^15 ~ 2^15 - 1,即 -32768 ~ 32767
  • int/32:-2^31 ~ 2^31 - 1,即 -2147483648 ~ 2147483647
  • float/32
  • long/64: -2^63 ~ 2^63 - 1,即 -9223372036854775808 ~ 9223372036854775807
  • double/64

String为final的好处

  • 可以缓存hash值
  • String pool的需要,只有 String 是不可变的,才可能使用 String Pool
  • 线程安全,String 不可变性天生具备线程安全,可以在多个线程中安全地使用

String.intern()

使用 String.intern() 可以保证相同内容的字符串变量引用**同一内存对象**

字符串常量池

运行时常量池(Runtime Constant Pool)是虚拟机规范中是方法区的一部分,在加载类和结构到虚拟机后,就会创建对应的运行时常量池;而字符串常量池是这个过程中常量字符串的存放位置。所以从这个角度,逻辑上字符串常量池属于虚拟机规范中的方法区,它是一个逻辑上的概念;而堆区,永久代以及元空间是**实际的存放位置**。

  • jdk7之前存在永生代,字符串常量池存放在永生代中
  • jdk7依然存在永生代,永久代(类型信息、字段、方法、常量)和(字符串常量池、静态变量)
  • jdk7之后移除永生代,元空间(类型信息、字段、方法、常量)和(字符串常量池、静态变量)

switch语法

  • switch 底层使用 int 来进行判断的,即使是枚举、String类型,最终也是转变成 int 型。由于 long 型表示范围大于 int 型,因此不支持 long 类型。
  • jdk7开始,switch支持String类型作为参数,编译时通过String的hashcode()转换成int型
  • switch不支持long、double类型的参数

Java 注解

  • 为类、方法等代码添加额外的注解元数据;
  • 注解可以给编译器提供信息;在编译阶段可以根据注解生成代码、文档等;运行阶段可以根据注解提取代码。

拆箱装箱的原理

1、在编译阶段把需要拆箱的代码替换成class.xxValue(),需要装箱的代码替换成.valueOf() 2、基础类型使用享元模式,short,byte,int,long:-128~127 boolean:true,false

ThreadLocal

线程变量,可以使变量在多个线程有不同的副本:不同线程自己的Map,存同一个ThreadLocal对应不同value

线程1 (Thread)
└── threadLocals (ThreadLocalMap)
    ├─ Entry[0]: key=ThreadLocalA(弱引用) → value=值A1
    ├─ Entry[1]: key=ThreadLocalB(弱引用) → value=值B1
    └─ ...

线程2 (Thread)
└── threadLocals (ThreadLocalMap)
    ├─ Entry[0]: key=ThreadLocalA(弱引用) → value=值A2
    ├─ Entry[1]: key=ThreadLocalB(弱引用) → value=值B2
    └─ ...

ThreadLocalA 实例 → 被线程1和线程2的Entry弱引用
ThreadLocalB 实例 → 被线程1和线程2的Entry弱引用	

核心特点:

  1. 每个线程有独立的 ThreadLocalMap(互不干扰)
  2. 每个 ThreadLocalMap 存储多个 Entry(键是 ThreadLocal 实例,值是线程私有副本)
  3. 键用弱引用(ThreadLocal 实例不用时可被回收)
  4. 值用强引用(保证线程运行时能正常访问)

细节补充:

  • hashcode使用**斐波那契散列法,当出现hash冲突时使用开放选址法(hash+1)(线性探测法**)进行定位
  • ThreadLocal.set时,会通过cleanSomeSlots方法**清除无效**的ThreadLocal数据

问题:没有解决hash冲突问题,变量很多时效率低。未及时清理ThreadLocal导致内存泄露