泛型提供了编译期间类型安全检测机制,该机制允许程序员在编译期间检测到非法的类型。
Java 的泛型是伪泛型,在编译后的字节码,所有的泛型信息都会被擦掉。但是为了反编译时识别泛型 LocalVariableTable 中的 Signature 会保留泛型信息。
Listl1 = new ArrayList<>(); List l2 = l1; l2.add('a');
上述代码编译后再反编译可得下面的代码
Listl1 = new ArrayList(); l1.add('a');
扩展阅读
深入探索Java泛型的本质 | 泛型
== 和 equals基本数据类型:== 比值
引用数据类型:== 比地址
对于没有重写的类,其 equals 继承自 Object,而 Object 的 equals 用的是 ==
hashCode 和 equals对于 HashMap、HashSet 这类容器而言,需要先使用 hashCode 判断在容器中是否存在 hashCode 相同的元素,如果存在再使用 equals 比较是否是相同元素。
hashCode 相同,可能是因为 hash 碰撞导致的。所以 equals 判断相等的两个对象,其 hashCode 也相等。而 hashCode 相等的对象,equals 判断未必会相等。
hashCode 的作用是降低所需要比较的成本。如果不使用基于 Hash 的容器,无需重写 hashCode。
扩展阅读
Java hashCode() 和 equals()的若干问题解答
包装类常量池Byte、Short、Integer、Long 常量池中缓存了 − 128 ~ 127 -128 ~ 127 −128~127 ,而 Character 缓存了 0 ~ 127 0~127 0~127。
Boolean、Float、Double 无常量池。
自动装拆箱装箱:将基本类型用它们对应的引用类型包装起来。调用包装类的 valueOf 方法拆箱:将包装类型转换为基本数据类型。调用了 xxxValue 方法
频繁的自动装拆箱会严重影响性能。
构造方法能否被重写(override)构造方法不能重写。因为重写必须发生在继承的基础上,而构造方法又无法被继承,所以不能重写。
虽然构造方法不能重写,但可以重载。
String、Stirng Builder 和 String Buffer被 final 修饰的方法同理,还有被 private 修饰的方法会隐式加上 final。
String 不可变性
String 不可变指的是对 String 二次赋值不是在原内存地址上修改数据,而是重新指向一个新对象。
- 底层字符数组是 final,导致数组引用不可变,但数组元素是可变的。底层字符数组是 private,也没提供暴露出去修改的方法。所以就杜绝了数组元素可变的情况。上述两点足以保证 String 不可变,但是仍然在类上加了 final 完全杜绝被子类破坏。
不可变性带来的好处
- 多线程安全:在多线程环境下,只有写 *** 作才会引起安全问题,然而 String 是不可写的所以在多线程下是安全的。字符串常量池:字符串不可变,所以可以利用常量池公用一个串,从而节省内存空间。
扩展:反射改变 Stirng 底层字符数组
- 所有引用相同字符串常量池的 String,都会发生变化String 的 hashCode 只会计算一次,所以不会出现改变前后 hashCode 对不上的问题。
不可变性带来的弊端
由于 String 不可变,每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象,开销较大。
从而引入了 StringBuilder 和 StringBuffer 来解决频繁修改字符串时带来的开销问题。StringBuilder 是线程不安全的,StringBuffer 是线程安全的。
综上所述:
- 对字符串修改不频繁,使用 String对字符串修改频繁且在单线程环境下,使用 StirngBuilder对字符串修改频繁且在多线程环境下,使用 StringBuffer
扩展阅读
如何理解 String 类型值的不可变? - 知乎提问
反射能否改变 final 字段final 修饰的字段代表该字段引用不能改变,但引用对象本体可以改变,所以 final 能够被反射改变。
但对于基本数据类型和 String 来说,在编译时会做内联优化,所以从结果上来说就失效了。
public class A { final String s = "a"; final int i = 1; public String getS() { return s; } public int getI() { return i; } }
编译后再反编译可得
public class A { final String s = "a"; final int i = 1; public A() { } public String getS() { return "a"; } public int getI() { return 1; } }深拷贝、潜拷贝、引用拷贝
深拷贝:堆内新创建的对象里的所有字段都是新创建的。
潜拷贝:堆内新创建的对象里的所有字段都是引用旧对象的字段。
引用拷贝:压根没在堆内创建新对象,而是直接引用了老对象。
这里所说的字段是引用类型
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)