【问题标题】:return serialized object (if it exists) otherwise return new instance?返回序列化对象(如果存在),否则返回新实例?
【发布时间】:2013-05-17 01:00:28
【问题描述】:

我想通过调用这个方法得到一个序列化的对象:

ArrayList<String> myArrayList = (ArrayList<String>) getSerializedObject(ArrayList.class, "arraylist.ser");

如果指定的文件(arraylist.ser)不存在,或者与我传入的类不匹配,我想返回该类的新实例。

private Object getSerializedObject(Class<?> c, String filename) {
    Object serObject = null;

        try {
            if (new File(filename).exists()) {
                ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename));
                Object tempObj = in.readObject();
                if (tempObj.getClass().equals(c)) {
                    System.out.println("Loading "+filename);
                    serObject = tempObj;
                }
                in.close();
            }
        }
        catch (FileNotFoundException e) { e.printStackTrace(); }
        catch (IOException e) { e.printStackTrace(); }
        catch (ClassNotFoundException e) { e.printStackTrace(); }

        if (serObject != null) {
            return serObject;
        }
        else {
            // return new instance of Class c here
        }
}

【问题讨论】:

    标签: java class object instance


    【解决方案1】:

    使用反射,您可以创建新的类实例。我会把你指向这个包,但你已经在使用 Class 类。

    当然,如果默认构造函数不存在,您可能必须以某种方式选择合适的构造函数。

    【讨论】:

      【解决方案2】:

      如果您已经知道该类,并且假设所有类都具有相同的构造函数签名,那么您可以使用反射来完成。

      以下是默认的无参数构造函数

      Class<?>[] args = {};
      Constructor<?> constructor = c.getConstructor(args);
      Object inst = constructor.newInstance((Object[])args);
      

      【讨论】:

        【解决方案3】:

        在您的方法中,您接受c 作为输入,这是由Class&lt;?&gt; 对象表示的某种未知类型。如果c所代表的类型有public无参数构造函数,那么你可以通过调用来创建一个新实例:

        try {
          // return c.newInstance(); -- DEPRECATED as of Java 9
          return c.getConstructor().newInstance();
        } catch (Exception e) {
          // handle case of no such public, no-arg constructor
        }
        

        如果没有,那么你会得到一个异常。如果您不想交叉手指猜测,而是想找出您可以访问哪些构造函数,Reflection API 将为您提供这些信息:

        Constructor[] publicConstructors = c.getConstructors(); // may have .length == 0
        for (Constructor ctor : publicConstructors) {
            // find the one you want
        }
        

        然后您可以检查这些对象以查看每个对象需要哪些类型的参数。找到所需的构造函数后,可以在 Constructor 对象上调用 newInstance(Object ...) 并传递适当的参数值。

        但是,即使有了这些信息,您也需要以某种方式提供适当的值。要解决“任何班级”的这个问题是极其困难的。如果您可以将所需的值限制为零或仅按已知顺序限制几个特定值,那么它会成为一个更容易解决的问题,但它会严重限制您的函数实际能够构造的对象的数量或类型。

        还要记住以下几点:

        您的方法接受任何Class&lt;?&gt;,其中包括interfaces,例如List。接口不能被实例化并且没有构造函数。您的示例是通过使用具体实现 ArrayList 来避免这种极端情况。

        构造函数在 Java 中不是继承的,因此即使您验证 cArrayList 的子类,您也不能安全地假设 c 具有与 ArrayList 相同的构造函数签名。 (也就是说,除非真的是同一个类:ArrayList.class.equals(c)。)

        如果此代码被重写为仅反序列化 List(或特别是 ArrayList)而不是接受任何类型,那么您可以简单地调用常规 ArrayList 构造函数并省去一些麻烦。

        但即使您这样做,您的代码也不会验证反序列化列表是否为ArrayList&lt;String&gt;。反序列化过程的结果将是ArrayList&lt;?&gt;,或List&lt;?&gt;,或者可能根本不是List。假设您足够幸运,该文件确实包含 ArrayList,那么知道列表“仅包含 String”的唯一方法是在反序列化后验证列表中的每个对象。

        泛型强制发生在编译时,运行时反序列化不会验证参数化类型是否是类型安全的。这就是为什么编译器会发出关于不安全强制转换的警告:

        Object out = getSerializedObject(ArrayList.class, "arraylist.ser");
        ArrayList<?> unboundedList = (ArrayList<?>) out;  // verifies that 'out' is really an ArrayList
        ArrayList<String> myArrayList =
           (ArrayList<String>) unboundedList; // UNSAFE!  Does not verify the list's contents!
        

        【讨论】:

        • 哇,这是很好的信息。我选择了你的答案。谢谢。
        猜你喜欢
        • 1970-01-01
        • 2017-05-19
        • 1970-01-01
        • 1970-01-01
        • 2019-09-28
        • 1970-01-01
        • 1970-01-01
        • 2021-11-19
        • 2015-11-11
        相关资源
        最近更新 更多