JavaSE->继承和多态

目录

一、继承

1.继承的概念

2.继承的语法

3.父类成员访问 

3.1子类中访问父类的成员变量

3.2子类中访问父类的成员方法

4.super关键字

4.1子类的构造方法

4.2super和this

5.继承关系

​编辑

6.再谈初始化

7.final关键字

7.1final修饰变量或字段(常量)

7.2final修饰类

7.3fianl修饰方法

8.继承和组合

二、多态

1.概念

2.多态实现条件

3.向上转型

3.1概念

3.2语法格式

3.3使用场景

4.向下转型

4.1instanceof

4.2向下转型

5.重写  

5.1概念

5.2重写的规则

5.3重写与重载的区别

6.动态绑定 (运行时绑定)

7.静态绑定

8.Object类

9.多态的优缺点

9.1降低圈复杂度

9.2可扩展能力更强

9.3缺点

10.避免在构造方法中调用重写方法


一、继承

1.继承的概念

用来进行共性抽取,实现代码的复用。

继承机制:在保持原有类特性的基础上进行扩展,增加新功能,产生新的类,称为派生类。

父类/基类/超类

子类/派生类 

2.继承的语法

修饰符 class 子类 extends 父类{

        //~~~~

}

class Animal {
//父类
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name + "正在吃饭");
    }
    
    public void sleep() {
        System.out.println(this.name + "正在睡觉");
    }
}

class Dog extends Animal {
//子类
    public void bark() {
        System.out.println(this.name + "汪汪汪");
    }
}

子类会将父类中的成员变量或者成员方法继承到子类中

子类继承父类之后,必须新添加自己特有的成员变量,体现出与基类的不同,否则就没有必要继承

3.父类成员访问 

3.1子类中访问父类的成员变量

如果访问的成员变量子类中有,优先访问自己的成员变量

如果访问的成员变量子类中无,则访问父类继承下来的,如果父类中也无定义,则编译报错

如果访问的成员变量与父类中成员变量同名,则优先访问自己的(成员变量访问遵循就近原则)

父类中的private修饰的成员变量在子类中不能直接访问,但也是继承到子类中了 

3.2子类中访问父类的成员方法

通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错

通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错

//父类
class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name + "正在吃饭");
    }

    public void sleep() {
        System.out.println(this.name + "正在睡觉");
    }
}

//子类
class Dog extends Animal{
    public void eat() {
        System.out.println(this.name + "正在吃饭");
    }

    public void sleep(int num) {
        System.out.println(this.name + "正在睡觉");
    }
    public void bark() {
        System.out.println(this.name + "汪汪汪");
    }
}

public class Text {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat(); //访问子类自己的eat()方法
        dog.sleep();//访问父类的sleep()方法
        dog.sleep(2);//访问子类的sleep()方法
        dog.bark();//访问子类自己的bark()方法
    }
}

4.super关键字

在子类中访问父类的成员

注意事项:

只能在非静态方法中使用

在子类方法中,访问父类的成员方法或变量

super只能指代当前的父类,不能指代父类的父类,甚至更远

4.1子类的构造方法

子类对象构造时,需要先调用父类构造方法,然后再执行子类的构造方法

若父类显示定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用父类的构造方法

如果父类构造方法是带有 参数的,此时需要用户为子类显示定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败

在子类构造方法中,super(~)调用父类构造方法时,必须是子类构造函数中的第一条语句

super(~)只能在子类构造方法中出现一次,并且不能和this()同时出现

class Base {
    public Base() {
        System.out.println("Base()");
    }
}

class Derived extends Base {
    public Derived() {
        //super();
        //用户没有写时,编译器·会自动添加,而且super()必须是子类构造方法中的第一条语句
        //比且只能出现一次
        System.out.println("Derived()");
    }
}

public class Text {
    public static void main(String[] args) {
        Derived d = new Derived();
    }
}
//Base()
//Derived()

//Base()比Derived()先执行
class Animal {
    public String name;
    public int age;

    public Animal(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(this.name + "正在吃饭");
    }

    public void sleep() {
        System.out.println(this.name + "正在睡觉");
    }
}

class Dog extends Animal{
    public Dog() {
//调用父类的构造方法
        super("小黑",5);
    }

    public Dog(String name,int age) {
//调用父类的构造方法
        super(name,age);
    }

    public void eat() {
        System.out.println(this.name + "正在吃饭");
    }

    public void bark() {
        System.out.println(this.name + "汪汪汪");
    }
}

public class Text {
    public static void main(String[] args) {
        Dog dog = new Dog("小黑",5);
        System.out.println(dog.name);
        System.out.println(dog.age);
        dog.eat(); //访问子类自己的eat()方法
        dog.sleep();//访问父类的sleep()方法
        dog.bark();//访问子类自己的bark()方法
    }
}
//小黑
//5
//小黑正在吃饭
//小黑正在睡觉
//小黑汪汪汪
4.2super和this

相同点:

都是Java中的关键字

只能在类的非静态方法中使用,用来访问非静态成员和字段

在构造方法调用时,必须是构造方法的第一句,并不能同时存在

不同点:

this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类中从父类其中继承下来部分成员的引用

在非静态成员方法中,this用来访问本类方法和属性,super用类访问子类继承父类下来的方法和属性

在构造方法中:this(~)用于调用本类构造方法,super(~)用于调用父类构造方法,两者调用不能同时出现在构造方法中

不带参数的构造方法中一定会存在super(~)的调用,用户没写编译器也会增加,但是this()用户不写则没有

5.继承关系

6.再谈初始化

父类静态代码块优先于子类静态代码块执行,且是最早执行的

父类实例代码块和父类构造方法紧接着执行

子类的实例代码块和构造方法紧接着再执行

第二次实例化子类对象时,父类和子类的静态代码块都将不会再次执行

class Base {

    static {
        System.out.println("Base()::静态代码块执行");
    }

    {
        System.out.println("Base()::实例代码块执行");
    }

    public Base() {
        System.out.println("Base()::构造方法执行");
    }
}

class Derived extends Base {

    static {
        System.out.println("Derived()::静态代码块执行");
    }

    {
        System.out.println("Derived()::实例代码块执行");
    }
    public Derived() {
        //super();
        //用户没有写时,编译器·会自动添加,而且super()必须是子类构造方法中的第一条语句
        //比且只能出现一次
        System.out.println("Derived()::构造方法执行");
    }
}

public class Text {
    public static void main(String[] args) {
        Derived d = new Derived();
    }
}
//Base()::静态代码块执行
//Derived()::静态代码块执行
//Base()::实例代码块执行
//Base()::构造方法执行
//Derived()::实例代码块执行
//Derived()::构造方法执行

7.final关键字

7.1final修饰变量或字段(常量)
public class Text {
    public static void main(String[] args) {
        final int a = 10; // fianl修饰的变量或者字符是常量
        a = 20; // 报错,a是常量,不能被修改
    }
}
7.2final修饰类

表示此类不能被继承

平时用的String字符串类,就使用final修饰的,不能被继承

7.3fianl修饰方法

表示方法不能被重写

8.继承和组合

//轮胎类
class Tire {
    //~~
}

//发动机类
class Engine {
    //~~
}

//车载系统类
class VehicleSystem {
    //~~
}

//组合
class Car {
    private Tire tire;//可以复用轮胎中的属性和方法
    private Engine engine;//可以复用发动机中的属性和方法
    private VehicleSystem vs;//可以复用车载系统中的属性和方法
    //~~
}

class Benz extends Car {
    //其汽车中包含的:轮胎、发动机、车载系统全部继承下来
}

组合和继承都可以实现代码的复用,一般建议:能组合尽量用组合

二、多态

1.概念

就是完成某个行为,当不同的对象去完成时会产生不同的状态

2.多态实现条件

必须在继承体系下

子类必须要对父类中的方法进行重写

通过父类的引用调用重写的方法

3.向上转型

3.1概念

实际就是创建一个子类对象,将其当成父类对象来使用

3.2语法格式

父类类型 对象名 = new 子类类型();

Animal cat = new Cat();
//cat是Animal的子类

 Animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换

3.3使用场景

直接赋值:

class Animal {
    String name;
    int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "吃饭~");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name + "吃骨头~");
    }
}

class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name + "吃鱼~");
    }
}

public class Text {
    public static void main(String[] args) {
        Animal dog = new Dog("小黑",2);//向上转型
        Animal cat = new Cat("小花",1);//向上转型
        dog.eat();
        cat.eat();
    }
}
//小黑吃骨头~
//小花吃鱼~

方法传参:形参为父类引用,可以接收任意子类的对象

class Animal {
    String name;
    int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "吃饭~");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name + "吃骨头~");
    }
}

class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name + "吃鱼~");
    }
}

public class Text {
//向上转型
    public static void eat(Animal a) {
        a.eat();
    }
    public static void main(String[] args) {
        Dog dog = new Dog("小黑",2);
        Cat cat = new Cat("小花",1);
        eat(dog);
        eat(cat);
    }
}
//小黑吃骨头~
//小花吃鱼~

 方法返回:返回任意子类对象

class Animal {
    String name;
    int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "吃饭~");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name + "吃骨头~");
    }
}

class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name + "吃鱼~");
    }
}

public class Text {
//向上转型
    public static Animal buyAnimal(String var) {
        if("狗".equals(var)) {
            return new Dog("狗狗",2);
        }else if("猫".equals(var)) {
            return new Cat("猫猫",1);
        }else {
            return null;
        }
    }

    public static void main(String[] args) {
        Animal animal1 = buyAnimal("狗");
        Animal animal2 = buyAnimal("猫");
        animal1.eat();
        animal2.eat();
    }
}

向上转型的优点:让代码直线更简单灵活

向上转型的缺点:不能调用子类特有的方法

4.向下转型

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类特有的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转型

4.1instanceof

保留关键字,二元运算符,左边对象,右边是类,返回类型为boolean,当对象是右边类或子类所创建对象时,返回true;否则,返回false。

4.2向下转型
class Animal {
    String name;
    int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "吃饭~");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name + "吃骨头~");
    }
    public void bark() {
        System.out.println(name + "汪汪汪~");
    }
}

class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name + "吃鱼~");
    }
    public void acting() {
        System.out.println(name + "抓老鼠~");
    }
}

public class Text {
    public static void main(String[] args) {
        Animal animal1 = new Dog("小黑",2);//向上转型
        Animal animal2 = new Cat("小花",1);//向上转型
        animal1.eat();
        animal2.eat();
        //向下转型
        if(animal1 instanceof Dog) {//判断animal1是否为Dog的实例对象
            Dog dog =(Dog)animal1;//向下转型
            dog.bark();
        }
        if(animal2 instanceof Cat) {//判断animal2是否为Cat的实例对象
            Cat cat =(Cat)animal2;//向下转型
            cat.acting();
        }
    }
}
//小黑吃骨头~
//小花吃鱼~
//小黑汪汪汪~
//小花抓老鼠~

5.重写  

5.1概念

也称为覆盖。重写是子类对父类非静态、非private、非final、非构造方法等的实现过程进行重新编写,返回值和形参都不能改变。

5.2重写的规则

子类在重写父类的方法时,一般必须与父类方法的原型一致 :返回值类型、方法名、参数列表,要完全一致

被重写的方法返回值类型可以不同,但是必须是具有父子关系的

访问权限不能比父类重写的方法的访问权限更低。例如:如果父类方法被public修饰,那么子类重写该方法就不能声明为proteccted

父类被static、private、final修饰的方法、构造方法都不能被重写

5.3重写与重载的区别
区别点重写(override)重载
参数列表一定不能修改必须修改
返回值类型一定不能修改【除非可以构成父子类关系】可以修改
访问限定符一定不能做更严格的限制可以修改

即:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现 

6.动态绑定 (运行时绑定)

在编译时,不能确定方法的行为,需要等到程序运行时,才能确定具体调用哪个类的方法

当父类引用子类对象,子类重写父类的方法时,通过父类的引用调用父类和子类的重写方法,最终显示是调用了子类的方法

7.静态绑定

在编译时,根据用户所传递实参类型就确定了具体调用了哪个方法,如:方法的重载

8.Object类

所有类的父类,就算没有显示继承关系,也是Object的子类

9.多态的优缺点

优点:

9.1降低圈复杂度

若要打印圆、花、花、圆。

class Shape {
    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 Text {
//用多态
    public static void main(String[] args) {
        Shape[] shapes = {new Cycle(),new Flower(),new Flower(),new Cycle()};
        for(Shape shape : shapes) {
            shape.draw();
        }
    }

//不用多态
    public static void main1(String[] args) {
        Cycle cycle = new Cycle();
        Flower flower = new Flower();
        String[] shapes = {"cycle","flower","flower","cycle"};
        for(String shape : shapes) {
            if(shape.equals("cycle")) {
                cycle.draw();
            }else if(shape.equals("flower")) {
                flower.draw();
            }
        }
    }
}
//⚪
//❀
//❀
//⚪
9.2可扩展能力更强

上述代码可一另加别的图形

public class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("🔺");
    }
}
9.3缺点

代码的运行效率低

属性没有多态性,当父类子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

通过父类引用不能调用子类特有的属性和方法(向上转型)

构造方法没有多态性

10.避免在构造方法中调用重写方法

class B {
    public B() {
        func();
    }
    public void func() {
        System.out.println("B.func()~");
    }
}

class D extends B {
    private int num1 = 1;
    final int num2 = 2;
    @Override
    public void func() {
        System.out.println("D.func()~" + num1 + " "  num2);
    }
}
public class Text {
    public static void main(String[] args) {
        new D();
    }
}
//D.func()~0 2

构造D对象的同时,会调用B的构造方法,B的构造方法中调用了func()方法,此时会触发动态绑定,会调用到D中的func()

此时D对象自身还没有构造,num1处于未初始化的状态,值为0。如果具有多态性,num1的值应为1

所以在构造函数内,尽量避免使用实例方法,除了final和静态方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值