1.java引用变量

之前谈过,在方法中并未真正“拥有”一个变量,而是在栈区中进行对堆区变量的引用,在java中,引用变量有两个类型,一个是编译时类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,而运行时由实际赋给变量的对象决定。如果编译时类型和运行时不一致,就会出现多态(Polymorphism)

Bird bird=new Bird();
/**
    Bird bird:编译时赋给该变量类型Bird
    new Bird:运行时赋给该变量Bird
*/

2.举个栗子

public class Bird {
    public void fly(){
        System.out.println("i am flying in the sky");
    }
}

public class Ostrich extends Bird {
    
    public void fly(){
        System.out.println("i am only running in the land");
    }
    public void callOverridedMethod(){
        super.fly();
    }
    public static void main(String[]args){
        //多态 访问Ostrich的方法
       Bird bird=new Ostrich();
       Bird bird1=new Bird();
       bird.fly();
       bird1.fly();

    }
}

3.说明

*对于上述代码而言 bird1的编译时类型为Bird 运行时类型也为Bird,但是对于bird而言 ,编译时类型为Bird,运行时类型为Ostrich,所以产生了多态现象,调用了子类(Ostrich)的fly方法。

*因为子类是一种特殊的父类,因此,java允许把一个子类对象直接赋给一个父类引用变量,无需进行任何类型转换(向上转型),由系统自动完成

*当把子类变量赋给父类引用变量时,被称为向上转型(upcasting),这种转型只是说明引用变量的编译类型是父类,但是实际执行的方法,依然表现出子类对象的行为方式,但是如果把父类对象如果赋给子类对象,需要强制转换,而且可能在运行时产生异常(ClassCastException)

Java运行期多态和编译期多态 java运行时多态简单例子_引用变量

4.如何安全的进行类型转换?

instanceof 运算符的前一个操作数通常是一个引用类型变量,后一个通常为操作类(或接口),它用来判断前面的对象是否是后面类的子类,实现类的实例 如果是 返回true 不是 返回 false

public class Ostrich extends Bird {
    /**
     * 方法重写:函数名一致 内部处理逻辑不通
     */
    public void fly(){
        System.out.println("i am only running in the land");
    }
    public void callOverridedMethod(){
        super.fly();
    }
    public static void main(String[]args){
        //多态 访问Ostrich的方法
       Bird bird=new Ostrich();
       Bird bird1=new Bird();
       
    System.out.println(bird instanceof Bird);  //true
    System.out.println(bird1 instanceof Ostrich);//false
    }
}

所以 如果使用强制转换之前,一般先判断一个对象是否可以强制类型转换,这样的话,可以使得代码更加健壮