当前位置: 首页 > article >正文

面向对象高级-继承

文章目录

      • 2.1 继承快速入门
      • 2.2 继承的好处
      • 2.3 权限修饰符
      • 2.4 单继承、Object
      • 2.5 方法重写(重点)
      • 2.6 子类中访问成员的特点
      • 2.7 子类中访问构造器的特点

在这里插入图片描述


2.1 继承快速入门

在这里插入图片描述

接下来,演示一下使用继承来编写代码

public class A{
    //公开的成员
    public int i;
    
    public void print1(){
        System.out.println("===print1===");
    }
    
    //私有的成员
    private int j;
    
    private void print2(){
        System.out.println("===print2===");
    }
}

然后,写一个B类,让B类继承A类。在继承A类的同时,B类中新增一个方法print3

public class B extends A{
    public void print3(){
        //由于i和print1是属于父类A的公有成员,在子类中可以直接被使用
        System.out.println(i); //正确
        print1(); //正确
        
        //由于j和print2是属于父类A的私有成员,在子类中不可以被使用
        System.out.println(j); //错误
        print2();	//错误
    }
}

再演示一下,创建B类对象,能否调用父类A的成员。再写一个测试类

public class Test{
    public static void main(String[] args){
        B b = new B();
        // 父类公有成员,子类对象是可以调用的
        System.out.println(i); //正确
        b.print1();	//正确
        
        // 父类私有成员,子类对象时不可以调用的
        System.out.println(j); //错误
        b.print2(); //错误
    }
}

继承的内存原理

需要关注一点:子类对象实际上是由子、父类两张设计图共同创建出来的。

所以,在子类对象的空间中,既有本类的成员,也有父类的成员。但是子类只能调用父类公有的成员。

在这里插入图片描述


2.2 继承的好处

观察代码发现,我们会发现 Teacher 类中和 Consultant 类中有相同的代码;其实像这种两个类中有相同代码时,没必要重复写。

可以把重复的代码提取出来,作为父类,然后让其他类继承父类就可以了,这样可以提高代码的复用性

接下来使用继承来完成上面的案例,这里只演示People类和Teacher类,然后你尝试自己完成 Consultant 类。

  • 先写一个父类 People,用来设计 Teacher 和 Consultant 公有的成员。
public class People{
    private String name;
    
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name=name;
    }
}
  • 再写两个子类Teacher继承People类,同时在子类中加上自己特有的成员。
public class Teacher extends People{
    private String skill; //技能
    
    public String getSkill(){
        return skill;
    }
    
    public void setSkill(String skill){
        this.skill=skill;
    }
    
    public void printInfo(){
        System.out.println(getName()+"具备的技能:"+skill);
    }
}
  • 最后再写一个测试类,再测试类中创建Teacher、Consultant对象,并调用方法。
public class Test {
    public static void main(String[] args) {
        // 目标:搞清楚继承的好处。
        Teacher t = new Teacher();
        t.setName("播仔");
        t.setSkill("Java、Spring");
        System.out.println(t.getName());
        System.out.println(t.getSkill());
        t.printInfo();
    }
}

执行代码,打印结果如下:

在这里插入图片描述

关于继承的好处只需要记住:继承可以提高代码的复用性


2.3 权限修饰符

在刚才使用继承编写的代码中我们有用到两个权限修饰符,一个是public(公有的)、一个是private(私有的),实际上还有两个权限修饰符,一个是protected(受保护的)、一个是缺省的(不写任何修饰符)。

接下来我们就学习一下这四个权限修饰符分别有什么作用。

什么是权限修饰符呢?

权限修饰符是用来限制类的成员(成员变量、成员方法、构造器…)能够被访问的范围。

每一种权限修饰符能够被访问的范围如下

在这里插入图片描述

用代码演示一下,在本类中可以访问到哪些权限修饰的方法。

public class Fu {
    // 1、私有:只能在本类中访问
    private void privateMethod(){
        System.out.println("==private==");
    }

    // 2、缺省:本类,同一个包下的类
    void method(){
        System.out.println("==缺省==");
    }

    // 3、protected: 本类,同一个包下的类,任意包下的子类
    protected void protectedMethod(){
        System.out.println("==protected==");
    }

    // 4、public: 本类,同一个包下的类,任意包下的子类,任意包下的任意类
    public void publicMethod(){
        System.out.println("==public==");
    }

    public void test(){
        //在本类中,所有权限都可以被访问到
        privateMethod(); //正确
        method(); //正确
        protectedMethod(); //正确
        publicMethod(); //正确
    }
}

接下来,在和 Fu 类同一个包下,创建一个测试类 Demo,演示同一个包下可以访问到哪些权限修饰的方法。

public class Demo {
    public static void main(String[] args) {
        Fu f = new Fu();
        // f.privateMethod();	//私有方法无法使用
        f.method();
        f.protectedMethod();
        f.publicMethod();
    }
}

接下来,在另一个包下创建一个Fu类的子类,演示不同包下的子类中可以访问哪些权限修饰的方法。

public class Zi extends Fu {
    //在不同包下的子类中,只能访问到public、protected修饰的方法
    public void test(){
        // privateMethod(); // 报错
        // method(); // 报错
        protectedMethod();	//正确
        publicMethod();	//正确
    }
}

接下来,在和Fu类不同的包下,创建一个测试类Demo2,演示一下不同包的无关类,能访问到哪些权限修饰的方法;

public class Demo2 {
    public static void main(String[] args) {
        Fu f = new Fu();
        // f.privateMethod(); // 报错
        // f.method();		  //报错
        // f.protecedMethod();//报错
        f.publicMethod();	//正确
    }
}

2.4 单继承、Object

一个子类可以继承多个父类吗?

Java语言只支持单继承,不支持多继承,但是可以多层继承。就像家族里儿子、爸爸和爷爷的关系一样:一个儿子只能有一个爸爸,不能有多个爸爸,但是爸爸也是有爸爸的。

public class Test {
    public static void main(String[] args) {
        // 目标:掌握继承的两个注意事项事项。
        // 1、Java是单继承的:一个类只能继承一个直接父类;
        // 2、Object类是Java中所有类的祖宗。
        A a = new A();
        B b = new B();

        ArrayList list = new ArrayList();
        list.add("java");
        System.out.println(list.toString());
    }
}

class A {} //extends Object{}
class B extends A{}
// class C extends B , A{} // 报错
class D extends B{}

为什么不能多继承

在这里插入图片描述

Object

在这里插入图片描述


2.5 方法重写(重点)

在继承的基础之上还有一个很重要的现象—叫做方法重写。

什么是方法重写

当子类觉得父类方法不好用,或者无法满足父类需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。

注意:重写后,方法的访问遵循就近原则。下面看一个代码演示

写一个A类作为父类,定义两个方法print1和print2

public class A {
    public void print1(){
        System.out.println("111");
    }

    public void print2(int a, int b){
        System.out.println("111111");
    }
}

再写一个B类作为A类的子类,重写print1和print2方法。

public class B extends A{
    // 方法重写
    @Override // 安全,可读性好
    public void print1(){
        System.out.println("666");
    }

    // 方法重写
    @Override
    public void print2(int a, int b){
        System.out.println("666666");
    }
}

接下来,在测试类中创建B类对象,调用方法

public class Test {
    public static void main(String[] args) {
        // 目标:认识方法重写,掌握方法重写的常见应用场景。
        B b =  new B();
        b.print1();
        b.print2(2, 3);
    }
}

执行代码,我们发现真正执行的是B类中的print1和print2方法

在这里插入图片描述

知道什么是方法重写之后,还有一些注意事项。

- 1.重写的方法上面,可以加一个注解@Override,用于标注这个方法是复写的父类方法
- 2.子类复写父类方法时,访问权限必须大于或者等于父类方法的权限
	public > protected > 缺省
- 3.重写的方法返回值类型,必须与被重写的方法返回值类型一样,或者范围更小
- 4.私有方法、静态方法不能被重写,如果重写会报错。

关于这些注意事项,同学们其实只需要了解一下就可以了。实际上我们实际写代码时,只要和父类写的一样就可以( 总结起来就8个字:声明不变,重新实现

方法重写的应用场景

还需要掌握方法重写,在实际中的应用场景。方法重写的应用场景之一就是:子类重写Object的toString()方法,以便返回对象的内容。

比如:有一个Student类,这个类会默认继承Object类。

public class Student extends Object{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = 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;
    }
}

其实Object类中有一个toString()方法,直接通过Student对象调用Object的toString()方法,会得到对象的地址值。

public class Test {
    public static void main(String[] args) {
        Student s = new Student("播妞", 19);
        // System.out.println(s.toString());
        System.out.println(s);
    }
}

但是,此时不想调用父类Object的toString()方法,那就可以在Student类中重新写一个toSting()方法,用于返回对象的属性值。

public class Student extends Object{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = 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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

重新运行测试类,结果如下

在这里插入图片描述

println方法源码分析

在这里插入图片描述


2.6 子类中访问成员的特点

继承至少涉及到两个类,而每一个类中都可能有各自的成员(成员变量、成员方法),就有可能出现子类和父类有相同成员的情况,那么在子类中访问其他成员有什么特点呢?

  • 原则:在子类中访问其他成员(成员变量、成员方法),是依据就近原则的

定义一个父类,代码如下

public class F {
    String name = "父类名字";

    public void print(){
        System.out.println("==父类的print1方法执行==");
    }
}

再定义一个子类,代码如下。有一个同名的name成员变量,有一个同名的print成员方法;

public class Z extends F {
    String name = "子类名称";
    public void showName(){
        String name = "局部名称";
        System.out.println(name); // 局部名称
    }

    @Override
    public void print(){
        System.out.println("==子类的print1方法执行了=");
    }

    public void showMethod(){
        print(); // 子类的
    }
}

接下来写一个测试类,观察运行结果,我们发现都是调用的子类变量、子类方法。

public class Test {
    public static void main(String[] args) {
        // 目标:掌握子类中访问其他成员的特点:就近原则。
        Z z = new Z();
        z.showName();
        z.showMethod();
    }
}
  • 如果子类和父类出现同名变量或者方法,优先使用子类的;此时如果一定要在子类中使用父类的成员,可以加this或者super进行区分。
public class Z extends F {
    String name = "子类名称";

    public void showName(){
        String name = "局部名称";
        System.out.println(name); // 局部名称
        System.out.println(this.name); // 子类成员变量
        System.out.println(super.name); // 父类的成员变量
    }

    @Override
    public void print(){
        System.out.println("==子类的print1方法执行了=");
    }

    public void showMethod(){
        print(); // 子类的
        super.print1(); // 父类的
    }
}

2.7 子类中访问构造器的特点

子类中访问构造器的语法规则

  • 首先,子类全部构造器,都会先调用父类构造器,再执行自己。

    执行顺序,如下图按照① ② ③ 步骤执行:

在这里插入图片描述

子类访问构造器的应用场景

  • 如果不想使用默认的super()方式调用父类构造器,还可以手动使用super(参数)调用父类有参数构造器。

在这里插入图片描述

在本类中访问自己的构造方法

有时候我们也需要访问自己类的构造器。语法如下

this(): 调用本类的空参数构造器
this(参数): 调用本类有参数的构造器

在这里插入图片描述

super空间的开辟

| 在这里插入图片描述
|
| ---------------------------------------------- |

| 在这里插入图片描述
|
| -------------------------------------------- |

最后我们被this和super的用法在总结一下

访问本类成员:
	this.成员变量	//访问本类成员变量
	this.成员方法()	//调用本类成员方法
	this()		   //调用本类空参数构造器
    this(参数)	  //调用本类有参数构造器
	
访问父类成员:
	super.成员变量	   //访问父类成员变量
	super.成员方法()   //调用父类成员方法
	super()		     //调用父类空参数构造器
    super(参数)	    //调用父类有参数构造器
    
注意:thissuper访问构造方法,只能用到构造方法的第一句,否则会报错。

[外链图片转存中...(img-ZxzA1FHW-1729995895668)]



super空间的开辟

| ![super空间的开辟](assets/super空间的开辟.png) |
| ---------------------------------------------- |

| ![super空间开辟2](assets/super空间开辟2.png) |
| -------------------------------------------- |



> **最后我们被this和super的用法在总结一下**

```java
访问本类成员:
	this.成员变量	//访问本类成员变量
	this.成员方法()	//调用本类成员方法
	this()		   //调用本类空参数构造器
    this(参数)	  //调用本类有参数构造器
	
访问父类成员:
	super.成员变量	   //访问父类成员变量
	super.成员方法()   //调用父类成员方法
	super()		     //调用父类空参数构造器
    super(参数)	    //调用父类有参数构造器
    
注意:this和super访问构造方法,只能用到构造方法的第一句,否则会报错。








http://www.kler.cn/news/367237.html

相关文章:

  • 基于Gin和GORM的在线判题系统后端
  • 阿里面试竟被“DPO微调”吊打...
  • 【论文阅读】ESRGAN+
  • 383. 赎金信 C#实现
  • vuex使用modules模块化
  • .NET使用QuestPDF高效地生成PDF文档
  • 世界时钟是怎么创建的?如何在桌面添加一个世界时钟
  • 双十一宝妈购物清单来了,请收下这篇好物攻略!
  • 【15】协方差
  • 《深入浅出HTTPS​》读书笔记(1):web
  • Jmeter自动化实战
  • 使用Python实现智能火山活动监测模型
  • 241026-RHEL如何以root身份卸载Docker
  • 改进YOLOv8系列:引入低照度图像增强网络Retinexformer | 优化低光照目标检测那题
  • 06. 函数
  • C#里使用最高性的网络通讯例子
  • echarts实现 水库高程模拟图表
  • Qt的信号槽机制学习一
  • k8s 部署 mysql 故障恢复记录
  • 【ESP32S3】VSCode 开发环境搭建
  • 打卡图论10.24
  • Qt元对象系统 —— 属性系统
  • 【论文阅读】Tabbed Out: Subverting the Android Custom Tab Security Model
  • 6.stm32 OLED显示屏
  • spring响应式编程
  • Python 流程控制专题:pass 与接口