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