Java基础 --- 包装类和自动拆装箱 Wrapper Class and Autoboxing
什么是包装类
为什么需要包装类and为什么需要基本数据类型
- Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,将每个基本数据类型设计一个对应的类进行代表,这种方式增强了Java面向对象的性质。把基本数据类型包装成引用类型可以解决很多问题。比如:
- 在集合类中,我们是无法将int 、double等类型放进去的,因为集合的容器要求元素是Object类型。
- 当我们想知道int取值范围的最小值,我们需要通过运算,如下面所示,但是有了包装类,我们可以直接使用Integer.MAX_VALUE即可
- 为什么需要基本数据类型
- 引用类型存储在heap上,读写速度较慢。而基本数据类型存储在stack上,读写速度的比较快
包装类和基本数据类型的转换
由primitive -> wrapper
- 以下写法已经被淘汰 --> 替换成第二张图,原因是因为value of有缓存
- 或者使用自动封箱(auto-boxing),Java自动调用ValueOf函数
由wrapper --> primitive
- 使用value函数
- 或使用自动拆箱(auto-unboxing)
Wrapper Class的缓存
- Java在第一次使用valueOf函数时,会通过静态代码块(static code block)创建一个常用值数组(ex: integer为-128到127),这样可以避免重复创建新的对象。
- 源代码分析(Integer类)
- 可以发现,只要Integer类第一次被使用到(因为创建Integer对象一定会用到valueOf函数,valueOf函数里用到了cache类,所以cache内部类会被加载),Integer的静态内部类就被加载,加载的时候会创建-128到127的Integer对象,同时创建一个数组cache来缓存这些对象。当使用valueOf()方法创建对象时,就直接返回已经缓存的对象,也就是说不会再新建对象;当使用new关键字or使用valueOf()方法创建小于-128大于127的值对象时,就会创建新对象。
各包装类的缓存范围
关于缓存:
public class ValuePassing {
public static void fun1(Integer i, Integer j) {
System.out.println("在fun1中赋值前i的地址是: " + System.identityHashCode(i));
System.out.println("在fun1中赋值后j的地址是: " + System.identityHashCode(j));
//对i和j同时赋予一样的值后(在-128-127之间),地址一样
i = 10;
j = 10;
System.out.println("-----------------------------------------------");
System.out.println("在fun1中赋值后i的地址是: " + System.identityHashCode(i));
System.out.println("在fun1中赋值后j的地址是: " + System.identityHashCode(j));
}
public static void main(String[] args) {
Integer i = 5;
Integer j = 20;
System.out.println("在main中i的地址是: " + System.identityHashCode(i));
System.out.println("在main中j的地址是: " + System.identityHashCode(j));
System.out.println("-----------------------------------------------");
fun1(i, j);
}
}
output:
在main中i的地址是: 1072591677
在main中j的地址是: 1523554304
-----------------------------------------------
在fun1中赋值前i的地址是: 1072591677
在fun1中赋值后j的地址是: 1523554304
-----------------------------------------------
在fun1中赋值后i的地址是: 1175962212
在fun1中赋值后j的地址是: 1175962212
public class ValuePassing {
public static void fun1(Integer i, Integer j) {
System.out.println("在fun1中赋值前i的地址是: " + System.identityHashCode(i));
System.out.println("在fun1中赋值后j的地址是: " + System.identityHashCode(j));
//不在-128-127之间地址则不一样
i = 1000;
j = 1000;
System.out.println("-----------------------------------------------");
System.out.println("在fun1中赋值后i的地址是: " + System.identityHashCode(i));
System.out.println("在fun1中赋值后j的地址是: " + System.identityHashCode(j));
}
public static void main(String[] args) {
Integer i = 5;
Integer j = 20;
System.out.println("在main中i的地址是: " + System.identityHashCode(i));
System.out.println("在main中j的地址是: " + System.identityHashCode(j));
System.out.println("-----------------------------------------------");
fun1(i, j);
}
}
output:
在main中i的地址是: 1072591677
在main中j的地址是: 1523554304
-----------------------------------------------
在fun1中赋值前i的地址是: 1072591677
在fun1中赋值后j的地址是: 1523554304
-----------------------------------------------
在fun1中赋值后i的地址是: 1175962212
在fun1中赋值后j的地址是: 918221580