共计 5545 个字符,预计需要花费 14 分钟才能阅读完成。
1、继承概述
1.1、什么是继承
继承是面向对象的核心特性,是面向对象的学习重点。同时继承是代码复用的重要方式,可以表示类与类之间的关系,是所有面向对象语言不可缺少的组成部分。
1.2、继承由来
1.2.1、子类与父类
当一个类的属性与行为均与现有类相似,属于现有类的一种时,这一个类可以定义为现有类的子类。
或者换成相反的角度来看,如果多个类具有相同的属性和行为,我们可以抽取出共性的内容定义父类,这时再创建相似的类时只要继承父类即可。
1.2.2、子类与父类关系
子类拥有父类的所有属性与方法,无需重新定义。并且可以直接使用非私有的父类成员。
从类与类之间的设计关系来看, 子类必须属于父类的一种时,才会继承。
我们在完成一个庞大项目体系的时候,都是将共性的内容抽取出,后续构建过程是从各种父类“向外”扩散的。
2、继承定义及使用
2.1、继承定义
格式:
class 子类 extends 父类 {// 父类的非私有方法与属性均继承过来 | |
} |
案例:
class Person{private String name; | |
public void eat(){System.out.println(“吃饭”); | |
} | |
//get/set 方法 | |
} |
子类继承父类的定义:
class ChinesePerson extends Person{}
2.2、继承使用
继承关系的产生通常是为了定义出功能更为具体、更为强大的子类。所以,定义子类后,一般创建子类对象使用。子类可以直接使用父类非私有的成员变量与成员方法 (注:如果成员变量没有使用 private 修饰,则子类也可直接访问。)
class PersonDemo{public static void main(String[] args) {ChinesePerson c = new ChinesePerson(); | |
c.setName("张大力"); | |
String name = c.getName(); | |
System.out.println(name);// 打印结果为张大力 | |
c.eat(); // 打印结果吃饭 | |
} |
3、继承关系
动物类可以有姓名、年龄的成员变量,可以有吃饭、睡觉的方法。
所有猫科与犬科均有动物的成员变量与成员方法,且猫科与犬科均属于动物,所以猫科与犬科均可以继承动物类。
猫科可以在动物的基础上再添加抓老鼠的方法
犬科可以在动物的基础上再添加看门的方法
犬科与猫科仍可以继续出现子类,如波斯猫、巴厘猫、沙皮狗、斑点狗等,而其子类仍可以再出现该品种的特性。
案例:
/* | |
* Animal 的类 | |
* 属性 | |
* name | |
* age | |
* 行为 | |
* 吃 | |
* 睡 | |
*/ | |
public class Animal {// 成员变量 | |
private String name; | |
private int age; | |
// 吃 | |
public void eat(){System.out.println("吃"); | |
} | |
// 睡 | |
public void sleep(){System.out.println("睡"); | |
} | |
//-----------get/set------------------- | |
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; | |
} | |
} |
/* | |
* 定义一个猫类 | |
* 属性 | |
* name | |
* age | |
* kind | |
* 行为 | |
* 吃 | |
* 睡 | |
* 抓老鼠 | |
*/ | |
public class Cat extends Animal {private String kind; | |
public void eat(){System.out.println("猫吃鱼"); | |
} | |
// 猫特有的功能 抓老鼠 | |
public void catchMouse(){System.out.println("抓耗子"); | |
} | |
public String getKind() {return kind; | |
} | |
public void setKind(String kind) {this.kind = kind; | |
} | |
} |
/* | |
* 定义一个狗类 | |
* 属性 | |
* name | |
* age | |
* kind | |
* 行为 | |
* 吃 | |
* 睡 | |
* 看门 | |
*/ | |
public class Dog extends Animal {private String kind; | |
public void eat() {System.out.println("狗吃肉"); | |
} | |
// 狗特有功能 看门 | |
public void lookDoor() {System.out.println("看门"); | |
} | |
public String getKind() {return kind; | |
} | |
public void setKind(String kind) {this.kind = kind; | |
} | |
} |
/* | |
* 自定义类型 家 | |
* 地址 | |
* 行为 | |
* 在家吃饭 | |
*/ | |
public class Home {private String address; | |
// 动物在家吃饭 | |
// 在所有使用父类类型的地方均可以传入其子类对象。 | |
public void eatAtHome(Animal a) {// 调用 Animal 的 eat 方法 | |
a.eat();} | |
// 狗在在家吃饭 | |
public void eatAtHome(Dog dog) {System.out.println("狗在家吃了"); | |
// 调用狗的 eat 方法 | |
dog.eat();} | |
// 猫在家吃饭 | |
public void eatAtHome(Cat cat) {System.out.println("猫在家吃了"); | |
// 调用猫的 eat 方法 | |
cat.eat();} | |
public String getAddress() {return address; | |
} | |
public void setAddress(String address) {this.address = address; | |
} | |
} |
/** | |
* 测试家类 | |
*/ | |
public class Test {public static void main(String[] args) {// 创建 Home 对象 | |
Home home = new Home(); | |
Animal a = new Animal(); | |
home.eatAtHome(a); | |
// 在所有使用父类类型的地方均可以传入其子类对象。 | |
Dog d = new Dog(); | |
home.eatAtHome(d); | |
Cat c = new Cat(); | |
home.eatAtHome(c); | |
} | |
} | |
运行结果:
4、继承注意事项
4.1、Java 只支持单继承,不支持多继承。即只能有一个父类。
// 单继承 | |
class A extends B{} | |
class B extends C{} | |
// 多继承, 错误 | |
class A extends B{} | |
class A extends C{} |
4.2、父类可以继续有父类。
class A extends B{} | |
class B extends C{} |
4.3、所有类均有父类,只有 Object 类没有父类。
class A{} | |
// 相当于 | |
class A extends Object{}// 所有类都继承了 Object 类,继承 Object 类可以省略 |
4.4、在所有使用父类类型的地方均可以传入其子类对象。
class B{} | |
class A extends B{} | |
// 测试类 | |
class C{public void c(B b){} | |
public static void main(String args[]){new C().c(new A()); | |
} | |
} |
5、方法重写
5.1、什么是方法重写
当子类继承父类后,拥有了父类的成员并可以直接调用父类非私有方法。如果子类认为父类提供的方法不够强大,子类可以按照子类自身的逻辑重新定义继承过来的父类方法,这个重新定义一个方法的过程叫做方法重写。(注:在学习完多态和抽象类后我们会对方法重写有更深的理解)
5.2、方法重写格式
子类中定义与父类一样的方法便将父类的方法重写了。此时,当创建子类对象,调用方法时,会调用子类重写后的方法。
案例 1:
父类
public class Person{private String name; | |
public void eat(){System.out.println("吃饭"); | |
} | |
//get/set | |
public String getName() {return name; | |
} | |
public void setName(String name) {this.name = name; | |
} | |
} |
子类
public class ChinesePerson extends Person{ //@Override 是用于强制规定当前定义的方法一定为重写的方法 | |
public void eat() {System.out.println("按照中国的习惯,使用筷子吃"); | |
} | |
} |
子类使用
public class PersonDemo{public static void main(String[] args) {ChinesePerson c = new ChinesePerson(); | |
c.setName("张大力"); // 父类继承方法直接调用 | |
String name = c.getName(); // 父类继承方法直接调用 | |
System.out.println(name); // 打印结果为张大力 | |
c.eat(); // 方法重写后调用的为重写后的方法 | |
// 打印结果:按照中国的习惯,使用筷子吃 | |
} | |
} |
运行结果:
案例 2:
public class Person{private String name; | |
String address;// 增加了一个地址成员,可初始化 | |
public void eat(){System.out.println("吃饭"); | |
} | |
//get/set | |
public String getName() {return name; | |
} | |
public void setName(String name) {this.name = name; | |
} | |
} | |
/* | |
* 定义类型 学生类 | |
* | |
* 姓名 年龄 | |
* | |
* 继承 | |
* 概念 1 | |
* | |
* 方法重写 | |
* 子类继承父类后,可以直接使用父类的非私有成员,但是如果觉得父类的成员方法不够强大,子类可以按照自身的逻辑 | |
* 将继承过来的父类方法,进行重写(方法重写,方法复写,方法覆盖)* | |
* 可以使用 @Override 来验证你的方法是不是重写方法。*/ | |
public class Student extends Person{private String number; | |
public void method(){System.out.println(address); | |
System.out.println(getName()); | |
} | |
// 重写父类 eat 方法 | |
public void eat(){System.out.println("学生吃学生套餐"); | |
} | |
public String getNumber() {return number; | |
} | |
public void setNumber(String number) {this.number = number; | |
} | |
} |
/* | |
* 测试继承后的 Studnet | |
*/ | |
public class StudentDemo {public static void main(String[] args) {// 创建 Studnet 对象 | |
Student s = new Student(); | |
s.setName("柳柳"); | |
s.setNumber("0900112"); | |
s.eat(); | |
String name = s.getName(); | |
System.out.println(name); | |
System.out.println(s.getNumber()); | |
System.out.println("-----------------"); | |
// 子类调用自己特有的方法 | |
s.method();} | |
} |
运行结果:
5.3、方法重写规则
子类重写方法时,在声明前加 @Override 可检测该方法是否为重写的方法
访问权限相同或子类方法访问权限更大 (访问权限顺序 public> 默认)
class Fu{void show(){} | |
public void method(){}} | |
class Zi extends Fu{public void show(){} // 编译运行没问题 | |
void method(){} // 编译错误 | |
} |
方法名称必须相同
参数列表必须相同
返回值为基本类型时必须相同
返回值为引用类型时相同或子类小 (了解)
案例:
/* | |
* 方法重写的注意事项 | |
* 子类重写方法时,在声明前加 @Override 可检测该方法是否为重写的方法 | |
* 访问权限相同或子类方法访问权限更大 (访问权限顺序 public> 默认) | |
* 方法名称必须相同 | |
* 参数列表必须相同 | |
* 返回值为基本类型时必须相同 | |
* 返回值为引用类型时相同或子类小 (了解) | |
*/ | |
public class Fu {public void method(){System.out.println("父类方法"); | |
} | |
public int sum(){return 0; | |
} | |
public Person get(){return null; | |
} | |
} |
public class Zi extends Fu{// 访问权限相同或子类方法访问权限更大 (访问权限顺序 public> 默认) | |
public void method(){System.out.println("子类方法"); | |
} | |
// 返回值为基本类型时必须相同 | |
public int sum(){return 100; | |
} | |
// 返回值为引用类型时相同或子类小 (了解) | |
public Student get(){return null; | |
} | |
} |
