“继承”和“多态”是面向对象编程(OOP)中的两个核心概念,它们共同为代码的复用性、可扩展性和灵活性提供了强大的支持。在Java中,这两个概念被广泛应用于类的设计和程序的实现中。
在Java中,继承是一种通过extends
关键字实现类与类之间关系的机制。它允许一个类(子类)继承另一个类(父类)的属性和方法。通过继承,子类可以复用父类的代码,同时还可以添加新的功能或修改父类的行为。
例如,假如我们有一个People类,它定义了所有人的通用属性和通用行为
public class People {
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;
}
public void eat() {
System.out.println("人吃饭");
}
}
现在,我们可以创建一个Student
类和一个Teacher
类,他们都继承自People
类:
class Student extends People {
private String studentId;
public void study() {
System.out.println("学生学习");
}
}
class Teacher extends People {
private String teacherId;
public void teach() {
System.out.println("老师教学");
}
}
过让Student
类和Teacher
类继承自People
类,我们可以复用People
类的通用属性和行为,同时为每个子类添加特定的功能。这不仅让代码更加简洁,还提高了可维护性和可扩展性。
在子类中访问其他成员(成员变量或者成员方法)时,按照就近原则,先子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果子父类中,出现了重名的变量,会优先使用子类的,如果一定要在子类中使用父类的相同变量,使用super。
class Fu {
String name="Fu的name";
}
class Zi extends Fu {
String name="Zi的name";
public void show(){
String name="show的name";
System.out.println(name); // show的name
System.out.println(this.name);// Zi的name
System.out.println(super.name);// Fu的name
}
}
在这个例子中,Zi
类继承了Fu
类的name
变量,但同时也定义了自己的name
变量。在show
方法中,name
变量的访问顺序如下:
局部变量name
(优先级最高)。
子类的成员变量name
(通过this.name
访问)。
父类的成员变量name
(通过super.name
访问)。
子类会继承父类中所有非私有的方法。在子类中可以直接调用父类的方法,就像这些方法是子类自己的方法一样。
class Fu {
public void show() {
System.out.println("Fu类的show方法");
}
}
class Zi extends Fu {
public void display() {
show(); // 调用继承自父类的show方法
}
}
方法重写是继承中的一个重要概念,它允许子类根据自己的需求重新定义父类的方法。当子类重写父类的方法时,子类方法的名称和参数列表必须与父类方法完全一致。重写的方法可以改变方法的行为,但不能改变方法的签名。
例如,假设我们有一个People
类,它定义了一个skill
方法:
class People {
public void skill() {
System.out.println("人有技能");
}
}
现在,我们创建了一个司机类和一个厨师类,司机和厨师都有各种特有的技能,因此这两类
都对父类的skill
方法进行重写:
class Driver extends People {
@Override
public void skill() {
System.out.println("司机会开车");
}
}
class Cooker extends People {
@Override
public void skill() {
System.out.println("厨师会做饭");
}
}
在这个例子中,Driver
类和Cooker
类都通过@Override
注解明确表示它重写了父类的skill
方法。当我们调用对象的skill
方法时,输出将是“司机会开车”和”厨师会做饭“,而不是“人有技能”。
需要注意的是,方法重写需要满足以下条件:
子类方法的名称和参数列表必须与父类方法完全一致。
子类方法的返回值类型必须与父类方法的返回值类型相同或兼容。
子类方法不能抛出比父类方法更宽的异常。
子类方法的访问修饰符不能比父类方法更严格。
在Java中,子类的构造器总是会先调用父类的构造器,然后再执行自己的构造代码。
class Fu {
public Fu() {
System.out.println("Fu类无参构造器执行了");
}
}
class Zi extends Fu {
public Zi() {
System.out.println("Zi类无参构造器执行了");
}
}
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
运行这段代码时,输出将是:
Fu类无参构造器执行了
Zi类无参构造器执行了
如果父类没有无参构造器,或者我们需要调用父类的有参构造器,可以通过super
关键字显式调用。
class People {
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
}
class Teacher extends People {
private String subject;
public Teacher() {
}
public Teacher(String name, int age, String subject) {
super(name, age); // 调用父类的有参构造器
this.subject = subject;
}
}
在这个例子中,Teacher
类的有参构造器通过super(name, age)
显式调用了父类People
的有参构造器。
虽然继承可以提高代码的复用性,但也有一些限制:
单继承限制:Java中一个类只能继承一个父类,不能实现多继承。如果需要实现多继承的功能,可以通过接口来实现。
继承的深度限制:虽然理论上可以有任意深度的继承关系,但过深的继承关系会使代码变得复杂,难以维护。因此,应尽量避免过深的继承关系。
继承的耦合性:继承会增加类之间的耦合性,子类对父类的依赖度过高。如果父类发生变化,可能会对子类产生较大的影响。因此,在设计类时,应尽量减少类之间的耦合性。
多态是面向对象编程(OOP)中的一个重要概念,是指允许通过父类的引用调用子类的方法,具体调用哪个方法取决于运行时对象的实际类型,而不是引用的类型。
继承关系:子类必须继承父类。
方法重写:子类必须重写父类的方法。
父类引用指向子类对象:通过父类类型的引用指向子类对象。
多态分为两种类型:编译时多态和运行时多态。
方法重载(Overloading)是指在同一个类中,方法名相同但参数列表不同的方法。编译器根据方法的参数类型和数量来决定调用哪个方法。
class Calculator {
public int add(int a, int b) {
System.out.println("调用 int 参数的 add 方法");
return a + b;
}
public double add(double a, double b) {
System.out.println("调用 double 参数的 add 方法");
return a + b;
}
}
public class Test {
public static void main(String[] args) {
Calculator calc = new Calculator();
calc.add(1, 2); // 调用 int 参数的 add 方法
calc.add(1.0, 2.0); // 调用 double 参数的 add 方法
}
}
运行时多态(Override)是指通过子类重写父类的方法,在运行时根据对象的实际类型调用相应的方法。这是多态的核心形式。
class People {
public void skill() {
System.out.println("人有技能");
}
}
class Driver extends People {
@Override
public void skill() {
System.out.println("司机会开车");
}
}
class Cooker extends People {
@Override
public void skill() {
System.out.println("厨师会做饭");
}
}
public class Test {
public static void main(String[] args) {
People people1 = new Driver();
People people2 = new Cooker();
Driver.skill(); // 输出:司机会开车
Cooker.skill(); // 输出:厨师会做饭
}
}
在这个例子中,people1
和people1
都是People
类型的引用,但它们的实际类型分别是Driver
和Cooker
。调用skill
方法时,运行时会根据对象的实际类型调用相应的方法。
多态在实际开发中有很多应用场景,例如:
接口编程:通过接口定义一组方法,然后让不同的类实现这些方法,从而实现多态。这种方式可以提高代码的可扩展性和可维护性。
框架设计:在框架设计中,多态可以用于定义通用的接口或抽象类,然后让不同的实现类根据具体需求实现相应的方法。这种方式可以提高框架的灵活性和可扩展性。
算法设计:在算法设计中,多态可以用于定义通用的算法接口,然后让不同的实现类根据具体需求实现相应的算法。这种方式可以提高算法的可扩展性和可维护性。
继承和多态是Java面向对象编程中的两个核心概念。继承通过extends
关键字实现了类之间的父子关系,让子类可以复用父类的代码,同时还可以通过方法重写来扩展或修改父类的行为。多态则通过父类引用指向子类对象的方式,让代码更加灵活和可扩展。