代码中创建的类,主要是为了抽象现实中的一些事物(包含属性和方法)
有的时候客观事物之间就存在一些关联关系,那么在表示成类和对象的时候也会存在一定的关联
例如, 设计一个类表示动物:
// Animal.java
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
// Cat.java
class Cat {
public String name;
public Cat(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
class Bird {
public String name;
public Bird(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
这个代码我们发现其中存在了大量的冗余代码
仔细分析,我们发现 Animal 和 Cat 以及 Bird 这几个类中存在一定的关联关系
此时我们就可以让 Cat 和 Bird 分别继承 Animal 类,来达到代码重用的效果
此时,Animal 这样被继承的类,我们称为 父类 、基类 或 超类,对于像 Cat 和 Bird 这样的类,我们称为 子类,派生类和现实中的儿子继承父亲的财产类似,子类也会继承父类的字段和方法,以达到代码重用的效果
基本语法
class 子类 extends 父类 {
}
class Animal {
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 显示的调用父类带有两个参数的的构造方法
super(name);
}
}
如果父类没有写构造函数,则子类也无序显示的调用父类的构造函数,例如:
class Animal {
public String name;
public int age;
}
class Cat extends Animal {
}
因为父类会隐式的生成一个无参的构造函数,而子类会隐式的调用父类的构造函数。
注意:super调用父类的构造方法时,只能放在第一行,否则会报错
继承的内存图:
当子类和父类中有同名的字段,优先使用子类的,如果想要使用父类字段,则需要使用super关键字,例如:
没有使用super:
使用super:
如果把字段设为 private,子类不能访问。但是设成 public,又违背了我们 “封装” 的初衷
所以应该使用protected关键字
在父类中使用private:
class Animal {
public String name;
public int age;
private String sex;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Bird extends Animal {
String wing;
String name;
public Bird(String name, int age, String wing) {
super(name, age);
this.wing = wing;
}
public void fly() {
System.out.println(super.name + wing);
}
public void PrintSex() {
System.out.println(super.sex);
}
}
在父类中使用protected:
Java 中对于字段和方法共有四种访问权限
在生或中,类与类之前可能存在非常复杂的关系,例如:
这个时候使用继承方式来表示,就会涉及到更复杂的体系
// Animal.java
public Animal {
...
}
// Cat.java
public Cat extends Animal {
...
}
// ChineseGardenCat.java
public ChineseGardenCat extends Cat {
...
}
// OrangeCat.java
public Orange extends ChineseGardenCat {
...
}
......
如刚才这样的继承方式称为多层继承,即子类还可以进一步的再派生出新的子类
时刻牢记,我们写的类是现实事物的抽象。而我们真正在公司中所遇到的项目往往业务比较复杂,可能会涉及到一系列复杂的概念,都需要我们使用代码来表示, 所以我们真实项目中所写的类也会有很多。 类之间的关系也会更加复杂。
但是即使如此,我们并不希望类之间的继承层次太复杂。一般我们不希望出现超过三层的继承关系。如果继承层次太多,就需要考虑对代码进行重构了。
如果想从语法上进行限制继承,就可以使用 final 关键字
final 关键字, 修饰一个变量或者字段的时候, 表示 常量 (不能修改)
final 关键字也能修饰类,此时表示被修饰的类就不能被继承
final 关键字的功能是限制类被继承
“限制” 这件事情意味着 “不灵活”。在编程中,灵活往往不见得是一件好事。灵活可能意味着更容易出错
使用 final 修饰的类被继承的时候,就会编译报错,此时就可以提示我们这样的继承是有悖这个类设计的初衷的
平时使用的 String 字符串类,就是用 final 修饰的,不能被继承
和继承类似,组合也是一种表达类之间关系的方式,也是能够达到代码重用的效果
例如表示一个学校:
public class Student {
...
}
public class Teacher {
...
}
public class School {
public Student[] students;
public Teacher[] teachers;
}
组合并没有涉及到特殊的语法(诸如 extends 这样的关键字),仅仅是将一个类的实例作为另外一个类的字段。这是我们设计类的一种常用方式之一。
组合表示 has - a 语义
在刚才的例子中,我们可以理解成一个学校中 “包含” 若干学生和教师
继承表示 is - a 语义
在上面的 “动物和猫” 的例子中,我们可以理解成一只猫也 “是” 一种动物