本着重新学习(看到什么复习什么)的原则,这一篇讲的是JAVA的封箱和拆箱。看了诸位大神的解释后详细的查了一些东西,记录下来,也感谢各位在网络上的分享!!!
参考链接如下:https://www.cnblogs.com/vilionzhan/p/8552067.html
https://www.cnblogs.com/xiaozhang2014/p/5347407.html
https://blog.youkuaiyun.com/tangyuan_sibal/article/details/86566687
https://www.cnblogs.com/cocoxu1992/p/10644217.html
(一)JAVA的基本数据类型及其包装器类
在总结封箱和拆箱之前先要了解JAVA的基本数据类型及其包装器类,表格如下:
数据类型 | 所占字节数 | 所占位数 | 取值范围 | 默认值 | 包装器类 | 缓存 |
---|---|---|---|---|---|---|
byte(字节) | 1 | 8 | -128 ~ 127 | 0 | Byte | -128 ~ 127 |
short(短整型) | 2 | 16 | -2^15~2^15-1 | 0 | Short | -128 ~ 127 |
int(整型) | 4 | 32 | -2^31~2^31-1 | 0 | Integer | -128 ~ 127 |
long(长整型) | 8 | 64 | -2^63~2^63-1 | 0 | Long | -128 ~ 127 |
float(浮点型) | 4 | 32 | -3.40292347E+38-3.40292347E+38 | 0.0f | Float | 无 |
double(双精度) | 8 | 64 | -1.79769313486231570E+308-1.79769313486231570E+308 | 0.0d | Double | 无 |
char(字符型) | 2 | 16 | ‘ \u0000 - u\ffff ’ | ‘\u0000 ’ | Char | 0 ~ 127 |
boolean(布尔型) | - | 1 | true/false | false | Boolean | true/false |
如下为代码,由代码可得JAVA基本数据类型的一些定值。
package com.day_1.excercise_1;
public class DataType {
public static byte byteType;
public static short shortType;
public static int intType;
public static long longType;
public static float floatType;
public static double doubleType;
public static char charType;
public static boolean booleanType;
public static void main(String[] args) {
System.out.println("byte\r\n位数:" + Byte.SIZE + ";"
+ "字节数:"+Byte.SIZE/8+";"
+ "默认值:" + byteType + ";"
+ "数据范围:" + Byte.MIN_VALUE + " ~ " + Byte.MAX_VALUE);
System.out.println("short\r\n位数:" + Short.SIZE+ ";"
+ "字节数:"+Short.SIZE/8 + ";"
+ "默认值:" + shortType + ";"
+ "数据范围:" + Short.MIN_VALUE + " ~ " + Short.MAX_VALUE);
System.out.println("int\r\n位数:" + Integer.SIZE+ ";"
+ "字节数:"+Integer.SIZE/8 + ";"
+ "默认值:" + intType + ";"
+ "数据范围:" + Integer.MIN_VALUE + " ~ " + Integer.MAX_VALUE);
System.out.println("long\r\n位数:" + Long.SIZE+ ";"
+ "字节数:"+Long.SIZE/8 + ";"
+ "默认值:" + longType + ";"
+ "数据范围:" + Long.MIN_VALUE + " ~ " + Long.MAX_VALUE);
System.out.println("float\r\n位数:" + Float.SIZE+ ";"
+ "字节数:"+Float.SIZE/8 + ";"
+ "默认值:" + floatType + ";"
+ "数据范围:" + Float.MIN_VALUE + " ~ " + Float.MAX_VALUE);
System.out.println("double\r\n位数:" + Double.SIZE+ ";"
+ "字节数:"+Double.SIZE/8 + ";"
+ "默认值:" + doubleType + ";"
+ "数据范围:" + Double.MIN_VALUE + " ~ " + Double.MAX_VALUE);
System.out.println("char\r\n位数:" + Character.SIZE+ ";"
+ "字节数:"+Character.SIZE/8 + ";"
+ "默认值:" + charType + ";"
+ "数据范围:" + Character.MIN_VALUE + " ~ "+ Character.MAX_VALUE);
System.out.println("boolean\r\n"
+ "默认值:" + booleanType + ";"
+ "数据范围:" + Byte.MIN_VALUE + " ~ " + Byte.MAX_VALUE);
}
}
下图为代码运行结果:
(二)概念解析和源码分析
1.包装类
首先看一下Integer类的说明:
java.lang.Integer
The Integer class wraps a value of the primitive type int in an object. An object of type Integer contains a single field whose type is int.
In addition, this class provides several methods for converting an int to a String and a String to an int, as well as other constants and methods useful when dealing with an int.
Implementation note: The implementations of the "bit twiddling" methods (such as highestOneBit and numberOfTrailingZeros) are based on material from Henry S. Warren, Jr.'s Hacker's Delight, (Addison Wesley, 2002).
Since:JDK1.0Author:Lee BoyntonArthur van HoffJosh BlochJoseph D. Darcy
可见“该类提供了几种将int转换为字符串和字符串转换为int的方法,以及处理int时有用的其他常量和方法。”,归纳也就是说,包装类内包含着各种基本数据类型的相关属性和方法等,除此之外将基本类型变成了对象类型。
2.自动装包与拆包
自动装包与拆包是JDK 1.5(后更名为JDK 5.0)出现的新内容,实现的就是在基本数据类型和对应的包装类之间的转换的功能。即自动装包为将基本类型自动转换为包装类,同理自动拆包为将包装类自动转换为基本类型。
3.堆内存,栈内存,方法区
堆内存:JAVA堆被是被所有线程共享的一块内存区域,在JAVA虚拟机启动时创建JAVA的堆,堆内存放的是由new创建的对象实例和数组,JAVA堆是由JAVA虚拟机的自动垃圾回收器管理。堆内存的分配方式类似于链表。
栈内存:在函数中定义的基本类型的变量和对象的引用变量都是在函数的栈内存分配。
方法区:也叫做静态区,同堆一样是被所有的线程共享的。在方法区中包含所有的class和static变量。(关于这个“class”我仔细查了一下,类的实例方法会存在方法区中,并不会随创建对象而随对象保存在堆中,即class对象依然是放在堆中,而类的方法等均存储在方法区。如果有不对的地方,麻烦一定指点,这个点我一直比较困扰,多谢!!!)
总结下来的话就是:在针对基本数据类型时,(int a = 1;)是直接在栈中分配内存的,而(Integer a = new Integer(1);)则分为两部分,Integer对象在堆内存中,而该Integer对象的引用变量是在栈内存中。相当于在堆中产生了一个Integer对象后,还在栈中定义了一个名为a的变量,并且栈中的这个变量a的取值等于该Integer对象在堆内存指向的首地址,栈中的这个变量a就变成了该Integer对象的引用变量。而在被释放的问题上,引用变量在程序运行到作用域外便被释放;对象只在没有引用变量指向时,才变成垃圾,但是仍会在短时间内占用内存,而后在某一个时间被垃圾回收器释放。
4.装箱,拆箱
由于JAVA对某些经常使用的基本数据类型采用缓存机制,当该数据存在在缓存范围内时,可以直接从缓存中获取,从而提高高JAVA程序的执行性能。但在基本数据类型中会分为两类,Integer,Short,Byte,Character,Long是有缓存范围的,Double,Float是没有缓存范围的,即每一个创建的对象都不同。来分别看一下Integer类型和Double类型的valueOf方法的具体实现。
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
/**
* Returns a {@code Double} instance representing the specified
* {@code double} value.
* If a new {@code Double} instance is not required, this method
* should generally be used in preference to the constructor
* {@link #Double(double)}, as this method is likely to yield
* significantly better space and time performance by caching
* frequently requested values.
*
* @param d a double value.
* @return a {@code Double} instance representing {@code d}.
* @since 1.5
*/
public static Double valueOf(double d) {
return new Double(d);
}
由上述代码注释和代码区别可以看出Integer类型的valueOf方法在处理时会由于该方法很可能被频繁使用,故会始终缓存范围为(-128 to 127)的数值,而Double类型的valueOf方法在处理中不会进行缓冲区的范围值判断。并且在Integer的valueOf方法中可以发现缓存范围的定义,即IntegerCache内部类。其中详细说明了缓存支持范围的自动装箱过程。
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
4.数值比较
“==”和equals()在比较时也是有区别的。
在“==”的数值比较中,如果同是包装类型(如:Integer)在比较时遵循缓存范围的数值,即数值范围内对象相同,范围外对象不同,但若该包装类型没有缓存范围(如:Double),则创建时一定是两个不同的对象,故比较结果一定不相等;如果是一个包装类型和一个基本数据类型之间比较(如:Integer和int进行加减乘除)会将该包装类型进行拆箱,故数值比较上一定相同。
在equals()的数值比较中,除了判断内容是否相同以外还要判断类型是否相同,因为该方法使用时使用的是包装类型.equals(基本数据类型)的方式,故会将该基本数据类型进行装箱,从而变成两个包装类型的数值比较,那么类型相同为true,而后便是判断数值是否相同。
而在不同类型的比较时会根据不同情况进行分析,具体分析方法包括上述所有方法,如下(Dv5 == (Iv10 + Iv11))中,Dv5是Double(包装类型),Iv10是Integer(包装类型),Iv11是int(基本数据类型),在((Iv10 + Iv11),后文中用Iv12接收结果,方便描述)中遵循包装类型和基本数据类型的加减乘除过程会先将包装类型进行拆箱后进行,而此时(Dv5 == Iv12)时,也会遵循包装类型和基本数据类型进行数据比较,故结果为true。而(Dv5.equals(Iv10 + Iv11))结果为false的原因更是一目了然,因为上述装换后,Dv5为包装类型,而Iv10和Iv11的加和结果Iv12是一个基本数据类型,故类型不相同,equals()判断固然会为false。
如下为一些代码测试,很多值都是在缓存范围值的临界线上,方便理解范围的概念:
package com.day_1.excercise_1;
public class DataSize {
public static void main(String args[]) {
Integer Iv1 = -128;
Integer Iv2 = -128;
Integer Iv3 = 128;
Integer Iv4 = 128;
System.out.println(Iv1 == Iv2); // true
System.out.println(Iv3 == Iv4); // false
Integer Iv5 = 127;
int Iv6 = 127;
int Iv7 = 1;
Integer Iv8 = 128;
int Iv9 = 128;
System.out.println(Iv5 == Iv6); // true
System.out.println(Iv4 == (Iv6 + Iv7)); // true
System.out.println(Iv5.equals(Iv6)); // true
System.out.println(Iv8.equals(Iv9)); // true
Double Dv1 = 100.0;
Double Dv2 = 100.0;
double Dv3 = 100;
double Dv4 = 100;
System.out.println(Dv1 == Dv2); // false
System.out.println(Dv3 == Dv4); // true
System.out.println(Dv1.equals(Dv3)); // true
Integer Iv10 = 100;
int Iv11 = 100;
Double Dv5 = 200d;
System.out.println(Dv5 == (Iv10 + Iv11)); // true
System.out.println(Dv5.equals(Iv10 + Iv11)); // false
}
}
如文首所提,我只是将很多我查看到的问题的解释尽量用我的理解说出来,所以大神们的文章各自都非常有价值,我只是进行了总结归纳,如果我说的不太明确,各位大神的原文链接都在文首,一定能解答你的疑惑,此外,如果有错误还请一定指教,谢谢!!!