1、继承的概念
在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类,子类会拥有父类所有可继承的属性和方法。
如人是一个类,有属性(头,胳膊,腿),方法(走路,吃饭)。而又可以由人这个类派生出黑人和白人两个类,为黑人添加一个黑皮肤,而为白人添加一个白皮肤。
如果想声明一个类继承另一个类,需要使用extends关键字。下面代码演示如何继承父类。
// 定义Animal类
class Animal {
String name; // 定义name属性
// 定义动物叫的方法
void shout() {
System.out.println("动物发出叫声");
}
}
// 定义Dog类继承Animal类
class Dog extends Animal {
// 定义一个新方法
public void printName() {
System.out.println("name=" + name);
}
}
public class Example01 {
public static void main(String[] args) {
Dog dog = new Dog(); // 创建一个Dog类的实例对象
dog.name = "沙皮狗"; // 为Dog类的name属性进行赋值
dog.printName(); // 调用dog类的getInfo()方法
dog.shout(); // 调用dog类继承来的shout()方法
}
}
结果
name=沙皮狗
动物发出叫声
2、类的继承中,需要注意的问题:
①Java语言中的类只支持单继承,而 接口支持多继承。一个类只能有一个直接父类。
②多个类可以继承一个父类。
③Java中支持多层继承,即一个类的父类可以再去继承另外的父类,例如c类继承自B类,B类继承自A类。C类也可以称作A类的子类。
④在子类的构造方法中一定会调用父类的某个构造方法,如果没有指定,会自动调用父类无参的构造方法。下面的代码会出错,
class Animal {
// 定义Animal类有参的构造方法
public Animal(String name) {
System.out.println("定义了有参的构造方法");
}
}
// 定义Dog类继承Animal类
class Dog extends Animal {
public Dog() { //这里错误,因为这里会自动调用父类无参的构造方法,父类无参构造方法被覆盖,且有参的构造方法没有传值,所以出错
System.out.println("定义的无参的构造方法");
}
}
// 定义测试类
public class Example2 {
public static void main(String[] args) {
Dog dog = new Dog(); // 实例化子类Dog对象
}
}
3、继承初始化顺序
①先对父类的属性初始化
②然后执行父类的构造方法
③然后再执行子类的属性进行初始化
④最后再进行子类的构造方法
package tcy01;
class Animal{
public Animal(){
System.out.println("Animal类中的构造方法在执行");
age=20;//后改变age的值
}
public int age=10;//它先对age赋初值(先对父类的属性初始化,然后执行父类的构造方法,然后再执行子类的属性进行初始化,最后再进行子类的构造方法)
public String name;
public void eat(){
System.out.println("Animal类中的普通方法执行 ");
}
}
class Dog extends Animal{
public Dog(){
System.out.println("Dog类的构造方法在执行");
}
}
public class Example02 {
public static void main(String[] args){
Animal animal=new Animal();
System.out.println("animal age:"+animal.age);
Dog dog=new Dog();
dog.eat();
}
}
结果:
Animal类中的构造方法在执行
animal age:20
Animal类中的构造方法在执行
Dog类的构造方法在执行
Animal类中的普通方法执行
4、重写父类方法
在子类中对继承的方法进行了一些修改,即对父类的方法进行重写。如下面代码,重写之后,只会调用子类重写后的方法,不会调用父类的shout()方法。
// 定义Animal类
class Animal {
//定义动物叫的方法
void shout() {
System.out.println("动物发出叫声");
}
}
// 定义Dog类继承动物类
class Dog extends Animal {
// 定义狗叫的方法
void shout() {
System.out.println("汪汪……");
}
}
public class Example02 {
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
dog.shout(); // 调用dog重写的shout()方法
}
}
结果:汪汪……
重写父类方法中,需要注意的问题:
①重写方法不能比被重写方法限制有更严格的访问级别,但是可以更广泛(比如父类方法是包访问权限friendly,子类的重写方法是public访问权限)
②参数列表必须与被重写方法的相同
③返回类型必须与被重写方法的返回类型相同。
④重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。但是可以抛出更少,更有限或者不抛出异常。
5、super关键字
①当子类重写父类的方法后,子类对象将无法访问父类被重写的方法,为解决这个问题,Java提供一个super关键字用于访问父类的成员。如下面代码
class Animal {
String name = "动物";
// 定义动物叫的方法
void shout() {
System.out.println("动物发出叫声");
}
}
// 定义Dog类继承动物类
class Dog extends Animal {
String name = "犬类";
// 重写父类的shout()方法
void shout() {
System.out.println("叫声改变了");
}
void shout1() {
super.shout(); // 访问父类的成员方法
}
// 定义打印name的方法
void printName() {
System.out.println("name=" + super.name); // 访问父类的成员变量
}
}
public class Example2{
public static void main(String[] args) {
Dog dog = new Dog(); // 创建一个Dog对象
dog.shout(); // 调用dog对象重写的shout()方法
dog.shout1(); // 调用shout1()方法
dog.printName(); // 调用dog对象的的printName()方法
}
}
运行结果:
②使用super关键字调用父类的构造方法。格式:super(参数1,参数2...).
class Animal {
// 定义Animal类有参的构造方法
public Animal(String name) {
System.out.println("我是一只" + name);
}
}
// 定义Dog类继承Animal类
class Dog extends Animal {
public Dog() {
super("沙皮狗"); // 调用父类有参的构造方法
}
}
// 定义测试类
public class Example04 {
public static void main(String[] args) {
Dog dog = new Dog(); // 实例化子类Dog对象
}
}
结果:
我是一只沙皮狗
但将上例代码的第10行 super("沙皮狗"); 去掉,出错。因为在子类的构造方法中一定会调用父类的某个构造方法,如果没有指定,会自动调用父类无参的构造方法。
为解决这类错误
①在子类中显式的调用父类中已经有的构造方法。
②在父类中定义无参的构造方法。如下代码
class Animal {
// 定义Animal无参的构造方法
public Animal() {
System.out.println("我是一只动物");
}
// 定义Animal有参的构造方法
public Animal(String name) {
System.out.println("我是一只" + name);
}
}
// 定义Dog类,继承自Animal类
class Dog extends Animal {
// 定义Dog类无参的构造方法
public Dog() {
// 方法体中无代码
}
}
public class Example05 {
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
}
}