java中的继承是面向对象编程的核心概念之一。当我们在对象之间有is-a关系时使用 Java 继承。Java 中的继承是使用extends
关键字实现的。
Java中的继承
Java中的继承是通过从其他类继承来在类之间创建层次结构的方法。
Java 继承是可传递的——所以如果 Sedan 扩展 Car 和 Car 扩展 Vehicle,那么 Sedan 也继承自 Vehicle 类。Vehicle 成为 Car 和 Sedan 的超类。
继承在java应用中被广泛使用,例如扩展Exception类来创建一个包含更多错误代码等信息的特定于应用程序的Exception类。例如NullPointerException
Java 继承示例
java中的每个类都隐式地扩展了java.lang.Object
类。所以Object类在java中处于继承层次结构的顶层。
下面通过一个简单的例子来看看如何在java中实现继承。
超类:Animal
package com.journaldev.inheritance;
public class Animal {
private boolean vegetarian;
private String eats;
private int noOfLegs;
public Animal(){}
public Animal(boolean veg, String food, int legs){
this.vegetarian = veg;
this.eats = food;
this.noOfLegs = legs;
}
public boolean isVegetarian() {
return vegetarian;
}
public void setVegetarian(boolean vegetarian) {
this.vegetarian = vegetarian;
}
public String getEats() {
return eats;
}
public void setEats(String eats) {
this.eats = eats;
}
public int getNoOfLegs() {
return noOfLegs;
}
public void setNoOfLegs(int noOfLegs) {
this.noOfLegs = noOfLegs;
}
}
Animal 是这里的基类。让我们创建一个继承自 Animal 类的 Cat 类。
子类:Cat
package com.journaldev.inheritance;
public class Cat extends Animal{
private String color;
public Cat(boolean veg, String food, int legs) {
super(veg, food, legs);
this.color="White";
}
public Cat(boolean veg, String food, int legs, String color){
super(veg, food, legs);
this.color=color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
请注意,我们extends
在 java中使用关键字来实现继承。
Java继承测试程序
让我们编写一个简单的测试类来创建一个 Cat 对象并使用它的一些方法。
package com.journaldev.inheritance;
public class AnimalInheritanceTest {
public static void main(String[] args) {
Cat cat = new Cat(false, "milk", 4, "black");
System.out.println("Cat is Vegetarian?" + cat.isVegetarian());
System.out.println("Cat eats " + cat.getEats());
System.out.println("Cat has " + cat.getNoOfLegs() + " legs.");
System.out.println("Cat color is " + cat.getColor());
}
}
输出:
Cat 类没有getEats()
方法,但该程序仍然可以工作,因为它是从 Animal 类继承的。
要点
-
代码重用是继承最重要的好处,因为子类继承了超类的变量和方法。
-
子类不能直接访问超类的私有成员。在这个例子中,Cat 类无法访问 Animal 变量 noOfLegs,但可以通过 getter 和 setter 方法间接访问它。
-
具有默认访问权限的超类成员只有在同一个包中才能被子类访问。
-
子类不继承超类构造函数。
-
如果超类没有默认构造函数,那么子类也需要定义一个显式构造函数。否则它会抛出编译时异常。在子类构造函数中,在这种情况下必须调用超类构造函数,它应该是子类构造函数中的第一条语句。
-
Java 不支持多重继承,一个子类只能继承一个类。Animal 类隐式扩展 Object 类,Cat 扩展 Animal 类,但由于 java 继承的可传递性,Cat 类也扩展了 Object 类。
-
我们可以创建一个子类的实例,然后将其分配给超类变量,这称为
upcasting(向上类型转换)
。下面是一个简单的向上转换示例:
Cat c = new Cat(); //subclass instance Animal a = c; //upcasting, it's fine since Cat is also an Animal
-
当 Superclass 的一个实例被分配给一个 Subclass 变量时,它被称为
downcasting(向下类型转换)
。我们需要将其显式转换为子类。例如;
Cat c = new Cat(); Animal a = c; Cat c1 = (Cat) a; //explicit casting, works fine because "c" is actually of type Cat
请注意,由于显式转换,即使我们做错了,Compiler 也不会抱怨。以下是它
ClassCastException
在运行时抛出的一些情况。Dog d = new Dog(); Animal a = d; Cat c1 = (Cat) a; //ClassCastException at runtime Animal a1 = new Animal(); Cat c2 = (Cat) a1; //ClassCastException because a1 is actually of type Animal at runtime
-
我们可以在子类中覆盖超类的方法。但是,我们应该始终使用@Override 注释来注释覆盖的方法。编译器会知道我们正在覆盖一个方法,如果超类方法发生了一些变化,我们将得到一个编译时错误,而不是在运行时得到不需要的结果。
-
我们可以使用super关键字调用超类方法并访问超类变量。当我们在子类中有相同名称的变量/方法但我们想要访问超类变量/方法时,它就派上用场了。这也用于在超类和子类中定义构造函数并且我们必须显式调用超类构造函数时。
-
我们可以使用
instanceof
指令来检查对象之间的继承关系,让我们用下面的例子来看看。
Cat c = new Cat(); Dog d = new Dog(); Animal an = c; boolean flag = c instanceof Cat; //normal case, returns true flag = c instanceof Animal; // returns true since c is-an Animal too flag = an instanceof Cat; //returns true because a is of type Cat at runtime flag = an instanceof Dog; //returns false for obvious reasons.
-
我们不能在 Java 中扩展 Final 类。
-
如果您不打算在代码中使用超类,即您的超类只是保留可重用代码的基础,那么您可以将它们保留为抽象类,以避免客户端类进行不必要的实例化。它还将限制基类的实例创建。