| 对比维度 | JIT(即时编译) | AOT(提前编译) |
|---|---|---|
| 编译时机 | 运行时编译 | 运行前编译 |
| 启动速度 | 较慢(需要预热) | 快(无需预热) |
| 峰值性能 | 更高(运行时优化) | 较低(缺少运行时信息) |
| 内存占用 | 较高 | 较低 |
| 打包体积 | 较小 | 较大(包含机器码) |
| 动态特性支持 | 完全支持 | 受限(反射、动态代理等) |
| 适用场景 | 长时间运行的服务 | 云原生、Serverless、CLI工具 |
AOT主要优势在于启动时间,内存占用,JIT则具有更高的极限处理能力,可以降低请求的最大延迟。
AOT因为其提前编译的特性,无法使用比如Spring,ASM等技术。当需要支持类似的动态特性时,使用JIT及时编译器。
基本数据类型存放在栈中是一个常见的误区。 基本数据类型的存储位置取决于它们的作用域和声明方式。如果它们是局部变量,那么它们会存放在栈中;如果它们是成员变量,那么它们会存放在堆/方法区/元空间中。
计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。
BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。
静态变量也就是被 static 关键字修饰的变量。它可以被类的所有实例共享,无论一个类创建了多少个对象,它们都共享同一份静态变量。也就是说,静态变量只会被分配一次内存,即使创建多个对象,这样可以节省内存。
/**
* native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。
*/
public final native Class<?> getClass()
/**
* native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。
*/
public native int hashCode()
/**
* 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。
*/
public boolean equals(Object obj)
/**
* native 方法,用于创建并返回当前对象的一份拷贝。
*/
protected native Object clone() throws CloneNotSupportedException
/**
* 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。
*/
public String toString()
/**
* native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
*/
public final native void notify()
/**
* native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
*/
public final native void notifyAll()
/**
* native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。
*/
public final native void wait(long timeout) throws InterruptedException
/**
* 多了 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 纳秒。。
*/
public final void wait(long timeout, int nanos) throws InterruptedException
/**
* 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
*/
public final void wait() throws InterruptedException
/**
* 实例被垃圾回收器回收的时候触发的操作
*/
protected void finalize() throws Throwable { }
hashcode()用于获取hash码,这个码用于确定该对象在hash表中的索引位置。
当把对象加入HashSet中,会先调用hashcode()获取hash码,再进行一次简单的转换,决定这个对象应该存在底层的哪个桶(bucket)里。
如果是空桶则直接插入,反之则在这个桶中的链表或红黑树中逐个比较:
因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals() 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals() 方法判断是相等的两个对象,hashCode 值却不相等。
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,而使用StringBuffer 与 StringBuilder时,会对对象本身进行操作,而不是生成新的对象并改变对象引用。
这两者都继承自AbstractStringBuilder类,StringBuffer对方法和调用的方法都加了同步锁,所以线程安全,StringBuilder没有对方法加同步锁,所以非线程安全。
两者的性能差异主要来源于线程安全机制:
// 1.在字符串常量池中查询字符串对象 "ab",如果没有则创建"ab"并放入字符串常量池
// 2.将字符串对象 "ab" 的引用赋值给 aa
String aa = "ab";
// 直接返回字符串常量池中字符串对象 "ab",赋值给引用 bb
String bb = "ab";
System.out.println(aa==bb); // true
Java中所有异常的父类,java.lang.Throwable ,有两个重要的子类:
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 。常见的受检查异常有:IO 相关的异常、ClassNotFoundException、SQLException 。
Unchecked Exception 即 不受检查异常 ,Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。RuntimeException 及其子类都统称为非受检查异常,常见的有:
// 此处的资源必须实现java.lang.AutoCloseable 或者java.io.Closeable,多个资源中间用分号分割
try(Scanner scanner = new Scanner(new File("test.txt"))){
} catch(){
}
| 特性 | 传统 try-catch-finally | try-with-resources |
|---|---|---|
| 代码量 | 冗长 | 简洁 |
| 资源关闭 | 手动,易遗漏 | 自动,编译期保证 |
| 异常处理 | 关闭异常可能覆盖主异常 | 关闭异常被抑制,主异常保留 |
| 可读性 | 较差(关闭逻辑分散) | 高(资源管理集中) |

List
Object[]数组Object[]数组Set
Queue
Map
对于插入:
对于删除: