四、关于包装类的使用

目录

1.什么是包装类

2.包装类的特点

3. 8 大基本数据类型对应的包装类

4.包装类的继承关系

5.数值型包装类的使用

6.探究 Integer 类的静态内部类:IntegerCache 类


1.什么是包装类

对于基本数据类型 int 、double 、float 等,虽然使用起来非常的方便,但由于它们不是对象,因此无法通过向上转型获取到 Object 类提供的方法,而像 String 类却可以,只因为 String 是一个对象而不是一个类型。基本数据类型由于这样的特性,导致无法参与转型,泛型,反射等过程。为了弥补这个缺陷,java 提供了包装类。

包装类是把基本数据类型用一个类包装起来,并提供了相应基本数据类型的操作方法,这样就定义了一个包装类。

2.包装类的特点

(1)所有包装类都是使用 final 修饰的,不能被继承。其中数值型对应的包装类都是继承自 Number 类。

(2)包装类是不可变类,包装类的对象被创建后,它所包含的基本数据类型值就不能改变。

3. 8 大基本数据类型对应的包装类

基本数据类型

包装类

int

Integer

char

Character

byte

Byte

short

Short

long

Long

double

Double

float

Float

boolean

Boolean

从上表我们注意到,除了 int 和 char 类型之外,其他的包装类名均为基本类型首字母大写。

对于数值型的包装类,我们以Integer类为例进行学习。

4.包装类的继承关系

5.数值型包装类的使用

(1)装箱与拆箱

装箱就是把基本数据类型装换为包装类,拆箱就是把包装类转换成基本数据类型。

其中装箱其实就是调用了包装类的构造方法,底层实现就是通过构造方法给包装类中的属性赋值。

      

数值型的包装类有两个构造方法,其中一个构造方法参数为对应的基本数据类型,另一个构造方法参数为 String 类型。需要注意的是,第二个构造方法的参数必须为相应基本数据类型格式的字符串,否则由于格式错误无法解析,会在运行时报错。

    public static void main(String[] args) {

        //参数为int型
        Integer integer1 = new Integer(4);

        //参数为String型
        Integer integer2 = new Integer("11");//参数为String类型
        //此时若参数不是整数格式,就会报错
        //Integer integer3 = new Integer("123.45");
        //Integer integer3 = new Integer("erer122");

        System.out.println(integer1);
        System.out.println(integer2);

    }

    结果为:
    
    4
    11

 而对于拆箱,则需要调用包装类中拆箱后相应类型的 value() 方法。

    public static void main(String[] args) {

        //参数为int型
        Integer integer1 = new Integer(4);
        
        //拆箱
        //把数值转为int型
        int a = integer1.intValue();
        //把数值转为double型
        double b = integer1.doubleValue();

        System.out.println(a);
        System.out.println(b);

    }

    结果:
    
    4
    4.0

 其中 value() 方法中实现了对包装类中的成员属性 value 的基本数据类型转换。

    public int intValue() {
        return value;
    }
  
    public long longValue() {
        return (long)value;
    }
    
    public float floatValue() {
        return (float)value;
    }
    
    public double doubleValue() {
        return (double)value;
    }

(2)自动装箱与自动拆箱

在 JDK 1.5 之前,是不能把一个基本类型直接赋值给引用类型的。需要引用类型,就必须使用创建对象的方式。

在 JDK 1.5 以后,JVM 会把基本数据类型和引用数据类型默认自动进行转换,这就有了自动装箱与自动开箱。这一新特性从代码上简化了装箱与拆箱的实现步骤。

下面举例说明自动装箱与自动拆箱

        //自动装箱
        Integer a = 3;
        //装箱
        Integer a = new Integer(3);
        
        //自动拆箱
        int b = a;
        //拆箱
        int b = a.intValue();

 此处需要注意的是,对于自动装箱和装箱,他们的实现原理是不一样的。自动装箱使用了 Integer 类中的 valueOf() 方法返回了一个新的 Integer 对象;而装箱是直接创建了一个新的 Integer 对象。对于此类情况衍生出的一系列问题问题,会在 6 小节中进行深入的探究。

(3)Integer 类中的 parseInt() 方法

parseInt() 方法是一个静态的方法,其主要作用是把指定进制的字符串解析成十进制带符号整数,字符串默认为十进制;该方法在给定字符串不符合规范的情况下会抛出数字格式异常。通常情况下该方法在 Integer 类带 String 参数的构造方法中被调用,开发者也可直接使用该方法进行字符串到基本数据类型的转换。

    public static void main(String[] args) {

        //10进制(默认),可省略radix参数
        int a = Integer.parseInt("-43354",10);
        System.out.println(a);

        //16进制
        int b = Integer.parseInt("ABC4335",16);
        System.out.println(b);

        //2进制
        int c = Integer.parseInt("1001010101",2);
        System.out.println(c);

    }

    结果为:

    -43354
    180110133
    597
    //Integer 类中参数为 String 类型的构造方法
    public Integer(String s) throws NumberFormatException {
        this.value = parseInt(s, 10);
    }

6.探究 Integer 类的静态内部类:IntegerCache 类

在我们使用 Integer 类的时候,常常会遇到以下的问题:

    public static void main(String[] args) {

        Integer a1 = 100;
        Integer a2 = 100;
        System.out.println(a1 == a2);

        Integer b1 = 200;
        Integer b2 = 200;
        System.out.println(b1 == b2);

        Integer c1 = new Integer(100);
        Integer c2 = new Integer(100);
        System.out.println(c1 == c2);

    }

    结果为:

    true
    false
    false

结合前面学过的知识,对于对象的创建,每个新对象都应该有一个新的地址,因此三个结果都为 false,但为什么第一个自动装箱结果却为 true 呢?在这里就涉及到了Integer 类中的静态内部类——IntegerCache。

    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() {}
    }

从源码中我们可以看出,IntegerCache 定义了上限 high、下限 low 以及一个数组 cache[ ] ,用于存储 Integer 的缓存对象,以提高内存利用率和效率。类似的缓存机制还有8种基本数据类型中除了 Double 和 Float 外的6种。而之所以 Double 和 Float 没有缓存机制,是因为 double 、float 是浮点型的,没有经常使用到的数据,缓存效果没有其它几种类型使用效率高。

在 IntegerCache 首次被使用时会执行其中的静态代码块,low 默认是 -128,high 的值默认127(high 的值也可以通过 JVM 进行修改);然后会根据 low 与 high 的值创建一个包含区间内所有整数的 Integer 数组。

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

下面我们再来看 valueOf( ) 方法:该方法首先会判断传入的 int 值是否在 low(-128)与high(默认127)之间。若符合条件则取出返回 cache[ ] 数组中的 Integer 对象;若不符合,则返回一个值为相应整数的新的 Integer 对象。

至此,上面的问题就能够解释的通了——对于自动装箱,在调用 valueOf( ) 方法时,区间 [ - 128 , 127 ] 之间的参数会使用静态内部类 IntegerCache 中的 cache[ ] 数组返回相应的 Integer 元素,因此它的地址就是数组的基址。只有在参数超出这个区间时,才会创建一个新的 Integer 对象。

但需要注意的是,该场景只适用于自动装箱。对于普通的装箱操作,是直接调用 Integer 的构造方法创建一个新的对象。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值