本文主要记录 Java 基础知识。
基础数据类型
数据类型
- byte - 8 字节
- short - 16 字节
- char - 16 字节
- int - 16 字节
- long - 32 字节
- float - 32 字节
- double - 64 字节
- boolean
boolean 类型没有明确的存储大小,只有两种值,分别是 true 和 false, 这两种值在存储时会转化成 int 类型,分别用 0 和 1 表示。boolean 数组将转换成 byte 数组存储。
缓冲池
new Integer(123) 与 Integer.valueOf(123) 的区别
- new Integer(123) 会创建一个对象
- Integer.valueOf(123) 会使用缓冲池中的对象,多次调用得到一个对象引用。Integer 的缓冲池范围时 -128 ~ 127
String
String 为什么不可变
String 底层数据结构是 char 类型的数组,其使用 final 被修饰,意味着不可以被继承修改。并且 String 内部没有改变数组的方法。因此可以保证 String 不可变。
重载和重写
重载是指在相同的类中,方法名称必须相同,参数个数,参数类型或者参数顺序不同,返回类型和修饰符可以不同。
重写是发生在继承关系的父类和子类中,子类可以重写父类的方法,重写过程中,不能改变父类的方法名称和参数列表,子类的返回值应该比父类更小或者相等。其中不能重新 private、final 和 static 修饰的方法。但是被 static 修饰的方法能够再次被声明。
抽象类和接口的区别
共同点:
- 不能被实例化
- 可以包含抽象方法
- 可以有默认的实现方法
区别:
- 接口是用于类的约束,实现了某个接口,则自动具有这个接口的对应行为。抽象类强调于代码的复用,强调的是所属关系。
- 一个类只能实现一个接口,但是可以继承多个抽象类。
- 接口的成员变量使用 public static final,不能被修改且必须有初始值。而抽象类的成员变量默认为 default,可以在子类中重新定义,也可以被重新赋值。
浅拷贝和深拷贝
浅拷贝: 浅拷贝是创建一个新对象,这个对象有着原始属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,则拷贝的就是引用类型的内存地址。
深拷贝: 深拷贝是将原有的对象重新复制一份,重新开辟一份内存空间进行存储,修改新的对象不会影响原有的对象。
== 和 equals() 的区别
== 如果是基本数据类型,比较的是其值是否相等,⚠️ 基本数据类型只能使用 == 进行比较。如果是对象类型,比较的是引用地址是否相同。
equlas 是 Object 中的一个方法,比较的是对象中的内容是否相同。
Exception 和 Error 的区别
Exception: 程序本身可以处理的异常,可以通过 catch 进行捕获。
Error: 程序本身不可以处理的异常,不可以通过 catch 进行捕获。例如发生 OOM 异常,JVM 会选择线程中止。
类加载机制
Java 类加载的全过程是怎么样的?双亲委派机制及其作用
Java 的类加载机制是指 Java 虚拟机在运行时将类的字节码加载到内存中,并将其进行解析,生成对应的 Java 类对象过程。Java 虚拟机定义了一套类加载体系,它负责将 .class 文件加载到虚拟机内存中,并生成各自对应的 Class 对象。
类的生命周期是:加载、链接(验证、准备、解析)、初始化使用和卸载
当一个类被使用时,Java 虚拟机会先寻找包含该类的 ClassLoader,如果找不到就会委托给父类加载器进行加载,直到最后到达根加载器可加载的范围。如果父类加载器无法加载该类,则会尝试使用子类加载器进行加载。最终都无法记载,则会抛出 ClassNotFoundException 异常。
双亲委派机制是指加载类时会将这个任务委派给负加载器进行加载,如果加载类找不到这个类,它才会自己去加载这个类。作用是可以保证一个类的加载器在执行加载任务时会优先考虑利用已经加载的同名类,使得多个类加载器不会重复加载多个同一类,从而避免了类的冲突和版本的冲突。如果不采用双亲委派机制,不同的类加载器可能会加载出多个相同全限定名的类,这些类之间的关系容易混乱。采用双亲委派机制可以保证每个类的唯一性和一致性,有效的解决了这个问题。
在 Java 中,类加载器按照双亲委派模型分为以下三种:
- 启动类加载器(Bootstrap ClassLoader):它是虚拟机的内置加载器,负责加载 Java 的核心库
- 扩展类加载器(Extention ClassLoader): 它是加载 Java 的扩展库
- 应用程序类加载器(Application ClassLoader): 它负责加载 classpath 目录下的类
- 自定义加载器:用户可以通过继承 java.lang.ClassLoader 抽象类,定制自己的类加载器逻辑。通过实现自定义类加载器,可以实现多个自定义的类加载器之间的相互隔离,实现特定的类加载控制逻辑。
如何排查 JVM
对于正常运行的系统
- jmap:使用 jmap 查看 JVM 各个区域的使用情况
- jstack:查看线程的运行情况,比如线程是否发生死锁,线程阻塞情况
- jstat: 查看垃圾回收的情况,特别是 full gc, 如果发现 full gc 特别频繁,就要进行调优了
对于发生 OOM 的系统
一般系统发生 OOM 时,会自动 dump 运行时文件,根据这份文件去分析,找到异常的实例和异常的线程,找到具体的代码进行,最终定位到问题