java验证类型 关键字_【Java关键字】理解instanceof关键字

本文详细介绍了Java关键字instanceof的用法,包括其用于测试对象是否为类或接口的实例,以及对null、实例对象、实现类和子类的处理。通过示例解释了instanceof的判断规则和编译时的检查,强调了使用instanceof时对象类型必须有继承或实现关系。
摘要由CSDN通过智能技术生成

前言

前文对static关键字进行了介绍,读者可以知道static关键字是一个可以控制成员变量、成员方法以及代码块的加载顺序和作用范围。我们在平时看源码的时候会时不时看到instanceof关键字,Java开发者对它的第一印象就是:instanceof是用于测试一个对象是否是另一个类的实例。

本文主要对instanceof关键字进行介绍:

了解instanceof

instanceof作用的类型

instanceof作用于null

instanceof作用于实例对象、实现类、直接或间接子类

instanceof的实现策略

了解instanceof

instanceof严格来说是Java中的一个双目运算符,用于测试一个对象是否为一个类的实例,用法如下:

boolean ans = obj instanceof Class

其中,obj作为一个对象,Class表示一个类或一个接口,当obj为Class对象时,或者是接口的实现类、或者是Class的直接或间接子类,ans都会返回true,否则返回false。

也就是说,如果用instanceof关键字做判断的时候,instanceof操作符的左右操作数必须有继承关系或实现关系。

其实,在进行编译之前,编译器会检查obj是否能转换成右边的Class类型,如果不能转换则直接报错,如果不能确定类型,则编译之后,视运行结果而定。

instanceof作用的类型

int a = 1;

System.out.println(a instanceof Integer); //编译不通过

System.out.println(a instanceof Object); //编译不通过

可以看出,instanceof运算符只能对引用类型进行判断,不能是基本类型。

instanceof作用于null

System.out.println(null instanceof Integer);//false

我们知道,Java有两种数据类型,一种是基本数据类型,笔者在Java的基本数据类型、拆装箱(深入版)有详细介绍,另一种是引用类型,包括类、接口、数组等。而在Java中还有一种特殊的null类型。

null类型没有名字,所以不可能声明null类型的变量或者转换为null类型,null引用是null类型表达式唯一可能的值,null引用也可以转换为任意引用类型。总的来说,null是可以成为任意引用类型的特殊符号。

在JavaSE规范中对instanceof运算符的规定就是:如果判断对象为null,那么将返回false。

instanceof作用于实例对象、实现类、直接或间接子类

instanceof作用于实例对象

Integer a = new Integer(1);

System.out.println(a instanceof Integer);

这是最普遍的一种用法,用于判断a对象是否是Integer类的实例。

instanceof作用于实现类

举个栗子:

了解Java集合类的小伙伴都知道,List作为上层接口,有不少经典的实现类如ArrayList、LinkedList等。

public class LinkedList

extends AbstractSequentialList

implements List, Deque, Cloneable, java.io.Serializable

在此我们可以使用instanceof关键字来判断,某个对象是否是List接口的实现类:

LinkedList linkedList = new LinkedList();

System.out.println(linkedList instanceof LinkedList); //true

当然,反过来也是返回true。

List list = new LinkedList<>();

System.out.println(list instanceof LinkedList);

instanceof作用于直接或间接子类

上面提到过用instanceof关键字做判断的时候,instanceof操作符的左右操作数必须有继承关系或实现关系。用暹罗猫和病毒来举个栗子:

interface Animal{} //动物接口

class Feline implements Animal{} //猫科类

class Cat extends Feline{} //猫类

class SiameseCat extends Cat{} //暹罗猫类

class Virus{}

public class TestInstanceof{

public static void main(String[] args){

System.out.println("Cat的对象是谁的实例?");

instanceofTest(new Cat());

System.out.println("----------------------------------------");

System.out.println("SiameseCat的对象是谁的实例?");

instanceofTest(new SiameseCat());

System.out.println("----------------------------------------");

System.out.println("Virus的对象是谁的实例?");

instanceofTest(new Virus());

}

public static void instanceofTest(Object o){

if(o instanceof Virus)

System.out.println(o.getClass()+"类的实例,是类Virus的实例");

if(o instanceof SiameseCat)

System.out.println(o.getClass()+"类的实例,是类SiameseCat的实例");

if(o instanceof Cat)

System.out.println(o.getClass()+"类的实例,是类Cat的实例");

if(o instanceof Feline)

System.out.println(o.getClass()+"类的实例,是类Feline的实例");

if(o instanceof Animal)

System.out.println(o.getClass()+"类的实例,是类Animal的实例");

if(o instanceof Object)

System.out.println(o.getClass()+"类的实例,是类Object的实例");

}

}

上面的程序,展示出来的继承树是如下图所示:

2bae07d3e4affe470c67ce06d1bf973d.png

运行结果为:

Cat的对象是谁的实例?

class Cat类的实例,是类Cat的实例

class Cat类的实例,是类Feline的实例

class Cat类的实例,是类Animal的实例

class Cat类的实例,是类Object的实例

----------------------------------------

SiameseCat的对象是谁的实例?

class SiameseCat类的实例,是类SiameseCat的实例

class SiameseCat类的实例,是类Cat的实例

class SiameseCat类的实例,是类Feline的实例

class SiameseCat类的实例,是类Animal的实例

class SiameseCat类的实例,是类Object的实例

----------------------------------------

Virus的对象是谁的实例?

class Virus类的实例,是类Virus的实例

class Virus类的实例,是类Object的实例

从结果我们可以看到,某个类(接口也可以看成是一种特殊的类,但类和接口只是类型上的区别而已)的对象是不是其他类(或接口)的实例,只需按上图箭头方法,以此对象所在的类为起点到达继承树分支终点,沿途经过的类(包括本类或接口)都是该对象的实例。

但是需要注意的是,在判断某个类(或接口)的对象是不是其他类(或接口)的实例,一定要先进行向上转型,然后才可以用instanceof关键字进行判断。举个栗子:

interface Animal{} //动物接口

class Feline implements Animal{} //猫科动物类

class Canine implements Animal{} //犬科动物类

public class TestInstanceof{

public static void main(String[] args) {

Animal a = new Feline();

System.out.println(a instanceof Feline); //true

System.out.println(a instanceof Canine); //false

}

}

上述程序的继承树为:

4fa6366ddbbec2c842dedf22e20ef6ad.png

在判断接口Animal的对象a是不是类Canine的实例时,因为没有先进行向上转型,所以instanceof关键字判断的时候返回为false。想了解向上转型的朋友可以参考这篇文章:8.JAVA-向上转型、向下转型

instanceof的实现

其实,在进行编译之前,编译器会检查obj是否能转换成右边的Class类型,如果不能转换则直接报错,如果不能确定类型,则编译之后,视运行结果而定。举个栗子:

Feline feline = new Feline();

System.out.println(feline instanceof String); //编译错误

System.out.println(feline instanceof List); //false

System.out.println(feline instanceof List>); //false

System.out.println(feline instanceof List); //编译错误

虽然Feline很显然不能转换为String对象,但是为什么feline instance List却能通过编译?而feline instanceof List 又不能通过编译?

instanceof的执行过程

我们可以在Java语言规范JavaSE 8版中看到这么一段话:

4a8a2f6a16f91243c056bd639e887e27.png

大家需要注意的是演示代码的上一句话:

At run time, the result of the instanceof operator is true if the value of the RelationalExpression is not null and the reference could be cast to the ReferenceType without raising a ClassCastException. Otherwise the result is false.

拙译:在运行时,如果需判断的参数不为null且转换类型的时候,不会触发ClassCastException,那么instanceof的操作结果为true,否则为false。

所以用伪代码描述,就是:

boolean result;

if (obj == null) {

result = false;

} else {

try {

T temp = (T) obj; // checkcast

result = true;

} catch (ClassCastException e) {

result = false;

}

}

总结一下:

如果instanceof运算符的obj操作数的类型必须是引用类型或空类型,否则会发生编译时的错误;

如果obj强制转换为T时发生编译错误,则关系表达式的instanceof同样会产生编译时错误;

在运行时,如果T的值不为null,且obj可以转换为T而不引发ClassCastException,则instanceof运算符的结果为true。

可以知道,因为(String)feline 不能通过编译,而(List)feline可以通过编译,所以才会出现上述问题。

instanceof的执行策略

而instanceof的执行策略可以在JavaSE 8版本:instanceof实现算法中了解。

fb9352a9de2ab91daa355c03abc7ba4a.png

因为资料中涉及了JVM中的操作,翻译出来的效果可能会有点云里雾里的感觉,有兴趣的朋友可以自行查看资料。在此大致总结instanceof的执行策略(笔者拙译,水平不高,欢迎指正):

如果objectref为null,则直接返回false;否则就需要检查,objectref代表的类型对象S是否为T的子类型;

如果S == T,则返回true;

如果S不属于上述两种情况,则需要分情况来进行“子类型检查”了,而Java语言的类型系统中包括数组类型、接口类型和普通类类型,三者的子类型规定都不一样,须分开讨论。

如果S是普通类类型:

如果T是类类型,则S必须是T类型或者是T的子类;

如果T是接口类型,则S必须实现T。

如果S是接口类型:

如果T是类类型,那么T必须是Object类型的;

如果T是接口类型,那么T必须是S类型或者是S的父接口。

如果S是数组类型:

如果T是类类型,那么T必须是Object类型的;

如果T是接口类型,那么T必须是由数组实现的接口之一;

如果T也是数组类型,那么S和T必须是同一种原始类型,且S和T是引用类型的,S在转换成T的时候符合运行时的规则。

结语

英语真的太重要了,而且水平也决定了看问题的角度和高度。知乎上这位大佬的回答很值得去看,但是能看明白又是一回事了,屁股决定脑袋。

关于Java基础,笔者还写了一些文章,有需要的读者可以看一看:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值