深入java包装类

2021/01/26 Java基础知识 共 5379 字,约 16 分钟
闷骚的程序员

1. 包装类API

package javase.demo08.baozhaunglei;
import org.junit.Test;

// 8大基本数据类型的包装类。
// 包装和解包装/装箱和拆箱。
public class Demo01Integer {
    Integer a;
    @Test
    public void testByte() {
        byte byteNumber = 10;// byte类型的数字
        System.out.println("输出 byte 基本类型的变量:" + byteNumber);
        Byte byteNumberByte = new Byte(byteNumber);// 包装基本类型为Byte对象/装箱
        System.out.println("输出包装后的对象 byteNumberByte:" + byteNumberByte);//包装类中覆盖了toString()方法,直接输出包装类对象输出的是具体的值。
        byte byteNumberAfter = byteNumberByte.byteValue();// 解包装/拆箱
        System.out.println("输出解包装后的对象:" + byteNumberAfter);
    }
    @Test
    public void testInteger() {
        int number = 100;
        System.out.println("输出 int 基本类型变量:" + number);
        Integer numberInteger = new Integer(number);// 通过构造方法包装成Integer对象/装箱
        Integer valueofInteger = Integer.valueOf(1234);//通过valueOf()方法装箱
        System.out.println("输出包装后的对象numberInteger:" + numberInteger);
        System.out.println("输出包装后的对象valueofInteger:" + valueofInteger);
        int numberAfter = numberInteger.intValue();// 解包装成基本类型/拆箱
        System.out.println("输出解包装后的对象 numberAfter:" + numberAfter);
    }
    @Test
    public void testBoolean() {
        boolean bool = true;
        System.out.println("输出 boolean 基本类型变量:" + bool);
        Boolean boolBoolean = new Boolean(bool);
        Boolean valueBollean = Boolean.valueOf(false);
        System.out.println("输出 boolBoolean 包装后的变量:" + boolBoolean);
        System.out.println("输出 valueBollean 包装后的变量:" + valueBollean);
        boolean boolBooleanAfter = boolBoolean.booleanValue();
        System.out.println("输出解包装后的对象 boolBooleanAfter:" + boolBooleanAfter);
    }
    @Test
    public void testChar() {
        char c = 'f';
        System.out.println("输出char 基本类型变量:" + c);
        Character cChar = new Character(c);// 将基本char类型包装成Character类型
        Character vChar = Character.valueOf('J');
        System.out.println("输出 Character 包装后的对象:" + cChar);
        System.out.println("输出 Character 包装后的对象:" + vChar);
        char cCharAfter = cChar.charValue();// 解包装
        System.out.println("输出解包装后的对象 cCharAfter:" + cCharAfter);
    }
    @Test
    public void testDefaultValue() {
        //测试包装类型的默认值
        //包装类型默认值是null值啊哈哈哈哈
        System.out.println("包装类型的默认值:" + a);
    }
}

2. 自动装箱和自动拆箱

JDK1.5之后实现了自动装箱和自动拆箱。

package javase.demo08.baozhaunglei;
import org.junit.Test;

// jdk1.5之后自动装箱和拆箱了,验证自动装箱操作和自动拆箱操作。
// 实际上对于基本类型的包装类,java基于一切皆对象的原则实现了包装类型,而为了方便的进行装箱和拆箱,jdk1.5之后实现了自动装箱和自动拆箱。
// 8大包装类型就是引用类型,是Object类的子类,而基本类型不是。所以基本类型无法调用方法。
public class Demo02Integer {
    @Test
    public void testInteger() {
        Integer a = 1;// 自动装箱
        Integer b = new Integer(2);// 手动装箱
        int aa = a;// 自动拆箱为int型
        int bb = b;// 自动拆箱为int型
        System.out.println(aa);
        System.out.println(bb);
        System.out.println(aa);
        System.out.println(b.toString());
        System.out.println(new Object().toString());
        System.out.println(new Person("zhaoerhu"));
        System.out.println(new Person("zhaoerhu").toString());
    }
    class Person {
        private String name;
        public Person(String name) {
            this.setName(name);
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "name = " + this.name;
        }
    }
}

3. 探究String和8大基本包装类不可变的本质

package javase.demo09.init;
import org.junit.Test;

// 探究String和8大基本包装类不可变的本质(Java中不可变的本质来源就是常量,即通过final修饰导致的对象的不可变性):
// 8大基本数据类型的包装类和String有相似点,他们的类和成员的声明都是final的,所以,8大包装类和String的内容一旦声明都不能修改,看似的修改实际上每次都会实例化一个新的对象来存储。
// java中对于引用类型,某些类设计成了不可变类(所谓不可变类指的是当前类的成员被设计成final类型的),不可变类的对象就是不可变对象。
// 最著名的:String类和8大基本包装类型是不可变的。
// 不可变性是由于JDK团队设计的,并不是语法限制;语法上来说,只要是变量,它的内容就可以被修改;只要是常量,它的内容就不可以被修改。
// 实际上String和基本类型包装类就是使用的final实现的不可变,即这些类中的成员就是final修饰的常量。
// 注意:不可变性指的是对象中保存的实体内容,即我们常说的不可变性指的是引用类型的对象实体,而不是指引用地址本身。
public class Demo05KeBian {
    
    // 测试基本类型的可变性:任何变量都可以被重置,注意此时的可变指的是重置变量本身,如果是基本类型指的是重置基本类型本身,如果是引用类型指的是重置引用本身;而不是对象实体。
    @Test
    public void testJiBenLeiXing() {
        // 为a变量分配空间,并初始化为10
        int a = 10;
        System.out.println("a重置之前的值:" + a);
        
        // 改变a空间的值为100,即重置a的值,之前的10等待被回收
        a = 100;
        System.out.println("a重置之后的值:" + a);
        // 结论,所有基本类型的值都是可以改变的。
    }
    
    // 测试引用类型的可变性
    @Test
    public void testYinYongLeiXing() {
        // 初始化per变量的值,为new Person11()返回的内存地址。
        Person05 per = new Person05();
        
        // 设置堆里面对象实体的内容
        per.setName("ZHAOERHU");
        per.setAge(22);
        System.out.println("per重置前:" + per);
        
        // 改变per空间的值,让它执指向新的内存地址
        per = new Person05();
        System.out.println("per重置后:" + per);
        // 结论:引用类型的值(地址)是可变的。
    }
    
    // 引出对象的可变性
    // 结论:这里正式开始了面向对象,new操作实际上就是实例化操作,new操作是在堆内存开辟空间保存对象实体的;
    // 而对象名实际上保存的是指向对象实体的堆内存地址。注意java中所有对象实体都保存在堆内存,引用地址可能保存在栈中/方法区中/和堆中。
    // Person per = new Person();per和所有基本类型没有任何区别,保存的就是地址值;当认识new之后,就有区别了,new Person();才是对象实体。
    // 所以,我们常说的对象不可变,一定指的是对象本身,即对象实体,而不是引用变量。即不可变指的是new Person();而不是per。
    @Test
    public void testBuKeBianXing() {
        // 初始化per变量的值-为new Person11()返回的内存地址。
        Person05 per = new Person05();
        // 设置堆里面对象实体的内容
        per.setName("ZHAOERHU");
        per.setAge(22);
        System.out.println("per内容重置前:" + per);
        // 如果说Person05被设计称不可变类,则要想改变上面堆内存中的成员,只能重新开辟一个新的对象来保存这个新值。
        // 现在因为引用类型其内容本身就是可变的,所以直接修改其内容的值。
        // 总结:对于普通的引用类型其对象实体是可以被重置的(除非这个类是不可变类)。
        per.setName("Daisy");
        per.setAge(18);
        System.out.println("per内容重置后:" + per);
    }

    // 测试8大包装类和String的不可变性
    // 总结:不可变性永远是对对象实体而言的,变量本身的可变和对象实体的可变不是一回事。
    // 变量本身的可变是变量本身存储的值或者地址可变不可变, 对象的可变指的是对象里面的内容可变不可变。
    // 我们常说的可变指的就是对象实体中的内容可变不可变。
    @Test
    public void testInteger() {
        // 在堆内存创建一个Integer对象,并初始化其里面的值为8
        Integer integer = new Integer(8);
        System.out.println("包装类型b重置之前的值:" + integer);
        System.out.println("integer对象的地址:" + System.identityHashCode(integer));
        // 很明显,对于包装类型,是不能直接更改它原来空间的值的,一个是因为Integer中的value是final的,二一个是因为Integer没有提供setter方法。
        // 包装类和String一样,所谓的重置和基本类型一样,真的是重置了一个新的内存空间,指向了新的对象,重新开辟了堆空间,而不像普通引用可以修改堆内容。
        integer = new Integer(198);
        System.out.println("包装类型b重置之后的值:" + integer);
        System.out.println("integer对象的地址:" + System.identityHashCode(integer));
        // 结论:包装类型和String被JVM设计成不可变的,每次的改变都是断开-连接的反复过程以求重新开辟空间,可以看出integer变量本身被重置为新的内存地址了。
    }
}
class Person05 {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String toString() {
        return "name = " + this.getName() + ";age = " + this.getAge();
    }
}

文档信息

Search

    Table of Contents