1.Java的类型系统
1.1 Java的类型系统由两部分组成:基本类型和引用类型(非基本类型)。
基本类型:基本类型就是那些最常用的类型,例如:boolean/char/byte/short/int/long/float/double,这些类型有个特点,就是变量直接存储值。举个栗子:int num=100;,变量num中存储的值就是100。
非基本类型:除了基本类型之外的都是非基本类型。非基本类型有个显著特点就是初始化的时候一般需要使用new来创建一个对象,变量存储的是指向对象的引用。
非基本类型跟基本类型的本质区别是非基本类型变量存储的不是值,而是引用。
1.2 基本类型和非基本类型的内存存储位置:
2. int 和 Integer 是什么?
每个基本类型都有一个对应的引用类型,称为装箱基本类型。在Java 5 中,引入了自动装箱和自动拆箱功能,Java可以根据上下文,自动进行转换,极大地简化了相关编程。但是自动装箱/自动拆箱模糊了但并没有完全抹去基本类型和装箱基本类型之间的区别,这两种类型是有真正差别的。
3. int 和 Integer 的区别
如下代码段,i 中存放的就是10,而 in 中存放的是 new 出来的对象在堆中的地址。
int i = 10;
Integer in = new Integer(10);
4. 理一理思路
上面写了一些int和Integer的概念,但好像对理解这个问题也并没有什么直接的影响,总感觉不痛不痒,说的东西大家都知道,但没有进一步思考,int 和 Integer 的具体使用场合。下面理一理思路:
首先我们知道在编写程序的过程中一定会存在大量的计算操作,那么计算操作直接用基本类型就好了啊。从两个方面考虑:
所以如果需要做计算,就使用基本类型。
“原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建爱你10W个Java对象的10W个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。“
既然如此,那为什么要有包装类呢?
(泛型后面专门写)
于是JAVA设计者的初衷估计是这样的:如果开发者要做计算,就应该使用primitive value如果开发者要处理业务问题,就应该使用object,采用Generic机制;反正JAVA有auto-boxing/unboxing机制,对开发者来讲也不需要注意什么。然后为了弥补object计算能力的不足,还设计了static valueOf()方法提供缓存机制,算是一个弥补。
基本了解和概念参考这篇:https://blog.csdn.net/weixin_40739833/article/details/80093527
语法糖。后面再写。
下面整理一下《Effective Java》第49条的内容:基本类型优先于装箱基本类型
基本类型和装箱基本类型有三个主要区别:
Comparator<Integer> naturalOrder = new Comparator<Integer>() {
public int compare(Integer first, Integer second) {
return first < second ? -1: (first == second ? 0: 1);
}
};
对上面的比较器如果这样使用:
System.out.println(naturalOrder.compare(new Integer(42),new Integer(42)));
则会输出1,标明第一个Integer的值大于第二个。
1. 对Integer类的总览
Integer 主要提供方法:
String toXXXString(int)
int parseInt(String, int)
Integer.valueOf()
hashCode(int)
仅仅返回value值,说明值相同hashCode()相同equals(Object obj)
比较vlaue是否相等Integer.getInteger(String)
Integer decode(String nm)
大多数都是静态方法。
Integer满足Java语言中不可变类型的要求:
2. 变量
首先可以看到,Integer类型里,所有的变量都被final修饰。并且除了value是成员变量,其余都是类变量(static修饰)。
value 是一个 int 类型的变量,也就是 Integer 所包装的值。
该变量被 private final 修饰,即无法被访问且经过构造函数赋值后无法被改变。
// Integer 的值
private final int value;
3. 构造方法
Integer对象有两种构造方式:
Integer i = new Integer(10);
Integer j = 10;
// 构造函数
public Integer(int value) {
this.value = value;
}
// 构造函数
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
使用Integer.valueOf()函数进行自动装箱。这个函数的逻辑如下:
// 返回表示指定的{@code int}值的{@code Integer}实例。
// 如果不需要新的{@code Integer}实例,通常应该优先使用此方法,而不是构造函数{@link #Integer(int)}。
// 因为此方法可能通过频繁缓存产生明显更好的空间和时间性能 要求的价值。
// 此方法将始终缓存-128到127(包括端点)范围内的值,并可以缓存此范围之外的其他值。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
4. 缓存设计
其中 IntegerCache 是 Integer 类的一个私有静态内部类,其中关键部分是static final Integer cache[];
,即它内部保存了Integer类型的数组,用以缓存值在 IntegerCache.low ~ IntegerCache.high 之间的Integer对象。
为什么设计这个缓存?
实践发现,大部分数据操作都是集中在有限的、较小的数值范围。所以在Java 5 中增加了静态工厂方法 valueOf(),在调用它的时候利用缓存机制,这样不用反复new 值相同的Integer对象,减少了内存占用。
个人认为可以用缓存的原因在于,Integer 的设计使得其内部包装的 value 在赋值之后是不可被改变的,所以只要 value 相同,这个Integer对象的状态就是相同的,没必要构建多个具有相同 value 的 Integer 对象。
// 根据JLS的要求,缓存以支持-128和127(包括)之间的值的自动装箱。
// 缓存在首次使用时初始化。
// 缓存的大小可以由{@code -XX:AutoBoxCacheMax = }选项控制。
// 在VM初始化期间,可以设置java.lang.Integer.IntegerCache.high属性并将其保存在私有系统属性sun.misc.VM类。
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() {}
}
除了 Integer 有缓存,其他包装类也有:
5. 继承关系
Integer 类继承了 Number 类,实现了 Comparable 接口。
public final class Integer extends Number implements Comparable<Integer>
Number 是一个抽象类,主要表示基本类型之间的转换。
public abstract class Number implements java.io.Serializable {
public abstract int intValue();
public abstract long longValue();
public abstract float floatValue();
public abstract double doubleValue();
public byte byteValue() {
return (byte)intValue();
}
public short shortValue() {
return (short)intValue();
}
private static final long serialVersionUID = -8742448824652078965L;
}
Integer 类中对 Nubmer 类方法的实现,涉及类型转换。
记得《深入理解JVM》里有相关内容,稍后再整理 。
public byte byteValue() {
return (byte)value;
}
public short shortValue() {
return (short)value;
}
public int intValue() {
return value;
}
public long longValue() {
return (long)value;
}
public float floatValue() {
return (float)value;
}
public double doubleValue() {
return (double)value;
}
其中自动拆箱被替换成intValue()方法。
6. equals(Object)方法
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
7. int和Integer的比较
这部分内容全部来自:https://www.cnblogs.com/guodongdidi/p/6953217.html
1、Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
2、Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
3、非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
4、对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false。
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
对于第4条的原因:
java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100);而java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了。
补一个《深入理解JVM》里的例子:
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
System.out.println(c==d); // true
System.out.println(e==f); // false
System.out.println(c==(a+b)); // true
System.out.println(c.equals(a+b)); // true
System.out.println(g==(a+b)); // true
System.out.println(g.equals(a+b)); // false
简单解释一下:
对 e == f
:比较Integer对象(即指向的地址),自动装箱时值超过默认缓存范围,new出来不一样。所以为false。
对 c == (a+b)
:a+b 先用基本类型做运算,按照上面第2条,c 自动拆箱了,两边比较值的大小是否相等。相等则true。
对 c.equals(a+b)
:a+b 先用基本类型做运算,然后自动装箱,由于值在缓存范围,所以都指向缓存。所以为true。
对 g==(a+b)
:比较的是值吧。
对 g.equals(a+b)
:have no idea
参考:
Java的基本类型存在哪里:https://blog.csdn.net/guchenjun789/article/details/82903775
https://blog.csdn.net/yang_154116/article/details/81227073
《深入理解JVM》Java语言中的线程安全
《Effective Java》
int和Integer的比较:https://www.cnblogs.com/guodongdidi/p/6953217.html