前言:Object类可以接收所有引用数据类型。然而在java中,数据类型可分为基本数据类型和引用数据类型,基本数据类型该如何处理呢?
java基本类型存储在栈中,因此它们的存取速度要快于存储在堆中的对应包装类的实例对象,从java5.0(1.5)开始,java虚拟机可以完成基本类型和它们的包装类之间的自动转换。因此我们在赋值、参数传递以及数学运算的时候使用基本类型一样可以使用它们的包装类,但这并不意味着可以通过基本类型调用包装类才具有的方法。另外,所有基本类型(包括void)的包装类都使用了final修饰,因此我们无法继承它们扩展的新类,也无法重写它们的任何方法。
一、基本原理
1.1、包装类就是将基本数据类型封装到类中。将基本类型封装成类的好处在于可以定义更多的功能方法操作该数据常用的操作之一:用于基本数据类型与字符串之间的转换。
1.2、八种基本类型对应的包装类:
byte ===> Byte
short ===> Short
int ===> Integer
long ===> Long
char ===> Character
float ===> Float
double ===> Double
boolean ===> Boolean
范例:自己定义一个包装类
class IntDemo {
private int num;
public IntDemo(int num) {
this.num = num;
}
public int intValue() {
return this.num;
}
}
这个时候的IntDemo实际上就是int数据类型的包装类,利用intValue就可以实现基本数据类型变为对象的需求。
范例;IntDemo使用
public static void main(String[]args){
//子类对象向上转型
Object obj = new IntDemo(18);
IntDemo temp = (IntDemo) obj;//向下转型
System.out.println(temp.intValue());//取出里面的基本类型操作
}
小结:将基本数据类型包括包装为一个类对象的本质就是使用Object进行接收处理。
java中有8种数据类型,如果每种数据类型都按照以上方式编写,存在以下问题:
(1)开发中代码重复
(2)在进行数学计算的时候,必须利用明确的方法将包装数据取出后才可以进行运算。
为了方便开发,Java专门提供了包装类的使用,而对于包装类的使用,提供了两种类型:
(1)对象型(Object的直接子类):Boolean、Character;
(2)数值型(Number的直接子类):Byte、Double、Short、Long、Integer(int)、Float;
说明:Number类的定义:
public abstract class Number implements Serializable
在Number类里面实际定义有6种重要方法:
public abstract int intValue();
public abstract long longValue();
public abstract float floatValue();
public abstract double doubleValue();
public byte byteValue() {
return (byte)this.intValue();
}
public short shortValue() {
return (short)this.intValue();
}
二、装箱与拆箱
2.1、在包装类与基本数据类型处理之中存在有两个悬念:
(1)装箱:将基本类型数据变为包装类对象,利用每一个包装类提供的构造方法实现装箱处理。
(2)拆箱:将包装类中的基本数据类型取出,利用Number类中提供的六种方法。
范例:(以int和Integer举例)
Integer num = new Integer(55); //装箱
int data = num.intValue(); //拆箱
System.out.println(data);
2.2、以上操作是手动拆装箱,在JDK1.5以后,提供了自动拆装箱的机制,从而实现了可以直接利用包装类的对象进行各种数学计算。
范例(自动拆装箱):
//自动装箱
Integer a = 3;
//可以直接利用包装类对象操作
System.out.println(++a * 3);
这个时候仍然存在"=="和"equals"问题
public class TestDemo1 {
public static void main(String[]args){
Integer num1 = new Integer(10);
Integer num2 = new Integer(10);
System.out.println(num1 == num2);
System.out.println(num1 == new Integer(10));
System.out.println(num1.equals(num2));
System.out.println(num1.equals(new Integer(10)));
}
}
运行结果:
false
false
true
true
阿里编码规范:所有相同的包装类对象之间值的比较,全部使用equals方法比较。
2.3、陷阱:对于Integer var = ?在-128至127范围内赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,在这个区间内的Integer值可以直接使用==进行判断,但这个区间之外的所有数据。都会在堆上产生,并不会复用已有对象,推荐使用equals方法进行判断。
范例(Integer判断陷阱):
public class TestDemo1 {
public static void main(String[]args){
Integer integer1;
Integer integer2;
integer1 = new Integer(10);
integer2 = new Integer(10);
//第一次比较
System.out.println("第一次比较:"+(integer1 == integer2));
//第二次比较
System.out.println("第二次比较:"+(integer1 == 10));
//第三次比较
integer1 = 127;
integer2 = 127;
System.out.println("第三次比较:"+(integer1 == integer2));
//第四次比较
integer1 = 128;
integer2 = 128;
System.out.println("第四次比较:"+(integer1 == integer2));
}
}
运行结果如下:
第一次比较:false
第二次比较:true
第三次比较:true
第四次比较:false
分析以上例子:第一次是直接把两个不同的对象进行比较,当然是fals,第二次比较,是把Integer对象和int型10进行比较,根据自动拆装箱机制,这时候的比较就等价于10 == 10,所以是true,那么为什么后面两个会产生不同的结果?
1、首先看Integer的两种定义方式:
Integer integer1 = new Integer(10);
Integer integer2 = 10;
第一种是我们常见的创建一个对象的方法,第二个,根据java自动拆装箱机制,这时在Integer内部实际上做了如下操作:
Integer integer2 = Integer.valueOf(10);
查看Intetger 源码可以看到关于valueOf方法的定义:
public static Integer valueOf(int var0) {
return var0 >= -128 && var0 <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[var0 + 128] : new Integer(var0);
}
这里我们注意到一个IntegerCache这个类,它是Integer的一个私有内部类,声明如下:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
private IntegerCache() {
}
static {
int var0 = 127;
String var1 = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
int var2;
if (var1 != null) {
try {
var2 = Integer.parseInt(var1);
var2 = Math.max(var2, 127);
var0 = Math.min(var2, 2147483518);
} catch (NumberFormatException var4) {
;
}
}
high = var0;
cache = new Integer[high - -128 + 1];
var2 = -128;
for(int var3 = 0; var3 < cache.length; ++var3) {
cache[var3] = new Integer(var2++);
}
assert high >= 127;
}
}
这里类缓存了-128到127之间的所有整形对象,意思是当使用自动装箱的方式定义一个值在-128到127的Integer对象时,我们得到的是从缓冲区(IntegerCache)中返回的实例。
所以在进行第三次比较时,此时integer1和integer2是同一个对象,所以结果是true,第四次是因为我们顶一个的值为128,它大于127,所以Integer内部会创建新的对象返回,所以当然不可能相等。
注:如果要进行两Integer对象基于数值的比较,因为Integer实现了Compaeable接口,所以直接使用compaerTo方法比较妥当,判断的话还是使用equals方法
public class TestDemo1 {
public static void main(String[]args){
Integer integer1 = new Integer(10);
Integer integer2 = new Integer(10);
//第一次比较
if (integer1.equals(integer2)) {
System.out.println("第一次比较"+true);
} else {
System.out.println("第一次比较"+false);
}
//第二次比较
System.out.println("第二次比较"+(integer1 == 10));
//第三次比较
integer1 = 127;
integer2 = 127;
if (integer1.equals(integer2)) {
System.out.println("第三次比较"+true);
} else {
System.out.println("第三次比较"+false);
}
//第四次比较
integer1 = 128;
integer2 = 128;
if (integer1.equals(integer2)) {
System.out.println("第四次比较"+true);
} else {
System.out.println("第四次比较"+false);
}
}
}
运行结果:
第一次比较true
第二次比较true
第三次比较true
第四次比较true
三、字符串与基本数据类型转换
以后要进行各种数据的输入,一定是字符串类型接收。所以在开发中,如何将字符串变为各个数据类型,这个时候就需要包装类的支持。
1、String变为int 类型(Integer类):
public static int parseInt(String var0) throws NumberFormatException
2、String 变为double类型(Double类):
public static double parseDouble(String var0) throws NumberFormatException
3、String类变为boolean类型(Boolean类):
public static boolean parseBoolean(String var0)
范例1(将字符串变为int型):
public class TestDemo1 {
public static void main(String[]args){
String str = "123456";//String类型
int num = Integer.parseInt(str);
System.out.println(num);
}
}
运行结果:
123456
范例2(将字符串变为double):
public class TestDemo1 {
public static void main(String[]args){
String str = "123456";//String类型
double num = Double.parseDouble(str);
System.out.println(num);
}
}
运行结果:
123456.0
需要注意的是,将字符串转为数字的时候,字符串的组成有非数字,那么转换就会出现错误(NumberFormatException),以后就因为这个异常,我们要写一堆程序回避这个问题。
范例3(观察非数字错误):
public class TestDemo1 {
public static void main(String[]args){
String str = "123456zxccf";
double num = Double.parseDouble(str);
System.out.println(num);
}
}
运行结果:
Exception in thread "main" java.lang.NumberFormatException: For input string: "123456zxccf"
而字符串与boolean转换就不受此影响。
public class TestDemo1 {
public static void main(String[]args){
String str = "hello";
boolean num = Boolean.parseBoolean(str);
System.out.println(num);
}
}
运行结果:
false
那么如果现在要将基本数据类型变为字符串:
1、toString方法,先把基本数据类型装箱,再用对象的toString()方法。--- a.toString();
2、使用String类中提供valueOf()方法,这是一个静态方法,几乎可以把各种类型转换为字符串,此方法不产生垃圾。--- String.valueOf(a)
注意区别包装类提供的valeOf()方法,那个也是静态方法,返回的是包装类型。
3、任何数据类型使用了"+"连接空白字符就变为了字符串类型。--- a+" ";
范例:
public class TestDemo1 {
public static void main(String[]args){
String str = String.valueOf(123478); // String类型
System.out.println(str);
}
}
运行结果:
123478
效率比较:
方法1效率最快、其次方法2、最后方法3
原因:
1、toString()方法可以直接调用进行转换
2、String.value(a)方法底层调用了Integer.toString()方法,但是在调用前会做空判断。
3、a+" "底层使用了StringBuffer实现,先用append方法拼接,再用toString()方法获取字符串。