简介
说明
本文介绍Java的基本类型知识,包括:拆箱与装箱、实例化顺序、区域问题、缓存问题、效率问题。
为什么基本类型需要包装类
- 基本数据类型方便、简单、高效,但泛型不支持、集合元素不支持
- 不符合面向对象思维
- 包装类提供很多方法,方便使用,如 Integer 类 toHexString(int i)、parseInt(String s) 方法
Long或Integer如何比较大小?
- 错误方法
- 使用==。 因为Long与Ineger都是包装类型,是对象。 而不是普通类型long与int
- 使用equals方法。因为equals方法只能比较同类型的类,例如两个都是Integer类型。
- 正确方法
- 先使用longValue()或intValue()方法来得到他们的基本类型的值然后使用==比较也是可以的。
拆箱与装箱原理
装箱就是将基本数据类型转化为包装类型,那么拆箱就是将包装类型转化为基本数据类型。
实例1:查看反汇编文件
package org.example.a; public class Demo{ public static void main(String[] args) { //自动装箱,底层其实执行了Integer a=Integer.valueOf(10); Integer a = 10; //自动拆箱,底层其实执行了int b=a.intValue(); int b = a; } }
生成并查看反汇编文件
依次执行如下命令
javac -d . org\example\a\Demo.java javap -c org.example.a.Demo
输出结果如下(没有任何手动添加内容(//xxx都是自己生成的)):
E:\work\idea_proj\test_java\src>javap -c org.example.a.Demo
Compiled from “Demo.java”
public class org.example.a.Demo {
public org.example.a.Demo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object.”<init>”:()V
4: returnpublic static void main(java.lang.String[]);
Code:
0: bipush 10
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
7: invokevirtual #3 // Method java/lang/Integer.intValue:()I
10: istore_2
11: return
}
Integer a = 10;
执行上面那句代码的时候,系统为我们执行了:Integer a = Integer.valueOf(10);
int b = a;
执行上面那句代码的时候,系统为我们执行了:int b = a.intValue();
实例:自动拆装箱
实例1:基本类型与其包装类
package org.example.a; public class Demo { public static void main(String[] args) { int int1 = 12; int int2 = 12; Integer integer1 = new Integer(12); Integer integer2 = new Integer(12); System.out.println("int1 == int2 : " + (int1 == int2)); System.out.println("int1 == integer1 : " + (int1 == integer1)); System.out.println("integer1 == integer2 : " + (integer1 == integer2)); } }
运行结果
int1 == int2 : true int1 == integer1 : true integer1 == integer2 : false
Integer是int的封装类,当Integer与int进行==比较时,Integer就会拆箱成一个int类型,所以还是相当于两个int类型进行比较,这里的Integer不管是直接赋值,还是new创建的对象,只要跟int比较就会拆箱为int类型,所以就是相等的。
实例2:自动拆箱引起的异常
首先思考,下边的程序会输出什么
package com.example.a; public class Demo { public static void main(String[] args) { Integer a = null; System.out.println(2 == a); } }
结果
Exception in thread "main" java.lang.NullPointerException at com.example.a.Demo.main(Demo.java:6)
原因:
因为是基本类型与包装类型进行比较,此时会将包装类型自动拆箱,调用a.intValue()获得其基本类型的值,但a是null,所以报空指针异常。
实例3:基本类型与Object
package org.example.a; public class Demo { public static void main(String[] args) { Object object = true; System.out.println(object); boolean b = (boolean)object; System.out.println(b); System.out.println(b == true); } }
运行结果
true true true
实例:实例化顺序
package org.example.a; public class Demo { public static void main(String[] args) { int int1 = 12; Integer integer1 = new Integer(12); Integer integer2 = new Integer(34); int int2 = 34; System.out.println("int1 == integer1 : " + (int1 == integer1)); System.out.println("int2 == integer2 : " + (int2 == integer2)); } }
执行结果(不影响)
int1 == integer1 : true int2 == integer2 : true
实例:区域问题
package org.example.a; public class Demo { public static void main(String[] args) { Integer integer1 = new Integer(12); Integer integer2 = 12; System.out.println("integer1 == integer2 : " + (integer1 == integer2)); } }
执行结果
integer1 == integer2 : false
实例:缓存问题
测试代码
package org.example.a; public class Demo{ public static void main(String[] args) { Integer a = 127; Integer b = 127; Integer c = 128; Integer d = 128; System.out.println(a == b); System.out.println(c == d); } }
执行结果
true false
原因
Integer有个缓存,原理如下。
valueOf源码
//private static class IntegerCache { // static final int low = -128; // static final int high = 127; public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
当i的值位于[-128,127]的时候,会直接返回Integer缓存数组中相应对象的引用,如果i大于127或小于-128,会重新创建一个Integer实例,并返回。
那么第一条式子a和b的值都在缓存范围内,因此他们指向同一个对象,因此返回true。c和d的值不在范围内,都是通过new创建出来的,因此不是同一个对象,返回false。
其他包装类的缓存
Byte、Short、Integer、Long、Character的valueOf()实现机制类似。
包装类 | 说明 |
Byte | 相同值的Byte比较永远返回true。因为byte取值范围就是[-128,127]。 |
Short、Integer、Long | 相同值在[-128,127]则返回true,不在则返回false |
Character | 要返回true,只需保证i <= 127。因为char最小值为0,本来就大于等于-128。 |
Float、Double | 永远返回false。因为其永远返回新创建的对象,因为一个范围内的整数是有限的,但是小数却是无限的,无法保存在缓存中。 |
Boolean | 只有两个对象,要么是true的Boolean,要么是false的Boolean,只要boolean的值相同,Boolean就相等。 |
实例:效率问题
package org.example.a; public class Demo { public static void main(String[] args) { long t1 = System.currentTimeMillis(); Long sum = 0L; for (int i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } long t2 = System.currentTimeMillis(); System.out.println(t2 - t1); } }
上述代码,结果为:6218
如果将sum改为long型,结果为:624
可见效率差别之大
请先
!