Java基础-数据类型

基本类型

  • byte/8
  • char/16
  • short/16
  • int/32
  • float/32
  • long/64
  • double/64
  • boolean/~

boolean只有两个值:true、false,可以用1bit来存储,但是具体大小没有明确规定。JVM会在编译时期将boolean类型的数据转换为int,使用1来表示true,0表示false。JVM支持数组,但是是通过读写byte数组来实现的。

包装类型

基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。

将原始值(例如int)转换为相应包装类(Integer)的对象的过程称为自动装箱。 当原始值是:Java编译器将应用自动装箱:

  • 作为参数传递给需要相应包装类对象的方法。
  • 分配给相应包装器类的变量。
//    自动装箱
    static void autoboxing(){
        List<Integer> li = new ArrayList<>();
        for (int i = 1; i < 50; i+=2){
            li.add(i);
        }
        System.out.println("自动装箱:"+li);
    }
    /**
     * 尽管您将int值作为原始类型(而不是Integer对象)添加到li,但代码将进行编译。因为li是一个整型对象的列表,而不是一个整型值的列表,
     * 所以您可能想知道为什么Java编译器没有发出编译时错误。编译器不会产生错误,因为它从i创建了一个Integer对象,并将该对象添加到li中。
     * 因此,编译器在运行时将前面的代码转换为下面的代码
     */
    static void autoboxing1(){
        List<Integer> li = new ArrayList<>();
        for (int i = 1; i < 50; i+=2){
            li.add(Integer.valueOf(i));
        }
        System.out.println("正常转换:"+li);
    }

 

将包装器类型(Integer)的对象转换为其对应的原始(int)值称为拆箱。 当包装器类的对象为:

  • 作为参数传递给需要相应原始类型值的方法。
  • 分配给相应原始类型的变量。
//   自动拆箱
    public static int sumEven(List<Integer> li) {
        int sum = 0;
        for (Integer i: li)
            if (i % 2 == 0)
                sum += i;
        return sum;
    }
    /**
     * 由于余数(%)和一元加号(+ =)不适用于Integer对象,因此您可能想知道Java编译器为何在不发出任何错误的情况下编译该方法。
     * 编译器不会生成错误,因为它在运行时调用intValue方法将Integer转换为int:
     */
    public static int sumEven1(List<Integer> li) {
        int sum = 0;
        for (Integer i : li)
            if (i.intValue() % 2 == 0)
                sum += i.intValue();
        return sum;
    }

运行结果:

public static void main(String[] args){
//        自动拆箱
        autoboxing();
        autoboxing1();

        List<Integer> li = new ArrayList<>();
        for (int i = 0; i < 50; i+=2){
            li.add(i);  // 2,4,6,8...48
        }

//        自动装箱
        System.out.println("自动拆箱:"+sumEven(li));
        System.out.println("正常转换:"+sumEven1(li));

    }

 自动装箱和拆箱使开发人员可以编写更简洁的代码,从而使其更易于阅读。 下表列出了原始类型及其对应的包装器类,Java编译器将其用于自动装箱和拆箱:

 缓存池

new Integer(123) 与 Integer.valueOf(123) 的区别在于:

  • new Integer(123) 每次都会新建一个对象;
  • Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y);    // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k);   // true
            

valueOf() 方法源码,其实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。

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

在 Java 8中,Integer 缓存池的大小默认为 -128~127。

 private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer[] cache;
        static Integer[] archivedCache;

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    h = Math.max(parseInt(integerCacheHighPropValue), 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            // Load IntegerCache.archivedCache from archive, if possible
            VM.initializeFromArchive(IntegerCache.class);
            int size = (high - low) + 1;

            // Use the archived cache if it exists and is large enough
            if (archivedCache == null || size > archivedCache.length) {
                Integer[] c = new Integer[size];
                int j = low;
                for(int i = 0; i < c.length; i++) {
                    c[i] = new Integer(j++);
                }
                archivedCache = c;
            }
            cache = archivedCache;
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

编译器会在自动装箱过程调用 valueOf() 方法,因此多个值相同且值在缓存池范围内的 Integer 实例使用自动装箱来创建,那么就会引用相同的对象;若该值不在缓存池内,则属于不同对象。

// Integer 缓存池的大小默认为 -128~127
//不在缓存池内
Integer a = 200;
Integer b = 200;
System.out.println(a==b);   // false
Integer c = -200;
Integer d = -200;
System.out.println(c==d);  // false
// 在缓存池内
Integer e = 10;
Integer f = 10;
System.out.println(e==f);   //true

基本类型对应的缓冲池如下:

  • boolean values true and false
  • all byte values
  • short values between -128 and 127
  • int values between -128 and 127
  • char in the range \u0000 to \u007F

在使用这些基本类型对应的包装类型时,如果该数值范围在缓冲池范围内,就可以直接使用缓冲池中的对象。

在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 - 128,上界默认是 127,但是这个上界是可调的,在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax=<size> 来指定这个缓冲池的大小,该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后 IntegerCache 初始化的时候就会读取该系统属性来决定上界。

上面内容学习自:cs-notes

 

posted @ 2021-03-25 14:57  +D  阅读(37)  评论(0编辑  收藏  举报