有了继承机制,就可区分父类和子类,被继承的那个类叫做父类(超类或基类),继承的那个类叫做子类(派生类)。子类可以继承父类所有的非私有属性与方法,还可以继续添加新的属性和方法,正如俗话所说的:青出于蓝而胜于蓝。
如上图所示,狗、猫和鸟都属于动物,程序中便可以描述为狗、猫和鸟继承自动物;同理,哈士奇和金毛犬继承自狗,波斯猫和折耳猫继承自猫,而孔雀和乌鸦继承自鸟。
在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类或基类,子类会自动拥有父类所有可继承的属性和方法。
如果想声明一个类继承另一个类,需要使用extends关键字。
[修饰符] class 子类名 extends 父类名 {
// 程序核心代码
}
在《Java讲课笔记10:类的封装》里,我们已经创建了Cat、Dog和Bird类,它们有共同点,都是动物,于是我们可以创建一个动物类Animal,然后让Cat、Dog和Bird都来继承Animal类。这样做有什么好处呢?比如,在Student类,有三个重载的feed方法,分别是feed(Cat cat)、feed(Dog dog)与feed(Bird bird),因为它们都是Animal类的子类,所以feed方法可以变得更抽象,feed(Animal animal),这样重载就没有了,但是会出现多态现象。什么叫多态?简而言之,同样的方法,同样的参数,但是作用在不同的对象上,结果有不同的呈现。
package net.hw.lesson13;
/**
* 功能:动物类
* 作者:华卫
* 日期:2020年05月03日
*/
public class Animal {
private String name;
private int age;
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 speak() {
System.out.println(name + "今年" + age + "岁了。");
}
public void move() {
System.out.println(name + "在动……");
}
public void eat() {
System.out.println(name + "在吃……");
}
}
package net.hw.lesson13;
/**
* 功能:继承动物类,创建猫类
* 作者:华卫
* 日期:2020年05月03日
*/
public class Cat extends Animal {
/**
* 子类添加新的方法
*/
public void play() {
System.out.println(getName() + "在玩老鼠。");
}
}
Cat子类继承了Animal类全部非私有成员:getName()、setName()、getAge()、setAge()、move()、eat()和speak(),然后我们还添加了一个新的方法play()。
package net.hw.lesson13;
/**
* 功能:继承动物类,创建狗类
* 作者:华卫
* 日期:2020年05月03日
*/
public class Dog extends Animal {
/**
* 子类添加新的方法
*/
public void play() {
System.out.println(getName()+ "在玩飞盘。");
}
}
Dog子类继承了Animal类全部非私有成员:getName()、setName()、getAge()、setAge()、move()、eat()和speak(),然后我们还添加了一个新的方法play()。
package net.hw.lesson13;
/**
* 功能:继承动物类,创建鸟类
* 作者:华卫
* 日期:2020年05月03日
*/
public class Bird extends Animal {
/**
* 子类添加新的方法
*/
public void play() {
System.out.println(getName()+ "在玩虫子。");
}
}
Bird子类继承了Animal类全部非私有成员:getName()、setName()、getAge()、setAge()、move()、eat()和speak(),然后我们还添加了一个新的方法play()。
package net.hw.lesson13;
/**
* 功能:测试狗类
* 作者:华卫
* 日期:2020年05月03日
*/
public class TestDog {
public static void main(String[] args) {
// 实例化狗类对象
Dog dog = new Dog();
// 设置对象属性
dog.setName("瑞瑞");
dog.setAge(1);
// 调用对象方法(继承父类的方法)
dog.speak();
dog.move();
dog.eat();
// 调用对象方法(父类没有的方法)
dog.play();
}
}
在Java中,类只支持单继承,不允许多重继承,也就是说一个类只能有一个直接父类。
class A {
……
}
class B {
……
}
// C类不可以同时继承A类和B类
class C extends A, B {
……
}
Java不允许一子多父,而C++是允许的,因此Java在这一点上比C++简单。
在Java中,多个类可以继承同一个父类。
class A {
……
}
class B extends A {
……
}
class C extends A {
……
}
B类和C类都继承了A类,也就是说,A类现在有两个子类:B类与C类。
在Java中,多层继承是可以的,即一个类的父类可以再去继承另外的父类。(有点像我们在实际生活里说的几代单传。)
class A {
……
}
class B extends A {
……
}
class C extends B {
……
}
package net.hw.lesson13;
/**
* 功能:演示多层继承
* 作者:华卫
* 日期:2020年05月03日
*/
class A {
}
class B extends A {
}
class C extends B {
}
public class Example1301 {
public static void main(String[] args) {
C c = new C();
System.out.println("c instance of C: " + (c instanceof C));
System.out.println("c instance of B: " + (c instanceof B));
System.out.println("c instance of A: " + (c instanceof A));
}
}
运行程序,查看结果:
说明:对象c是C类的实例,也是B类的实例,甚至还是A类的实例。
在继承关系中,子类会自动继承父类中公共的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。
package net.hw.lesson13;
/**
* 功能:继承动物类,创建狗类
* 作者:华卫
* 日期:2020年05月03日
*/
public class Dog extends Animal {
/**
* 子类改写父类的同名同参方法(多态)
*/
@Override
public void move() {
System.out.println(getName() + "在走路。");
}
/**
* 子类改写父类的同名同参方法(多态)
*/
@Override
public void eat() {
System.out.println(getName() + "爱吃骨头。");
}
/**
* 子类添加新的方法
*/
public void play() {
System.out.println(getName() + "在玩飞盘。");
}
}
package net.hw.lesson13;
/**
* 功能:测试狗类
* 作者:华卫
* 日期:2020年05月03日
*/
public class TestDog {
public static void main(String[] args) {
// 实例化狗类对象
Dog dog = new Dog();
// 设置对象属性
dog.setName("瑞瑞");
dog.setAge(1);
// 调用对象方法(继承父类的方法)
dog.speak();
dog.move();
dog.eat();
// 调用对象方法(父类没有的方法)
dog.play();
}
}
修改Cat类和Bird类,重写move()与eat()方法,然后运行TestCat与TestBird。
子类中重写的方法需要和父类被重写的方法具有相同的方法名、参数列表以及返回值类型。
子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限。
问题: 在继承关系中,当子类重写父类的方法后,子类对象将无法直接访问父类被重写的方法。
解决方法: 在Java中专门提供了一个super关键字来访问父类的成员,例如访问父类的成员变量、成员方法和构造方法。
super.成员变量
super.成员方法([参数1, 参数2...])
super([参数1, 参数2 ...])
package net.hw.lesson13;
/**
* 功能:测试super用法
* 调用成员变量与方法
* 作者:华卫
* 日期:2020年05月03日
*/
// 定义Flower类
class Flower {
String name = "鲜花";
public void bloom() {
System.out.println("鲜花绽放。");
}
}
// 定义Jasmine类继承Flower类
class Jasmine extends Flower {
String name = "茉莉花";
@Override
public void bloom() {
super.bloom(); // 调用父类成员方法
}
public void printSuperName() {
System.out.println("父类name属性 = " + super.name); // 调用父类成员变量
}
public void printThisName() {
System.out.println("子类name属性 = " + this.name); // this可以省略
}
}
// 定义测试类
public class Example1303 {
public static void main(String[] args) {
Jasmine jasmine = new Jasmine();
jasmine.bloom();
jasmine.printSuperName();
jasmine.printThisName();
}
}
package net.hw.lesson13;
/**
* 功能:测试super关键字
* 调用父类构造方法
* 作者:华卫
* 日期:2020年05月03日
*/
class Vehicle {
// 定义有参构造方法
public Vehicle(String name) {
System.out.println("这是一辆" + name);
}
}
class Car extends Vehicle {
public Car(String name) {
super(name); // 调用父类有参构造方法
System.out.println("实例化了轿车对象~");
}
}
public class Example1304 {
public static void main(String[] args) {
Car car = new Car("奔驰轿车");
}
}
package net.hw.lesson13;
/**
* 功能:测试super关键字
* 调用父类构造方法
* 作者:华卫
* 日期:2020年05月03日
*/
class Vehicle {
// 定义无参构造方法
public Vehicle() {
System.out.println("这是一辆车");
}
// 定义有参构造方法
public Vehicle(String name) {
System.out.println("这是一辆" + name);
}
}
class Car extends Vehicle {
public Car() {
super(); // 调用父类无参构造方法(可以省略)
System.out.println("实例化了轿车对象~");
}
public Car(String name) {
super(name); // 调用父类有参构造方法
System.out.println("实例化了轿车对象~");
}
}
public class Example1304 {
public static void main(String[] args) {
Car car = new Car();
}
}
选中某个类,比如Animal类,按组合键
,就会显示该类的层次结构图:
对于Animal类,没有使用extends关键字为这个类显式地指定父类,那么该类会默认继承Object类。
方法声明 | 功能描述 |
---|---|
boolean equals(Object obj) | 判断某个对象与此对象是否相等 |
final Class> getClass() | 返回此Object的运行时类 |
int hashCode() | 返回该对象的哈希码值 |
String toString() | 返回该对象的字符串表示 |
void finalize() | 垃圾回收器调用此方法来清理没有被任何引用变量所引用对象的资源 |
package net.hw.lesson13;
/**
* 功能:调用toString()方法
* 作者:华卫
* 日期:2020年05月04日
*/
public class Example1305 {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal.toString());
}
}
运行程序,查看结果:
在Example1305中,第11行代码调用了Animal对象的toString()方法,虽然Animal类中并没有定义这个方法,但程序并没有报错,这是因为Animal类默认继承了Object类,在Object类中定义了toString()方法,在该方法中输出了对象的基本信息。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
getClass().getName()
:代表返回对象所属类的类名,即包名+类名的全限定名称。hashCode()
:代表返回该对象的哈希值。Integer.toHexString(hashCode())
:代表将对象的哈希值用16进制表示。其中,hashCode()是Object类中定义的一个方法,这个方法将对象的内存地址进行哈希运算,返回一个int类型的哈希值。在实际开发中,通常希望toString()方法返回的不仅仅是对象的基本信息,而是一些特有的信息,为此我们可以通过重写Object的toString()方法来实现。
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
package net.hw.lesson13;
/**
* 功能:调用toString()方法
* 作者:华卫
* 日期:2020年05月04日
*/
public class Example1305 {
public static void main(String[] args) {
Animal animal = new Animal();
animal.setName("金毛犬");
animal.setAge(4);
System.out.println(animal.toString());
}
}