Java 继承
Java面向对象设计 - Java继承
子类可以从超类继承。超类也称为基类或父类。子类也称为派生类或子类。
从另一个类继承一个类非常简单。我们在子类的类声明中使用关键字extends,后跟超类名称。
Java不支持多重继承的实现。
Java中的类不能有多个超类。
语法
一般的语法是
<class modifiers>class <SubclassName> extends <SuperclassName> { // Code for the Subclass }
例子
以下代码显示如何使用从Employee类创建Manager类。
class Employee { private String name = "Unknown"; public void setName(String name) { this.name = name; } public String getName() { return name; } } class Manager extends Employee { } public class Main { public static void main(String[] args) { // Create an object of the Manager class Manager mgr = new Manager(); // Set the name of the manager mgr.setName("Tom"); // Get the name of the manager String mgrName = mgr.getName(); // Display the manager name System.out.println("Manager Name: " + mgrName); } }
上面的代码生成以下结果。
注意
我们没有为Manager类编写任何代码,它的工作原理与Employee类相同,因为它继承自Employee类。
您可以使用Manager类的构造函数创建一个管理器对象。
Manager mgr = new Manager();
创建管理器对象后,Manager对象的工作方式与Employee对象相同。
我们对manager对象使用了setName()和getName()方法。
mgr.setName("Tom"); String mgrName = mgr.getName();
Manager类不声明setName()和getName()方法。Manager类“扩展Employee"。
当一个类从另一个类继承时,它继承它的超类成员,实例变量,方法等。
对象父类
对象类是默认超类。
所有类都隐式继承自Object类。因此所有类的对象都可以使用Object类的方法。
public class P { }
类P从Object扩展,即使我们没有指定父类。
Object类声明了hashCode()和toString()方法。因为Employee类是Object类的一个子类,它可以使用这些方法。
Employee emp = new Employee(); int hc = emp.hashCode(); String str = emp.toString();
Employee类不使用extends子句指定其超类。这意味着它继承自Object类。
Object类声明了hashCode()和toString()方法。因为Employee类是Object类的一个子类,它可以使用这些方法。
向上转换和向下转换
现实世界中的“is-a”关系在软件中转化为继承类层次结构。
例如,Manager是特定类型的Employee。 Employee是一种特定类型的Object。
当您在继承层次结构中向上移动时,将从特定类型移动到更一般的类型。
从子类到超类的赋值称为上转换,它在Java中始终允许。
class Employee { private String name = "Unknown"; public void setName(String name) { this.name = name; } public String getName() { return name; } } class Manager extends Employee { } public class Main { public static void printName(Employee emp) { String name = emp.getName(); System.out.println(name); } public static void main(String[] args) { Employee emp = new Employee(); emp.setName("Tom"); Manager mgr = new Manager(); mgr.setName("Jack"); // Inheritance of setName() at work // Print names printName(emp); printName(mgr); // Upcasting at work } }
为子类变量分配超类引用称为向下转换。
向下转换与向上转换相反。
在向上转换中,赋值向上移动类层次结构,而在向下转换中,赋值向下移动类层次结构。
我们必须在向下转换中使用类型转换。
Manager mgr = (Manager)emp; // OK. Downcast at work
上面的代码生成以下结果。
instanceof运算符
Java instanceof运算符帮助我们确定引用变量是否有对类或子类的引用。
它需要两个操作数,并计算为布尔值true或false。
它的语法是
<Class Reference Variable> instanceof <Class Name or Interface>
如果<Class Reference Variable>引用类<Class Name>或其任何后代的对象,instanceof返回true。
如果引用变量为null,instanceof总是返回false。
我们应该在向下转换之前使用instanceof运算符。
Manager mgr = new Manager(); Employee emp = mgr; if (emp instanceof Manager) { // downcast will succeed mgr = (Manager)emp; }else { // emp is not a Manager type }
禁用继承
我们可以通过声明类final来禁用子类。
最终的类不能被子类化。
下面的代码声明了一个名为MyClass的最终类:
public final class MyClass{ }
我们也可以声明一个方法为final。最终方法不能被子类覆盖或隐藏。
public class A { public final void m1() { } public void m2() { } }
Java 方法重写
Java面向对象设计 - Java方法重写
方法重写
重新定义从超类继承的类中的实例方法称为方法重写。
例子
让我们考虑类A和类B的以下声明:
public class A { public void print() { System.out.println("A"); } } public class B extends A { public void print() { System.out.println("B"); } }
类B是类A的子类。类B从其超类继承print()方法并重新定义它。
类B中的print()方法覆盖类A的print()方法。
如果一个类覆盖了一个方法,它会影响覆盖的类及其子类。考虑下面的类C的声明:
public class C extends B { // Inherits B.print() }
类C不声明任何方法。它继承类B中的print()方法。
注意
类总是继承它的直接超类的可用的。
方法必须是实例方法。重写不适用于静态方法。
重写方法必须具有与重写方法相同的名称。
重写方法必须具有与重写方法相同顺序的相同类型的相同数量的参数。
当方法的参数使用通用类型时,考虑通用类型参数的擦除,而不是通用类型本身与其他方法比较。
参数的名称无关紧要。
如果重写方法的返回类型是引用类型,则重写方法的返回类型必须与重写方法的返回类型兼容。
访问级别
重写方法的访问级别必须至少与重写方法的访问级别相同或更宽松。
下表列出了重写方法允许的访问级别
重写方法访问级别 | 允许重写方法访问级别... |
---|---|
public | public |
protected | public, protected |
package-level | public, protected, package-level |
方法可以在其throws子句中包括检查异常的列表。重写方法无法向重写方法中的异常列表添加新的异常。
它可以删除一个或所有异常,或者可以用另一个异常替换异常。
访问重写方法
从子类访问重写的方法。子类可以使用关键字 super
作为限定符来调用超类的重写方法。
class MySuperClass { public void print() { System.out.println("Inside MySuperClass"); } } class MySubClass extends MySuperClass { public void print() { // Call print() method of MySuperClass class super.print(); // Print a message System.out.println("Inside MySubClass.print()"); } public void callOverridenPrint() { // Call print() method of MySuperClass class super.print(); } } public class Main { public static void main(String[] args) { MySubClass aoSub = new MySubClass(); aoSub.print(); aoSub.callOverridenPrint(); } }
上面的代码生成以下结果。
Java 继承和构造函数
Java面向对象设计 - Java继承和构造函数
构造函数不是类的成员,它们不是由子类继承的。
它们用于初始化实例变量。
class CSuper { public CSuper() { System.out.println("Inside CSuper() constructor."); } } class CSub extends CSuper { public CSub() { System.out.println("Inside CSub() constructor."); } } public class Main { public static void main(String[] args) { CSub cs = new CSub(); } }
上面的代码生成以下结果。
例子
下面显示了如何编译器注入一个super()来立即调用父类的无参数构造函数。
class CSuper { public CSuper() { super(); // Injected by the compiler System.out.println("Inside CSuper() constructor."); } } class CSub extends CSuper { public CSub() { super(); // Injected by the compiler System.out.println("Inside CSub() constructor."); } } public class Main { public static void main(String[] args) { CSub cs = new CSub(); } }
上面的代码生成以下结果。
关键字super指的是类的直接父类。
我们可以调用超类构造函数,只使用super关键字作为构造函数中的第一个语句。
无参数构造函数
我们可以将超类的no-args构造函数或任何其他构造函数显式地调用为类的构造函数中的第一个语句。
只有在没有明确添加的情况下,编译器才会注入no-args构造函数调用。
class Employee { private String name = "Unknown"; public Employee(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } } class Manager extends Employee { public Manager(String name) { super(name); } } public class Main { public static void main(String[] args) { Manager mgr = new Manager("Tom"); String name = mgr.getName(); System.out.println("Manager name: " + name); } }
上面的代码生成以下结果。
每个类都必须直接或间接地从其构造函数中调用其超类的构造函数。
如果超类没有no-args构造函数,我们必须显式地调用超类的任何其他构造函数。
Java 继承隐藏
Java面向对象的设计 - Java继承隐藏
方法隐藏
类从其超类继承所有非私有静态方法。
重定义类中继承的静态方法称为方法隐藏。
子类中的重定义静态方法隐藏其超类的静态方法。
在类中重定义非静态方法称为方法覆盖。
关于方法隐藏的重定义方法(名称,访问级别,返回类型和异常)的所有规则与方法覆盖相同。
class MySuper { public static void print() { System.out.println("Inside MySuper.print()"); } } class MySubclass extends MySuper { public static void print() { System.out.println("Inside MySubclass.print()"); } } public class Main { public static void main(String[] args) { MySuper mhSuper = new MySub(); MySubclass mhSub = new MySubclass(); MySuper.print(); MySubclass.print(); ((MySuper) mhSub).print(); mhSuper = mhSub; mhSuper.print(); ((MySubclass) mhSuper).print(); } }
上面的代码生成以下结果。
字段隐藏
类中的字段声明(静态或非静态)在其父类中隐藏具有相同名称的继承字段。
在字段隐藏的情况下,不考虑字段的类型及其访问级别。
字段隐藏仅基于字段名称。
class MySuper { protected int num = 100; protected String name = "Tom"; } class MySub extends MySuper { public void print() { System.out.println("num: " + num); System.out.println("name: " + name); } } class MySub2 extends MySuper { // Hides num field in MySuper class private int num = 200; // Hides name field in MySuper class private String name = "Jack"; public void print() { System.out.println("num: " + num); System.out.println("name: " + name); } } public class Main { public static void main(String[] args) { MySub fhSub = new MySub(); fhSub.print(); MySub2 fhSub2 = new MySub2(); fhSub2.print(); } }
上面的代码生成以下结果。
例子
以下代码显示了如何使用super关键字访问超类的隐藏字段
class MySuper { protected int num = 100; protected String name = "Tom"; } class MySub extends MySuper { // Hides the num field in MySuper class private int num = 200; // Hides the name field in MySuper class private String name = "Jack"; public void print() { System.out.println("num: " + num); System.out.println("super.num: " + super.num); System.out.println("name: " + name); System.out.println("super.name: " + super.name); } } public class Main { public static void main(String[] args) { MySub s = new MySub(); s.print(); } }
上面的代码生成以下结果。
字段隐藏发生在一个类声明一个变量与来自其超类的继承变量具有相同名称的时候。
字段隐藏仅基于字段的名称。
类应该使用关键字super来访问超类的隐藏字段。
类可以使用简单的名称来访问其主体中的重定义字段