概念和特点
概念:
继承是面向对象软件技术当中的一个概念。如果一个类别 B “继承自” 另一个类别 A,就把这个 B 称为 “A 的子类”,而把 A 称为 “B 的父类别” 也可以称 “A 是 B 的超类”。继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码。
Java 语言提供了类的继承机制。利用继承,新建的类可以在原有类的基础上,使用或者重写原有类的成员方法,访问原有类的成员变量。新建的类成为子类,而原有类为新建类的父类,如果 A 是 B 的父类,B 是 C 的父类,那么 C 也是 A 的子类。
特点:
Java 中的继承为单一继承,也就是说,一个子类只能拥有一个父类,一个父类可以拥有多个子类。
另外,所有的 Java 类都继承自 Java.lang.Object,所以 Object 是所有类的祖先类,除了 Object 外,所有类都必须有一个父类。我们在定义类的时候没有显示指定其父类,它默认就继承自 Object 类。
子类一旦继承父类,就会继承父类所有开放的特征,不能选择性地继承父类特征。
继承体现的是类与类之间的关系,这种关系是 is a 的关系,也就是说满足 A is a B 的关系就可以形成继承关系。
实现继承
在 Java 语言中,我们通过 extends 关键字声明一个类继承自另一个类
// 父类
class SuperClass {
...
}
例如,宠物猫和宠物狗都是宠物,都有昵称、年龄等属性,都有吃东西、叫喊等行为。我们可以定义一个父类:宠物类。并且宠物猫和宠物狗类都继承宠物类,继承树形图如下:
public class Pet {
private String name; // 昵称
private int age; // 年龄
// getter 和 setter
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(this.getName() + "在吃东西");
}
// 叫喊
public void shout() {
System.out.println("宠物会叫");
}
}
父类宠物类中 name 和 age 都是私有属性,而对应的 getter、setter 方法,eat 和 shout 方法都是公有方法。
宠物狗类:
public class Dog extends Pet {
// 特有属性体重
private float weight;
// getter和setter
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
// 特有的方法 run
public void run() {
System.out.println("胖成了" + this.getWeight() + "斤的狗子在奔跑");
}
}
宠物狗类有一个自己特有的属性 weight,还有一个特有的方法 run。
宠物猫类:
public class Cat extends Pet {
public void sleep() {
System.out.println(this.getName() + "睡大觉zzz");
}
}
宠物猫类有一个特有的方法 sleep,在方法中可以调用其父类 Pet 的 getName 方法
调用类的方法:
// 实例化一个宠物狗
Dog dog = new Dog();
dog.setName("欢欢");
dog.setWeight(30f);
// 调用继承自父类的公有方法
dog.eat();
// 调用其特有方法
dog.run();
// 实例化一个宠物猫
Cat cat = new Cat();
cat.setName("豆豆");
// 调用继承自父类的公有方法
cat.eat();
// 调用其特有方法
cat.sleep();
运行结果:
欢欢在吃东西
胖成了30.0斤的狗子欢欢在奔跑
豆豆在吃东西
豆豆睡大觉zzz
方法重写
概念:
如果一个类从它的父类继承了一个方法,如果这个方法没有被标记为 final 或 static,就可以对这个方法进行重写。重写的好处是:能够定义特定于子类类型的行为,这意味着子类能够基于要求来实现父类的方法。
实例:
在上述父类 Pet 中有一个 shout 方法,我们知道小猫和小狗的叫声是不同的,此时可以使用方法重写,在 Dog 类和 Cat 类中重写 shout 方法。
Dog 类:
class Dog extends Pet{
// 重写 shout 方法
public void shout() {
System.out.println(this.getName() + "汪汪汪地叫~");
}
}
Cat类:
class Cat extends Pet{
@Override // 使用注解
public void shout() {
System.out.println(this.getName() + "喵喵喵地叫~");
}
}
方法重写规则
- 重写方法的参数列表应该与原方法完全相同
- 返回值类型应该和原方法的返回值类型一样或者是它在父类定义时的子类型;
- 重写方法访问级别限制不能比原方法高。例如:如果父类方法声明为公有的,那么子类中的重写方法不能是私有的或是保护的。具体限制级别参考访问修饰符;
- 只有被子类继承时,方法才能被重写;
- 方法定义为 final,将不能被重写(final 关键字将在本节后面讲到);
- 一个方法被定义为 static,将使其不能被重写,但是可以重新声明
- 一个方法不能被继承,那么也不能被重写;
- 和父类在一个包中的子类能够重写任何没有被声明为 private 和 final 的父类方法;
- 一个重写方法能够抛出任何运行时异常,不管被重写方法是否抛出异常。然而重写方法不应该抛出比被重写方法声明的更新更广泛的已检查异常。重写方法能够抛出比被重写方法更窄或更少的异常;
- 构造方法不能重写。
方法重写和方法重载的区别
Java 中的方法重写(Overriding)是说子类重新定义了父类的方法。方法重写必须有相同的方法名,参数列表和返回类型。覆盖者访问修饰符的限定大于等于父类方法。
而方法重载(Overloading)发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。
访问修饰符
- private:私有的,只允许在本类中访问;
- protected:受保护的,允许在同一个类、同一个包以及不同包的子类中访问;
- 默认的:允许在同一个类,同一个包中访问;
- public:公共的,可以再任何地方访问。
下表按照限定能力从大到小列出了访问修饰符在不同作用域的作用范围:
super关键字
super 是用在子类中的,目的是访问直接父类的变量或方法。注意:
- super 关键字只能调用父类的 public 以及 protected 成员;
- super 关键字可以用在子类构造方法中调用父类构造方法;
- super 关键字不能用于静态 (static) 方法中。
super 与 this 的对比
this 关键字指向当前类对象的引用,它的使用场景为:
- 访问当前类的成员属性和成员方法;
- 访问当前类的构造方法;
- 不能在静态方法中使用。
另外,需要注意的是,在构造方法调用时,super 和 this 关键字不能同时出现。
final关键字
final 关键字可以作用于类、方法或变量,分别具有不同的含义。在使用时,必须将其放在变量类型或者方法返回之前,建议将其放在访问修饰符和 static 关键字之后,例如:
// 定义一个常量
public static final int MAX_NUM = 50;
- final 作用于类:当 final 关键字用于类上面时,这个类不会被其他类继承
- final 作用于方法:当父类中方法不希望被重写时,可以将该方法标记为 final
- final 作用于变量: 对于实例变量,可以使用 final 修饰,其修饰的变量在初始化后就不能修改