一、Java基础模块
1、Java为什么不支持多继承?
在Java语言中,不支持多继承的主要原因是为了简化语言设计和避免潜在的问题(如:菱形继承),同时又因为在实际工作中,确实很少用到多继承,所以在Java语言中,并不支持多继承。
具体来说,Java不支持多继承的原因又有以下几个:
a、避免菱形继承问题:菱形继承问题也叫钻石继承,指的是当一个类从两个不同的父类继承相同的方法,而再次被子类继承时会导致调用该方法的二义性,这会造成设计上和编译上的困惑和复杂性。
b、简化编程语言:Java的设计目的之一是简化语言,使其易于学习和使用。多继承在类的设计和使用上增加了复杂性,包括方法解析的问题、命名冲突、继承混乱等。
c、避免多重继承的层次膨胀:多继承可能导致继承层次的膨胀,如果一个类继承多个父类,再将该类作为基础类,避免子类再继承该类,会造成继承层次的复杂和混乱。
扩展:
如果想要在Java中实现“多继承”可以使用以下方案:
①实现多接口:Java语言中是支持类实现多个接口的,所以我们可以使用一个类实现多个接口,从而获得多个接口定义的行为。通过接口可以实现类似多继承的效果,同时避免了多继承可能引发的冲突和复杂性问题。
②组合多个类:使用组合类的方式,通过将多个类作为成员变量组合到一个类中,以实现复用和组合多个类的功能。通过使用组合,一个类可以间接的获得多个类的功能。
③继承加接口:在某些情况下,一个类可以继承一个类同时实现一个或多个接口,这样的组合可以使类同时继承父类的属性和方法,并实现接口定义的行为。
2、==和equals有什么区别?
==用于基础数据类型时,是用来比较两个变量值是否相等的,而对于引用类型来说,是用来比较对象的引用是否相同,而equals默认是比较两个对象的引用是否相同,但大部分时候都会被重写为比较两个对象的值是否相同。
例如,对于Object来说,==和equals都是一样的,都是用来比较两个对象的引用是否相同,而String或Integer等类中,又重写了equals让其变成了比较值是否相同(而非引用是否相同)。
所以,我们通常会使用==来对比两个对象的引用是否相同,而使用equals对比两个值是否相同(前提条件是重写了equals方法)。
对于Object来说,equals方法的底层实现就是“==”,如以下源码所示:
public boolean equals(Object obj){
return (this == obj);
}
所以,对于Object对象来说,equals和==是一样的,都是比较对象的引用是否相同。
但是,Integer中的equals实现源码如下:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
从上述源码可以看出,Integer 中会先将 Integer 对象转换成基础类型 int 值来进行比较,所以此时就不再是对比两个对象的引用了,而是对比两个对象的值是否相等。
String 中的 equals 实现源码如下:
public boolean equals(Object anObject) {
if (this == anObject) { // 引用相同返回 true,引用相同,那么值肯定相同了
return true;
}
return (anObject instanceof String aString)&& (!COMPACT_STRINGS || this.coder == aString.coder)&& StringLatin1.equals(value, aString.value); // equals 为下面的 equals 方法
}
@IntrinsicCandidate
public static boolean equals(byte[] value, byte[] other{
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) { // 循环每个字符对比(本质是比较 String 的值)
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
从 String 中的 equals 中可以看出,它和 Integer 一样,是将 Object 中的引用比较重写成了值比较了。
知识扩展:如何实现自定义equals方法?
如果是我们的业务类中想要重写 equals 方法,实现对象值的比较(非引用),可以这样实现:
public class Person {
private String name;
private int age;
// 忽略构造方法和 Getter、Setter 方法......
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass(){
return false;
}
Person other = (Person) obj;
return this.age==other.age&&this.name.equals(other.name);
}
}
3、返回值不同算方法重载吗?为什么?
返回值不同不算方法重载。
方法重载(Overloading)是指在同一个类中定义了多个同名方法,但他们参数列表不同,方法重载要求方法:
a、名称相同。
b、参数类型、参数个数、参数顺序至少有一个不同。
方法重载的目的是提供更多的方法选择,方便程序员根据不同的参数类型或个数来调用合适的方法。
所以,从上面方法的重载要求可以看出,返回值不同是不作为方法重载的依据的。
考点分许:
为什么返回值不同不算方法重载的原因有两个:
a、从程序的执行层面来讲:返回值不同如果作为方法重载,那么会产生歧义;
b、从jvm方法签名的角度来讲:返回值并不属于方法签名的那一部分,因此无法定位到具体的调用方法。
什么是方法签名?
方法签名(Method Signature)指的是方法的唯一标识,包括方法的名称、参数列表和参数的顺序。方法签名用于区分不同的方法,以便编译器和虚拟机能够正确识别和调用特定的方法。
①程序执行面
从程序的执行面来讲,如果方法的返回值作为方法重载的依据的话,那么程序的执行就会产生起义,例如以下代码:
public Integer method(){
return 1111;
}
public String method(){
return "String";
}
public static void main(String[] args){
method();
}
此时,我们程序就不知道执行什么代码了。
②JVM方法签名层面
方法签名有两部分组成:
a、方法名称:方法的名称用于标识该方法的功能和用途。
b、参数列表:参数列表指定了方法接受的参数类型和参数顺序,每个参数都包括参数的类型和参数的名称。
JVM是通过方法的签名来定位到要调用的方法的,而在方法签名中并没有返回值,因此返回值并不能作为方法重载的依据。