最近因工作繁忙,到时将博客落下,实在惭愧,刚好最近整理了一些资料,看最近有空陆续上传吧
这次来讲解一下java的包装类型和引用类型,算是对上次的补充吧
一、包装类
包装类本质为了解决java基本数据类型不面向对象的不足,于是设计类时为每个基本数据类型都设计了一个对应的包装类,其各自基本数据类型对于包装类如下图:
基本类型 | 包装类型 |
byte | Byte |
int | Integer |
short | Short |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
基本类型和包装类型的区别:
(1)包装类是引用传递 而基本类型是值传递;
(2)包装类是对象,拥有方法和字段,对象的调用都是通过引用对象的地址 ;
(3)初始值不同,基本数据类型如int 的初始值为0,而包装类型Integer初始值为null;
(4)变量的值存储在堆栈里,而对象是存储在堆里,相比而言,堆栈更高效,这也是java保留基本类型的原因。
讲完区别,来讲讲为什么要使用包装类:
(1)集合不允许存放基本数据类型,所以可以使用包装类来代替基本数据类型进行操作;
(2)将一些有关这些基本数据类型的参数封装在相应的对象里,例如最大值、最小值等,以及对相关的操作方法。
二、包装类与基本数据类型的转换
包装类与基本数据类型的互相转换如下图所示:
包装类 | 包装类转基本类型 | 基本类型转包装类 |
---|---|---|
Byte | Byte.valueOf(byte) | byteInstance.byteValue() |
Short | Short.valueOf(short) | shortInstance.shortValue() |
Integer | Integer.valueOf(int) | integerInstance.intValue() |
Long | Long.valueOf(long) | longInstance.longValue() |
Float | Float.valueOf(float) | floatInstance.floatValue() |
Double | Double.valueOf(double) | doubleInstance.doubleValue() |
Character | Character.valueOf(char) | charInstance.charValue() |
Boolean | Boolean.valueOf(booleann) | booleanInstance.booleanValue() |
例:
int a = 50;
Integer b = Integer.valueOf(a);//基础数据类型转包装类
Integer c = 50;
int d = c.intValue();//包装类转基础数据类型
三、自动装箱和自动拆箱
在JDK1.5以前,我们不可以直接把基本数据类型赋值给引用数据类型 ,构建一个包装类需要通过构造器来构造包装类对象,但这样又过于臃肿,所以从JDK1.5开始就引入了自动装箱和自动拆箱,系统将自动进行基本数据类型和与之相对应的包装类型之间的转换。
例
Integer a = 50;//自动装箱,将基础数据类型打包成包装类
int b = a;//自动拆箱,将成包装类拆成基础数据类型
注意
Integer a =null;
int b = a;
System.out.println(b);//报空指针
这个在编译时不报错,但运行时会抛异常,int b = a
实际上是int b = a.intValue()
,由于a的引用值为null,在空对象上调用方法就会抛出NullPointException。
又来看一个的例子:
Integer a1 = 100;
Integer a2 = 100;
System.out.println(a1==a2);//返回true
Integer b1 = 200;
Integer b2 = 200;
System.out.println(b1==b2);//返回false
前面我们说了包装类时引用传递,通过地址来调用对象,比较的应该是地址,但第一个又为什么会返回true了?这时我们得去看一下Integer的底层了
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
我们说过Integer a = 100实际上是等于Integer a = Integer.valueOf(100),但Integer内置有常量池,从最小-128到最大127都存在常量池,如果你赋值的是在这个范围内的,他会直接从常量池读取,所以无论赋值多少个地址都是一样的,但一旦超出这个范围,它就会自己new一个Integer对象来存储,所以地址才会不同。
还有类似的是String,来看一个例子:
String str1 = "ABC";
String str2 = "ABC";
String str3 = "AB" + "C";
System.out.println(str1 == str2);//true
System.out.println(str1 == str3);//true
String str1 = "ABC" 可能创建一个或不创建对象,这取决于字符串常量池里是否有“ABC”这个字符,如果”ABC”这个字符串在字符串常量池里不存在,会在字符串常量池里创建一个创建一个String对象(“ABC”),然后str1指向这个内存地址,无论以后用这种方式创建多少个值为”ABC”的字符串对象,始终只有一个内存地址被分配,之后的都是String的拷贝,Java中称为“字符串驻留”,所有的字符串常量都会在编译之后自动地驻留。
而String str = new String(“ABC”)则至少创建一个对象,也可能两个。因为用到new关键字,肯定会在堆中创建一个str2的String对象,它的value是“ABC”。同时如果这个字符串再java String池里不存在,会在java池里创建这个String对象“ABC”。
tip:String.intern():检查字符串常量池中是否存在String并返回池里的字符串引用;若池中不存在,则将其加入池中,并返回其引用。 这样做主要是为了避免在堆中不断地创建新的字符串对象
四、equals和==的区别
“==” 符号判断的内存地址所对应的值得相等性,具体来说,基本类型判断值是否相等,引用类型判断其指向的地址是否相等。
“equals”先比较地址是否相同,在比较引用类型地址指向的对象所存储的内容是否相等。
例如
public boolean equals(Object var1) {
if (this == var1) {
return true;
} else {
if (var1 instanceof String) {
String var2 = (String)var1;
int var3 = this.value.length;
if (var3 == var2.value.length) {
char[] var4 = this.value;
char[] var5 = var2.value;
for(int var6 = 0; var3-- != 0; ++var6) {
if (var4[var6] != var5[var6]) {
return false;
}
}
return true;
}
}
return false;
}
}
上面是String的equals源码,equals是先比较是否同一个对象,再去取出各自对象存储的值来进行比较,所以在用String进行比较时,最好使用equals方法