面向对象 - 多态、static关键字、代码块

继续深入认识面向对象

狂神说Java BV12J41137hu

多态

多态讲的很好的一篇文章 Java多态性:Java什么是多态?
我建议直接看这一篇,我自己跟着狂神学,根本没听懂...

这里的案例上一篇 面向对象 - super、方法重写Override 已经写过了,这次写的简洁一些

Person.java

package oop.app.polymorphism;

public class Person {
}

Student.java

package oop.app.polymorphism;

public class Student extends Person{
}

这里看一下 子类(Student) 的继承树

HierarchyClassStudent.png

我暂时理解为,

new Student(); 可以沿着继承树向上指向引用类型,也即可以由父类的引用指向子类

Application.java

package oop.app.polymorphism;

public class Application {
    public static void main(String[] args) {
        Student s1 = new Student();
        Person s2 = new Student();
    }
}

调用方法

Type 对象 = new Class(input);

在上式中,Type 指定 对象 的类型,对象可以调用自己的方法(很合理)

而通过多态来创建对象的时候,由于类型声明为父类,因而无法直接调用右侧子类的方法

但是IDEA提供了调用的策略

Person.java

package oop.app.polymorphism;

public class Person {
    public void sayHi() {
        System.out.println("Person: Hi");
    }
}

Student.java

package oop.app.polymorphism;

public class Student extends Person{
    public void sayHello() {
        System.out.println("Student: Hello");
    }
}

Application.java

package oop.app.polymorphism;

public class Application {
    public static void main(String[] args) {
        Student s1 = new Student(); // Student 类型 (sayHello 方法 ,继承的 sayHi 方法)
        Person s2 = new Student(); // Person 类型 (sayHi 方法)
        s1.sayHello();
        s1.sayHi();

        s2.sayHi();
        // 当从Person类型调用Student类型的方法时
        // s2.sayHello(); 报错  Cannot resolve method 'sayHello' in 'Person'
        // 能执行哪些方法,主要看对象左边的类型,和右边的关系不大
        // 我还是不建议子类写和父类同名的方法
    }
}
Student: Hello
Person: Hi
Person: Hi

小总结:IDEA真的牛!

父类强制调用子类方法,需要做类型转化:

((Student) s2).sayHello(); // Person 转换为 Student 类型

注意事项

  • 多态是方法的多态,属性没有多态;
  • 父类和子类是有联系的,没有关系的类进行转换会抛出 类型转换异常
    • ClassCastException
  • 多态存在的条件:
    • 存在继承关系
    • 方法需要重写(不需要重写也就没有必要使用多态,我自己理解的)
    • 父类的引用指向子类对象 Father f1 = new Son();

再次温习一下关键字

  1. static 修饰方法,属于类(这里指与类一起加载),它不属于实例
  2. final 常量
  3. private 私有方法,不能重写

总结

多态,即统一方法可以根据发送对象的不同而采用多种不同的行为方式;

一个对象的实际类型时确定的,但是可以指向对象的引用的类型有很多(沿引用树向上)

多态是方法的多态,属性没有用多态(复读一遍)

instanceof

Java 实例 - instanceof 关键字用法

直观理解,其判断一个对象是什么类型

instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。

用法

yourOBJ instanceof targetOBJ

没啥好讲的,暂时用不到这里,而且我觉得狂神讲的稀碎 - -||

static 关键字

静态变量

静态变量是跟随类的,可以使用类名访问(推荐);

静态变量在内存中只跟随类储存一个,无论这个类生成了多少个对象;

所有对象(实例)共享静态变量;

Student.java

package oop.app.mystatic;

public class Student {
    protected static int numberOfStudent; // 静态变量
    private int age; // 非静态变量

    public static int getNumberOfStudent() {
        return numberOfStudent;
    }

}

Application.java

package oop.app.mystatic;

public class Application {
    public static void main(String[] args) {
        Student juniorMing = new Student();
        Student juniorPing = new Student();
        Student.numberOfStudent = 2;

        System.out.println("juniorMing\t" + juniorMing.getNumberOfStudent());
        System.out.println("juniorPing\t" + juniorPing.getNumberOfStudent());
        System.out.println("Student\t" + Student.numberOfStudent);
        
        //juniorMing	2
        //juniorPing	2
        //Student	2
    }
}

静态方法

同静态变量

代码块

  • 静态代码块 static {} 跟随类,只执行一次;
  • 匿名代码块 {} 跟随对象(实例),在构造器之前执行;
  • 存在继承情况时,记得构造器第一句是 super()即可

案例-单独类

Person.java

package oop.app.mystatic;

public class Person {
    {
        System.out.println("匿名代码块");
    }

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

    public Person() {
        System.out.println("构造器方法");
    }

    public static void main(String[] args) {
        Person person1 = new Person();
        //静态代码块
        //匿名代码块
        //构造器方法
        System.out.println("==================");
        Person person2 = new Person();
        //匿名代码块
        //构造器方法
    }
}

案例-继承类

Person.java

package oop.app.mystatic;

public class Person {
    {
        System.out.println("Person\t匿名代码块");
    }

    static {
        System.out.println("Person\t静态代码块");
    }

    public Person() {
        System.out.println("Person\t构造器方法");
    }

}

Teacher.java

package oop.app.mystatic;

public class Teacher extends Person{
    {
        System.out.println("Teacher\t匿名代码块");
    }

    static {
        System.out.println("Teacher\t静态代码块");
    }

    public Teacher() {
        System.out.println("Teacher\t构造器方法");
    }
}

Application.java

package oop.app.mystatic;

public class Application {

    public static void main(String[] args) {
        Teacher teacher = new Teacher();
    }
}

STDOUT

Person	静态代码块
Teacher	静态代码块
Person	匿名代码块
Person	构造器方法
Teacher	匿名代码块
Teacher	构造器方法

分析:

部分文字参考 萌小Q

但是值得讨论的是,他的总结有点乱,并且有问题,基于他的写法我重新总结了一遍

  1. 执行父类的静态代码块(如果是第一次调用方法),并初始化父类静态成员变量
  2. 执行子类的静态代码块(如果是第一次调用方法),并初始化子类静态成员变量
  3. 执行父类匿名代码块 + 父类的构造代码块,始化父类普通成员变量,执行父类的构造函数
  4. 执行子类匿名代码块 + 子类的构造代码块,初始化子类普通成员变量,执行子类的构造函数

因而不看代码,推理如下:

  • new Teacher(), 由于 Teacher继承 Person,因而按分析得

    1. (第一次调用Person)Person静态代码块 - "Person\t静态代码块"
    2. (第一次调用Teacher) Teacher静态代码块 - "Teacher\t静态代码块"
    3. Person 匿名代码块 - "Person\t匿名代码块"
    4. Person构造代码块 - "Person\t构造器方法"
    5. Teacher匿名代码块 - "Teacher\t匿名代码块"
    6. Teacher构造代码块 - "Teacher\t构造器方法"
  • 上述的 3.4. / 5.6. 都可以认为是第一调用这个方法时,执行对象构造器之前要保证匿名代码块至少执行过一次

当我继续声明新的Teacher对象时,静态代码块就不会被执行了,只剩下匿名代码块和构造代码块

Application.java

package oop.app.mystatic;

public class Application {

    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        System.out.println("=============================");
        Teacher teacher1 = new Teacher();
    }
}

STDOUT

Person	静态代码块
Teacher	静态代码块
Person	匿名代码块
Person	构造器方法
Teacher	匿名代码块
Teacher	构造器方法
=============================
Person	匿名代码块
Person	构造器方法
Teacher	匿名代码块
Teacher	构造器方法

Process finished with exit code 0

可见分析结果和测试案例结论一致

静态导入包*

不建议使用

Math.random() 这个方法为例

package oop.app.mystatic;
import static java.lang.Math.random; // 静态导入包

public class Application {

    public static void main(String[] args) {
        System.out.println(random());
    }
}

final 类

绝后的类,带有final关键字的类不允许被继承

posted @ 2021-12-15 21:19  jentreywang  阅读(45)  评论(0)    收藏  举报