【JavaSE 语法】-继承和多态

目录

一、继承

1、继承的概念

2、访问父类成员

2.1、同名子类优先访问

2.2、同名指定访问父类成员(super)

2.3、super和this

3、子类构造方法

3.1、先父后子构造方法

3.2、静态代码块、实例代码块、构造方法执行顺序

4、继承方式  继承和组合

4.1、继承方式

4.2、继承和组合

5、protected限定符和final关键字

5.1、protected限定符

5.2、final关键字

二、多态

1、多态的实现

1.1、多态的概念

1.2、多态实现条件

1.3、例子

2、重载和重写

2.1、重载(overload)

2.2、重写(override)

3、向上转型和向下转型

3.1、向上转型

3.2、向下转型

4、静态绑定和动态绑定

4.1、静态绑定

4.2、动态绑定

5、多态的优缺点

5.1、多态优点:

5.2、多态缺点

5.3、注意

6、避免在构造方法中调用重写的方法


一、继承

1、继承的概念

概念:一个类继承了父类(基类)的属性和方法,这个类叫做子类(派生类/超类),继承时子类会将父类的属性和方法全部继承,所以私有成员可以被继承但不能被访问非要访问就用get和set。

作用:共性的抽取,实现代码复用

格式修饰词+class+子类+extends+父类{}

代码如下:

//父类
class Animal {
    public String name = "动物";
    public void sleep() {
        System.out.println(name+"正在睡觉");
    }
}
//Dog子类
class Dog extends Animal {
    //子类独有的属性和方法
    public boolean loyal = true;
    public void judge() {
        System.out.println("dog " + name + " 忠诚:" + loyal);//Cat子类继承了父类Animal的属性name
    }
}
//Cat子类
class Cat extends Animal {
    //子类独有的方法
    public void catchMouse() {
        System.out.println("cat " + name + " 在抓老鼠");//Cat子类继承了父类Animal的属性name
    }
}
 
public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Cat cat = new Cat();
        //子类单独有的方法
        dog.judge();
        cat.catchMouse();
        //子类从父类继承的方法
        dog.sleep();
        cat.sleep();
    }
}

结果演示:

dog 动物 忠诚:true
cat 动物 在抓老鼠
动物正在睡觉     
动物正在睡觉

注意:

一、访问属性

  1. 父类对象可以访问父类属性
  2. 子类对象访问父类同名属性,优先访问子类自己属性。

二、访问方法

  1. 父类对象可以调用父类方法
  2. 子类对象调用父类同名方法,优先调用子类自己方法。

2、访问父类成员

2.1、同名子类优先访问

一、访问父类成员方式

  • 子类对象访问
  • 子类方法访问

二、子类对象(方法)访问父类成员变量或方法

  1. 如果不同名,可以正常访问
  2. 如果同名,优先访问子类的同名成员变量(成员方法找同名的方法中合适参数的那一个方法,如果找不到,就报错)。

注:如果成员方法同名,传参也相同(或成员变量同名),要访问父类成员方法(成员变量),需要用到下面的super、。即同名时要指定访问父类成员时用super

2.2、同名指定访问父类成员(super)

1、super作用在子类方法中访问父类成员

  • super.data;   访问父类非静态成员变量
  • super.func(); 调用父类非静态成员方法
  • super();         调用父类的构造方法

2、被static修饰成员:用类名+变量(方法)访问

注意

  • super只能在非静态方法中使用
  • super在子类方法中,访问父类的成员变量和方法

super访问成员变量和方法:

//父类
class Animal {
    public String name;
    public void sleep() {
        System.out.println(name+"正在睡觉");
    }
}
//Dog子类
class Dog extends Animal {
    public String name="旺财";  //如果无super,结果中name="旺财"
    //子类独有的属性和方法
    public boolean loyal;

    public void func() {
        System.out.println(super.name);//super访问父类成员变量
        super.sleep();  //super访问父类成员方法
    }
}
public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.func();
    }
}

结果演示:

null
null正在睡觉

2.3、super和this

一、访问成员原则

在继承中

  • super访问父类成员变量或成员方法(super范围是父类继承的成员
  • this优先访问子类成员,子类没有才会访问父类成员(this范围是父类继承的+子类独有的

二、相同点

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

三、不同点

  • this是当前对象的引用,当前对象即调用实例方法的对象。
  • super则是专门访问从父类继承的成员的访问,不是父类的引用

3、子类构造方法

3.1、先父后子构造方法

组成
子类对象中成员是由两部分组成的,父类继承下来的以及子类新增加的部分 。
原则
在构造子类对象时候 ,先要调用父类的构造方法,将从父类继承下来的成员构造完整
然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 先父后子

先父后子构造方法:

//构造方法
//父类
class Animal {
    public String name;
    public void sleep() {
        System.out.println(name+"正在睡觉");
    }

    //默认提供的父类构造方法
//    public Animal() {
//
//    }
    //父类构造方法
    public Animal(String name) {
        this.name = name;
        System.out.println("Animal(String )");
    }
}
//Dog子类
class Dog extends Animal {
    //子类独有的属性和方法
    public String name;
    public boolean loyal;

    public void func() {
        System.out.println(super.name + "是忠诚的: " + loyal);
        System.out.println(this.name + "是忠诚的: " + loyal);

    }
    //默认提供的子类构造方法
//    public Dog() {
//        super();
//    }
    //子类构造方法
    public Dog(String name1, boolean loyal) {
        super("旺财");       //1、帮助父类进行初始化,如果父类有name和age两个值可以:super(name,age)
        this.name = name1; //2、子类自己初始化
        this.loyal = loyal;
        System.out.println("Dog(String , boolean )");
    }
}
public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog("hello",true);
        dog.func();
    }
}

结果演示:

Animal(String )         //父类的构造方法
Dog(String , boolean )  //子类的构造方法
旺财是忠诚的: true     //子类调用func,用super访问父类时打印
hello是忠诚的: true    //子类调用func,用this访问子类时打印

注意:

  1. 父类构造方法无参或为默认(没有人为创建的)构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用父类构造方法
  2. 父类构造方法带有参数,这时需要在子类中人为创建构造方法,并在子类构造方法第一行选择相应的父类构造方法调用,否则编译失败。
  3. 在子类构造方法中,super();调用父类构造方法时必须是子类构造方法中的第一句
  4. super();后并不生成父类对象,只是将子类从父类继承的内容初始化了。
  5. super();只能在子类构造方法中出现一次,且不能和this();同时出现

3.2、静态代码块、实例代码块、构造方法执行顺序

//父类
class Animal {
    public String name;

    static {
        System.out.println("static Animal()!");
    }
    {
        System.out.println("example Animal()!");
    }
    public Animal() {
        System.out.println("public Animal()!");
    }
}
//Dog子类
class Dog extends Animal {
    //子类独有的属性
    public boolean loyal;
    static {
        System.out.println("static Dog()!");
    }
    {
        System.out.println("example Dog()!");
    }
    public Dog() {
        System.out.println("public Dog()!");
    }

}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        System.out.println("--------------------------");
        Dog dog2 = new Dog();
    }
}

结果演示:

static Animal()!
static Dog()!
example Animal()!
public Animal()!
example Dog()!
public Dog()!
--------------------------
example Animal()!  //第二次实例化没有静态代码块了
public Animal()!
example Dog()!
public Dog()!

注:

  1. 静态代码块先执行,并且只执行一次在类加载阶段执行
  2. 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
  3. 顺序为:父类静态块->子类静态块->父类实例块->父类构造方法->子类实例块->子类构造方法。总之:父类总是先于子类执行,静态块先于实例块先于构造方法。

4、继承方式  继承和组合

4.1、继承方式

种类:单继承,多层继承,不同类继承同一个类,多继承(Java不支持)

注:一般来说不会超过三层继承关系,可以用final修饰类来限制

4.2、继承和组合

一、组合概念

组合和继承一样,也是表达类之间的关系的方式,是将一个类的实例作为另一个类的字段

作用:达到代码复用

二、形式

继承:如狗是动物(狗继承动物共性)

组合:  如汽车(汽车有轮胎、方向盘、发动机等等)

三、组合代码

//学校(学生和老师组合)

class Student {
    private String name;

    public Student(String name) {
        this.name=name;
    }
}
class Teacher {
    private String name;

    public Teacher(String name) {
        this.name=name;
    }
}

class School {
    //字段(属性)
    public Student[] students;
    public Teacher[] teachers;

    public static void main(String[] args) {

    }
}

四、继承和组合代码

//车(轮胎等共性的组合)

package inherit;

//轮胎类
class Tire {

}
//发动机类
class Engine {

}
//组合了Tire和Engine类
public class Car {
    private Tire tire;      //可以复用轮胎中的属性和方法
    private Engine engine;  //可以复用发动机中的属性和方法

}
//奔驰是汽车
class Benz extends Car {
    //继承了汽车的共性,如轮胎,发动机等全部继承
}

5、protected限定符和final关键字

5.1、protected限定符

注:限定符属于关键字。

1、protected的最大权限为:访问不同包中的子类(子类访问protected修饰的不同包父类成员变量

2、类的修饰符只能是public或者默认修饰符,即公开权限和包权限

包一:

package inherit;

//包一中的类作为父类
public class Animal {
    protected String name="旺财";

    public void func() {
        System.out.println(name);
    }
}

包二:

package inherit_2;

import inherit.Animal;

//包二中的类作为子类
public class Dog extends Animal {
    public void func() {
        System.out.println(super.name); //用super访问父类成员
    }

    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.func();
    }
}

结果演示:

旺财

5.2、final关键字

作用:

  1. 修饰变量或成员变量,表示常量
  2. 修饰,表示该类不能被继承
  3. 修饰方法,表示该方法不能重写

二、多态

1、多态的实现

1.1、多态的概念

多种形态,具体来说就是:同一个引用调用同一个方法,结果不一样,(因为引用对象不同)

1.2、多态实现条件

Java中要实现多态,必须满足以下条件:

  1. 必须在继承体系下
  2. 子类必须对父类中的方法进行重写
  3. 通过父类的引用调用重写的子类方法

1.3、例子

代码如下:

package multiple_forms;

//父类
class Animal {
    public String name;
    public Animal(String name) {
        this.name=name;
    }

    //重写
    public void eat() {
        System.out.println(name + "吃饭");
    }
}
//Dog子类
class Dog extends Animal {
    public Dog(String name) {
        super(name);  //调用父类的构造方法进行初始化
    }

    @Override
    public void eat() {
        System.out.println(super.name + "吃狗粮");
    }
}
//Cat子类
class Cat extends Animal {
    public Cat(String name) {
        super(name); //调用父类的构造方法
    }

    @Override
    public void eat() {
        System.out.println(super.name + "吃猫粮");
    }
}

//Mulf类
public class Mulf {
    //向上转型法一
    public static void func(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        //dog对象和cat对象调用同一个方法,结果不同
        Dog dog = new Dog("旺财2.0");
        func(dog);
        Cat cat = new Cat("咪咪2.0");
        func(cat);
    }
    //向上转型法二
//    public static void main(String[] args) {
//        Animal animal = new Dog("旺财");  //向上转型
//        Animal animal1 = new Cat("咪咪"); //向上转型
//        animal.eat();  //
//        animal1.eat();
//    }
}

结果演示:

旺财2.0吃狗粮
咪咪2.0吃猫粮

理解:

dog对象和cat对象调用同一个方法func,func方法用Animal类型(父类)的animal接收打印结果不同。即同一个引用调用同一方法产生不同结果,因为对象不同,所以调用方法执行的行为不一样。

2、重载和重写

2.1、重载(overload)

同一个类中或具有继承关系的两个类(父子类)中,满足:

  1. 成员方法名相同
  2. 参数(数据类型,个数,顺序)不同
  3. 返回值无要求
  4. 必须是同一类或者是父子类

2.2、重写(override)

一定发生在继承上,子类对父类非static非final非private方法的重新实现。满足:

  1. 成员方法名参数都相同
  2. 返回值类型相同或为父子关系
  3. 该方法访问权限必须子类大于等于父类
  4. 父类被static,final,private修饰的方法不能重写。
  5. 给子类+@Override检查重写是否正确

3、向上转型和向下转型

3.1、向上转型

一、概念

1、核心:创建一个子类对象,把它当成父类对象来使用。

2、格式:父类类型 对象名 = new 子类类型();如:

Animal animal = new Dog(),此时animal这个引用指向的是一个Dog类。从右往左理解:狗是动物,所以,Animal类型的animal可以接收Dog类型的对象。

3、向上转型优缺点:优:让代码更灵活简便。缺:不能调用到子类特有的方法和属性

二、向上转型的三种方式

 法一:直接接收(animal接收Dog对象)

//直接接收(animal接收Dog对象)
//Mulf类
public class Mulf {
    public static void main(String[] args) {
        Animal animal = new Dog("旺财2.0");  //向上转型
        animal.eat();
    }
}

法二:方法传参

//利用方法传参
//Mulf类
public class Mulf {
    public static void func(Animal animal) {  //用Animal类型的animal接收Dog类型的dog对象
        animal.eat();
    }
    public static void main(String[] args) {
        Dog dog = new Dog("旺财2.0");
        func(dog);
    }
}

法三:方法返回

注意:方法必须加static,因为传进去时还没有建立对象

//利用返回值   返回子类,再转换为父类
public static Animal func(String var) { //注意方法必须加static,因为传进来时还没有对象
        if(var.equals("狗")) {
            return new Dog("旺财3.0");
        }else if(var.equals("猫")) {
            return new Cat("咪咪3.0");
        }else {
            return null;
        }
    }
    public static void main(String[] args) {
        func("狗").eat();
    }
}

3.2、向下转型

目的
在向上转型完成后能够访问子类的方法和属性
理解
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为(强转)子类对象即可,即向下转换。
建议:很少使用向下转型,可以理解为动物是狗说不通。
核心代码:
    Animal animal= new Dog();
    //确认animal是否引用了Dog对象
    if(animal isstanceof Dog) { //返回值是Boolean类型
    Dog dog = (Dog)animal;//向下转型
    dog.//特有属性或方法
    }

4、静态绑定和动态绑定

4.1、静态绑定

也叫前期绑定,在编译时,根据传入参数(类型,个数,顺序)确定调用的方法。

4.2、动态绑定

一、概念

也叫后期绑定,在编译时,不能确定方法,运行时才能确定。即:
Animal animal = new Dog()

animal.eat()

animal对象编译时用Animal.eat,但运行时animal对象调用Dog.eat打印。

简单来说就是:满足重写和向上转型,父类类型引用调用重写方法时,调用的是子类的

动态绑定的条件是:

  • 有重写
  • 发生向上转型

二、例子:

package multiple_forms;

//父类
class Animal {
    public String name;
    public Animal(String name) {
        this.name=name;
    }

    //重写
    public void eat() {
        System.out.println(name + "吃饭");
    }
}
//Dog子类
class Dog extends Animal {
    public Dog(String name) {
        super(name);  //调用父类的构造方法进行初始化
    }

    @Override
    public void eat() {
        System.out.println(super.name + "吃狗粮");
    }
}
//Cat子类
class Cat extends Animal {
    public Cat(String name) {
        super(name); //调用父类的构造方法
    }

    @Override
    public void eat() {
        System.out.println(super.name + "吃猫粮");
    }
}

//Mulf类
public class Mulf {
    public static void main(String[] args) {
        Animal animal = new Dog("旺财");  //向上转型
        Animal animal1 = new Cat("咪咪"); //向上转型
        animal.eat();  
        animal1.eat();
    }
}

结果演示:

旺财吃狗粮
咪咪吃猫粮

三、Mulf解析

Mulf代码:

//Mulf类
public class Mulf {
    public static void main(String[] args) {
        Animal animal = new Dog("旺财");  //向上转型
        Animal animal1 = new Cat("咪咪"); //向上转型
        animal.eat();  
        animal1.eat();
    }
}

Mulf反汇编:

理解:

  • 子类没有eat时(即没有重写):编译和运行都是父类Animal的eat方法。
  • 子类有eat时(重写):编译的时候还是父类Animal的eat()方法,但是程序运行的时候变成了子类的,所以叫做动态绑定

四、扩展:反汇编调用方法

  1. 右键Open in ->Explorer->找到out路径->production路径->工程名->包名
  2. 点项目名.class->点上面的路径->输入cmd调出命令指示符->输入javap -c 项目名

5、多态的优缺点

5.1、多态优点:

  1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else
  2. 可扩展能力更强

优点1:降低代码的 "圈复杂度"

多态实现代码

//多态
class Shape {
    //属性...
    public void draw() {
        System.out.println("画图形");
    }
}
class Rate extends Shape {
    @Override
    public void draw() {
        System.out.println("画矩形");
    }
}
class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("画圆");
    }
}
class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("画花");
    }
}

public class Mulf {
    public static void drawShapes() {
        Shape[] shapes = {new Cycle(),new Rate(),new Flower()};
        for (Shape shape:shapes) {
            shape.draw();
        }
    }
    public static void main(String[] args) {
        drawShapes();
    }
}
 不用多态 例如我们现在需要打印 多个形状图形 . 如果不基于多态 实现代码如下
public static void drawShapes() { 
    Rect rect = new Rect();
    Cycle cycle = new Cycle(); 
    Flower flower = new Flower(); 
    String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"}; 
    for (String shape : shapes) { 
        if (shape.equals("cycle")) {
        cycle.draw(); 
        } else if (shape.equals("rect")) { 
            rect.draw(); 
        } else if (shape.equals("flower")) { 
            flower.draw(); 
        }
    }
}
用多态 , 则不必写这么多的 if - else 分支语句 , 代码更简单:
public static void drawShapes() { // 我们创建了一个 Shape 对象的数组. 
     Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), new Rect(), new Flower()};
     for (Shape shape : shapes) {
         shape.draw(); 
     }
}

优点2:可扩展能力更强

如:Shape[] shapes = { new Cycle(), new Rect() },里面可以继续追加加对象,很方便,不像if-else还需要改方法内部。

5.2、多态缺点

代码的运行效率降低 。(效率影响不太大)

5.3、注意

  1. 属性没有多态性 ,当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
  2. 构造方法没有多态性

6、避免在构造方法中调用重写的方法

例子代码如下:

class B {
    public B() { // do nothing func(); }
        public void func () {
            System.out.println("B.func()");
        }
    }
}
class D extends B {
    private int num = 1;
    @Override
    public void func() { System.out.println("D.func() " + num);
    }
}
public class Test {
    public static void main(String[] args) { 
        D d = new D(); 
    }
}// 执行结果 D.func() 0
  1. 构造 D 对象的同时, 会调用 B 的构造方法
  2. B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  3. 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0. 如果具备多态性,num的值应该是1
  4. 所以在构造函数内,尽量避免使用实例方法,除了final、static、private方法。 ​​​​

注意:"用尽量简单的方式使对象进入可工作状态", 尽量不要在构造方法中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题.

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学Java的冬瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值