阅读到
Java 代理模式详解 | JavaGuide(Java面试 + 学习指南)
toString
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
hashCode() 有什么用?
hashCode() 的作用是获取哈希码(int 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。
⚠️ 注意:该方法在 Oracle OpenJDK8 中默认是 "使用线程局部状态来实现 Marsaglia's xor-shift 随机数生成", 并不是 "地址" 或者 "地址转换而来", 不同 JDK/VM 可能不同在 Oracle OpenJDK8 中有六种生成方式 (其中第五种是返回地址), 通过添加 VM 参数: -XX:hashCode=4 启用第五种。参考源码:
- hg.openjdk.org/jdk8u/jdk8u… (1127行)
- hg.openjdk.org/jdk8u/jdk8u… (537行开始)
为什么重写 equals() 时必须重写 hashCode() 方法?
因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。思考:重写 equals() 时没有重写 hashCode() 方法的话,使用 HashMap 可能会出现什么问题。
总结:
equals方法判断两个对象是相等的,那这两个对象的hashCode值也要相等。- 两个对象有相同的
hashCode值,他们也不一定是相等的(哈希碰撞)。 hashCode只在HashSet、Hashtable、HashMap等等这些本质是散列表的数据结构中才会用到,如果一个类不会创建这些数据结构,那么它的hashCode其实是没有用的。
String && StringBuilder && StringBuffer
- String是线程安全的(不可变性),但每次变都要重新生成一个新的对象,性能最差
- StringBuiler非线程安全,性能略好于StringBuffer(10%~15%)
- StringBuffer线程安全
java不支持操作符重载,但“+”和“+=”是两个专门为String重载过的操作符
- 字符串的“+”底层是通过StringBuilder实现的
字符串常量池
- 字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
字符串拼接
- 字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化。例如
"str" + "ing"拼接得到的"string"会被放进字符串常量池,被成为“常量折叠”。 - 被
final修饰的String对象,会被当做常量处理,例如final String s = "str",s会被当做"str"常量来处理,可以被“常量折叠”。
异常
- Throwable
- Exception
- Checked Excepption - 必须处理
- 必须要cache或用throws关键字声明,否则编译器会报错
- 除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常
- 常见的RuntimeException:
- NullPointerException
- IllegalArgumentException
- ArrayIndexOutOfBoundsException
- Unchecked Exception - 可以不处理
- Checked Excepption - 必须处理
- Error - 程序无法处理
- Exception
- 当在
try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。 - 不要在 finally 语句块中使用 return! 当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。
- finally 中的代码在以下场景不会执行
- System.exit(1); // 终止当前正在运行的Java虚拟机
- 程序所有线程死亡
- 关闭CPU
- try-with-resources
- 适用范围(资源的定义): 任何实现 java.lang.AutoCloseable或者 java.io.Closeable 的对象
- 关闭资源和 finally 块的执行顺序: 在 try-with-resources 语句中,任何 catch 或 finally 块在声明的资源关闭后运行
api(Application Programming Interface)和spi(service provider interface)
- api的接口由实现方提供
- spi的接口由调用方提供
序列化与反序列化
- 序列化:将数据结构或对象转换成二进制字节流的过程
- 反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
transient关键字
- 对于不想进行序列化的变量,使用 transient 关键字修饰。 对FASTJSON也生效。
I/O
- I/O 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。
- Java I/O 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
- InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
参数传递
- java只有值传递
- 没有引用传递,在传递引用类型的参数时,其实传的是实参的地址,还是一个值
上界通配符 && 下界通配符
- 假设
Class Apple extends Fruit extends Food - 上界通配符:
<? extends T>ArrayList<? extends Fruit>: 表示这个ArrayList可能是ArrayList<Fruit>或ArrayList<Apple>- 上界不能
set或add: 例如你想往ArrayList<? extends Fruit>中add一个Apple,因为ArrayList<? extends Fruit>可能是ArrayList<Fruit>,所以是不行的。 - 上界可以
get: 因为ArrayList<Fruit>或ArrayList<Apple>中的元素都可以用Fruit接收
- 下界通配符:
<? super T>ArrayList<? super Fruit>: 表示这个ArrayList可能是ArrayList<Fruit>或ArrayList<Food>- 下界可以
set或add:ArrayList<Fruit>或ArrayList<Food>都可以add一个Fruit - 下界不可
get:Fruit没办法接收ArrayList<Food>的元素
- PECS(Producer Extends Consumer Super)原则
- 频繁往外读取内容的,适合用上界
extends。 - 经常往里插入的,适合用下界
super。
- 频繁往外读取内容的,适合用上界