instanceof和Class.isAssignableFrom(...)有什么区别?

以下哪项更好?

a instanceof B

要么

B.class.isAssignableFrom(a.getClass())

我所知道的唯一区别是,当'a'为空时,第一个返回false,第二个抛出异常。 除此之外,他们总是给出相同的结果吗?


使用instanceof ,您需要在编译时了解B的类。 当使用isAssignableFrom()它可以是动态的,并在运行时更改。


instanceof只能用于引用类型,而不能用于基本类型。 isAssignableFrom()可以用于任何类对象:

a instanceof int  // syntax error
3 instanceof Foo  // syntax error
int.class.isAssignableFrom(int.class)  // true

请参阅http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class)。


就性能而言:

TL; DR

使用具有相似性能的isInstanceinstanceofisAssignableFrom稍微慢一些。

按性能排序:

  • isInstance
  • instanceof (+ 0.5%)
  • isAssignableFrom (+ 2.7%)
  • 基于JAVA 8 Windows x64 2000次迭代的基准测试,包含20次热身迭代。

    理论上

    使用类似于字节码查看器的软件,我们可以将每个操作符转换为字节码。

    在以下方面:

    package foo;
    
    public class Benchmark
    {
      public static final Object a = new A();
      public static final Object b = new B();
    
      ...
    
    }
    

    JAVA:

    b instanceof A;
    

    字节码:

    getstatic foo/Benchmark.b:java.lang.Object
    instanceof foo/A
    

    JAVA:

    A.class.isInstance(b);
    

    字节码:

    ldc Lfoo/A; (org.objectweb.asm.Type)
    getstatic foo/Benchmark.b:java.lang.Object
    invokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z);
    

    JAVA:

    A.class.isAssignableFrom(b.getClass());
    

    字节码:

    ldc Lfoo/A; (org.objectweb.asm.Type)
    getstatic foo/Benchmark.b:java.lang.Object
    invokevirtual java/lang/Object getClass(()Ljava/lang/Class;);
    invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);
    

    测量每个运算符使用多少字节码指令,我们可以预期instanceofisInstanceisAssignableFrom快。 但是,实际的性能不是由字节码决定的,而是由机器码决定的(与平台相关)。 让我们为每个运营商做一个微观基准。

    基准

    信用:正如@ aleksandr-dubinsky所建议的,并感谢@yura提供的基本代码,以下是JMH基准测试(请参阅本调整指南):

    class A {}
    class B extends A {}
    
    public class Benchmark {
    
        public static final Object a = new A();
        public static final Object b = new B();
    
        @Benchmark
        @BenchmarkMode(Mode.Throughput)
        @OutputTimeUnit(TimeUnit.MICROSECONDS)
        public boolean testInstanceOf()
        {
            return b instanceof A;
        }
    
        @Benchmark
        @BenchmarkMode(Mode.Throughput)
        @OutputTimeUnit(TimeUnit.MICROSECONDS)
        public boolean testIsInstance()
        {
            return A.class.isInstance(b);
        }
    
        @Benchmark
        @BenchmarkMode(Mode.Throughput)
        @OutputTimeUnit(TimeUnit.MICROSECONDS)
        public boolean testIsAssignableFrom()
        {
            return A.class.isAssignableFrom(b.getClass());
        }
    
        public static void main(String[] args) throws RunnerException {
            Options opt = new OptionsBuilder()
                    .include(TestPerf2.class.getSimpleName())
                    .warmupIterations(20)
                    .measurementIterations(2000)
                    .forks(1)
                    .build();
    
            new Runner(opt).run();
        }
    }
    

    得到以下结果(得分是一个时间单位的操作次数,所以得分越高越好):

    Benchmark                       Mode   Cnt    Score   Error   Units
    Benchmark.testIsInstance        thrpt  2000  373,061 ± 0,115  ops/us
    Benchmark.testInstanceOf        thrpt  2000  371,047 ± 0,131  ops/us
    Benchmark.testIsAssignableFrom  thrpt  2000  363,648 ± 0,289  ops/us
    

    警告

  • 该基准是JVM和平台相关的。 由于每个操作之间没有显着差异,因此可能会在不同的JAVA版本和/或Solaris,Mac或Linux等平台上获得不同的结果(也可能有不同的顺序!)。
  • 当“B扩展A”时,基准比较“B是A的实例”的性能。 如果类层次更深且更复杂(如B扩展X扩展Y,扩展Z扩展A),则结果可能会不同。
  • 通常建议先编写代码首先选择一个操作员(最方便),然后对您的代码进行配置以检查是否存在性能瓶颈。 也许这个运算符在代码的上下文中可以忽略不计,或者......
  • 关于以前的观点,代码上下文中的instanceof可能比isInstance更容易优化,例如...
  • 给你一个例子,采取以下循环:

    class A{}
    class B extends A{}
    
    A b = new B();
    
    boolean execute(){
      return A.class.isAssignableFrom(b.getClass());
      // return A.class.isInstance(b);
      // return b instanceof A;
    }
    
    // Warmup the code
    for (int i = 0; i < 100; ++i)
      execute();
    
    // Time it
    int count = 100000;
    final long start = System.nanoTime();
    for(int i=0; i<count; i++){
       execute();
    }
    final long elapsed = System.nanoTime() - start;
    

    感谢JIT,代码在某个时候进行了优化,我们得到:

  • instanceof:6ms
  • isInstance:12ms
  • isAssignableFrom:15ms
  • 注意

    最初这篇文章是使用原始JAVA中的for循环做自己的基准测试,由于像Just In Time这样的一些优化可以消除循环,所以给出了不可靠的结果。 所以它主要测量JIT编译器花费多少时间来优化循环:请参阅独立于迭代次数的性能测试以了解更多详细信息

    相关问题

  • instanceof运算符是否会产生很多开销? 为什么?
  • 如何在JAVA内部实现instanceof?
  • 在Java中使用instanceof的性能影响
  • 链接地址: http://www.djcxy.com/p/13327.html

    上一篇: What is the difference between instanceof and Class.isAssignableFrom(...)?

    下一篇: MSSQL: alter column not null, in general