继承(Inheritance)是面向对象编程中的一个重要概念和核心机制之一,它允许一个类(称为子类或派生类)继承另一个类(称为父类、基类或超类)的属性和方法。通过继承,子类可以获得父类的特性,并且可以在此基础上添加自己的特性或修改继承的属性和方法。
继承的主要目的是实现代码的重用和扩展。通过继承,我们可以定义一个通用的父类,其中包含多个子类共享的属性和方法。子类可以直接使用父类的属性和方法,而无需重新编写相同的代码。这样可以减少代码冗余,并提高代码的可维护性和可扩展性。
在继承中,父类通常具有通用的属性和方法,而子类可以通过继承来获取这些属性和方法,并可以添加自己的特定属性和方法。子类还可以重写继承的方法,以适应自己的需求。
继承关系可以形成一个类的层次结构,其中父类可以有多个子类,而子类也可以成为其他类的父类,从而形成更复杂的继承关系。这种层次结构可以提供更好的代码组织和管理,以及更高的代码复用性。
需要注意的是,继承并不适用于所有情况,过度的继承可能导致类的层次结构过于复杂,代码可读性和可维护性下降。因此,在设计和使用继承时,需要谨慎考虑,并遵循适当的设计原则和最佳实践。
在 Java 中,接口也可以进行继承,这被称为接口继承(Interface Inheritance)。接口继承允许一个接口(称为子接口)继承另一个或多个接口(称为父接口)的方法签名。
子接口继承了父接口的方法签名,但并不继承其实现。子接口可以添加自己的方法声明,以及重新声明父接口中的方法(在子接口中提供不同的实现)。
一个类可以实现一个或多个接口,这样它就需要实现这些接口中的所有方法。如果一个接口继承了其他接口,那么实现该接口的类也需要实现所有父接口中的方法。
接口继承的主要目的是为了实现接口的分层和组织。通过接口继承,我们可以将相关的方法签名组织到一个父接口中,然后在子接口中进行扩展和特殊化。这样可以提高代码的可读性、可维护性和可扩展性。
需要注意的是,接口继承可以形成多层继承关系,但是接口之间不存在多重继承的问题。一个接口可以继承多个父接口,但一个类只能继承一个类或抽象类。这是因为 Java 中的类只支持单一继承,而接口可以实现多个。
接口继承使用关键字 extends,子接口通过 extends 关键字来继承一个或多个父接口。语法如下:
interface 子接口名 extends 父接口1, 父接口2, ... { // 子接口的方法声明 }
创建一个 Person 接口代表人类,拥有吃饭和睡觉的动作,然后创 Student 学生和 Worker 工人接口,这两接口均继承 Person 接口,且分别自定义了自己的动作,如下:
public interface Person { /** 吃饭 */ public void eat(); /** 睡觉 */ public void sleep(); } public interface Student extends Person { /** 学习 */ public void learn(); } public interface Worker extends Person { /** 工作 */ public void work(); }
类继承是面向对象编程中的一个重要概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类、基类或超类)的属性和方法。通过类继承,子类可以获得父类的特性,并且可以在此基础上添加自己的特性或修改继承的属性和方法。
子类继承了父类的属性和方法,包括父类的公有和受保护成员(私有成员不可继承)。子类可以直接使用父类的属性和方法,而无需重新编写相同的代码。这样可以减少代码冗余,并提高代码的可维护性和可扩展性。
在类继承中,父类通常具有通用的属性和方法,而子类可以通过继承来获取这些属性和方法,并可以添加自己的特定属性和方法。子类还可以重写继承的方法,以适应自己的需求。
类继承可以形成一个类的层次结构,其中父类可以有多个子类,而子类也可以成为其他类的父类,从而形成更复杂的继承关系。这种层次结构可以提供更好的代码组织和管理,以及更高的代码复用性。
需要注意的是,Java 中的类只支持单一继承,即一个类只能继承一个父类。这是为了避免多重继承可能带来的复杂性和冲突。如果需要实现多个类的特性,可以使用接口来实现多重继承的效果。
类继承使用关键字 extends,子类通过 extends 关键字来继承一个父类。语法如下:
class 子类名 extends 父类名 { // 子类的属性和方法 }
下面示例创建一个普通人 Person(拥有姓名和年龄),然后通过继承 Person 类创建一个 Student 学生,该学生还新添加了所读学校和学号属性。如下:
Person.java 代码如下:
/** * 通用人员信息 * @author hxstrive.com */ public class Person { /** 名称 */ private String name; /** 年龄 */ private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
Student.java 代码如下:
/** * 学生,学生也是普通人中的一员,只是比较特殊,他们都有成绩属性 * @author hxstrive.com */ public class Student extends Person { /** 所读学校 */ private String school; /** 学号 */ private String studentNumber; public String getSchool() { return school; } public void setSchool(String school) { this.school = school; } public String getStudentNumber() { return studentNumber; } public void setStudentNumber(String studentNumber) { this.studentNumber = studentNumber; } @Override public String toString() { return "Student{" + "name='" + getName() + '\'' + ", age=" + getAge() + ", school='" + school + '\'' + ", studentNumber='" + studentNumber + '\'' + '}'; } }
Test.java 代码如下:
/** * 测试类 * @author hxstrive.com */ public class Test { public static void main(String[] args) { Person person = new Person(); person.setName("Tom"); person.setAge(30); System.out.println("person = " + person); Student student = new Student(); student.setName("Helen"); student.setAge(35); student.setSchool("实验中学"); student.setStudentNumber("S001"); System.out.println("student = " + student); } }
运行示例,输出如下:
person = Person{name='Tom', age=30} student = Student{name='Helen', age=35, school='实验中学', studentNumber='S001'}
在 Java中,子类可以重写(override)父类的方法。重写是指子类提供了与父类具有相同名称、参数列表和返回类型的方法,但是子类提供了自己的实现。
重写的语法规则如下:
(1)子类的方法必须与父类的方法具有相同的名称、参数列表和返回类型。
(2)子类的方法不能比父类的方法具有更低的访问权限。例如,如果父类的方法是 public,那么子类的方法也必须是 public 或 protected,不能是 private。
(3)子类的方法不能比父类的方法抛出更多的异常,或者抛出与父类方法不兼容的异常。子类可以不抛出异常,或者只抛出父类方法声明的异常或其子类异常。
当一个对象调用一个被重写的方法时,Java 会根据对象的实际类型来决定调用哪个方法。如果对象是子类的实例,那么将调用子类的方法;如果对象是父类的实例,那么将调用父类的方法。
重写的目的是在子类中提供对父类方法的特殊实现。子类可以通过重写来修改父类方法的行为,或者添加额外的功能。重写可以实现多态性,即通过父类引用调用子类的方法。
注意:重写只能发生在父类和子类之间。如果一个类没有继承关系,那么它们之间的方法称为重载(overload),而不是重写。重载是指在同一个类中定义了多个方法,它们具有相同的名称但不同的参数列表。重载允许使用不同的参数来调用相同的方法名,以提供更多的灵活性和便利性。
示例:
public class Person { public void show() { System.out.println("Person"); } } public class Student extends Person { // 重载方法,注:@Override 是一个注解,后续章节介绍 @Override public void show() { System.out.println("Student"); } // 重载方法,和 show() 方法同名但是参数列表不一样 public void show(String msg) { System.out.println(msg); } public static void main(String[] args) { Person p1 = new Person(); p1.show(); Person p2 = new Student(); p2.show(); //p2.show("重载方法"); // 调用失败,Person 类型中没有该方法 // 强制类型转换 ((Student)p2).show("调用重载方法"); } }
在 Java 中,super 是一个关键字,主要用来引用父类的成员变量、方法和构造方法。我们可以通过 super 关键字调用父类的构造方法、成员变量和成员方法。
注意:super 关键字只能在子类中使用,并且只能用于引用父类的成员。在父类中使用 super 关键字是非法的,因为父类没有父类。另外,super 关键字必须在子类的构造方法的第一行使用,用于调用父类的构造方法。如果没有显式调用父类的构造方法,Java 会自动调用父类的默认构造方法。
在子类的构造方法中,可以使用super关键字来调用父类的构造方法。通过 super 关键字,可以在子类的构造方法中调用父类的构造方法,从而完成对父类的初始化。这对于子类需要继承父类的属性和行为非常有用。例如:
public class Person { private int id; private String name; public Person(int id, String name) { this.id = id; this.name = name; } } public class Student extends Person { private float score; public Student(int id, String name, float score) { // 调用父类 Person 的构造方法 Person(int id, String name) super(id, name); this.score = score; } }
在子类中,可以使用 super 关键字来引用父类的成员变量。这在子类中存在与父类同名的成员变量时特别有用。通过 super 关键字,可以访问父类的成员变量,而不是子类的成员变量。例如:
public class Person { protected String name = "Person"; } public class Student extends Person { // 和父类成员同名 private String name = "Student"; public void show(boolean flag) { if(flag) { // 引用父类 name 成员 System.out.println(super.name); // 输出:Person } else { // 引用当前类的 name 成员 System.out.println(name); // 输出:Student } } public static void main(String[] args) { new Student().show(true); new Student().show(false); } }
在子类中,可以使用 super 关键字来调用父类的方法。这在子类中存在与父类同名的方法时特别有用。通过 super 关键字,可以调用父类的方法,而不是子类的方法。这对于在子类中扩展父类的方法非常有用。例如:
public class Person { public String getName() { return "Person"; } } public class Student extends Person { public String getName() { return "Student"; } public Student() { // 调用本类的 getName() 方法 System.out.println(getName()); // 输出:Student // 调用父类的 getName() 方法 System.out.println(super.getName()); // 输出:Person } public static void main(String[] args) { new Student(); } }
Java 语言中只支持单重继承,而不允许多重继承。所谓单重继承是指一个 Java 类只能有一个直接父类,即 extends 关键字后面只跟写一个类。而多重继承是指一个类可以同时继承多个直接父类,这可能导致子类成员在追溯上的不确定性。
如果允许多重继承,则可能出现如下情况:
public class A { public void show() { System.out.println("In class A!"); } } public class B { public void show() { System.out.println("In class B!"); } } public class C extends A,B { public static void main(String[] args) { C c = new C(); c.show(); } }
上述代码中,C 类的对象 c 在调用 show() 方法时会出现问题,系统无法确定到底该执行其父类 A 类中定义的 show() 方法,还是该执行 B 类中的 show() 方法呢。