首页 > 编程语言 >Java学习笔记(八)

Java学习笔记(八)

时间:2023-08-12 11:01:18浏览次数:39  
标签:Java void System 笔记 学习 类型 println public out

6.3 多态

6.3.1 多态的概念

1、什么是多态?

多态:多种形态,多种类型的形式


两个角度:

(1)一个父类的变量,可以赋值给它各种子类的对象

换句话说,一个父类的变量,可以在运行时体现为多种不同的子类对象

==> 编译时都是父类类型的变量,运行时是各种子类的对象类型


(2)一个子类对象,可以赋值给不同类型的父类变量,

在编译时,编译器识别的类型也不同,可以用.访问的成员也不同。

==> 运行时是同一个子类的类型,但是编译时却呈现为不同的父类类型。

/*
(1)声明父类:Person类
- 包含属性:姓名,年龄,性别,属性私有化,
- 包含get/set方法
- 重写toString方法:例如:姓名:张三,年龄:23,性别:男

 */
public class Person {
    private String name;
    private int age;
    private char gender;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }
}
/*
(2)声明子类:Student类,继承Person类

- 新增属性:score成绩,属性私有化,
- 包含get/set方法
- 重写toString方法:例如:姓名:张三,年龄:23,性别:男,成绩:89

 */
public class Student extends Person {
    private int score;

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return super.toString() + ",成绩:" + score;
    }
}
/*
(3)声明子类:Teacher类,继承Person类

- 新增属性:salary薪资,属性私有化,
- 包含get/set方法
- 重写toString方法:例如:姓名:张三,年龄:23,性别:男,薪资:10000

 */
public class Teacher extends Person {
    private double salary;

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return super.toString() +",薪资:" + salary;
    }
}
public class TestPerson {
    public static void main(String[] args) {
        Student s = new Student();
        s.setName("熊大");
        s.setAge(18);
        s.setGender('男');
        s.setScore(100);

        Teacher t = new Teacher();
        t.setName("胖虎");
        t.setAge(24);
        t.setGender('男');
        t.setSalary(20000);

        Person p = s;//左边是Person类型,右边s是Student,Student是Person的子类
        System.out.println(p);//Person{name='熊大', age=18, gender=男},成绩:100

        p = t;//此时左边是Person类型,右边t是Teacher,Teacher是Person的子类
        System.out.println(p);//Person{name='胖虎', age=24, gender=男},薪资:20000.0
        //(1)p变量可以赋值不同子类对象

        System.out.println("------------------------");
        //(2)同一个学生对象,可以赋值给不同的父类变量
        Student student = new Student();
        Person person = student;
        Object object = student;

        System.out.println(student);
        System.out.println(person);
        System.out.println(object);

    }
}

Java学习笔记(八)_多态

6.3.2 多态引用的概念和表现

2、多态引用的概念?

当父类的变量指向子类的对象时,就叫做多态引用。

   父类类型 变量名 = 子类的对象;


3、多态引用后的表现:

   编译时类型和运行时类型不一致。

编译时:看左边,是父类类型

运行时:看右边,是子类类型


编译时如果按父类类型处理的话,那么只能访问父类中声明的成员,不能访问子类“扩展”的成员。


运行时是按照实际类型处理的,看new对象的类型,如果是子类的话,并且调用方法时,

那么一定执行子类重写的方法体。如果子类没有重写,仍然执行父类中的方法体。


4、多态的弊端和好处:

(1)编译时,只能调用父类声明的方法,不能调用子类扩展的方法

(2)实现方法的动态绑定

  运行时,看“子类”,如果子类重写了方法,一定是执行子类重写的方法体;变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活、功能更强大,可维护性和扩展性更好了。

  当然,如果子类没有重写该方法,那么仍然执行从父类继承的方法。

public class TestPolymorphism {
    public static void main(String[] args) {
        Person p = new Student();//多态引用
        //编译时看左边,是Person类型
        p.setName("张三");
        p.setGender('男');
        p.setAge(23);
        //问题:编译时无法访问子类扩展的成员
//        p.setScore(100);

        System.out.println(p.toString());
        //按照编译时,p是Person类型,编译器会去Person类中看是否有toString方法
        //运行时,按照new的类型处理,运行时以Student类型处理,执行Student类中重写的toString

        Person p2 = new Teacher();
        p2.setName("李四");
        p2.setAge(24);
        p2.setGender('女');
        //问题:编译时无法访问子类扩展的成员
//        p2.setSalary(15000);
        System.out.println(p2.toString());//自动调用Teacher的toString方法
    }
}

Java学习笔记(八)_向上转型和向下转型_02

6.3.3 多态的应用

1、没有多态:死板,不便于维护和扩展

案例:

(1)声明一个Dog类,包含public void eat()方法,输出“狗狗啃骨头”

(2)声明一个Cat类,包含public void eat()方法,输出“猫咪吃鱼仔”

(3)声明一个Person类,

- 包含姓名和宠物属性

- 包含喂宠物吃东西的方法 public void feed(),实现为调用宠物对象.eat()方法

public class Dog {
    public void eat(){
        System.out.println("狗狗啃骨头");
    }
}
public class Cat {
    public void eat(){
        System.out.println("猫咪吃鱼仔");
    }
}
public class Person {
    private String name;
    private Dog dog;//宠物是狗

    public void feed(){//喂养
        dog.eat();
    }
}
/*
思考:
    如果这个代码写完了,用户认为他不喜欢养狗,喜欢养猫。
    如果修改成Cat,另一些用户,有喜欢养狗的。
 */
2、有了多态:灵活、便于维护和扩展

案例:

(1)声明一个Pet类,包含public void eat()方法,输出“吃~~~”

(2)声明一个Dog类,继承Pet类,重写public void eat()方法,输出“狗狗啃骨头”

(3)声明一个Cat类,继承Pet类,重写public void eat()方法,输出“猫咪吃鱼仔”


(3)声明一个Person类,

- 包含姓名和宠物属性

- 包含喂宠物吃东西的方法 public void feed(),实现为调用宠物对象.eat()方法


(4)声明一个Person2类,

  - 包含姓名和宠物数组属性

  - 包含喂宠物吃东西的方法 public void feed(),实现为遍历数组,调用宠物对象.eat()方法


(5)声明一个PetShop类

   -包含public Pet buy(String type),根据用户要求的宠物类型不同,返回不同对象

public class Pet {
    private String nickname;

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public void eat() {
       //啥也不写也可以
        System.out.println(nickname + "吃~~");
    }
}
public class Dog extends Pet{
    @Override
    public void eat(){
        System.out.println(getNickname() + "狗狗啃骨头");
    }
}
public class Cat extends Pet{
    @Override
    public void eat() {
        System.out.println(getNickname() + "吃鱼仔");
    }
}
public class Person {
    private String name;
    private Pet pet;

    public String getName() {
        return name;
    }

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

    public Pet getPet() {
        return pet;
    }

    public void setPet(Pet pet) {
        this.pet = pet;
    }

    public void feed(){
        pet.eat();
        //动态邦定,根据pet变量赋值的对象不同,
        // 赋值给Dog,就调用Dog的eat方法
        // 赋值给Cat,就调用Cat的eat方法
    }
}
public class TestPerson {
    public static void main(String[] args) {
        Person p = new Person();
        p.setName("胖虎");

        Dog dog = new Dog();
        dog.setNickname("旺财");
        p.setPet(dog);
        p.feed();

        Cat cat = new Cat();
        cat.setNickname("小白");
        p.setPet(cat);
        p.feed();
    }
}
public class Person2 {
    private String name;
    private Pet[] pets;

    public String getName() {
        return name;
    }

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

    public Pet[] getPets() {
        return pets;
    }

    public void setPets(Pet[] pets) {
        this.pets = pets;
    }

    public void feed(){
        for(int i=0; i<pets.length; i++){
            pets[i].eat();
            /*
            pets数组的元素,可能是Dog,或Cat,或Pet的其他子类对象
            // 如果是Dog对象,就调用Dog的eat方法
        // 如果是Cat对象,就调用Cat的eat方法
             */
        }
    }
}
public class TestPerson2 {
    public static void main(String[] args) {
        Person2 person2 = new Person2();
        person2.setName("熊大");

        Pet[] arr = new Pet[2];
        arr[0] = new Dog();
        arr[1] = new Cat();
        arr[0].setNickname("小黑");
        arr[1].setNickname("小白");

        person2.setPets(arr);
        person2.feed();

    }
}
public class PetShop {
    public Pet buy(String type){
        switch (type){
            case "狗":
                return new Dog();
            case "猫":
                return new Cat();
        }
        return null;
    }
}
public class TestPetShop {
    public static void main(String[] args) {
        PetShop ps = new PetShop();
        Pet dog = ps.buy("狗");
        dog.setNickname("旺财");
        dog.eat();

        Pet cat = ps.buy("猫");
        cat.setNickname("小白");
        cat.eat();
    }
}

TestPerson.java 的运行结果:

Java学习笔记(八)_父类_03

TestPerson2.java 的运行结果

Java学习笔记(八)_向上转型和向下转型_04

3、多态的好处:灵活、便于维护、扩展、实现方法的动态绑定

1、要声明几个不同类型的图形类,例如:有圆、矩形、三角形等

2、然后要把不同图形类的对象放在一起,按照面积从小到大排序

public class Graphic {
    public double area(){
        return 0.0;//只是为了保证语法不报错
                //因为方法的返回值类型是double,方法体中必须有return 值;的语句结束方法
                //否则编译报错。
    }
}
public class Circle extends Graphic {
    private double radius;

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public double area(){
        return Math.PI * radius * radius;
    }

    @Override
    public String toString() {
        return "Circle{" +
                "radius=" + radius +
                ",area = " + area() +
                '}';
    }
}
public class Rectangle extends Graphic {
    private double length;
    private double width;

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    public double area(){
        return length * width;
    }

    @Override
    public String toString() {
        return "Rectangle{" +
                "length=" + length +
                ", width=" + width +
                ", area=" + area() +
                '}';
    }
}
public class Triangle extends Graphic {
    private double a;
    private double b;
    private double c;

    public double getA() {
        return a;
    }

    /*public void setA(double a) {
        this.a = a;
    }*/

    public double getB() {
        return b;
    }

    /*public void setB(double b) {
        this.b = b;
    }*/

    public double getC() {
        return c;
    }

    /*public void setC(double c) {
        this.c = c;
    }*/

    public void setBase(double a, double b, double c){
        if(a>0 && b>0 && c>0 && a+b>c && b+c>a && a+c>b) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }

    public double area(){
        double p = (a+b+c)/2;
        return Math.sqrt(p * (p-a) * (p-b) * (p-c));//海伦公式
    }

    @Override
    public String toString() {
        return "Triangle{" +
                "a=" + a +
                ", b=" + b +
                ", c=" + c +
                ",area = " + area() +
                '}';
    }
}
public class TestGraphic {
    public static void main(String[] args) {
        Circle c = new Circle();
        c.setRadius(2.5);

        Rectangle r = new Rectangle();
        r.setLength(2);
        r.setWidth(3);

        Triangle t = new Triangle();
        t.setBase(3,4,5);

        /*Circle[] arr = new Circle[3];
        arr[0] = c;
        arr[1] = r;//报错,因为r是Rectangle类型,和Circle没有继承关系
        arr[2] = t;//报错,因为t是Triangle类型,和Circle没有继承关系*/

        Graphic[] arr = new Graphic[3];//创建数组对象,不是图形对象
        arr[0] = c;//Graphic是Circle的父类,arr[0]是Graphic类型,c是Circle类型
                //父类>子类,子类对象可以赋值给父类的变量,元素。
        arr[1] = r;
        arr[2] = t;

        //遍历输出这些图形
        System.out.println("排序之前:");
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        //排序
         for(int i=1; i<arr.length; i++){
             for(int j=0; j<arr.length-i; j++){
                 if(arr[j].area() > arr[j+1].area()){
                     Graphic temp = arr[j];
                     arr[j] = arr[j+1];
                     arr[j+1] = temp;
                 }
             }
         }

        System.out.println("排序之后:");
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
     }
}

Java学习笔记(八)_向上转型和向下转型_05

Java学习笔记(八)_多态_06

6.3.4 向上转型和向下转型

总结:向上转型和向下转型都是针对编译器来说的,对象的运行时类型从头到尾都不会发生改变

1、向上转型和向下转型

1、回忆:

基本数据类型的:

(1)自动类型转换

int a = 15;

double d = a;//自动类型提升

           //把a里面的15copy(复制)了一份,放到d中,

           //因为a里面的15是以4个字节表示,

           //而double需要8个字节,所以要对4个字节的15的二进制进行处理,

           //用8个字节表示。


(2)强制类型转换

double d = 1.5;

int a = (int)d;//强制类型转换

   //因为d里面的1.5是以8个字节存储,而int类型的a只有4个字节的空间

   //需要把复制的1.5的8个字节,处理成4个字节才能放进去


2、向上转型和向下转型

(1)向上转型:可以自动完成,也可以强制完成

自动完成向上转型:

   当把子类的对象赋值给父类的变量时,就自动完成向上转型。

   Dog d = new Dog();

   Animal a = d;//向上转型

               //把d变量中存储的首地址,复制了一份给a,字节数没有变化。

               //发生变化的是编译器接下来的态度不同

               //编译时看d是Dog类型,看a是Animal类型,它们里面存储的地址值完全相同

               //如果此时通过a.成员,只能调用Animal里面有的成员,不能调用Dog类扩展的成员。


   向上转型的弊端:编译时失去调用子类扩展的成员的能力


   为什么要这样写?

   为了多态引用,通过父类的变量管理子类的对象。


强制完成向上转型:

   (父类类型)子类对象

   (父类类型)子类变量


(2)向下转型:只能强制完成

把父类的变量值赋值给子类变量时,必须强制完成。


    Animal a = new Dog();

    Dog d = (Dog)a;//强制完成,向下上转型

                   //a变量和d变量中存储的地址值是完全一致的,

                   //只是编译器识别的a和d的类型不同


    向下转型的好处:又可以调用子类扩展的成员


结论:无论是向上转型还是向下转型,对象的本质类型(运行时类型,new)从头到尾都没有发生过变化。

     只是编译器在识别变量的类型时,按照不同的类型处理而已。


package com.panghu.zhuanxing;

public class Animal {
    public void eat(){
        System.out.println("吃~~");
    }
}



package com.panghu.zhuanxing;

public class Cat extends Animal {
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}



package com.panghu.zhuanxing;

public class Dog extends Animal {
    public void watchHouse(){
        System.out.println("狗狗看家");
    }
    @Override
    public void eat(){
        System.out.println("狗狗啃骨头");
    }
}



package com.panghu.zhuanxing;
//哈士奇
public class Huskie extends Dog {
}
public class TestClassCast {
    public static void main(String[] args) {
        Dog d = new Dog();
        Animal a = d;

        System.out.println(a);
        System.out.println(d);
//        com.atguigu.cast.Dog@1b6d3586
//        com.atguigu.cast.Dog@1b6d3586

       //a.watchHouse();//编译报错,因为此时a被识别为Animal
        d.watchHouse();//编译通过

        Animal[] animals = new Animal[2];
        animals[0] = d;//向上转型,自动完成
        animals[1] = new Cat();//也是向上转型,自动完成
        /*
        编译时,animals[0]和animals[1] 都是以Animal类型处理
         */
        animals[0].eat();//编译时,寻找Animal类中eat()方法
                        //运行时,如果Dog重写了eat()方法,就执行重写的方法
        animals[1].eat();//编译时,寻找Animal类中eat()方法
                    //运行时,如果Cat重写了eat()方法,就执行重写的方法

       // ((Animal)d).watchHouse();//报错
                                //强制把d在编译时向上转型为Animal类型
                                //非要让编译器把d类型按照Animal处理

        Dog dog = (Dog) animals[0];//向下转型
        dog.watchHouse();
    }
}
2、ClassCastException 和 instanceof

3、ClassCastException异常

当对象的运行时类型(真实的类型,new的类型) <= 接收的变量的类型,赋值可以成功,

不满足<=的关系,运行时就会发生ClassCastException异常。


Dog类型 < Animal类型(父子类,子类<父类)

Dog类型 = Dog类型(一样是相等)

Huskie类型 < Animal类型

Huskie类型 < Dog类型


Huskie对象可以赋值给Huskie类型、Dog类型和Animal类型

Dog类型对象可以赋值给Dog类型和Animal类型

Animal类型可以只能赋值给Animal类型


4、如何避免这个异常呢?

if(变量 instanceof 类型A)条件满足,再把这个变量向下转为这个类型

这个instanceof的作用是判断某个变量中的对象的运行时类型(new)是否<=类型A,成立,就返回true

public class TestClassCastException {
    public static void main(String[] args) {
        Animal[] animals = new Animal[2];
        animals[0] = new Dog();//向上转型,自动完成
        animals[1] = new Cat();//向上转型,自动完成

        Dog dog = (Dog) animals[0];//向下转型
        dog.watchHouse();    //狗狗看家

        if(animals[0] instanceof Cat) {
            Cat cat = (Cat) animals[0];//向下转型,编译时,看animals[0]是Animal类型,
            //cat是Cat类型,它们是父子类关系,可以,允许向下转型,编译通过
            //如果没有if条件,运行时报错ClassCastException类型转换异常
        }
        Animal animal = new Animal();
        if(animal instanceof  Dog) {
            Dog d2 = (Dog) animal;//向下转型
            //编译时,animal是Animal类型,Dog和它是父子类关系,允许向下转型
            //编译通过,如果没有if条件,运行时报错ClassCastException类型转换异常
        }

        Animal a2 = new Huskie();//向上转型
        Dog d3 = (Dog) a2;//向下转型
        //因为a2编译时是Animal类型,Dog<Animal向下转型,编译通过
        //a2中对象的运行时类型是Huskie,huskie<Dog,运行不会报错
    }
}
3、getClass
package com.panghu.zhuanxing;

public class TestClass {
    public static void main(String[] args) {
        Huskie h = new Huskie();
        Animal a1 = h;
        Dog d1 = h;

        //获取一个变量中对象的运行时类型,用getClass方法
        System.out.println(h.getClass());//Huskie
        System.out.println(a1.getClass());//Huskie
        System.out.println(d1.getClass());//Huskie

        //instanceof判断的是对象的运行时类型 <= xx类型成立吗
        System.out.println(h instanceof  Huskie);//true
        System.out.println(h instanceof  Animal);//true
        System.out.println(h instanceof  Dog);//true

        System.out.println(a1 instanceof Huskie);//true
        System.out.println(a1 instanceof Animal);//true
        System.out.println(a1 instanceof Dog);//true

        System.out.println(d1 instanceof Huskie);//true
        System.out.println(d1 instanceof Animal);//true
        System.out.println(d1 instanceof Dog);//true

        Animal a2 = new Cat();
        System.out.println(a2 instanceof Huskie);//false
        System.out.println(a2 instanceof Animal);//true
        System.out.println(a2 instanceof Cat);//true
        System.out.println(a2 instanceof Dog);//false

        Animal a3 = new Dog();
        System.out.println(a3 instanceof Huskie);//false      Dog > Huskie
        System.out.println(a3 instanceof Animal);//true
        System.out.println(a3 instanceof Cat);//false
        System.out.println(a3 instanceof Dog);//true

        Animal a4 = new Animal();
        System.out.println(a4 instanceof Huskie);//false
        System.out.println(a4 instanceof Animal);//true
        System.out.println(a4 instanceof Cat);//false
        System.out.println(a4 instanceof Dog);//false
    }
}

6.3.5 虚方法

1、虚方法:可以被子类重写的方法称为虚方法。

2、当我们通过“对象.虚方法”的格式调用一个方法时,要注意如下的原则:

(1)先看这个对象的“编译时类型”是什么,先从编译时类型(或其父类)中寻找匹配的方法

匹配:

A:方法名相同

B:实参列表要么和形参列表完全一致,要么可以兼容

   以实参的“编译时类型”与形参的类型匹配


如果可以找到匹配的方法,编译通过,否则就编译报错。


(2)运行时,在看这个对象的“运行时类型new”是什么,看运行时类型是否重写了刚刚匹配的方法,

如果有重写,就一定执行重写的方法体,

如果没有重写,依然执行刚刚匹配的方法。


3、如果调用的方法不是虚方法(例如,后面会学习的静态方法等不能被子类重写的方法)

或通过 “super.方法”格式发起的方法调用,不适用于上面的规则。

public class TestVirtualMethod {
    public static void main(String[] args) {
        Father f = new Father();
        Son s = new Son();
        Daughter d = new Daughter();

        MyClass my = new MySub();//多态引用
        my.method(f);//my对象.method虚方法,运行结果是sub--father
        /*
        (1)编译时
        my对象的编译时类型是MyClass,在MyClass类中找到匹配的方法
        实参是f,f的编译时类型是Father类型,
        在MyClass类中有没有名字是method,形参类型是Father的方法,可以找到。
        public void method(Father f) {
            System.out.println("father");
        }

        (2)运行时
        my对象的运行时类型,看new的类型,是MySub类型。
        看一下MySub类中是否有对刚刚匹配的方法进行重写。
        发现有重写:
        public void method(Father d) {//这个和父类的 method(Father f)是重写关系
            System.out.println("sub--father");
        }
        只要是又重写,就执行重写的方法体。
        运行结果是sub--father
         */

        my.method(s);
        /*
        (1)编译时
        my对象的编译时类型是MyClass,在MyClass类中找到匹配的方法
        实参是s,s的编译时类型是Son类型,
        在MyClass类中有没有名字是method,形参类型是Son的方法,可以找到。
        public void method(Son s) {
            System.out.println("son");
        }

        (2)运行时
        my对象的运行时类型,看new的类型,是MySub类型。
        看一下MySub类中是否有对刚刚匹配的方法进行重写。
        发现没有重写:

        如果没有重写,依然执行刚刚匹配的方法。
        运行结果是 son

         */

        my.method(d);
        /*
        (1)编译时
        my对象的编译时类型是MyClass,在MyClass类中找到匹配的方法
        实参是d,d的编译时类型是Daughter类型,
        在MyClass类中有没有名字是method,形参类型是Daughter的方法,没有找到。
        在MyClass类中有没有名字是method,形参类型可以兼容Daughter的方法,可以找到。
        public void method(Father f) {
            System.out.println("father");
        }

        (2)运行时
        my对象的运行时类型,看new的类型,是MySub类型。
        看一下MySub类中是否有对刚刚匹配的方法进行重写。
        发现有重写:
        public void method(Father d) {//这个和父类的 method(Father f)是重写关系
            System.out.println("sub--father");
        }
        只要是又重写,就执行重写的方法体。
        运行结果是sub--father
         */

        System.out.println("-------------------");
        MySub sub = new MySub();
        sub.method(d);//对象.虚方法
        /*
        (1)编译时,
        看sub的编译时类型,是MySub类型,直接去MySub类中匹配方法。
        实参d是Daughter类型,可以找到最匹配的方法
         public void method(Daughter d) {
            System.out.println("daughter");
        }
        (2)运行时
        看sub的运行时类型,仍然是MySub类型,和编译时类型一致,
        刚刚匹配谁,就执行谁。
         */

        sub.method(s);
        /*
        (1)编译时
        sub的编译时类型是MySub,在MySub类中找匹配的方法。
        此时参与匹配的方法:
            public void method(Son s) {
                System.out.println("son");
            }
            public void method(Father d) {
                System.out.println("sub--father");
            }
            public void method(Daughter d) {
                System.out.println("daughter");
            }
         最终和method(Son s)匹配上了。
         (2)运行时
         sub的运行时类型仍然是MySub,刚刚匹配谁就执行谁
         */
    }
}

class MyClass{
    public void method(Father f) {
        System.out.println("father");
    }
    public void method(Son s) {
        System.out.println("son");
    }
}
class MySub extends MyClass{
    public void method(Father d) {//这个和父类的 method(Father f)是重写关系
        System.out.println("sub--father");
    }
    public void method(Daughter d) {//这个和父类的两个method是重载关系
        System.out.println("daughter");
    }
}
class Father{

}
class Son extends Father{

}
class Daughter extends Father{

}

6.3.6 xx.成员变量访问只看xx的编译时类型(面试题)

当多态引用时,通过xx.成员变量方式访问成员变量,此时只看xx的编译时类型。

public class TestVariable {
    public static void main(String[] args) {
        Father f = new Son();
        System.out.println("f.a = " + f.a);//1
        //f的编译时类型是Father

        Son s = new Son();
        System.out.println("s.a = " + s.a);//2
        //s的编译时类型是Son

        /*
        无论是f还是s对象,堆内存中都有2个a。
         */
        System.out.println("((Son)f).a = " + ((Son)f).a );//对f向下转型
        System.out.println("((Father)s).a = " + ((Father)s).a);//对s向上转型
    }
}

class Father{
    int a = 1;
}
class Son extends Father{
    int a = 2;//子类声明了一个和父类重名的成员变量
                //这种情况,实际开发中是要极力避免的
}

Java学习笔记(八)_父类_07

//原则:xx.成员变量,只看xx的编译时类型
//编译时类型是什么?(1)如果没有()强转的语法,那么变量的编译时类型就是声明它的时候左边的类型
//                (2)如果有()强转的语法,那么就以()中强转的类型为准
public class Test1 {
    public static void main(String[] args) {
        A a = new B();//多态引用
        System.out.println(a.num);//1
                //a的编译时类型A
        System.out.println(((B)a).num);//2
                //((B)a)的编译时类型B
        System.out.println(((A)((B)a)).num);//1
                //((A)((B)a))的编译时类型A
        System.out.println("-------------------");
        B b = new B();//本态引用
        System.out.println(b.num);//2
                //b的编译时类型B
        System.out.println(((A)b).num);//1
                //((A)b)的编译时类型A
        System.out.println(((B)((A)b)).num);//2
                //((B)((A)b))的编译时类型B
    }
}
class A{
    int num = 1;
}
class B extends A{
    int num = 2;
}

6.3.7 通过方法访问成员变量遵循就近原则

通过方法去访问成员变量时,该如何确定是哪个成员变量呢?

(1)先确定运行时执行哪个方法

(2)再看方法体中的代码,变量遵循就近原则

public class Father {
    private int a = 1;
    private int b = 1;

    public int getA() {
        return a;//此处的a一定是Father类声明的a
    }

    public int getB() {
        return b;//此处的b一定是Father类声明的b
    }
}
public class Son extends Father {
    private int a = 2;
    private int b = 2;
    private int c = 2;

    @Override
    public int getA() {
        return a;//此处的a一定是Son类声明的a,就近原则
    }

    public int getC() {
        return c;//此处的c一定是Son类声明的c
    }
}
public class TestMethodVariable {
    public static void main(String[] args) {
        Father f = new Son();
        System.out.println(f.getA());//2
        /*
        f的编译时类型是Father,去Father匹配,可以找到getA()
        f的运行时看Son类,有重写,执行重写的
        f.getA()运行时Son类中getA()
         */
        System.out.println(f.getB());//1
        /*
         f的编译时类型是Father,去Father匹配,可以找到getB()
         f的运行时类型是Son,但是Son类中没有重写getB()
        f.getB()运行时Father类的getB()
         */

//        System.out.println(f.getC());//报错
        /*
        f.getC()为什么报错?
        因为f的编译时类型是Father,
        此时Father类中没有getC()方法,
        编译不通过
         */

        System.out.println(((Son)f).getA());//2
        /*
        ((Son)f)的编译时类型是Son,去Son类中匹配的,
        ((Son)f)的运行时类型也是Son,仍然执行Son的getA()
         */


        System.out.println(((Son)f).getB());//1
        /*
       ((Son)f)的编译时类型是Son,去Son类中找没找到,可以找到父类中getB()
       ((Son)f)的运行时类型也是Son,因为Son类没有重写,仍然执行父类中getB()
         */
        System.out.println(((Son)f).getC());//2
         /*
       ((Son)f)的编译时类型是Son,去Son类中找getC(),找到了
       ((Son)f)的运行时类型也是Son,仍然执行Son类中getC()
         */
    }
}

Java学习笔记(八)_父类_08

标签:Java,void,System,笔记,学习,类型,println,public,out
From: https://blog.51cto.com/u_16213911/7057045

相关文章

  • java之手搓简单ORM框架--SQL的DELETE
    1.手搓简单SQL增删改查框架-删除1.1创建简单类,并使用泛型类,这里可能使用到之间写的三篇知识的内容,如果不了解的小伙伴可以去java高级之泛型java高级之映射java高级之反射当然,前提是必须要把数据库相关连接弄好,这里会专门出一篇java之jdbc现在咱们继续手搓框架开始叭!1.2前......
  • 基于时态差分法的强化学习:Sarsa和Q-learning
    时态差分法(TemporalDifference,TD)是一类在强化学习中广泛应用的算法,用于学习价值函数或策略。Sarsa和Q-learning都是基于时态差分法的重要算法,用于解决马尔可夫决策过程(MarkovDecisionProcess,MDP)中的强化学习问题。下面是最简单的TD方法更新:它只使用当前行动之后的奖励值......
  • Java 图片、文件 Base64 互转
    Java图片、文件Base64互转packagecom.thoth.his.base.util;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;importjava.util.Base64;publicclassImageUtil{publicstaticStringFileToBase64(StringfilePath)......
  • java之手搓简单ORM框架--SQL的UPDATA
    1.手搓简单SQL增删改查框架-更改1.1创建简单类,并使用泛型类,这里可能使用到之间写的三篇知识的内容,如果不了解的小伙伴可以去java高级之泛型java高级之映射java高级之反射当然,前提是必须要把数据库相关连接弄好,这里会专门出一篇java之jdbc现在咱们继续手搓框架开始叭!由于上......
  • [学习笔记]Dirichlet
    Dirichlet学习笔记Dirichlet前缀和狄利克雷前缀和是求解形如\[b_k=\sum\limits_{i|k}a_i\]的式子首先我们可以想到枚举\(i\),再枚举\(i\)的倍数\(j\)$$b_j=b_j+a_i$$此时的时间复杂度为\(n/1+n/2+n/3+...+n/1\)。由于调和级数,时间复杂度为\(O(nlogn)\)进一步分析......
  • 【刷题笔记】17. Letter Combinations of a Phone Number
    题目Givenastringcontainingdigitsfrom 2-9 inclusive,returnallpossiblelettercombinationsthatthenumbercouldrepresent.Amappingofdigittoletters(justlikeonthetelephonebuttons)isgivenbelow.Notethat1doesnotmaptoanyletters.Ex......
  • C语言小白,下面是一些笔记,大神勿入!
    ~ --按位取反,在二进制中,原来的1变为0,0变为1,得到补码原码:直接按照正负写出的二进制序列反码:原码的符号位不变,其他位按位取反得到补码:反码加一得到inta=0;intb=~a;//b是有符号的整形,其二进位最左边一个数为0为正数,为1是负数printf("%d\n",b);//打印的是这个数的原码inta=10;int......
  • Javascript 面向对象编程
    avascript是一个类C的语言,他的面向对象的东西相对于C++/Java比较奇怪,但是其的确相当的强大,在 Todd同学的“对象的消息模型”一文中我们已经可以看到一些端倪了。这两天有个前同事总在问我Javascript面向对象的东西,所以,索性写篇文章让他看去吧,这里这篇文章主要想从一个整体的角度......
  • Java 观察者模式的浅析
    简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象。这样一个主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象,使这些观察者对象能够自动更新。 观察者模式的结构 观察者(Observer)模式是对象的行为型模式,又叫做发表-订阅(P......
  • MySQL数据库笔记(二)
    聚集函数聚集函数:SQL提供的方法统计函数count(字段):统计表中记录的个数.语法: selectcount(*)from表名; 练习: --统计exam中有多少个学生: selectcount(name)fromtb_exam; selectcount(id)fromtb_exam; selectcount(*)fromtb_exam;--根据任意字段进行统计......