java基础-继承

 

 

 女神镇楼

1、类的继承

 继承是面向对象中的一个非常重要的概念,是的整个程序具有一定的弹性,在程序中,复用一些已经定义的完善的类,不仅可以减少软件的开发周期,还可以提高软件的可维护性和可拓展性。

    继承的基本思想是基于某个父类的拓展,制造出一个新的子类,子类可继承父类的属性和方法,也可以增加原父类不具备的属性和方法,或者重写父类中的某个方法。比如平行四边形继承了四边形类,这个时候,平行四边形类将所有四边形类中的属性和方法保留下来,并拓展了新的属于自己的属性和方法。

代码示例---父类:

public class Test {
    public Test(){ //构造方法
        //SomeSentence
    }
    protected void doSomething(){
        // 成员方法
    }
    protected Test dolt(){
        // 方法返回值类型为Test类型
        return new Test();
    }
}

 

代码示例---子类:

public class Test2 extends Test {
    public Test2(){
        //构造方法
        super(); // 调用父类的构造方法
        super.doSomething(); //调用父类的成员方法
    }
    public void doSomethingNeW(){
        //新增方法
    }
    public void doSomeThing(){
        //重写父类方法
    }
    protected Test2 dolt(){
        //重写父类方法,返回值类型为Test2类型
        return new Test2();
    }
}

 

实例说明:

    实例中,Test类是Test2类的父类,Test2类是Test类的子类,在子类中连同初始化父类的构造方法来完成子类的初始化操作,即可以在子类的构造方法中使用super()语句调用父类的构造方法,也可以在子类中通过super关键字调用父类的成员方法。但是在子类中没有权限调用父类中使用private修饰的方法,只可以调用父类中修饰为public或者protected的成员方法。

    继承不止可以拓展父类的功能,还可以对父类的方法进行重写,重写也可以成为覆盖,就是在子类中,将父类的成员方法的名称保留,重写成员方法实现的内容,可更改成员方法的储存权限,或者修改成员方法的返回值类型,还可以将权限修饰符修改为public。

    继承中还有一种特殊的重写方式,子类与父类的成员方法的返回值、方法名称、参数类型及个数完全相同,唯一不同的是方法实现内容,这种特殊的重写方式称为重构。 

当重写父类方法时,修改方法的修饰权限只能从小的范围到大的范围改变,例如,父类中的
doSomething0方法的修饰权限为protected, 继承后子类中的方法doSomething0的修饰权限只能修改
为public,不能修改为private。

 

    在java中,一切都是一对象的形式进行处理,在继承机制中,创建一个子类对象,将包含一个父类子对象,这个对象与父类创建的对象是一样的,两者的区别就在于,后者来自于外部,而前者来自子类对象内部。当实例化子类对象时,父类对象也相应被实例化,也就是说,在实例化子类对象时,java编译器会在子类的构造方法中自动调用父类的无参构造方法。

 代码示例:

public class Parent {
    Parent(){
        System.out.println("调用父类的Parent()方法");
    }
}

 

public class Subparent extends Parent { //继承Parent
    Subparent(){
        System.out.println("调用子类Subparent的构造方法");
    }
}
public class Subbroutine extends Subparent { // 继承 Subparent
    Subbroutine(){
        System.out.println("调用子类Subbroutine的方法。");
    }

    public static void main(String[] args) {
        Subbroutine s = new Subbroutine();
    }
}

 运行结果:

 

 运行结果说明:

    子类Subroutine的主方法中只调用子类的构造方法实例化子类对象,并且在子类的构造方法中没有调用父类构造方法的任何语句,但是在实例化子类对象时,他会相应的调用父类的构造方法,调用构造方法的顺序是:顶级父类==上一级父类==子类,也就是实例化子类对象时首先实例化父类对象,再实例化子类对象。在子类的构造方法访问父类的构造方法之前,已经完成了父类的实例化操作。

    说明:

    在实例化子类对象时,父类无参构造方法将会被自动调用,但是有参构造方法不能被自动调用,只能借用super关键字显式的调用父类方法。

    如果使用finalize()方法对对象进行清理,需要确保子类的finalize()方法最后一个动作是调用父类的finalize()方法,用来保证垃圾回收对象所占用的内存时,对象所有部分都能被正常终止。

极客时间视频课总结:

 1、继承的语法就是在类名后面使用extends 加 要继承的类名
 被继承的类叫做父类(Parent Class),比如本例中的MerchandiseV2。
 继承者叫做子类(Sub Class),比如本例中的PhoneExtendsMerchandise。
 Java中只允许一个类有一个直接的父类(Parent Class),即所谓的单继承
 没错,别的类也可以继承子类,比如可以有一个HuaweiPhone继承PhoneExtendsMerchandise
 这时候,HuaweiPhone就是PhoneExtendsMerchandise的子类了。
 子类继承了父类什么呢?所有的属性和方法。
 但是子类并不能访问父类的private的成员(包括方法和属性)。

通过使用和父类方法签名一样,而且返回值也必须一样的方法,可以让子类覆盖(override)掉父类的方法
子类并不是只能把父类的方法拿过来,而且可以通过覆盖来替换其中不适合子类的方法
public class PhoneExtendsMerchandise extends MerchandiseV2 {
	...
}
public class HuaweiPhone extends PhoneExtendsMerchandise {

    public HuaweiPhone(String name, String id, int count, double soldPrice, double purchasePrice, double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os) {
        super(name, id, count, soldPrice, purchasePrice, screenSize, cpuHZ, memoryG, storageG, brand, os);
    }
}

2、super与父类沟通的桥梁

使用super可以调用父类的方法和属性(当然必须满足访问控制符的控制)
public double buy(int count) {
        if (count > MAX_BUY_ONE_ORDER) {
            System.out.println("购买失败,手机一次最多只能买" + MAX_BUY_ONE_ORDER + "个");
            return -2;
        }
        return super.buy(count);
    }
super是子类和父类交流的桥梁,但是并不是父类的引用
使用super可以调用父类的public属性,但是super不是一个引用。
super的用法就像是一个父类的引用。它是继承的一部分,像组合的那部分,但不是全部
public void useSuper() {
        super.describe();
        super.buy(66);
        System.out.println("父类里的count属性:" + super.count);
    }
可以认为,创建子类对象的时候,也就同时创建了一个隐藏的父类对象
 
  public Phone(
        String name, String id, int count, double soldPrice, double purchasePrice,
        double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os
    ) {
        // >> TODO 可以认为,创建子类对象的时候,也就同时创建了一个隐藏的父类对象

        this.screenSize = screenSize;
        this.cpuHZ = cpuHZ;
        this.memoryG = memoryG;
        this.storageG = storageG;
        this.brand = brand;
        this.os = os;

        // >> TODO 所以,才能够setName,对name属性进行操作。
        this.setName(name);
        this.setId(id);
        this.setCount(count);
        this.setSoldPrice(soldPrice);
        this.setPurchasePrice(purchasePrice);
    }
 3、调用父类的构造方法
使用super调用父类的构造方法,必须是子类构造方法的第一个语句
可以使用表达式
super调用构造方法,不可以使用super访问父类的属性和方法,不可以使用子类成员变量和方法
可以使用静态变量和方法
 
 public Phone(
            String name, String id, int count, double soldPrice, double purchasePrice,
            double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os
    ) {
        super(name, id, count, soldPrice * 1.2, purchasePrice);
        init(screenSize, cpuHZ, memoryG, storageG, brand, os);
    }
 4、父类和子类的引用赋值关系
可以用子类的引用给父类的引用赋值,也就是说,父类的引用可以指向子类的对象
MerchandiseV2 m = ph;
        MerchandiseV2 m2 = new Phone(
            "手机002", "Phone002", 100, 1999, 999,
            4.5, 3.5, 4, 128, "索尼", "安卓"
        );
但是反之则不行,不能让子类的引用指向父类的对象。因为父类并没有子类的属性和方法
 
Phone notDoable = new MerchandiseV2();

因为子类继承了父类的方法和属性,所以父类的对象能做到的,子类的对象肯定能做到
换句话说,我们可以在子类的对象上,执行父类的方法
当父类的引用指向子类的实例(或者父类的实例),只能通过父类的引用,像父类一样操作子类的对象
也就是说"名"的类型,决定了能执行哪些操作
ph和m都指向同一个对象,通过ph可以调用getBrand方法

Phone ph = new Phone(
            "手机001", "Phone001", 100, 1999, 999,
            4.5, 3.5, 4, 128, "索尼", "安卓"
        );

        MerchandiseV2 m = ph;

因为ph的类型是Phone,Phone里定义了getBrand方法
ph.getBrand();
ph和m都指向同一个对象,但是通过m就不可以调用getBrand方法
因为m的类型是MerchandiseV2,MerchandiseV2里没有你定义getBrand方法
m.getBrand();

如果确定一个父类的引用指向的对象,实际上就是一个子类的对象(或者子类的子类的对象),可以强制类型转换

Phone aPhone = (Phone) m2;

 

完整代码(父类):

package com.geekbang.supermarket;

public class MerchandiseV2 {

    public String name;
    public String id;
    public int count;
    public double soldPrice;
    public double purchasePrice;


    public MerchandiseV2(String name, String id, int count, double soldPrice, double purchasePrice) {
        this.name = name;
        this.id = id;
        this.count = count;
        this.soldPrice = soldPrice;
        this.purchasePrice = purchasePrice;
    }

    public MerchandiseV2(String name, String id, int count, double soldPrice) {
        // double purPrice = soldPrice * 0.8;
        // this(name, id, count, soldPrice, purchasePrice);
        this(name, id, count, soldPrice, soldPrice * 0.8);
        // double purPrice = soldPrice * 0.8;
    }

    public MerchandiseV2() {
        this("无名", "000", 0, 1, 1.1);

    }

    public void describe() {
        System.out.println("商品名字叫做" + name + ",id是" + id + "。 商品售价是" + soldPrice
            + "。商品进价是" + purchasePrice + "。商品库存量是" + count +
            "。销售一个的毛利润是" + calculateProfit());
    }

    public double calculateProfit() {
        double profit = soldPrice - purchasePrice;
//        if(profit <= 0){
//            return 0;
//        }
        return profit;
    }

    public double buy(int count) {
        if (this.count < count) {
            System.out.println("购买失败,库存不够");
            return -1;
        }
        this.count -= count;
        double cost = count * soldPrice;
        System.out.println("购买成功,花费为" + cost);
        return cost;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

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

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public double getSoldPrice() {
        return soldPrice;
    }

    public void setSoldPrice(double soldPrice) {
        this.soldPrice = soldPrice;
    }

    public double getPurchasePrice() {
        return purchasePrice;
    }

    public void setPurchasePrice(double purchasePrice) {
        this.purchasePrice = purchasePrice;
    }
}

 完整代码(phone子类):

package com.geekbang.supermarket;

public class Phone extends MerchandiseV2 {

    // 给Phone增加新的属性和方法
    private double screenSize;
    private double cpuHZ;
    private int memoryG;
    private int storageG;
    private String brand;
    private String os;
    private static int MAX_BUY_ONE_ORDER = 5;

    public Phone(
        String name, String id, int count, double soldPrice, double purchasePrice,
        double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os
    ) {
        super(name, id, count, soldPrice * 1.2, purchasePrice);
        this.screenSize = screenSize;
        this.cpuHZ = cpuHZ;
        this.memoryG = memoryG;
        this.storageG = storageG;
        this.brand = brand;
        this.os = os;
    }

    public double buy(int count) {
        if (count > MAX_BUY_ONE_ORDER) {
            System.out.println("购买失败,手机一次最多只能买" + MAX_BUY_ONE_ORDER + "个");
            return -2;
        }
        return super.buy(count);
    }

    public String getName() {
        return this.brand + ":" + this.os + ":" + super.getName();
    }

    public void describe() {
        System.out.println("此手机商品属性如下");
        super.describe();
        System.out.println("手机厂商为" + brand + ";系统为" + os + ";硬件配置如下:\n" +
            "屏幕:" + screenSize + "寸\n" +
            "cpu主频" + cpuHZ + " GHz\n" +
            "内存" + memoryG + "Gb\n" +
            "存储空间" + storageG + "Gb");
    }

    public boolean meetCondition() {
        return true;
    }

    public double getScreenSize() {
        return screenSize;
    }

    public void setScreenSize(double screenSize) {
        this.screenSize = screenSize;
    }

    public double getCpuHZ() {
        return cpuHZ;
    }

    public void setCpuHZ(double cpuHZ) {
        this.cpuHZ = cpuHZ;
    }

    public int getMemoryG() {
        return memoryG;
    }

    public void setMemoryG(int memoryG) {
        this.memoryG = memoryG;
    }

    public int getStorageG() {
        return storageG;
    }

    public void setStorageG(int storageG) {
        this.storageG = storageG;
    }

    public String getBrand() {
        return brand;
    }

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

    public String getOs() {
        return os;
    }

    public void setOs(String os) {
        this.os = os;
    }
}

完整代码(ShellColorChangePhone孙子类):

 

package com.geekbang.supermarket;

public class ShellColorChangePhone extends Phone {
    private boolean enableShellColorChange;

    public ShellColorChangePhone(String name, String id, int count, double soldPrice, double purchasePrice,
                                 double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os) {
        super(name, id, count, soldPrice, purchasePrice, screenSize, cpuHZ, memoryG, storageG, brand, os);
        enableShellColorChange = false;
    }

    public boolean isEnableShellColorChange() {
        return enableShellColorChange;
    }

    public void setEnableShellColorChange(boolean enableShellColorChange) {
        this.enableShellColorChange = enableShellColorChange;
    }

    @Override
    public void describe() {
        super.describe();
        System.out.println("壳色随着屏幕色变的功能开启状态:" + enableShellColorChange);
    }

    @Override
    public double calculateProfit() {
        // TODO 厂家提供10个点的返点
        return super.calculateProfit() + super.getPurchasePrice() * 0.1;
    }
}

完整代码(使用类):

 

package com.geekbang;

import com.geekbang.supermarket.MerchandiseV2;
import com.geekbang.supermarket.Phone;
import com.geekbang.supermarket.ShellColorChangePhone;

public class ReferenceAssign {
    public static void main(String[] args) {
        Phone ph = new Phone(
            "手机001", "Phone001", 100, 1999, 999,
            4.5, 3.5, 4, 128, "索尼", "安卓"
        );

        // >> TODO 可以用子类的引用给父类的引用赋值,也就是说,父类的引用可以指向子类的对象

        MerchandiseV2 m = ph;
        MerchandiseV2 m2 = new Phone(
            "手机002", "Phone002", 100, 1999, 999,
            4.5, 3.5, 4, 128, "索尼", "安卓"
        );

        // >> TODO 但是反之则不行,不能让子类的引用指向父类的对象。因为父类并没有子类的属性和方法呀

//         Phone notDoable = new MerchandiseV2();

        // >> TODO                          重点
        // >> TODO 因为子类继承了父类的方法和属性,所以父类的对象能做到的,子类的对象肯定能做到
        //    TODO 换句话说,我们可以在子类的对象上,执行父类的方法
        // >> TODO 当父类的引用指向子类的实例(或者父类的实例),只能通过父类的引用,像父类一样操作子类的对象
        //    TODO 也就是说"名"的类型,决定了能执行哪些操作


        // >> TODO ph和m都指向同一个对象,通过ph可以调用getBrand方法
        //    TODO 因为ph的类型是Phone,Phone里定义了getBrand方法
        ph.getBrand();
        // >> TODO ph和m都指向同一个对象,但是通过m就不可以调用getBrand方法
        //    TODO 因为m的类型是MerchandiseV2,MerchandiseV2里没有你定义getBrand方法
        // m.getBrand();

        // TODO 如果确定一个父类的引用指向的对象,实际上就是一个子类的对象(或者子类的子类的对象),可以强制类型转换
        Phone aPhone = (Phone) m2;

        // MerchandiseV2是Phone的父类,Phone是shellColorChangePhone的父类
        ShellColorChangePhone shellColorChangePhone = new ShellColorChangePhone(
            "手机002", "Phone002", 100, 1999, 999,
            4.5, 3.5, 4, 128, "索尼", "安卓"
        );

        // TODO 父类的引用,可以指向子类的对象,即可以用子类(以及子类的子类)的引用给父类的引用赋值
        MerchandiseV2 ccm = shellColorChangePhone;

        // TODO 父类的引用,可以指向子类的对象。
        // TODO 确定MerchandiseV2的引用ccm是指向的是Phone或者Phone的子类对象,那么可以强制类型转换
        Phone ccp = (Phone) ccm;

        // TODO 确定MerchandiseV2的引用ccm是指向的是ShellColorChangePhone或者ShellColorChangePhone的子类对象
        // TODO 那么可以强制类型转换
        ShellColorChangePhone scp = (ShellColorChangePhone) ccm;

        // TODO 会出错,因为m2指向的是一个Phone类型的对象,不是ShellColorChangePhone的对象
        ShellColorChangePhone notCCP = (ShellColorChangePhone) m2;


    }
}

 

posted @ 2021-12-18 15:50  庞某人  阅读(49)  评论(0编辑  收藏  举报