JavaSE—继承与多态

本文详细探讨了Java中的继承与多态。继承允许子类从父类继承属性和方法,提供了代码重用和扩展性。子类不能选择性继承,但可以通过方法重写来覆盖父类的方法。多态依赖于继承,通过向上转型,一个父类引用可以指向不同的子类对象,运行时决定具体形态。多态提高了代码的灵活性和可维护性。
摘要由CSDN通过智能技术生成

1、继承

1.1、继承的定义

在Java中,继承是一种面向对象编程的概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。通过继承,子类可以获得父类的特性,并且可以在其基础上添加新的属性和方法,或者修改继承的属性和方法的行为。继承使用关键字extends来指示子类继承父类,并且在继承关系中,子类可以访问父类的非私有成员。这种机制使得代码的重用和扩展更加方便,同时也符合面向对象编程的原则之一——"is-a"关系,即子类是父类的一种特殊类型。

注意子类不能选择性的继承父类。

        子类不能选择性地继承父类的属性和方法。当一个类继承另一个类时,它会继承所有非私有的属性和方法,无法选择性地排除某些成员。子类继承了父类的全部特性,包括父类的公有成员、受保护成员和默认(包内可见)成员,但不会继承父类的私有成员。

        如果子类需要排除父类的某些成员,可以考虑在子类中重新定义这些成员,并覆盖(override)父类的实现。通过覆盖父类的方法,子类可以提供自己的实现逻辑,并完全替代父类的方法行为。这种方式称为方法重写(method overriding)。

        总结起来,子类不能选择性地继承父类,但可以通过方法重写来替代父类的方法实现

如果有两个对象A和B,若可以描述为“A是B”,则可以表示A继承B,其中B是被继承者称之为父类或者超类或者基类,A是继承者称之为子类或者派生类。

实际上继承者是被继承者的特殊化,它除了拥有被继承者的特性外,还拥有自己独有得特性例如猫有抓老鼠、爬树等其他动物没有的特性。同时在继承关系中,继承者完全可以替换被继承者,反之则不可以,例如我们可以说猫是动物,但不能说动物是猫就是这个道理,其实对于这个我们将其称之为“向上转型”。 

1.2、继承的特点

  1. 使用extends关键字来表示继承关系
  2. 相当于子类把父类的功能复制了一份
  3. Java只支持单继承
  4. 继承可以传递(爷爷/儿子/孙子这样的关系)
  5. 父类的私有成员由于私有限制访问,所以子类不能使用父类的私有资源
  6. 继承多用于功能的修改,子类可以在拥有父类功能的同时,进行功能拓展
  7. 像是is a的关系

1.3、构造方法

        构造方法是一种特殊的方法,用于创建和初始化对象。它在对象被创建时自动调用,用于设置对象的初始状态。构造器的名称必须与类名完全相同,并且没有返回类型,同一个类可以定义多个构造器(可以重载),

        默认构造方法:如果类没有定义任何构造方法,编译器会自动生成一个默认构造方法。默认构造方法没有参数,不执行任何操作,仅仅是提供了一个无参的构造方法,以便于创建对象。如果父类没有默认构造方法,我们就要必须显示的使用super()来调用父类构造方法,而且必须是在子类构造方法中做的第一件事(第一行代码)。否则编译器会报错无法找到符合父类形式的构造方法。

1.4、protected关键字

使用 protected 关键字时,需要注意以下几点:

  1. 访问权限protected 访问修饰符提供了更大的访问权限,允许子类和同一包内的其他类访问被修饰的成员(字段、方法、构造方法)。但是,其他包中的类无法直接访问被 protected 修饰的成员。

  2. 封装性:使用 protected 可能会破坏类的封装性,因为子类和同一包内的其他类可以直接访问被 protected 修饰的成员。确保在使用 protected 时仍然遵循良好的封装原则,只暴露必要的接口给外部。

  3. 继承关系protected 修饰符在继承中具有特殊的作用。子类可以访问父类的 protected 成员,无论子类和父类是否在同一包中。这种机制允许在继承关系中进行一定程度的扩展和重写,但需要谨慎使用,避免破坏封装性和导致不必要的耦合。

  4. 可见性控制:使用 protected 应慎重考虑哪些成员需要被子类和同一包内的其他类访问。确保只有需要被继承和扩展的成员才使用 protected 修饰符,避免暴露过多的实现细节。

  5. 文档和指导:对于被 protected 修饰的成员,应提供适当的文档和指导,清楚说明其使用方式和限制条件,以帮助其他开发人员正确理解和使用。

总结来说,使用 protected 修饰符需要权衡访问权限和封装性,确保合理使用,并遵循设计原则和最佳实践。它在继承关系中扮演重要角色,用于控制子类对父类成员的访问,但需要注意其潜在的影响和限制。

1.5、 super

我们可以把super看作是父类的对象:Father super = new Father();
1.当父类的成员变量与子类的变量同名时,使用super指定父类的成员变量
2.使用super在子类构造方法的第一行调用父类构造方法的功能
super();–调用的是父类的无参构造
super(参数);–调用的是父类对应参数的构造方法
注意:在构造方法里,出现的调用位置必须是第一行

1.6、 方法重写Override

1、继承以后,子类就拥有了父类的功能
2、在子类中,可以添加子类特有的功能,也可以修改父类的原有功能
3、子类中方法的签名(具有相同的方法名、参数列表和返回类型,)与父类完全一样时,会发生覆盖/复写的现象
4、注意: 父类的私有方法不能被重写
5、重写的要求:两同两小一大
        两同:方法名 参数列表 要完全一致
        两小:子类返回值类型小于等于父类的返回值类型(注意此处说的是继承关系,不是值大小),子类抛出异常小于等于父类方法抛出异常
        一大:子类方法的修饰符权限要大于等于父类被重写方法的修饰符权限

1.7、拓展

1.7.1 继承的好处与坏处

1、继承的好处:
a. 提高了代码的复用性(多个类相同的成员可以放在同一个类中)
b. 提高了代码的维护性(如果方法的代码需要修改,只修改一处即可)
2、继承的坏处:
a. 继承让类与类建立了关系,类的耦合性增强
b. 当父类发生变化时,子类实现也不得不跟着变化,削弱了子类的独立性

1.7.2 this与super的区别

 注意:1. super的使用前提是继承,没有父子类关系,就没有super
            2. this调用构造方法或者super调用构造方法,都必须出现在构造方法的第一行
            3. 如果父类没有无参构造,需要手动在子类构造方法的第一行调用其他的含参构造

            4. 如果子类重写了父类的方法以后,可以使用super.方法名(参数列表)来调用

1.7.3 重载Overload 与重写Override的区别
重载:在一个类中的现象:同一个类中,存在方法名相同,参数列表不同的方法
重写:是指建立了继承关系以后,子类对父类的方法不满意,可以重写,遵循两同两小一大原则
重载的意义:是为了外界调用方法时方便,不管传入什么样的参数,都可以匹配到对应的同名方法
重写的意义:在不修改源码的情况下,进行功能的修改与拓展(OCP原则:面向修改关闭,面向拓展开放)
 

 2、多态

2.1、多态的定义

多态是建立在继承的基础上的,一个父类对象可以产生多个不同的子类对象,根据这些子类对象的不同可以具备不同的方法,也就是说表现出了不同的形态即多态。

2.2 、特点

Java实现多态有三个必要条件:继承、重写、向上转型

    1、继承:在多态中必须存在有继承关系的子类和父类。

    2、重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。

    3、向上转型:父类引用指向子类对象,如:Animal a = new Cat();

    4、多态中,编译看左边,运行看右边

2.3、练习:多态入门案例

/*本类用作多态的入门案例*/
/*1.多态的前提:继承+重写*/
//1.创建父类
class Animal{
    //3.创建父类的普通方法
    public void eat(){
        System.out.println("小动物Animal吃啥都行~");
    }
}
//2.1创建子类1
class Cat extends Animal{
    //4.1添加重写的方法
    public void eat(){
        System.out.println("小猫爱吃小鱼干~");
    }
    //5.1添加子类的特有功能
    public void jump(){
        System.out.println("小猫Cat跳的老高啦~");
    }
}
//2.2创建子类2
class Dog extends Animal{
    //4.2添加重写的方法
    @Override
    public void eat(){
        System.out.println("小狗爱吃肉骨头~");
    }
    //5.2添加子类的特有功能
    public void run(){
        System.out.println("小狗Dog跑的老快啦~");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        //6.创建“纯纯的”对象用于测试
        Animal a = new Animal();
        Cat c = new Cat();
        Dog d = new Dog();
        a.eat();//小动物Animal吃啥都行~调用的是父类自己的功能
        c.eat();//小猫爱吃小鱼干~调用的是子类重写后的功能
        d.eat();//小狗爱吃肉骨头~调用的是子类重写后的功能
        /*2.父类对象不可以使用子类的特有功能*/
        //a.jump();//报错,Animal类里并没有这个方法
        //a.run();//报错,Animal类里并没有这个方法
        c.jump();//小猫Cat跳的老高啦~,子类可以调用自己的功能
        d.run();//小狗Dog跑的老快啦~,子类可以调用自己的功能

        //7.创建多态对象进行测试
        /*3.口诀1:父类引用指向子类对象
        * 解释:创建出来的子类对象的地址值,交给父类类型的引用类型变量来保存*/
        Animal a2 = new Cat();//Cat类对象的地址值交给父类型变量a2来保存
        Animal a3 = new Dog();//Dog类对象的地址值交给父类型变量a3来保存
        //8.测试多态对象
        /*4.口诀2:编译看左边,运行看右边
        * 解释:必须要在父类定义这个方法,才能通过编译,把多态对象看作是父类类型
        *      必须要在子类重写这个方法,才能满足多态,实际干活的是子类*/
        a2.eat();//小猫爱吃小鱼干~,多态对象使用的是父类的定义,子类的方法体
    }
}

 2.4、多态的使用

前提:多态对象把自己看做是父类类型

1. 成员变量: 使用的是父类的(若父类和子类中同时拥有同名的成员变量,想要访问子类的成员变量,就需要强制类型转换)

class Animal {
    String name = "Animal";
}

class Dog extends Animal {
    String name = "Dog";
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();  // 多态
        System.out.println(animal.name);  // 输出 "Animal"
    }
}
class Animal {
    String name = "Animal";
}

class Dog extends Animal {
    String name = "Dog";
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();  // 多态
        
        // 强制类型转换为 Dog 类型
        Dog dog = (Dog) animal;
        
        // 使用 Dog 类型引用访问 Dog 类中的成员变量
        System.out.println(dog.name);  // 输出 "Dog"
    }
}

2. 成员方法: 由于存在重写现象,所以使用的是子类的

3. 静态成员: 随着类的加载而加载,谁调用就返回谁的

2.5 拓展

2.5.1 案例练习

package cn.tedu.oop2;
/*本类用于完成汽车设计案例*/
public class DesignCar {
    public static void main(String[] args) {
        //9.创建一个纯纯的父类对象进行测试
        Car c = new Car();
        System.out.println(c.getColor());//null
        c.start();
        c.stop();
        //c.swim();//报错,父类对象不可以调用子类的特有功能

        //10.创建纯纯的子类对象做测试
        BMW b = new BMW();
        System.out.println(b.color);//五彩斑斓的黑
        System.out.println(b.getColor());//null
        b.start();//都让开,我的车要起飞啦~
        b.stop();//唉呀妈呀熄火了~

        //11.创建多态对象进行测试
        Car c2 = new TSL();
        //System.out.println(c2.color);
        System.out.println(c2.getColor());
        c2.stop();
        c2.start();
        //c2.swim();
    }
}
//1.通过分析,抽象形成一个汽车类
class Car{
    //2.定义并封装汽车类的属性--成员变量
    private String brand;//品牌
    private String color;//颜色
    private int id;//编号
    private double price;//价格

    //3.定义功能
    public void start(){
        System.out.println("我的小车车启动啦~");
    }
    public void stop(){
        System.out.println("唉呀妈呀熄火了~");
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

//4.创建子类
class BMW extends Car{
    String color = "五彩斑斓的黑";
    //5.重写父类的方法
    @Override
    public void start(){
        System.out.println("都让开,我的车要起飞啦~");
    }
}
//6.创建子类2
class TSL extends Car{
    //7.重写父类的方法
    @Override
    public void stop(){
        System.out.println("唉呀妈,怎么停不下来呢");
    }
    //8.添加子类的特有功能
    public void swim(){
        System.out.println("没想到吧,我还是个潜水艇");
    }
}

2.5.2  多态为了统一调用标准

package cn.tedu.oop2;

public class TestFruit {
    public static void main(String[] args) {
        Fruit f = new Fruit();
        Apple a = new Apple();
        Orange o = new Orange();
        get(f);
        get(a);
        get(o);
    }
    //只需要创建一个方法,就可以执行截然不同的效果
    //忽略子类对象的差异统一看作父类类型
    public static void get(Fruit f){
        f.clean();
    }
}
class Fruit{
    public void clean(){
        System.out.println("水果要洗洗再吃");
    }
}
class Apple extends Fruit{
    @Override
    public void clean(){
        System.out.println("苹果需要削皮");
    }
}
class Orange extends Fruit{
    @Override
    public void clean(){
        System.out.println("橙子需要剥皮");
    }
}

2.5.3  静态变量和实例变量的区别

1、在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
2、在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
 

2.5.4 向上转型和向下转型
在JAVA中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。
那么在这个过程中就存在着多态的应用。存在着两种转型方式,分别是:向上转型和向下转型
1、向上转型:可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
比如:父类Parent,子类Child
父类的引用指向子类对象:Parent p=new Child();
说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类中声明过的方法,方法体执行的就是子类重过后的功能。但是此时对象是把自己看做是父类类型的,所以其他资源使用的还是父类型的。
比如:花木兰替父从军,大家都把花木兰看做她爸,但是实际从军的是花木兰,而且,花木兰只能做她爸能做的事,在军营里是不可以化妆的。

2、向下转型(较少):子类的引用的指向子类对象,过程中必须要采取到强制转型。这个是之前向上造型过的子类对象仍然想执行子类的特有功能,所以需要重新恢复成子类对象
Parent p = new Child();//向上转型,此时,p是Parent类型
Child c = (Child)p;//此时,把Parent类型的p转成小类型Child
其实,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的
说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
比如:花木兰打仗结束,就不需要再看做是她爸了,就可以”对镜贴花黄”了
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值