【问题标题】:Service Loader implementations needing default public constructors需要默认公共构造函数的服务加载器实现
【发布时间】:2013-07-01 06:10:20
【问题描述】:

我试图了解 Java Service Loader 的工作原理?我遇到了这个blog

能否请您帮助我理解作者声称的原因:

实现必须有一个公共的无参数构造函数。

好的,我得到第一部分。现在是一个后续问题。我可以将其作为后续问题发布,但我认为 将它作为同一个问题的一部分会更好。

考虑java doc

上面写着:

此外,非私有内部成员类的构造函数必须经过编译,使其第一个参数是一个表示直接封闭实例的附加隐式参数(第 8.1.3 节)。

这是否意味着我不能将内部类外部化。请考虑以下代码:

import java.io.*;

class Y {
  class ABC {
    ABC() {
      System.out.println("ABC Constructor");
    }
    public void writeExternal(ObjectOutput out)
      throws IOException {
      System.out.println("ABC.writeExternal");
    }
    public void readExternal(ObjectInput in)
      throws IOException, ClassNotFoundException {
      System.out.println("ABC.readExternal");
    }
  }

  public void foo() throws IOException, ClassNotFoundException {
    System.out.println("Constructing objects:");
    ABC abc = new ABC();
    ObjectOutputStream o = new ObjectOutputStream(
    new FileOutputStream("InnerClass.out"));
    System.out.println("Saving objects:");
    o.writeObject(abc);
    o.close();
    // Now get them back:
    ObjectInputStream in = new ObjectInputStream(
      new FileInputStream("InnerClass.out"));
    System.out.println("Recovering abc:");
    // OOPS! Throws an exception:
    abc = (ABC)in.readObject();
  }
}

public class InnerClass {
  public static void main(String[] args) throws IOException, ClassNotFoundException {
    System.out.println("Hello World\n");
    Y y = new Y();
    System.out.println(y);
    y.foo();
  }  
}

在运行时失败:

Hello World

Y@6bc7c054
Constructing objects:
ABC Constructor
Saving objects:
Exception in thread "main" java.io.NotSerializableException: Y$ABC
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
    at Y.foo(InnerClass.java:24)
    at InnerClass.main(InnerClass.java:40)

如何使用 Externalize 类 ABC?

谢谢

【问题讨论】:

    标签: java static constructor externalizable


    【解决方案1】:

    那是因为需要通过反射创建一个新实例。

    I guess here's relevant code, line 362:

    S p = service.cast(Class.forName(cn, true, loader).newInstance());
    

    因此它自 Class.newInstance() can only invoke the zero-argument constructor 以来调用“无参数”构造函数。

    编辑:

    实际上,不需要public class创建一个零参数的构造函数,除非你定义了另一个构造函数。

    javadoc

    您不必为您的类提供任何构造函数,但这样做时必须小心。编译器会自动为任何没有构造函数的类提供无参数的默认构造函数。

    所以,下面的代码是完全正确的:

    public final class MyServiceImpl implements MyService { 
        @Override
        public long getTime() {
            return System.currentTimeMillis();
        } 
    }
    

    MyServiceImpl 的默认构造函数将与:

    public MyServiceImpl() { }
    

    自从MyServiceImplis a public class

    如果类被声明为public,则默认构造函数被隐式赋予访问修饰符public

    因此MyServiceImpl 中的构造函数是多余的。

    我的猜测是,博文的作者只是想确保ServiceLoader 能够实例化服务提供者,即使它包含一些带有参数的显式构造函数。


    结论: 服务提供者 应该只有当它包含带参数的显式构造函数时才具有显式的无参数构造函数。否则默认构造函数就足够了。

    【讨论】:

    • 感谢您的解释。我已经用另一部分编辑了我的问题。谢谢
    • @gudge 目前,我没有什么要添加到this answer。对你有帮助吗?
    • @gudge 但是,in general,内部类可以这样实例化:OuterClass.InnerClass innerObject = outerObject.new InnerClass();
    • 感谢您的帮助。我已经接受了你的回答。第一部分是绝对清楚的。第二个不是。我们如何反序列化非静态内部类。谢谢。
    • 好吧,类应该实现标记接口Serializable(or Externalizable),以便有可能被序列化/反序列化,因此内部和外部类都应该实现Serializable,但不推荐(@ 987654328@)。所以你可以class Y implements Serializableclass ABC implements Serializable那样实现Serializable,但这根本不是一个好主意。我个人试图在我的代码中避免内部类。
    【解决方案2】:

    可以在官方java doc阅读原因

    此工具强制执行的唯一要求是提供程序类必须具有零参数构造函数,以便它们可以在加载期间被实例化。

    如果你声明了一个带参数的构造函数,你就不能正确使用它。 通过示例阅读此article

    【讨论】:

      【解决方案3】:

      许多技术需要无参数构造函数,因此可以创建实例。如果只有构造函数需要参数,则机制不知道要传递什么。

      一个值得注意的例外是序列化机制,它不需要无参数构造函数,但这只是因为它从 VM 中添加了一些“魔法帮助”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-24
        • 1970-01-01
        • 1970-01-01
        • 2018-10-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多