java冷知识:在Java中,子类数组的引用可以转换成父类数组的引用,而不需要采用强制类型转换。

1.多态

什么是多态?

同类型的对象,执行同一个行为,会表现出不同的行为特征

多态成员的访问特点:

  • 方法调用:编译时看左边,运行时看右边
  • 变量调用:编译、运行时都看左边
public abstract class Animal {
    public String name="动物";
    public abstract void run();
}
public class Dog extends Animal{

    private String name="狗";
    @Override
    public void run() {
        System.out.println("奔跑");
    }

    public static void main(String[] args) {
        Animal animal = new Dog();
        System.out.println(animal.name);
        animal.run();
    }
}

Java 获取父类对象的属性 java获取父类的所有子类_java

当我们使用多态时,父类在左边,右边是实现类。

调用成员变量时,输出的是父类变量。当调用方法时,需要父类中有这个方法,是由子类实现的。

多态好处:

  • 右边对象可以实现解耦合,便于扩展和维护
  • 定义方法时,使用父类类型作为参数,该方法就可以接收这父类的一起子类对象,体现多态的扩展性与便利
public static void main(String[] args) {
        Animal animal = new Dog();
//        System.out.println(animal.name);
//        animal.run();
        go(animal);
    }

    public static void go(Animal animal){
        System.out.println("开始~");
        animal.run();
        System.out.println("结束~");
    }

Java 获取父类对象的属性 java获取父类的所有子类_Java 获取父类对象的属性_02

虽然传参是父类类型Animal,但是调用的方法确实子类Dog的,这又被成为方法回调。

但是不能调用子类中独有的方法。

在上面我们说过,调用方法时,编译看左边,运行看右边。左边父类中如果没有定义该方法,那么在编译期间便会报错。

类型转换问题:

  • 自动类型转换(从子到父):子类对象赋值给父类类型的变量指向
  • 强制类型转换(从父到子):此时必须进行强制类型转换,子类 对象 = (子类)父类类型。

原因:父类的范围大,子类范围小,小范围转大范围不需要强转,大范围转小范围需要强转。

public static void main(String[] args) {
        //子类转父类
        Animal animal = new Dog();
        
        //父类转子类
        Dog dog = (Dog) animal;
        
    }

注意:有继承或者实现关系,编译阶段可以强转,运行时可能报错。

public static void main(String[] args) {
        //子类转父类
        Animal animal = new Dog();

        //父类转子类
        Cat cat = (Cat) animal;

    }

Java 获取父类对象的属性 java获取父类的所有子类_外部类_03

虽然编译期间不会报错,但是运行时会报错ClassCastException。

当我们在转换之前,使用instanceof判断当前对象的真实类型,在进行强转。

public static void main(String[] args) {
        //子类转父类
        Animal animal = new Dog();

        //父类转子类
        if(animal instanceof Dog){
            Dog dog = (Dog) animal;
        }else if(animal instanceof Cat){
            Cat cat = (Cat) animal;
        }

    }

2.内部类

使用场景及作用

  • 一个事物的内部有一部分需要一个完整的结构来进行描述,而这个内部完整的结构又只为了外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计
  • 内部类可以直接访问外部类的静态成员,不能直接访问外部类的实例成员
  • 内部类提供了更好的封装性,内部类本身就可以用private protected等修饰,封装性可以做更多控制

1.静态内部类(了解)

由static修饰,与普通类用法相同。

public class People {

    public static class Heart{
        private double width;
        private double height;

        public double getWidth() {
            return width;
        }

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

        public double getHeight() {
            return height;
        }

        public void setHeight(double height) {
            this.height = height;
        }
    }
}

创建方法

外部类名.内部类名 变量名 = new 外部类名.内部类名

public static void main(String[] args) {
        People.Heart heart = new People.Heart();
    }

 静态内部类不可以直接访问外部类的实例对象。

2.成员内部类(了解)

创建方法

外部类名.内部类名 变量名 = new 外部类名().new 内部类名

public static void main(String[] args) {
        People.Heart heart = new People().new Heart();
    }

注意:

  • 成员内部类可以直接访问外部类静态成员
  • 成员内部类的实例方法可以直接访问外部类的实例成员(必须先有外部类,才能有成员内部类)

成员内部类可以通过外部类名去调用外部类的成员变量

public class People {

    private int h = 666;
    public class Heart{
        private int h = 888;
        private double width;
        private double height;

        public void show(){
            int h = 999;
            System.out.println(h);
            System.out.println(this.h);
            System.out.println(People.this.h);
        }
        public double getWidth() {
            return width;
        }

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

        public double getHeight() {
            return height;
        }

        public void setHeight(double height) {
            this.height = height;
        }
    }
}
public static void main(String[] args) {
        People.Heart heart = new People().new Heart();
        heart.show();
    }

Java 获取父类对象的属性 java获取父类的所有子类_父类_04

3.局部内部类(了解,几乎不会用到)

  • 局部内部类放在方法、代码块、构造器等执行体中
  • 局部内部类的类文件名为:外部类$N内部类.class

随用随消失,使用范围在main函数内

public static void main(String[] args) {
        class Eye{
            int dre;
        }
        
        Eye eye = new Eye();
        
    }

4.匿名内部类(重要)

作用:方便创建子类对象,最终目的为了简化代码编写

特点:

  • 匿名内部类是一个没有名字的内部类
  • 匿名内部类写出来就会产生一个匿名内部类对象
  • 匿名内部类的对象类型相当于是当前new的内个类型的子类

举例子:

栗子1

public abstract class Animal {
    public abstract void run();
}
class t1{
    public static void main(String[] args) {
        Animal animal = new Animal() {
            @Override
            public void run() {
                System.out.println("我是匿名内部类~~");
            }
        };
        animal.run();
    }
}

栗子2 

public abstract class Animal {
    public abstract void run();
}
class t1{
    public static void main(String[] args) {
        new Animal() {
            @Override
            public void run() {
                System.out.println("我是匿名内部类~~");
            }
        }.run();
    }
}