Java 继承
代码中创建的类,主要是为了抽象现实中的一些事物。而现实中的事物往往存在这这样的一种关系,如:猫是动物,橘猫是猫,为了描述显示中这种 is - a 的关系,我们就有了继承的概念。
目录
一 继承概念
继承是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
继承主要解决的问题是共性的抽取,猫和鸟都属于动物,那么我们就可以抽取出它们的共性,比如猫和鸟都有雌雄之分,都会吃食物等。
由于猫和鸟继承自动物类,而动物类中有属性性别和吃的方法,那么在它的子类猫和鸟类中我们可以不写性别和吃方法,只添加子类自己的方法和属性即可。
继承的语法规则如下:
class Cat extends Animal {}
子类 关键字 父类
使用的是 extends 关键字,原意指 "扩展"。而我们所写的类的继承,也可以理解成基于父类进行代码上的“扩展”。
下面给出一段完整的代码:
不使用继承的语法的情况:
// 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 + "正在飞 ︿( ̄︶ ̄)︿");
}
}
上面存在大量重复的代码,使用继承可以更好的简化代码,下面为使用继承语法的写法:
class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 调用父类的构造方法.
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
Cat 和 Bird 类继承了 Animal 类,继承了其中的 name 字段和 eat 方法,简化了代码。
当 Cat 类或者 Bird 类继承了 Animal 类之后,就会把父类的中的所有属性和方法继承过来,所以在子类当中,可以通过 this 关键字访问到 name 属性。
当我们把字段和方法的属性改写成 private 时,这时子类虽然继承了父类的所有属性和方法,但是子类不能访问,可见我的上一篇博客。Java 封装性
二 构造子类实例
我们说子类继承了父类的属性和方法实际上是子类在构造实例是也构造了一个父类的实例,下面给出内存结构图。
1. 内存结构图
我们给出一段简单的代码:
class Base {
public int m;
}
class Derieve extends Base {
public int n;
}
public class TestDemo {
public static void main(String[] args) {
Base base1 = new Base();//语句1
Derieve derieve = new Derieve();//语句2
}
}
在 new 了子类和父类之后内存的布局如下所示:
可以看到,当我们说子类继承了父类,实际上是子类创造实例的时候会先构造父类的实例,所以子类才可以拥有父类的方法和属性,至于在子类中访问不访问的到,就要看父类中的权限修饰符是什么了。
2. super 关键字
前面我们讲过 this 关键字,它是代表指向当前对象的指针,而 super 关键字在继承中使用,代表指向父类对象的引用。
上面的例子中,super 指向的就是父类的对象 new Base() ,所以可以使用 super.m 访问到从父类继承的属性 m ,如果有从父类继承的 public 方法也可以用 super. 的方式访问到。
需要注意的是:
a) 在继承中,如果子类的属性是通过父类继承来的,那么 this.属性 和 super.属性 是一个东西,此时修改一个,另一个也会改变。
b) 如果某个属性是子类新添加的,那么,通过 super 不能访问到。
c) 如果新添加的属性和父类中的属性重名,那么,this 和 super 访问的是两个不同的属性,修改一个不影响另外一个。
3. 构造方法
前面我们说过了,在构造子类对象时会先构造父类对象,而构造对象就要牵扯到构造方法,下面就来详细讲解继承中构造方法的情况。
由于要先构造父类,所以我们可以使用 super() 的方式显式的选择调用父类的哪种构造方法(根据传入参数的不同,这里是方法重载的知识),而且这句代码必须放在子类构造方法的第一行。
下面我们给出在子类构造方法中调用父类构造方法涉及的一些规则:
a) 若父类没有构造方法或者父类中含有无参构造方法,那么在子类中可以不用显式调用父类构造方法,编译器会自己调用父类的默认构造或无参构造方法。
c) 若父类中只有含参构造方法,没有无参构造,那么子类也必须显式调用父类构造方法,否则编译器不知道你要调用哪一个方法,传入什么参数。
三 继承和组合的区别
继承我们已经讲了这么多了,组合是什么?
简单来讲就是把别的类当做此类的一个成员,组合在一起。例如表示一个学校:学校的组成有老师学生等,这就是一个组合的关系。
public class Student {
...
}
public class Teacher {
...
}
public class School {
public Student[] students;
public Teacher[] teachers;
}
上面的代码我们看出,组合是一种 has - a 的语义,表示的是什么有什么的关系。
而继承则是一种 is - a 的语义,表示什么是什么,如我们上面说的猫是动物,所以可以写猫继承自动物类。
学校里我们常见老师讲继承时举的一个例子:有一个 点类,圆类 继承自点类,在圆类中又添加了半径等属性,这样就属于把继承和组合搞混了,我们可以把点类当做圆心写成一个属性组合进圆类中,这样才是符合逻辑的。
四 其他
Java 多继承
多继承是指一个子类有多个父类,但是在 Java 中是不允许的,但是可以有别的方法来实现多继承的目的,就是接口,这个之后整理。
多层继承
Java 虽然不支持多继承但是支持多层继承,例如 猫类继承自动物类,橘猫类又继承自猫类,这种好几次的继承关系就是多继承。
访问父类的属性和方法时我们可以使用 super ,但是访问爷爷类的属性和方法时使用 super.super 就会直接报错,这是需要注意的一点。
但是爷爷类中的属性如果在父类和子类中都没有重名的情况,那么可以通过 this.属性名 访问爷爷属性,或者通过 super.属性名 也可以。
final 关键字对继承的影响
由于多层继承的继承链条比较长,所以编写代码的时候往往会出现问题,所以我们有时候尽量要避免多层的继承,最多两三层就可以了。
有时候我们希望写出来的类不要被别的类继承,我们就可以在类的前面加上 final 关键字表示这个类不可被继承,如果还有别的类要继承它就会报错。
public final class OrangeCat extends Cat {}
protected 关键字
刚才我们发现,如果把字段设为 private ,子类不能访问。但是设成 public,又违背了我们“封装”的初衷。两全其美的办法就是 protected 关键字。对于类的调用者来说, protected 修饰的字段和方法是不能访问的对于类的子类和同一个包的其他类来说,protected 修饰的字段和方法是可以访问的。
但是实际的开发中还是使用 public 和 private 较多一点,这里只做了解。
今天的内容就分享到这里,希望大家多多评论互相提高。