有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!


共 (6) 个答案

  1. # 1 楼答案

    就表现而言:

    TL;DR

    使用性能相似的isInstanceinstanceofisAssignableFrom稍慢

    按性能排序:

    1. isInstance
    2. 实例(+0.5%)
    3. 可从中识别(+2.7%)

    基于JAVA 8 Windows x64上2000次迭代的基准测试,其中有20次预热迭代

    理论上

    使用类似bytecode viewer的软代码,我们可以将每个运算符转换为字节码

    在以下方面:

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

    爪哇:

    b instanceof A;
    

    字节码:

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

    爪哇:

    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);
    

    爪哇:

    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);
    

    通过测量每个运算符使用的字节码指令数,我们可以预期instanceofisInstance的速度要比isAssignableFrom快。然而,实际性能不是由字节码决定的,而是由机器代码(取决于平台)决定的。让我们为每个运营商做一个微观基准测试

    基准

    信贷:根据@aleksandr dubinsky的建议,并感谢@yura提供的基本代码,这里有一个JMH基准测试(见此tuning guide):

    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,代码在某个时刻得到了优化,我们得到:

    • 瞬时频率:6ms
    • i持续时间:12毫秒
    • isAssignableFrom:15毫秒

    注意

    最初,这篇文章使用原始JAVA中的for循环来做自己的基准测试,这会给出不可靠的结果,因为一些优化(如准时制)可以消除循环。因此,它主要是测量JIT编译器优化循环所需的时间:有关更多详细信息,请参见Performance test independent of the number of iterations

    相关问题

  2. # 3 楼答案

    还有另一个区别:

    不管X是什么,X的空实例都是false

    空。getClass()。isAssignableFrom(X)将抛出NullPointerException

  3. # 4 楼答案

    除了上面提到的基本区别之外,instanceof操作符和类中的isAssignableFrom方法之间还有一个核心细微的区别

    instanceof读作“这(左部分)是这个或这个(右部分)的任何子类的实例吗?”把x.getClass().isAssignableFrom(Y.class)读作“我能写X x = new Y()”。换句话说,instanceof运算符检查左对象是否与右类相同或是右类的子类,而isAssignableFrom检查是否可以将参数类(from)的对象分配给调用该方法的类的引用
    请注意,这两个都考虑实际实例而不是引用类型。

    考虑3个类A、B和C的例子,其中C扩展了B,B扩展了.< /p>

    B b = new C();
    
    System.out.println(b instanceof A); //is b (which is actually class C object) instance of A, yes. This will return true.  
    System.out.println(b instanceof B); // is b (which is actually class C object) instance of B, yes. This will return true.  
    System.out.println(b instanceof C); // is b (which is actually class C object) instance of C, yes. This will return true. If the first statement would be B b = new B(), this would have been false.
    System.out.println(b.getClass().isAssignableFrom(A.class));//Can I write C c = new A(), no. So this is false.
    System.out.println(b.getClass().isAssignableFrom(B.class)); //Can I write C c = new B(), no. So this is false.
    System.out.println(b.getClass().isAssignableFrom(C.class)); //Can I write C c = new C(), Yes. So this is true.
    
  4. # 5 楼答案

    a instanceof B更直接的等价物是

    B.class.isInstance(a)
    

    a也是null时,该方法有效(返回false)

  5. # 6 楼答案

    使用instanceof时,需要在编译时知道B的类。当使用isAssignableFrom()时,它可以是动态的,并且在运行时会发生变化