继承的定义
继承可以看成是类与类之间的衍生关系。比如狗类是动物类,牧羊犬类又是狗类。于是我们可以说狗类继承了动物类,而牧羊犬类就继承了狗类。于是狗类就是动物类的子类(或派生类),动物类就是狗类的父类(或基类)。
所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
我们创建一个类需要它来继承某一个类的语法:
class 子类 extends 父类
例如我们定义了一个 Animal 类,我们在创建一个 Dog 类,我们需要它继承 Animal 类
class Dog extends Animal {
...
}
接下来我们就来练习一下吧!
我们先创建一个父类 Animal
package com.shiyanlou;
public class Animal {
public int legNum; //动物四肢的数量
//类方法
public void bark() {
System.out.println("动物叫!");
}
}
接下来创建一个子类,我们可以自己输入代码,也可以利用 Eclipse 帮我们自动建立继承关系。
1、点击创建 class 类
2、输入子类名称,点击Superclass
后面的Browse...
寻找父类,与之建立继承关系
3、在Choose a type:
下的输入框内输入要寻找的父类,然后选择,结束。
package com.shiyanlou;
public class Dog extends Animal {
}
上面便是系统生成的代码,Dog 类继承了父类 Animal,我们 Dog 类里什么都没有写,其实它继承了父类 Animal,所以 Dog 类拥有 Animal 类的全部方法和属性(除开 private 方法和属性)。我们创建一个测试类测试一下。
class Test{
public static void main(String[] args) {
Dog a = new Dog();
a.legNum = 4;
a.bark();
}
}
为什么需要继承?
如果有两个类相似,那么它们会有许多重复的代码,导致后果就是代码量大且臃肿,后期的维护性不高。通过继承就可以解决这个问题,将两段代码中相同的部分提取出来组成一个父类,实现代码的复用。
继承的特点:
- 子类拥有父类除private以外的所有属性和方法
- 子类可以拥有自己的属性和方法
- 子类可以重写实现父类的方法
- Java 中的继承是单继承,一个类只有一个父类
注: Java 实现多继承的一个办法是 implements(实现)接口
2.2 方法的重写
虽然子类可以继承父类的方法,但如果子类对父类的方法不满意,想在里面加入适合自己的一些操作时,我们就需要将方法进行重写。并且子类在调用方法中,优先调用子类的方法。
比如上一个例子中 Animal 有bark()
这个方法代表了动物叫,但是不同的动物有不同的叫法,比如狗是汪汪汪,猫是喵喵喵。
当然在方法重写时我们需要注意,重写的方法一定要与原父类的方法语法保持一致,比如返回值类型,参数类型及个数,和方法名都必须一致。
例如:
package com.shiyanlou;
public class Dog extends Animal {
//重写父类的bark方法
public void bark() {
System.out.println("汪!汪!汪!");
}
}
我们写个测试类来看看输出结果:
public class Test{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Dog d = new Dog(); // Dog 对象
Animal b = new Dog(); // Dog 对象,向上转型为Animal类型,具体会在后面的内容进行详解
a.bark();// 执行 Animal 类的方法
d.bark();//执行 Dog 类的方法
b.bark();//执行 Dog 类的方法
}
}
运行结果:
我们在运行程序后,发现控制台上是不是输出了我们预想的结果呢?
2.3 继承的初始化顺序
我们学习了继承后,知道在一个继承关系中一定会有一个父类和至少一个子类,那么在程序运行的过程中,是先为父类进行初始化,还是先调用的子类进行初始化的呢?
继承的初始化顺序是先初始化父类再初始化子类。
我们根据代码来验证一下。
package com.shiyanlou;
public class Animal {
public int legNum = 4; // 动物四肢的数量
//Animal的构造方法
public Animal(){
System.out.println("动物有" + legNum + "腿");
System.out.println("父类的构造方法被调用!");
}
}
package com.shiyanlou;
public class Dog extends Animal {
//Dog类的构造方法
public Dog(){
System.out.println("子类的构造方法被调用!");
}
}
package com.shiyanlou;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Dog a = new Dog(); //生成一个Dog类对象a
}
}
最后输出结果:
由此可知,系统先创建了父类对象,再创建了子类对象,先初始化了属性,再调用了构造函数。
最后再讲一个小知识。
final
关键字可以修饰类、方法、属性和变量
-
final 修饰类,则该类不允许被继承,为最终类
-
final 修饰方法,则该方法不允许被覆盖(重写)
-
final 修饰属性:则该类的属性不会进行隐式的初始化(类的初始化属性必须有值)或在构造方法中赋值(但只能选其一)
-
final 修饰变量,则该变量的值只能赋一次值,即变为常量
super
关键字在子类内部使用,代表父类对象。
-
访问父类的属性
super.属性名
-
访问父类的方法
super.bark()
-
子类构造方法需要调用父类的构造方法时,在子类的构造方法体里最前面的位置:
super()