【疯狂Java_突破程序员基本功的16课】charpt7 面向对象的陷阱

7.1 instanceof运算符的陷阱

instanceof运算符的前一个操作数通常是一个引用类型的变量,后一个 操作数通常是一个类(也可以是接口,可以把接口当成一种特殊的类),它用于判断前面的对象是否是后面的类或其子类、实现类的实例。如果是,返回true,否则返回false。

根据java语言规范,使用instanceof运算符有一个限制:instanceof运算符前面操作数的编译时类型必须是如下3种情况:

  • 要么与后面的类相同;
  • 要么是后面的类的父类;
  • 要么是后面类型的子类

如果前面操作数的编译时类型与后面的类型没有任何关系,程序将无法通过编译。

观察下面程序:

public class InstanceofTest {
    public static void main(String[] args) {
        Object str = "疯狂Java讲义";
        // 执行强制类型转换,让math引用原来str引用的对象
        Math math = (Math) str;              // 1
        System.out.println("字符串是否是String的实例:" + (math instanceof String));     //2
    }
}

粗看之下,可能会得到一个结论:这个程序不能通过编译,编译期应该在1行代码处提示编译错误,因为1行代码尝试把一个String强制转型为Math。但是编译该程序时,却发现编译期提示如下的错误:

InstanceofTest.java:6: 不可转换的类型
找到: java.lang.Math
需要: java.lang.String
        System.out.println("字符串是否是String的实例:" + (math instanceof String));
                                                 ^
1 错误

至于1行出的代码为何没有出现编译错误,这和强制转型的机制有关。对于Java的强制转型而言,也可以分为编译、运行两个阶段来分析它。

  • 在编译阶段,强制转型要求被转型变量的编译时类型必须是如下3种情况之一:
  1. 被转型变量的编译时类型与目标类型相同;
  2. 被转型变量的编译时类型是目标类型的父类;
  3. 被转型变量的编译时类型是目标类型子类,这种情况下可以自动向上转型,无需强制转换。

如果被转型变量的编译时类型与目标类型没有任何继承关系,编译期将提示错误。通过上面的分析可以看出,强制类型转换的编译阶段只关心引用变量的编译时类型,至于该引用变量实际引用对象的类型,编译器并不关心,也没法关心。

  • 在运行阶段,被转型变量所引用对象的时实际类型必须是目标类型的实例,或者是目标类型的子类、实现类的实例,否则在运行时将引发ClassCastException异常。

另外如果给一个引用变量赋值null,例如以下程序,将返回false:

public class InstanceofTest {
    public static void main(String[] args) {
        String str = null;
        System.out.println("null是否是String的实例:" + (str instanceof String));
    }
}
原文地址:https://www.cnblogs.com/yangfengtao/p/2787814.html