如果我们想在数组列表中存入一些整型数据,像这样子存肯定是不可以的:
var list = new ArrayList<int>();//错误!尖括号里不允许是基本类型
其实所有的基本类型都有一个与之对应的类。
int -> Integer
long -> Long
short -> Short
byte -> Byte
float - > Float
double -> Double
char -> Character
boolean -> Boolean
这些类被称为包装器
关于包装器有两点需要注意:
1.包装器类是不可变的。
也就是说一旦构造了包装器就不允许更改包装在其中的值了。对于这句话有的小伙伴可能会有困惑,比如下边这段代码
Integer a = 10;
a = 11;
System.out.println(a);
输出结果是11啊,这值不是更改了吗。但事实其实是,类似于指针,a最初指向内存中的地址A,里边有个数10,然后a又指向了新开辟出的地址B,里面存个11,而A地址中的10并没有发生改变,而是你的引用变了。
2.包装器类是final,不允许派生子类。
我们先建立个整型数组列表:
var list = new ArrayList<Integer>();
我们可以很容易的向这个数组列表中加入一个新的数据:
list.add(1);
这段代码会自动变换成:
list.add(Integer.valueOf(1));
这种变换就是自动装箱。不过呢这些都是编译器要做的,它会在生成字节码文件的时候插入必要的方法调用,虚拟机只是去执行。
相反,我们还可以将一个Integer对象赋给一个int值:
int n = list.get(i);
它将转换成:
int n = list.get(i).intValue();
甚至这些操作对于算术表达式也是适用的,比如自增,自减:
Integer a = 1;
a++;
关于这个自动装箱和拆箱也有几点需要说明:
1.比较两个包装器对象调用equals方法。
在比较两个整型数时候我们习惯用 ==运算符,同样它也可以用在包装器对象上,不过就可能会失败,因为这个运算符检测的是对象是否具有相同的内存位置。像这个例子:
Integer a = 1000;
Integer b = 1000;
System.out.println(a==b)
输出的结果就是false。不过呢自动装箱规范要求boolean,byte,char<=127,介于-128和127之间的short和int被包装在对象中。所以如果把上边的例子改一下,像这样:
Integer a = 100;
Integer b = 100;
System.out.println(a==b)
那么输出的结果就是true。很显然我们不希望有这种一会儿正确一会儿错误的结果,所以一个好的办法就是调用equals方法。
2.包装器类引用null,自动装箱可能抛异常。
Integer n = null;
System.out.println(2*n);//throws NullPointerException
3.一个条件表达式如果混用了Integer和Double,Integer会拆箱,提升为Double,再装箱。
Integer n = 1;
Double x = 2.0;
System.out.println(true?n:x)//prints 1.0
4.数字字符串转换为数值
利用Integer.parseInt();方法可以很方便的把一个字符串形式的数转换成数值
String str = "123";
int a = (int)str//天啦噜!这肯定不可以的啦!
int a = Integer.parseInt(str);//可以呦
PS:更多有趣的小方法可以去查查API
加个鸡腿吧!
参考资料:
[1]凯·S.霍斯特曼著;林琪等译.Java核心技术 卷I.[M].第十一版.北京:机械工业出版社,2019.192-195.