【问题标题】:How Marker Interface is handled by JVMJVM如何处理标记接口
【发布时间】:2018-08-16 04:24:47
【问题描述】:

标记界面没有任何东西。它只包含接口声明,那么JVM如何为实现这个标记接口的类处理它?

我们可以创建任何新的标记接口吗?

【问题讨论】:

    标签: java jvm


    【解决方案1】:

    您的问题应该是编译器如何处理标记接口,答案是:与任何其他接口没有不同。例如,假设我声明了一个新的标记接口Foo

    public interface Foo {
    }
    

    ...然后声明一个实现Foo的类Bar

    public class Bar implements Foo {
      private final int i;
    
      public Bar(int i) { this.i = i; }
    }
    

    我现在可以通过 Foo 类型的引用来引用 Bar 的实例:

    Foo foo = new Bar(5);
    

    ...并且(在运行时)检查对象是否实现Foo

    if (o instanceof Foo) {
      System.err.println("It's a Foo!");
    }
    

    后一种情况通常是使用标记接口的驱动程序;前一种情况几乎没有什么好处,因为没有可以在 Foo 上调用的方法(无需先尝试向下转换)。

    【讨论】:

    • JVM 必须有一种方式可以理解这个接口是一个标记接口,如果 JVM 遇到一个标记接口,它应该采取一些措施。如果我声明一个没有方法的接口并希望任何实现它的类都可以序列化,它就行不通:-)
    • @Pankaj 不,JVM 不在乎。进行检查的是 Java 代码
    • 给出的答案非常完美和清晰。我正在通过“ObjectOutputStream”类,writeObject0 方法正在检查 Object 是否为可序列化实例类型(obj instanceof Serializable)。如果不是,则抛出 NotSerializableException。除了大多数常规类如 String、ArrayList 等已经实现了 Serializable 接口,所以有时它会从我们的脑海中跳出来检查我们要序列化的 Class 是否实现了接口。
    • @Pankaj 你得到答案了吗(在 Pankaj 2015 年 8 月 5 日 9:56 提问)?如果是,请告诉我,我也遇到了同样的问题。
    • @uniquephase :instanceof 运算符用于检查我们的类是否实现了(标记)接口。因此,例如,如果您在没有实现 Serializable 接口的情况下序列化一个类,则条件 yourObject instanceof Serializable 将失败。
    【解决方案2】:

    然后JVM如何处理实现的类 这个标记界面?

    实现 Java 标记接口的类实例受益于特定行为,因为某些 JDK 类或 HotSpot JVM 为它们提供了特定行为。

    Serializable 接口为例。
    如果你深入研究ObjectOutputStreamObjectInputStream 可以看到其中实现了序列化/反序列化行为。

    这是由ObjectOutputStream.writeObject() 调用的ObjectOutputStream.writeObject0() 的sn-p,它说明了这一点:

    public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants {
    
       ...
       private void writeObject0(Object obj, boolean unshared) throws IOException   {
                ...    
                if (obj instanceof String) {
                    writeString((String) obj, unshared);
                } else if (cl.isArray()) {
                    writeArray(obj, desc, unshared);
                } else if (obj instanceof Enum) {
                    writeEnum((Enum<?>) obj, desc, unshared);
                } else if (obj instanceof Serializable) {
                    writeOrdinaryObject(obj, desc, unshared);
                } else {
                    if (extendedDebugInfo) {
                        throw new NotSerializableException(
                            cl.getName() + "\n" + debugInfoStack.toString());
                    } else {
                        throw new NotSerializableException(cl.getName());
                    }
                }
                ...
        }
    }
    

    对于Cloneable 接口,查看Object.clone() 方法,您会发现它引用了一个应用Cloneable 规范的本机方法。

    在 HotSpot 源代码中,src\share\vm\prims\jvm.cpp,你可以找到Object.clone() 的实现:

    JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
      JVMWrapper("JVM_Clone");
      Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
      const KlassHandle klass (THREAD, obj->klass());
      JvmtiVMObjectAllocEventCollector oam;
    
      // I skip all the processing that you can read in the actual source file
      ...
    
      return JNIHandles::make_local(env, oop(new_obj));
    JVM_END 
    

    对于这个标记接口,行为不是直接在 JDK 类中实现的,而是由 JVM 本身实现的,但总体思路是相同的。

    我们可以创建任何新的标记接口吗?

    如果您创建自己的标记接口,您应该像 JVM 和 JDK 类一样处理实现 Java 标记接口的类的内置实例:即添加代码来专门处理标记接口的实例。
    Adamski 的非常好的答案显示了这样做的总体思路。

    【讨论】:

      【解决方案3】:

      就编译器和JVM而言,标记接口和任何其他接口绝对没有区别。

      是的,您可以随意创建标记界面。

      【讨论】:

        【解决方案4】:

        我觉得幕后可能有一些逻辑。在尝试调用 clone() 而不实现 Cloneable 时,我们如何得到 CloneNotSupportedException,除非编译器有一些指导方针可以在看到 clone() 时检查一些事情!

        http://javamagic.wordpress.com/2011/12/02/marker-interface-in-java-what-why-uses-etc/

        根据这个线程(Confusion in marker interface),这些都是标记接口............ Serializable、Clonable、SingleThreadModel、EventListener、RandomAccess、Remote 等。

        如果场景背后没有逻辑,或者 JVM/编译器没有特殊说明来区别对待它们,那么它们如何表现得只是它们所期望的(& JVM/编译器理解 Clonable 和 Serializable 之间的区别)?

        【讨论】:

        • 您在运行时得到异常。 Ergo 编译器对此无能为力。
        【解决方案5】:

        标记接口有助于识别被检查的对象是否实际上是我们感兴趣的类型(实现的接口)。但是它与其他接口没有什么不同(除了它们没有任何行为义务)

        例如,ObjectOutputStream 可以发现,如果一个类实现了Serializable,那么用户已经明确表示同意该对象可以被序列化。

        【讨论】:

        • Nrj:你的意思是说 JVM 明确地有一些逻辑来处理序列化。当我们实现一个具有 Serializable 接口的类时会执行?
        • No Srini,它是 ObjectOutputStream 用于检查 Serializable 检查,因为 ObjectOutputStream 是实际执行序列化的类。
        • @Nrj:是的,你是对的。在这种情况下,“可序列化”一词令人困惑,因为像我这样的许多新手程序员都认为它意味着“可序列化”接口进行序列化,即将抽象数据类型转换为字节的过程。但是正如文献所显示的,可序列化的接口只是一个“标记”,而将 ADT 转换为字节的真正工作(我称之为获取元数据的过程)是由 ObjectOutputStream 完成的。类似的反序列化由 ObjectInputStream 完成。
        【解决方案6】:

        “可序列化”一词在这种情况下令人困惑,因为像我这样的许多新手程序员都将其理解为“可序列化”接口进行序列化,即将抽象数据类型转换为字节的过程。但是正如文献所显示的,可序列化的接口只是一个“标记”,而将 ADT 转换为字节的真正工作(我称之为获取元数据的过程)是由 ObjectOutputStream 完成的。类似的反序列化是由 ObjectInputStream 完成的。

        【讨论】:

          【解决方案7】:

          clone() 为例。实际上 clone() 是在 Object 类中定义的。但它是protected。只有当你的类实现Cloneable 接口时,你才能使用它。所以发生的情况是,当你实现Cloneable,你就有权使用clone()。接口不包含任何方法!知道了 ?

          【讨论】:

          • 这说明不了什么。
          猜你喜欢
          • 2013-11-04
          • 1970-01-01
          • 2014-10-01
          • 2014-09-08
          • 2019-11-13
          • 2016-04-13
          • 2012-10-28
          • 2012-05-16
          • 1970-01-01
          相关资源
          最近更新 更多