JavaSE--继承和多态(上)

一丶继承概念以及写法

(1)基础概念

首先为什么会有继承?
在类的实现中,我们可以发现有时候会有很多重复的代码,比如,用类来形容两个植物:

名称:牡丹花
年龄:三个月
颜色:红色
食物来源:光合作用
功能:可以释放花香

名称:猪笼草
年龄:4个月
颜色:绿色
食物来源:昆虫
功能:捕捉昆虫

可以发现,他们属性的名称相同,都有“年龄”,“名称”,“颜色”,“功能”,如果我们将这些属性抽取出来重新生成一个植物类,这个类的属性有名称,年龄,颜色,食物来源。那么牡丹花类和猪笼草类生成的时候就可以省下大量的代码,这个时候创建牡丹花对象和猪笼草对象,再给对象赋值,无疑可以很大程度的提高效率。
在这里插入图片描述

这就是继承,继承解决的主要问题就是如上所述:共性抽取,以及代码复用
而上述的植物类就是父类,也叫基类,而猪笼草和牡丹花的类,叫做子类,也叫派生类。

接 下 来 给 出 继 承 的 概 念 : \color{red}{接下来给出继承的概念:}

继承指的是一个类型的对象获得另外一个类型的对象的属性和方法。即就是子类继承父类的特征和行为,使得子类拥有和父类拥有相同的实例域和方法。或者说是子类继承父类的方法,使得子类拥有和父类相同的行为。

(2)写法

继承的写法如下:

修饰符 class 子类 extends 父类 {
 // ... 
}

根据上述条件,我们对上述的继承来进行书写:

public class plant {//这是植物类,是父类/基类
    String name;
    int age;
    String color;

    public void eat(){
        System.out.println(name + "正在进食");
    }
}
=================================================
public class ZhuLongCao extends plant{//这是猪笼草类
    public void action(){
        System.out.println(name + "捕捉昆虫");
    }
}
=================================================
public class MuDanHua extends plant{//这是牡丹花类
    public void action(){
        System.out.println(name + "释放花香");
    }
}

可以发现,在上述代码中,猪笼草类和牡丹花类我使用了变量name,但是在这两个类中我并没有定义任何变量,所以这个变量是它们从父类–植物类继承下来。

给 出 结 论 : \color{red}{给出结论:}

1.子类继承父类中除了构造方法以外的所有属性和方法,比如:成员方法,成员变量
2.子类继承父类之后,一定要加上属于自己的内容,不然的话继承的意义就不复存在

二丶关于成员访问

(1)子类访问父类

1>同名问题

现在子类已经成功的继承了父类的变量和方法,那么子类访问父类的成员和方法是要遵循那些规则呢?如果子类的部分属性和父类相同,那又怎么办呢?

public class plant {

    int a = 10;//父类独有变量a
    int b = 20;//这是父类和子类同名变量b

    public  void methodA(){//这是父类和子类同名方法A
        System.out.println("这是父类方法A");
    }

    public  void methodB(){//父类独有方法B
        System.out.println("这是父类方法B");
    }

}

public class TestPlant extends plant{
    byte b = 2;//这是父类和子类同名变量b
    int c = 3;//子类独有变量c

    public  void methodA(){//这是父类和子类同名方法A
        System.out.println("这是子类方法A");
    }

    public  void methodC(){//子类独有方法C
        System.out.println("这是子类方法C");
    }

    public void method(){
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
    }
    public static void main(String[] args) {
        TestPlant test1 = new TestPlant();
        test1.method();//测试变量
        test1.methodA();//测试同名方法
        test1.methodB();
        test1.methodC();//测试不同名方法
    }
}

一步步来讨论, 首 先 对 于 m e t h o d : \color {blue} {首先对于method:} method:

这设置了变量a (父类独有),b(子类父类同名),c(子类独有),然后methodA的打印就是为了探讨变量的访问顺序

再 接 着 对 于 m e t h o d B 和 m e t h o d C 以 及 m e t h o d A : \color{blue}{再接着对于methodB和methodC以及methodA:} methodBmethodCmethodA:

方法我一共构建了三个方法methodA(子类父类同名),methodB(父类独有
),methodC(子类独有)

那么接下来,看运行结果:

在这里插入图片描述
可以看出,打印遵循以下规则:

1.如果访问的 成员变量/方法 子类中有,优先访问自己的成员变量。
2.如果访问的 成员变量/方法 子类中无,则访问父类继承下来的,如果父类也没有定义,则会编译报错。
3.如果访问的 成员变量/方法 与父类中成员变量同名,则优先访问自己的。

此 处 有 几 点 需 要 着 重 注 意 ( 易 错 ) : \color{red}{此处有几点需要着重注意(易错):}
1. 变 量 的 访 问 只 看 名 称 , 不 会 在 乎 变 量 类 型 \color{red}{1.变量的访问只看名称,不会在乎变量类型} 1.访
2. 方 法 是 否 相 同 注 意 除 了 名 称 外 , 还 有 参 数 列 表 。 ( 方 法 重 载 ) \color{red}{2.方法是否相同注意除了名称外,还有参数列表。(方法重载)} 2.

2>super的使用(重写在后面)

我们可以从上面看出,在子类中可以访问父类成员,但是有一个问题,我们在子类当中,如果想要更改变量的值轻易可以做到,但是怎么在子类中给父类的变量赋值呢?如果是不同名还好说,如果是同名我们要怎么办呢?

此处给出super关键字的定义以及使用说明:

  1. 只能在非静态方法中使用
  2. 在子类方法中,访问父类的成员变量和方法。

具体怎么使用呢?代码如下:

public class Fu {
    int a = 1;
    int b = 2;

    public void print(){
        System.out.println("我是父类方法");
    }
}

public class Zi extends Fu{
    int a = 10;
    int b = 20;

    public void print(){//父子同名方法
        System.out.println("我是子类方法");
    }
    public void give(){
        a = 1000;//子类赋值
        super.a = 100;//父类赋值
        System.out.println(a);//打印子类
        System.out.println(super.a);//打印父类
    }
    public void direct(){.
        print();//打印子类
        super.print();//打印父类
    }
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.give();
        zi.direct();
    }
}

这里我要特别说明!!!!

虽 然 我 没 写 , 但 是 方 法 是 否 相 同 除 了 方 法 名 以 外 还 有 参 数 列 表 \color{red}{虽然我没写,但是方法是否相同除了方法名以外还有参数列表}

接着,我们看一下运行的结果:
在这里插入图片描述

3>super 和 this

super和this都是JAVA中的关键字,都可以调用变量和方法,而且都可以作为方法的第一条语句,那么它的特殊之处在哪里呢?

在解释这两个关键字之前,我想先对对象模型有一个大概的解释:
在这里插入图片描述
c和d是子类自己新增加的成员,这是它自己构造方法应该做的事情,而继承下来的父类的信息,就需要使用super进行调用
那 么 在 此 时 给 出 关 于 t h i s 和 s u p e r 的 相 同 点 : \color{red}{那么在此时给出关于this和super的相同点:} thissuper

1.都是Java中的关键字
2.只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3 .在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

再 详 解 t h i s 和 s u p e r 的 不 同 点 : \color{red}{再详解this和super的不同点:} thissuper

1.this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是父类对象的引用
2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3. this是非静态成员方法的一个隐藏参数,super不是隐藏的参数
4. 成员方法中直接访问本类成员时,编译之后会将this还原,即本类非静态成员都是通过this来访问的;在子类中如果通过super访问父类成员,编译之后在字节码层面super实际是不存在的(通过字节码文件可以验证)

关于这些,我想使用一段继承代码在栈丶堆丶区中的来解释:
在这里插入图片描述
在这段代码中,可以很清晰的看到,this其实是这个方法整体的引用,它是隐藏的,但是在编译器编译之后会进行返回,但是super不会。
这 里 的 话 还 有 一 点 需 要 注 意 : 构 造 方 法 中 s u p e r 和 t h i s 不 能 同 时 \color{red}{这里的话还有一点需要注意:构造方法中super和this不能同时} superthis
出 现 , 原 因 很 简 单 , 因 为 一 个 变 量 不 能 被 初 始 化 两 次 \color{red}{出现,原因很简单,因为一个变量不能被初始化两次}

(2)初始化顺序

1>关于构造器

子类和父类的对象在创建之初肯定都要被构造器初始化,那么构造方法的初始化顺序是是什么呢?
继承关系也就是父子关系,那么先有儿子还是先有父亲呢?显而易见,看如下代码:

public class Fu {
   Fu(){
       System.out.println("我是父类构造器");
   }
}
public class Zi extends Fu{

    Zi(){
        System.out.println("我是子类构造器");
    }
    public static void main(String[] args) {
        Zi zi = new Zi();
        System.out.println("=========================");
        Fu fu = new Fu();
    }
}

在此处,我们需要注意两个问题,我创建了两个对象,一个子类对象,一个父类对象,那么具体构造方法怎么调用,由代码运行结果来告诉我们:
在这里插入图片描述

这里先看父类对象,我们很好理解,创建父类对象,那么当然要被父类构造器初始化,所以就只输出了“我是父类构造器”。
那么子类是什么情况呢?我们打开DeBug模式来运行:
在这里插入图片描述

可以发现,这里是由子类构造器跳转到了父类构造器,那么什么情况下子类可以访问父类呢?

没错,子类构造器隐含了一个调用父类构造器的super()

这里特别注意一下: 创 建 那 个 对 象 就 调 用 那 个 对 象 的 构 造 方 法 \color{red}{创建那个对象就调用那个对象的构造方法}

这里用一道题来进行练习:
在这里插入图片描述
这里的答案是什么呢?感兴趣的小伙伴可以做一下,答案在下方:
在这里插入图片描述
所以最后的答案是:YXYZ

2>初始化顺序

在代码中,各个部分的执行,总要排个序,什么意思?
比如:我创建一个对象,那么这个对象的初始化还有方法的执行次序总要排个序,不然还不得乱了套了,那么具体次序是什么呢?这个就需要让编译器告诉我们了。
如下:

public class Fu {
    int age;
    String name;
    String gender;

    public Fu(int age, String name, String gender) {
        this.age = age;
        this.name = name;
        this.gender = gender;
        System.out.println("执行了父类构造方法");
    }

    {
        System.out.println("父类实例代码块执行");
    }
    static{
        System.out.println("父类静态代码块执行");
    }

}

public class Zi extends Fu{
    int age;
    String name;
    String gender;

    public Zi(int age, String name, String gender) {
        super(age,name,gender);
        this.age = age;
        this.name = name;
        this.gender = gender;
        System.out.println("子类执行了构造方法");
    }
    {
        System.out.println("子类实例代码块执行");
    }
    static{
        System.out.println("子类静态代码块执行");
    }
}

public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi(18,"光头大魔王","男");
        System.out.println("==================================");
        Zi zi2 = new Zi(23,"索大","男");
    }
}

执 行 具 体 结 果 如 下 : \color{red}{执行具体结果如下:}
在这里插入图片描述

得 到 结 论 如 下 : \color{red}{得到结论如下:}

在继承关系中,初始化的顺序为:
父类静态代码块 -->子类静态代码块–>父类实例代码块=–>父类构造方法–>子类实例代码块–>子类构造方法

在初始化的时候,类加载阶段静态代码块就会执行,并且最先执行,而且只执行一次。

三丶继承拓展

(1)关于访问权限

在这里的访问权限,其实之前就有提过,但是因为之前没有子类的涉及,所以部分知识点无法展开,这一次的话具体来说一下。

首 先 给 图 : \color{red}{首先给图:}
在这里插入图片描述

这里的话有一个点需要说明,就是如果当前类对变量进行了封装,那么一定要提供对外公开的接口来使得自己的数据可以被访问。
什么意思呢?
在这里插入图片描述

就是提供getter和setter方法。

(2)关于继承方式

1.JAVA一般继承不超过三层
2.就想父子关系一样,一个父亲可以有好几个儿子。一个父类可以有一个子类,一个父类也可以有好几个子类,但是不可以一个子类同时继承好几个父类。

(3)关于final

<1> f i n a l 修 饰 类 \color{red}{final修饰类} final
在这里插入图片描述
在这里插入图片描述

当final修饰类的时候,该类不能有派生类,也就是不能再作为父类被继承。

<2> f i n a l 修 饰 方 法 \color{red}{final修饰方法} final
在这里插入图片描述
在这里插入图片描述

修饰方法的时候,该方法不能被重写

<3> 修 饰 变 量 \color{red}{修饰变量}
在这里插入图片描述

修饰变量的时候,必须要在刚开始就给出初始值,然后该变量只能引用不能修改其值。

重点来了,这里有一点需要特别声明:

如果该变量是一个对象,那么该对象引用地址不可修改,但是对象属性可以修改。

(4)关于继承与组合

这里其实就是就是关于继承中一个问题的解决方式:

如果说,在继承中我更改了一个父类的属性或者方法,那么所有引用了该属性或者方法的子类都要更改,但是如果我把这些属性或者方法用一个对象进行引用,那么更改属性和方法就不会影响其他的引用,而是直接修改内部就可以。

PS:这一个解释在后续的抽象类和接口也适用,就是为什么接口可以多继承。

例如:

// 轮胎类
class Tire{
 // ...
}
// 发动机类
class Engine{
 // ...
}
// 车载系统类
class VehicleSystem{
 // ...
}
class Car{
 private Tire tire; // 可以复用轮胎中的属性和方法
 private Engine engine; // 可以复用发动机中的属性和方法
 private VehicleSystem vs; // 可以复用车载系统中的属性和方法
 
 // ...
}
// 奔驰是汽车
class Benz extend Car{
 // 将汽车中包含的:轮胎、发送机、车载系统全部继承下来

如果我给这些对象属性里面添加东西,那么影响的是对象的内部,而不会对对象本身的引用产生影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值