继承(extends)

继承(extends)

前言

继承是面向对象的三大特征之一。可以使子类具有超类的属性和方法,还可以在子类中重新定义,追加属性和方法。

一、继承问题的引出

下面首先看两段代码

public class Father{
    public String name;
    public String age;
    private String job;
    
    public void setName(String name){
        this.name=name;
    }
    public void setAge(String age){
        this.age=age;
    }
    public void setJob(String job){
        this.job=job;
    }
    
    public String getName(){
        return this.name;
    } 
     public String getAge(){
        return this.age;
    } 
     public String getJob(){
        return this.job;
    } 
}
public class Son{
	public String name;
    public String age;
    private String school;
    
    public void setName(String name){
        this.name=name;
    }
    public void setAge(String age){
        this.age=age;
    }
    public void setSchool(String school){
        this.school=school;
    }
    
    public String getName(){
        return this.name;
    } 
     public String getAge(){
        return this.age;
    } 
     public String getSchool(){
        return this.school; 
    } 
}

通过上面的两个程序,我们可以发现代码中有明显的重复部分,但是之前学的内容无法避免这个重复,这就会导致代码量大,不便于维护,于是在Java中引入了继承机制。

二、继承的概念

继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。

格式:

class 子类 extends 父类{} 

那么利用这一点我们可以改进上面的代码:

package text_extends;

public class Father{...}

public class son extends father{
    private String school;
    public void setSchool(String school){
        this.school=school;
    }
    public String getSchool(){
        return this.school;
    }
}


public class FSDemo {
    public static void main(String[] args) {
        father f = new father();
        f.setName("王富贵");
        f.setAge("35");
        f.setJob("程序员");
        System.out.println("姓名:"+f.getName()+" 年龄:"+f.getAge()+" 工作:"+f.getJob());
        son s = new son();
        s.setName("王小明");
        s.setAge("17");
        s.setSchool("长春市第一中学");
        System.out.println("姓名:"+s.getName()+" 年龄:"+s.getAge()+" 学校:"+s.getSchool());
    }
}

运行结果:

姓名:王富贵 年龄:35 工作:程序员
姓名:王小明 年龄:17 学校:长春市第一中学

通过代码发现,子类(son)中没有定义name和age,但是仍然可以拥有超类(father)的方法和属性,而且可以有自己的方法和属性,我们可以发现子类更像是对超类的具体化,我们将大范围的定义放在了父类中,将小范围的定义放在了子类中,方便我们使用。

继承的好处:

  1. 提高了代码的复用性(多个类相同的成员可以放在一个类中)
  2. 提高了代码的维护性(如果方法需要修改,只需要修改一处即可)

继承的弊端:

继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时,子类也变化,削弱了子类的独立性。

注意事项:

  1. 父类中的私有成员子类无法继承;(与访问权限有关)
  2. Java中只支持单继承,不可多继承;(一个子类只能有一个超类,但是一个超类可以有多个子类)
  3. Java中可以多层继承(A继承B,B继承C,那么A也能继承到C中的属性和方法)

三、继承中访问的特点

访问变量:

例一
public class father {
    public int number=100;
}

public class son extends father {
    public int number = 50;

    public void show() {
        int number = 30;
        System.out.println(number);
    }
    public static void main(String[] args) {
        son s = new son();
        s.show();
    }
}

运行结果:

30
例二
public class father {
    public int number=100;
}

public class son extends father {
    public int number = 50;

    public void show() {
        System.out.println(number);
    }
    public static void main(String[] args) {
        son s = new son();
        s.show();
    }
}

运行结果:

50
例三
public class father {
    public int number=100;
}

public class son extends father {
    public void show() {
        System.out.println(number);
    }
    public static void main(String[] args) {
        son s = new son();
        s.show();
    }
}

运行结果:

100

通过三个例子的对比我们可以知道继承访问变量的特点:

  1. 先在方法内部寻找并访问;
  2. 然后在子类成员中寻找并访问;
  3. 最后在超类成员中寻找并访问;
  4. 如果在之前的范围中都没有找到该变量,则访问失败

访问成员方法:

(访问成员方法的特点和访问变量的特点类似,在此就不举例总结了)

访问成员方法的特点:

  1. 在子类的方法中寻找并访问;
  2. 在父类的方法中寻找并访问;
  3. 如果在之前的范围中都没有找到该变量,则访问失败

构造方法的访问:

在介绍构造方法的访问特点之前,先来学习一个关键字super

在上面的代码中我们看到当代码的三个范围中都存在同名变量时,我们只能访问到方法中的同名变量,而通过之前学习的关键字this我们可以访问成员变量,但是我们无法访问父类中的变量,于是在Java中就提供了一个关键字super

来看一段代码:

public class father {
    public int number=100;
}
public class son extends father {
    public int number = 50;

    public void show() {
        int number = 30;
        System.out.println(number);
        System.out.println(this.number);
        System.out.println(super.number);
    }
    public static void main(String[] args) {
        son s = new son();
        s.show();
    }
}

运行结果:

30
50
100

我们可以看到superthis的用法相似,不同的是this是对本类对象的引用,而super是父类储存空间的标识,现阶段可以理解为对父类对象的引用。

现在来介绍继承中对构造方法的访问的特点,先看代码:

public class father {
   public father(){
       System.out.println("father中的无参方法被调用");
   }
   public father(String name){
       System.out.println("father中的带参方法被调用");
   }
}
public class son extends father {
    public son(){
        System.out.println("son中的无参方法被调用");
    }

    public son(int age){
        System.out.println("son中的带参方法被调用");
    }
}
public class Demo {
    public static void main(String[] args) {
        son s1 = new son();

        son s2 = new son(14);
    }
}

运行结果为:

father中的无参方法被调用
son中的无参方法被调用
father中的无参方法被调用
son中的带参方法被调用

我们可以看到,无论是调用子类中的无参构造方法,还是调用子类中的带参构造方法,都会访问父类中的无参构造方法,为什么会调用父类的构造方法呢?为什么都会调用超类的无参构造方法呢?

我们都知道,子类会继承超类的属性和方法,可能还会用到超类中的数据,所以在子类初始化之前,超类的数据要完成初始化;因为我们在访问构造方法是要完成数据的初始化,虽然我们在访问子类的带参构造方法是传入了一个数据age,但是这个数据是完成了对子类的初始化,而超类中并没有数据传入,所以此时默认调用超类的无参构造方法。而上面我们说过super关键字,它是超类储存空间的标识,可以看做是对超类成员的引用,所以Java默认每个子类构造方法的第一条语句是:super();,我们也就不难看出没有数据传入超类,所以会默认调用无参构造方法。

通过了解我们也就明白了如何调用父类中的带参方法了,代码如下:

public class father {
   public father(){
       System.out.println("father中的无参方法被调用");
   }
   public father(String name){
       System.out.println("father中的带参方法被调用");
   }
}
public class son extends father {
    public son(){
        super();
        System.out.println("son中的无参方法被调用");
    }

    public son(int age){
        super("王小明");
        System.out.println("son中的带参方法被调用");
    }
}
public class Demo {
    public static void main(String[] args) {
        son s1 = new son();

        son s2 = new son(14);
    }
}

运行结果:

father中的无参方法被调用
son中的无参方法被调用
father中的带参方法被调用
son中的带参方法被调用

继承中访问构造方法的特点:

  1. 每个子类构造方法的第一条语句默认是super();
  2. 子类中的所以构造方法都默认访问超类的无参构造方法;

四、方法重写

概念:

子类中出现和超类重名的方法。

当子类需要父类的属性和方法,但方法主体子类有自己独特的内容时,我们可以进行方法重写。

来看一段代码:

public class father {
    public void work(String name){
        System.out.println(name+"的今天工作是打100行代码");
    }
}
public class son extends father {
    public void work(String name){
        System.out.println(name+"的今天工作是写100行代码和debug");
    }
}
public class Demo {
    public static void main(String[] args) {
        son s = new son();
        s.work("王小明");
    }
}

运行结果:

王小明的今天工作是debug

我们可以看到在子类中对超类的方法进行了重写,改变了work方法的内容。那我们如何来调用被改写的超类中的方法呢?

还是要用到super关键字,改写后的代码如下:

public class father{...}
public class son extends father {
    public void work(String name){
        super.work(name);
        System.out.println("和debug");
    }
}
public class Demo{...}

这样我们既减少了代码量,又使子类拥有了自己特别的内容。

注意事项:

  1. 超类的私有方法不能重写;
  2. 子类重写方法的访问权限不得低于超类;
王小明的今天工作是debug

我们可以看到在子类中对超类的方法进行了重写,改变了work方法的内容。那我们如何来调用被改写的超类中的方法呢?

还是要用到super关键字,改写后的代码如下:

public class father{...}
public class son extends father {
    public void work(String name){
        super.work(name);
        System.out.println("和debug");
    }
}
public class Demo{...}

这样我们既减少了代码量,又使子类拥有了自己特别的内容。

注意事项:

  1. 超类的私有方法不能重写;
  2. 子类重写方法的访问权限不得低于超类;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值