20、基本数据类型包装类
在Java中有一个设计的原则:“一切皆对象”,Java中的基本数据类型就安全不符合这种设计思想,因为八种基本数据类型并不是引用数据类型,所以Java为了解决这种问题,JDK1.5以后引用了八种数据类型的包装类。
八种包装类分为两大类型:
Number:Integer、Short、Long、Double、Float、Byte都是Number的子类,表示一个数字。
Object:Character、Boolean都是Object的直接子类。
基本数据类型 | 包装类 |
int | Integer |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
byte | Byte |
short | Short |
long | Long |
我们在使用的时候借助一下API会好一点,这个API前面说过,在网上可以搜到。
之后再下图查找里面输入Integer
下图可以看到,Number里都有Byte, Double, Float, Integer, Long, Short这几个子类(包装类)
装箱及拆箱操作:
将一个基本数据类型转换成包装类,那么这样的操作称为装箱操作。将一个包装类转换为一个基本数据类型,这样的操作称为拆箱操作。这样理解:装箱就是打包,拆箱就是把包里面的东西拿出来。
方法 | 描述 |
byteValue() | Byte → byte |
doubleValue() | Double → double |
floatValue() | Float → float |
intValue() | Integer → int |
longValue() | Long → long |
shortValue() | Short → short |
public class Demo1
{
public static void main(String[] args)
{
Integer i1 = new Integer(10); //装箱操作,把基本数据类型转换成包装类,把10装到了一个i1对象里,称为自动装箱
int i2 = i1.intValue();//拆箱操作,把包装类转换成基本数据类型,此时新创建了一个对象i2,称为自动装箱
System.out.println(i1+" "+i2);
}
}
/*
结果:
10 10
*/
再Java中为了便于我们使用这个基本数据类型它还给我们提供这样一个特性,包装类也可以像基本数据类型一样直接赋值(除char外)。
public class Demo1
{
public static void main(String[] args)
{
Integer i1 = new Integer(10); //装箱操作,把基本数据类型转换成包装类,把10装到了一个i1对象里,称为自动装箱
int i2 = i1.intValue();//拆箱操作,把包装类转换成基本数据类型,此时新创建了一个对象i2,称为自动装箱
Integer i3 = 20;//这种是建议方式
System.out.println(i1+" "+i2+" "+i3);
}
}
/*
结果:
10 10 20
*/
接着往下看JDK
应该是数字才对啊,注意,往字符串里输入字符是,只能是数字,不能是其他东西,输入的是数字的话,可以将其转换为数字对象。
public class Demo1
{
public static void main(String[] args)
{
Integer i1 = new Integer(10); //装箱操作,把基本数据类型转换成包装类,把10装到了一个i1对象里,称为自动装箱
int i2 = i1.intValue();//拆箱操作,把包装类转换成基本数据类型,此时新创建了一个对象i2,称为自动装箱
Integer i3 = 20;
Integer i4 = new Integer("123");//将字符转换成数字
System.out.println(i1+" "+i2+" "+i3+" "+i4);
}
}
/*
结果:
10 10 20 123
*/
其他方法的应用……还有一个,我们可以用字符串转换的方法:
转型操作:
在包装类中,可以将一个字符串转换为指定的基本数据类型,一般在输入时会使用较多。
在Integer中将String变为int数据:public static int parselnt(String s)
在Float中将String变为float数据:public static float parselnt(String s)
这种方法叫parseInt(String s),前面我们跟大家说了API文档里面我们看到了,可以把一个字符串转成一个Integer,也可以把一个字符串转换成一个int类型,注意,这两个方法是一个静态方法。我们用一下parseInt(String s)方法:
public class Demo1
{
public static void main(String[] args)
{
Integer i1 = new Integer(10); //装箱操作,把基本数据类型转换成包装类,把10装到了一个i1对象里,称为自动装箱
int i2 = i1.intValue();//拆箱操作,把包装类转换成基本数据类型,此时新创建了一个对象i2,称为自动装箱
Integer i3 = 20;
Integer i4 = new Integer("123");
String num1 = "12";
int i5 = Integer.parseInt(num1); //这样,就把String转换成了一个int类型
System.out.println(i1+" "+i2+" "+i3+" "+i4+" "+i5);
}
}
/*
结果:
10 10 20 123 12
*/
我们还有一种方法,在上面输出语句前一行加一句Integer i6 = Integer.valueOf(num1);//记住,后面返回值是什么类型,你就用什么类型去接收,这也是个静态方法。那有人就说了,我们能不能这样写呢:int i6 = Integer.valueOf(num1);其实这样是没问题的,程序自动拆箱了,但是这样会导致系统多做一部拆箱的工作。所以如果未来大家想接到的值是基本数据类型的话,建议大家还是用parseInt()方法,就不要用valueOf()方法了(parseInt()方法返回的是int,valueOf()方法返回的是Integer再去拆箱变成int,后面这个效率没有前面的高)。
其实,这上面,也有parseFloat()、parseDouble()那些方法呢,就不一一介绍了。
我们说还有一个特点,这个特点就是,用包装类的时候,我是使用new的方式好呢还是用“=”的方式好呢?刚才在代码里说了一下不知道大家注意了没,用等的方式比较好。
解释:
public class Demo1
{
public static void main(String[] args)
{
Integer x1 = new Integer(10);
Integer x2 = new Integer(10);
System.out.println(x1 == x2);
System.out.println(x1.equals(x2));
}
}
/*
结果:
false
true
*/
这里我们看到,x1与x2比较时,用==比较,比较的是两个数的内存地址,只要是新建一个内存,内存地址就不一样。后面用equals比较时,比较的是内存里面的内容,两者都等于10,所以相等。
public class Demo1
{
public static void main(String[] args)
{
Integer x3 = 10;
Integer x4 = 10;
System.out.println(x3 == x4);
System.out.println(x3.equals(x4));
}
}
/*
结果:
true
true
*/
因为上面比较是基本类型比较,这时候x3等于x4,包括里面的内容,都相等。
public class Demo1
{
public static void main(String[] args)
{
Integer x5 = 128;
Integer x6 = 128;
System.out.println(x5 == x6);
System.out.println(x5.equals(x6));
}
}
/*
结果:
false
true
*/
这个结果应该跟上面的结果一样啊,这是在那么回事呢?我们首先打开下面文件夹里的Integer.java文件
在780行,有一个静态的IntegerCache(Integer缓存)方法:
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() {}
}
上面我们可以看到,第二行low是-128,h是127,也就是说,在上面里面有一个Integer Cache[]这样一个数组,里面会存有的就是-128~127的数,超过这些数这里面就不存了,所以我们输入128的时候,结果返回的是假,接着往下看享元模式。
享元模式(Flyweight Pattern):
它使用共享对象,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似对象;它适合用于当大量对象只是重复因而导致无法令人接受的使用大量内存。通常对象中的部分状态是可以分享。常见的做法是把它们放在外部数据结构,当需要使用是再将它们传递给享元。
运用共享技术有效的支持大量细粒度的对象。
享元是什么意思我们来说一下,就是说,整数啊,Java里面它认为你是在一个字节(1B)以内的数,java认为你是经常会使用的数,所以,它用享元模式把一个字节以内的数都做了一个缓存,也就是说,当你直接赋值的时候,如果上面从这里往上数第二个代码那个10已经被赋值了(被x3赋值),那他会把这个10放在常量池里面,当x4再用到10的时候,他先从常量池里面的共享元素里面系密度地找有没有10,如果有10,就直接让x4指向10,所以x3与x4用的是同一个对象,所以第一个返回的是true;但是为什么128就不行了呢?因为128就是1B(128==1B),这时候Java就没有享元的情况了,如果把128改成127,返回的两个布尔值就都是真了