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(操作)
决定对象是否属于所给定的类型
indexbyte1和indexbyte2是instanceof指令的两个参数
instanceof
indexbyte1
indexbyte2instanceof = 193 (0xc1)
根据描述,objectref 指instanceof左边的引用类型,result为最终比较的结果,0是false,1是true
..., objectref →
..., result
无符号的indexbyte1 和indexbyte2 被构造成了一个索引(index),存储在当前类的.class文件的常量池中,这个索引的值为: (indexbyte1
<<
8) | indexbyte2。这个索引上的各个项必须是一个指向某个类、数组或接口符号引用。所以可以理解为,indexbyte1 和indexbyte2 分别指向了程序中instanceof左右参数所代表的的类型。若左边的引用类型为null,则结果肯定为0(压如操作栈中)。
否则,根据右边的引用类型(类、数组或接口),判断左边引用类型是否是右边引用类型(类、数组或接口)的实例,是则返回1,否则返回0.
下面列出了判断左边类型(不为null)是否是右边类型的实例的几点规则。假设S是左边引用所指向的类型,T是右边的引用类型(类、数组或接口),那么instanceof指令在下列情况下会返回true:
以下是英文原文:
Operation
Determine if object is of given type
Format
instanceof
indexbyte1
indexbyte2Forms
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 anint
result of 0 as anint
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 anint
on the operand stack; otherwise, it pushes anint
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.