instanceof关键字(Java SE8官方文档)

Java SE8语言规范的介绍 

根据《Java语言规范 基于Java SE8中文》一书,instanceof是一个类型比较操作符,表达式为:

                           关系表达式(RelationalExpression) instanceof 可具化的引用类型(Reifiable ReferenceType)

其中左边的关系表达式为null或引用类型,右边的可具化的引用类型在书中有提到,一个类型当且仅当满足下列条件之一时,才是可具化类型:

  • 它引用的是非泛化的类或接口类型的声明。
  • 它是参数化类型,其中所有类型引元都是无界的通配符。
  • 它是原生类型。
  • 它是简单类型。
  • 它是数组类型,且其元素类型是可具化的。
  • 它是嵌套类型,且其中用“.”分隔开的每一个类型T都是可具化的。

这里可具化引用的概念还涉及到原生类型和简单类型,我就不列举了,感兴趣的可以找到相关书籍去看看,总之按照经验来看,可以是类、接口和数组限定名,如:

public class Test5  {
    public static void main(String[] args){
        MyThread[] threadArr = new MyThread[10];
        MyThread myThread = new MyThread();

        System.out.println(myThread instanceof Object);            // 右边是类
        System.out.println(myThread instanceof Runnable);          // 右边是接口
        System.out.println(threadArr instanceof Runnable[]);       // 右边是数组
    }

    private static class MyThread  implements Runnable {
        @Override
        public void run() { }
    }
}

/** 打印结果为:
*    true
*    true
*    true
*/

当程序编译时,对于instanceof操作符,首先会检查右边的参数是不是可具化引用,不是就产生编译时错误,是就检查右边参数的类型能否强转成左边的引用类型,否的话也会产生编译时错误。

查看instanceof字节码

通过javap -c Test5来编译上面的代码,结果如下,可以看到instanceof在JVM中就是通过instanceof字节码指令来执行的。

"D:/jdk 1.8u191/bin/javap" -c Test5
Compiled from "Test5.java"
public class Test5 {
  public Test5();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: anewarray     #2                  // class Test5$MyThread
       5: astore_1
       6: new           #2                  // class Test5$MyThread
       9: dup
      10: aconst_null
      11: invokespecial #3                  // Method Test5$MyThread."<init>":(LTest5$1;)V
      14: astore_2
      15: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      18: aload_2
        // 第一个
      19: instanceof    #5                  // class java/lang/Object
      22: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
      25: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      28: aload_2
      29: instanceof    #7                  // class java/lang/Runnable
      32: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
      35: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      38: aload_1
      39: instanceof    #8                  // class "[Ljava/lang/Runnable;"
      42: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
      45: return
}

Java SE8虚拟机规范对instanceof指令的介绍

下面是Java SE8虚拟机规范对instanceof指令的介绍,我根据自己的理解作了简单翻译。

Operation(操作)

决定对象是否属于所给定的类型

Format(格式)

indexbyte1和indexbyte2是instanceof指令的两个参数

instanceof
indexbyte1
indexbyte2

Forms

instanceof = 193 (0xc1)

Operand Stack(操作数栈)

根据描述,objectref 指instanceof左边的引用类型,result为最终比较的结果,0是false,1是true

..., objectref →

..., result

Description(描述)

无符号的indexbyte1 和indexbyte2 被构造成了一个索引(index),存储在当前类的.class文件的常量池中,这个索引的值为: (indexbyte1 << 8) | indexbyte2。这个索引上的各个项必须是一个指向某个类、数组或接口符号引用。所以可以理解为,indexbyte1 和indexbyte2 分别指向了程序中instanceof左右参数所代表的的类型。

若左边的引用类型为null,则结果肯定为0(压如操作栈中)。

否则,根据右边的引用类型(类、数组或接口),判断左边引用类型是否是右边引用类型(类、数组或接口)的实例,是则返回1,否则返回0.

下面列出了判断左边类型(不为null)是否是右边类型的实例的几点规则。假设S是左边引用所指向的类型,T是右边的引用类型(类、数组或接口),那么instanceof指令在下列情况下会返回true:

  • 若S是一个类(class):

    • 若T是一个类,那S要么与T是相同的类,要么是T的子类。

    • 若T是一个接口,那么S必须是T的实现类。

  • 若S是一个接口:

    • 若T是一个类,那T必须是Object类。

    • 若T是一个接口,那T要么和S是相同的接口,要么是S的父接口。

  • 若S是一个数组SC[]:

    • 若T是一个类,那T必须是Object类。

    • If T is an interface type, then T must be one of the interfaces implemented by arrays (不明白这个arrays是指SC[],还是在jdk中的Arrays).

    • 若T是一个数组TC[]:

      • TC 和SC是相同的基本类型。

      • TC和SC都是引用类型,类型SC能通过JVM运行时规则转换成TC。(为啥不是TC转换成SC?)

以下是英文原文:

Operation

Determine if object is of given type

Format
instanceof
indexbyte1
indexbyte2

Forms

instanceof = 193 (0xc1)

Operand Stack

..., objectref →

..., result

Description

The objectref, which must be of type reference, is popped from the operand stack. The unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class (§2.6), where the value of the index is (indexbyte1 << 8) | indexbyte2. The run-time constant pool item at the index must be a symbolic reference to a class, array, or interface type.

If objectref is null, the instanceof instruction pushes an int result of 0 as an int on the operand stack.

Otherwise, the named class, array, or interface type is resolved (§5.4.3.1). If objectref is an instance of the resolved class or array or implements the resolved interface, the instanceof instruction pushes an int result of 1 as an int on the operand stack; otherwise, it pushes an int result of 0.

The following rules are used to determine whether an objectref that is not null is an instance of the resolved type: If S is the class of the object referred to by objectref and T is the resolved class, array, or interface type, instanceof determines whether objectref is an instance of T as follows:

  • If S is an ordinary (nonarray) class, then:

    • If T is a class type, then S must be the same class as T, or S must be a subclass of T;

    • If T is an interface type, then S must implement interface T.

  • If S is an interface type, then:

    • If T is a class type, then T must be Object.

    • If T is an interface type, then T must be the same interface as S or a superinterface of S.

  • If S is a class representing the array type SC[], that is, an array of components of type SC, then:

    • If T is a class type, then T must be Object.

    • If T is an interface type, then T must be one of the interfaces implemented by arrays (JLS §4.10.3).

    • If T is an array type TC[], that is, an array of components of type TC, then one of the following must be true:

      • TC and SC are the same primitive type.

      • TC and SC are reference types, and type SC can be cast to TC by these run-time rules.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值