对象的多态|继承的本质

73 阅读7分钟

继承的本质

图解

图解

看个例子 要按照查找关系来返回信息: 1.首先看子类是否有该属性 2.如果子类有该属性,并且可以直接访问,则返回信息 3.如果子类没有该属性,就看父类有没有这个属性(如果有,并且可以访问,就返回信息) 4.如果父类没有就按照3的规则,继续找上级父类,直到Object

package com.hspedu.extend;

public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();//内存的布局
        System.out.println(son.name);
        System.out.println(son.getAge());//
        System.out.println(son.hobby);
    }
}

class GrandPa{
    String name = "大头爷爷";
    String hobby = "旅游";
//    int age =88;//如果下面的不能直接访问,到更上一级这也不行
}
class Father extends GrandPa{
    String name = "大头爸爸";
    private int age = 35;

    public int getAge(){//如果要能访问的话这要用getAge()
        return age;
    }
}
class Son extends Father{
    String name = "大头儿子";
}

运行结果

运行结果

练习题: 编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息, 编写PC子类,继承Computer类,添加特有属性[ 品牌brand],编写NotePad子类,继承Computer类,添加特有属性[颜色color],在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信息。

Computer类:

package com.hspedu.extend;

import sun.plugin2.gluegen.runtime.CPU;

public class Computer {
    private String CPU;
    private int memory;
    private int disk;

    public Computer(String CPU, int memory, int disk) {
        this.CPU = CPU;
        this.memory = memory;
        this.disk = disk;
    }

    public String getCPU() {
        return CPU;
    }

    public void setCPU(String CPU) {
        this.CPU = CPU;
    }

    public int getMemory() {
        return memory;
    }

    public void setMemory(int memory) {
        this.memory = memory;
    }

    public int getDisk() {
        return disk;
    }

    public void setDisk(int disk) {
        this.disk = disk;
    }

    public String getDetails() {
        return "" + CPU +  '\t' +  "" + memory  +  '\t' +  "" + disk;
    }
}

PC子类,继承Computer类:

package com.hspedu.extend;

public class PC extends Computer {
    private String brand;
    //这里IDEA根据继承的规则,自动把构造器的调用写好
    //这里也体现:继承设计的基本思想,父类的构造器完成属性初始化
    //子类的构造器完成子类属性初始化

    public PC(String CPU, int memory, int disk, String brand) {
        super(CPU, memory, disk);
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }
    public void printInfo(){
//        System.out.println(getCPU() + getDisk() + getMemory());
        System.out.println(getDetails() + '\t' + brand);
    }
}

NotePad子类,继承Computer类:

package com.hspedu.extend;

public class NotePad extends Computer{
    private String color;

    public NotePad(String CPU, int memory, int disk, String color) {
        super(CPU, memory, disk);
        this.color = color;
    }

    public String getColor() {
        return color;
    }

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

    public void printInfor(){
        System.out.println(getDetails() + '\t' + color);
    }
}

测试:

package com.hspedu.extend;

import sun.plugin2.gluegen.runtime.CPU;

public class ExtendsExercise {
    public static void main(String[] args) {
        Computer computer = new Computer("yyrt",64,757);
        computer.getDetails();

        PC pc = new PC("yyrt",64,757,"fhg");
        pc.printInfo();

        NotePad notePad = new NotePad("yyrt",64,757,"red");
        notePad.printInfor();
    }
}

运行结果

运行结果

super关键字

1.访问父类的属性,但不能访问父类的private属性 super.属性名; 2.访问父类的方法,不能访问父类的private方法 super.方法名(参数列表); 3.访问父类的构造器: super(参数列表);只能放在构造器的第一句,只能出现-句!

属性和方法是一样的

希望调用父类-A的cal方法, 因为子类B没有cal方法,因此我可以使用下面三种方式 找cal方法时, 顺序是: (1)先找本类,如果有,则调用 (2)如果没有,则找父类(如果有,并可以调用,则调用) (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到0bject类提示:如果查找方法的过程中,找到了,但是不能访问, 则报错,cannot access 如果查找方法的过程中,没有找到,则提示方法不存在

父类:

package com.use;

public class A {
    public int n1 = 100;
    protected int n2 =200;
    int n3 = 300;
    private int n4 = 400;
    
    public A(){}
    public A(String name){}
    public A(String name,int age){}

    public void cal(){
        System.out.println("A的cal方法");
    }
    public void test100(){

    }
    protected void test200(){

    }
    void test300(){

    }
    private void test400(){

    }
}

子类:

package com.use;

public class B extends A {

    public int n1 = 888;
    //访问父类的属性,但不能访问父类的private属性 [案例]super.属性名
    public void hi() {
        System.out.println(super.n1 + "" + super.n2 + "" + super.n3);
    }
    public void sum() {
        System.out.println("B类的sum()");
//        希望调用父类-A的cal方法
//        这时,因为子类B没有cal方法,因此我可以使用下面三种方式
//        找cal方法时,顺序是:
//        (1)先找本类,如果有,则调用
//        (2)如果没有,则找父类(如果有,并可以调用,则调用)
//        (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object类
//        提示:如果查找方法的过程中,找到了,但是不能访问,则报错,cannot access
//        如果查找方法的过程中,没有找到,则提示方法不存在
        //caL();
        //this.cal(); //等价cal
        super.cal();//找cal方法的顺序是直接查找父类,其他的规则一样

        System.out.println(n1);//888
        System.out.println(this.n1);//888
        //找n1 (super.n1) 的顺序是直接查找父类属性,其他的规则一样
        System.out.println(super.n1);//100
    }
    //访问父类的方法,不能访问父类的private方法super.方法名(参数列表);
    public void ok() {
        super.test100();
        super.test200();
        super.test300();
        //super. test400();//不能访问父类private方法
    }
    //访问父类的构造器(这点前面用过): super(参 数列表);只能放在构造器的第一句,只能出现一句!
    public B(){
        //super();
        //super("hft",13);
        super("yyf");
    }
}
package com.use;

public class Super {
    public static void main(String[] args) {
        B b = new B();
        b.sum();
    }
}

结果

结果

1.super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员; 如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C

2.super和this的比较

3.方法重写也叫方法覆盖,需要满足下面的条件:

4.方法的多态 方法重载体现多态 传入不同的参数,就会调用不同方法,就体现多态 方法重写体现多态

对象的多态(核心,困难,重点)

重要的几句话: (1)一个对象的编译类型和运行类型可以不一致 (2)编译类型在定义对象时,就确定了,不能改变 (3)运行类型是可以变化的. (4)编译类型看定义时=号的左边, 运行类型看=号的右边

例子:Animal animal = new Dog(); [ animal编译类型是Animal,运行类型Dog ] animal = new Cat(); [animal 的运行类型变成了Cat,编译类型仍然是Animal]

package com.hspedu.extend.poly;

public class PolyObject {
    public static void main(String[] args) {
        //体验对象多态特点
        //animal编译类型就是Animal ,运行类型Dog
        Animal animal = new Dog();
        //因为运行时, 执行到改行时,animal运行类型是Dog,所以cry就是Dog的cry
        animal.cry();//Dog中小狗叫
        //animaL编译类型Animal, 运行类型就是Cat
        animal = new Cat();
        animal.cry();//Cat中小猫叫
    }
}
package com.hspedu.extend.poly;

public class Animal {
    public void cry(){
        System.out.println("Animal 中动物叫");
    }
}
package com.hspedu.extend.poly;

public class Dog extends Animal {
    public void cry(){
        System.out.println("Dog中小狗叫");
    }
}
package com.hspedu.extend.poly;

public class Cat extends Animal {
    public void cry(){
        System.out.println("Cat中小猫叫");
    }
}

这么理解->

披着羊皮的狼

披着羊皮的狼

向上转型代码被我搞丢了找不到了,随便看吧

向上转型调用方法的规则如下: 1可以调用父类中的所有成员(需遵守访问权限) 2但是不能调用子类的特有的成员 因为在编译阶段,能调用哪些成员,是由编译类型来决定的,按照从子类(运行类型)开始查找方法 4最终运行效果看子类(运行类型)的具体实现,即调用方法时,

向下转型 1语法:子类类型 引用名= (子类类型) 父类引用 2只能强转父类的引用,不能强转父类的对象 3要求父类的引用必须指向的是当前目标类型的对象 4可以调用子类类型中所有的成员

属性没有重写之说,属性的值看编译类型:

package com.hspedu.extend.uihy;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

public class Polyhgcv {
    public static void main(String[] args) {
        //属性没有重写之说,属性的值看编译类型
        Base base = new Sub();
        System.out.println(base.count);//10
        Sub sub = new Sub();
        System.out.println(sub.count);//20
    }
}

class Base {
    int count = 10;
}

class Sub extends Base {
    int count = 20;
}

instanceOf比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型