Java-128陷阱、抽象类和接口的区别、为什么 hashCode()需要和equals()一起重写、封装继承多态
128陷阱
Integer a = 100;
Integer b = 100;
System.out.println(a==b); //true
Integer c= 1000;
Integer d= 1000;
System.out.println(c==d);//false
int e = 1000;
System.out.println(c==e);//true
分析以上代码运行的结果
源码:
Integer a=128;
编译器执行的是:Integer a=Integer.valueOf(128); 也就是调用了valueof()方法,进行了自动装箱。
Integer 的 valueOf 方法当中,存储着一个 cache 数组,该数组相当于一个缓存,范围在 -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);
}
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() {}
}
128陷阱:Integer 数据类型使用“==”比较时,
- 如果传入的int值在cache缓存-128~127的范围中,则直接返回预先创建好的对象,如果不在范围中,则创建一个新的Integer对象返回。
- 如果传入value数值在-128~127范围内,那么所有在这个范围内创建的数值相同的Integer变量,实际都指向同一个对象,因此地址也相同,所以用“==”比较的结果为true。
- 如果传入value数值不在cache范围内,那么每次被创建的对象都是一个新的对象,即通过new关键字由JVM分配新地址。因此地址不相同,所以用“==”比较的结果为false。
抽象类和接口的区别
两者的特点:
-
抽象类用于描述类的共同特性和行为,可以有成员变量、构造方法和具体方法。适用于有明显继承关系的场景。
-
接口用于定义行为规范,可以多实现,只能有常量和抽象方法(Java 8 以后可以有默认方法和静态方法)。适用于定义类的能力或功能。
抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。
两者的区别:
-
实现方式:实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
-
方法方式:接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
-
访问修饰符:接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。
-
变量:抽象类可以包含实例变量和静态变量,而接口只能包含常量(即静态常量)。
-
访问类型:抽象类中抽象方法的访问类型可以是public,protected,但接口中抽象方法的访问类型只能是public,并且默认为public abstract(省略则自动默认补全)。
-
抽象类可以有构造方法,接口中不能有构造方法。 抽象类中可以有静态方法,接口中不能有静态方法。 抽象类中可以有普通方法,接口中所有方法都必须是抽象的。 抽象类中可以有成员变量,接口中没有成员变量。
为什么 hashCode()
需要和 equals()
一起重写?
-
保证单一原则:equals相同的两个对象的hashcode必须相同。如果重写了equals而没有重写hashcode,会出现equals相同hashcode不相同这个现象。
-
在无序集合中(如Set),使用hashcode来计算key应存储在hash表的索引,如果重写了equals而没有重写hashcode,会出现两个完全相同的对象。因为hashcode不同,计算出的索引不同,那么这些集合就会混乱。
-
提高效率,当比较两个对象是否相同时,先比较hashcode是否相同,如果hashcode不相同肯定不是一个对象,如果hashcode相同再调用equals来进行比较,减少了比较次数从而提高效率
封装、继承、多态
Java面向对象的三大特性包括:封装、继承、多态:
-
封装:封装是指将对象的属性(数据)和行为(方法)结合在一起,对外隐藏对象的内部细节,仅通过对象提供的接口与外界交互。封装的目的是增强安全性和简化编程,使得对象更加独立。
-
继承:继承是一种可以使得子类自动共享父类数据结构和方法的机制。它是代码复用的重要手段,通过继承可以建立类与类之间的层次关系,使得结构更加清晰。
-
多态:多态是指允许不同类的对象对同一消息作出响应。即同一个接口,使用不同的实例而执行不同操作。多态性可以分为编译时多态(重载)和运行时多态(重写)。它使得程序具有良好的灵活性和扩展性。