2013-12-19 3 views
0

Я читал Эффективную Java и наткнулся на этот абзац (немного плотный для меня в настоящее время). Может кто-нибудь объяснить, почему внутренние классы не должны реализовывать Serializable более подробно?Внутренние классы shouldnt реализовать serializable

Внутренние классы (пункт 22) не должны вводить Serializable. Они используют синтетические поля, генерируемые компилятором, для хранения ссылок на окружающие экземпляры и для хранения значений локальных переменных из охватываемых областей . Как эти поля соответствуют определению класса , не указано, как и имена анонимных и локальных классов. Поэтому стандартная сериализованная форма стандартного классапо умолчанию не определена.
+0

Получите mac используя OpenJDK и Linux-машину с помощью JRockit (или какой-либо другой JVM), затем создайте внутренний класс Serializable и используйте RMI для передачи его между этими машинами ... –

+0

Если это работает, попробуйте IBM JDK .. , –

ответ

1

Так хочется себе это:

import java.io.*; 

public class A { 

    private Object mFoo; 

    public A(Object foo) { 
     mFoo = foo; 
    } 

    public Serializable getData() { 
     String niceString = "Nice String"; 
     return new B(niceString); 
    } 

    public class B implements Serializable { 
     private Object mBlob; 

     public B (Object blob) { 
      mBlob = blob; 
     } 

     public String toString() { 
     return String.format("%s-%s-%s", getClass(), mBlob, mFoo); 
     } 

    } 

    public static void main(String[] args)throws Exception { 
     A a = new A("Have a nice Day"); 
     Serializable s = a.getData(); 
     System.out.println("Before:" + s); 
     ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 
     ObjectOutputStream ostream = new ObjectOutputStream(bytesOut); 
     ostream.writeObject(s); 
     ostream.flush(); 
     ostream.close(); 
     ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesOut.toByteArray()); 
     ObjectInputStream istream = new ObjectInputStream(bytesIn); 
     System.out.println("After:" + istream.readObject()); 
    } 
} 

где виртуальные конструкции, которые имеют являются вещи, как mFoo в этом случае. Ссылка на них в toString() здесь возможна, потому что этот jvm имеет ссылку на экземпляр A с строкой «Have a nice Day». Теперь, если это статично, это другое дело. Но без статичности это не может быть сериализовано.

Before:class A$B-Nice String-Have a nice Day 
Exception in thread "main" java.io.NotSerializableException: A 
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1165) 
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535) 
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) 
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413) 
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159) 
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329) 
    at A.main(A.java:35) 

Теперь давайте изменим B чуть-чуть что-то вроде:

public static class B implements Serializable { 

    private Serializable mData2; 
    private Object mBlob; 

      public B (Object blob) { 
       mBlob = blob; 
       mData2 = new Serializable() { 
       String data = "Foo"; 
       public String toString() { 
        return data; 
       } 
       }; 
      } 

      public String toString() { 
      return String.format("%s-%s-%s", getClass(), mBlob, mData2); 
      } 
} 

Теперь нет никаких ссылок на A, B имеет анонимный класс, который указывает обратно B, так как это технически внутренний класс. Это была бы та же проблема, кроме того, что B также является Serializable. Какой результат. Я бегу по горячей точке.

Before:class A$B-Nice String-Foo 
After:class A$B-Nice String-Foo 

Таким образом, все детали могут быть написаны отлично! ... но все еще есть проблема.

ПРИМЕЧАНИЕ Это работает только потому, что мы контролируем несколько вещей.

Скажем, вместо сериализации в буфер и повторного чтения его внутри того же приложения мы делаем что-то более полезное. Предположим, мы сохранили экземпляр B в файл. Это, по сути, сериализация анонимного внутреннего класса за пределами данной среды выполнения.

Если вы описывали класс B кому-то еще, что бы вы назвали этим анонимным внутренним классом? Это анонимно. Вероятно, вы бы назвали это тем, что имело для вас смысл, и так последовательно ссылаться на него. Это то, что делают sdk/runtime. Он назвал это чем-то, что не столкнулось бы с каким-либо другим классом в classpath. В Hotspot я думаю, что он будет называться A.B$1, так как он является первым анонимным внутренним классом B (почему это не индексируется 0 всегда меня беспокоило). Это, однако, реализация деталей Hotspot, которые я считаю. Поэтому, если вы должны взять один и тот же источник и скомпилировать его с помощью другого набора инструментов sdk, а затем запустите код и попросите его десериализовать этот файл, если он не назвал этот анонимный внутренний класс тем же именем, вероятно, будет ClassNotFoundException бросили, и вы были бы как HUH? КАК? ГДЕ? и было бы болью преследовать, поскольку кто знает, когда был написан этот файл.

Существует спецификация формата Serialization, с которой происходит этот тип.Обычно есть какое-то магическое число, чтобы указать начало данных, а затем имя класса сериализованного blob, которому предшествует L, поэтому я думаю, что в этом случае файл будет содержать что-то вроде LA.B$1, если он скомпилирован и запущен на Hotspot. Таким образом, вся среда выполнения может действительно выполняться, когда она считывает этот поток, который ищет A.B.$1, так как он не знает, на каком именно этапе исполнения, или на экземпляре времени выполнения, из которого был получен этот файл. (Отключение памяти здесь, поэтому есть много деталей, которые я пропускаю).

Смежные вопросы