2013-08-17 3 views
7

Ich arbeite an einem Projekt, das Java Dateien generiert. Ich würde gerne in der Lage sein, die serialVersionUID wie mit dem serialver Werkzeug optional hinzuzufügen.Wie generiere ich serialVersionUID programmatisch in Java?

Gibt es eine Möglichkeit, dies zu tun, wenn ich den Code Java erzeuge, oder muss ich den Benutzer des Tools bitten, UIDs manuell zur Verfügung zu stellen? Um klar zu sein, ich suche nicht automatisch dies durch Eclipse oder das serialver Werkzeug zu tun, sondern es über Java selbst zu tun.

+0

Vielleicht würde der Hashcode des Klassennamens der generierten Klasse funktionieren? –

+0

Ich habe über so etwas nachgedacht und mich gefragt, ob es einen Weg gibt, dies zu tun, der, wenn möglich, dem Seriell-Tool entspricht. –

+0

Sicher, Sie können es tun, wie das Seriell-Tool tut es. Finden Sie einfach die Logik hinter seiner 'serialVersionUID'-Generation. Ich habe bisher keinen Erfolg gehabt. –

Antwort

7

Es ist eine Version der serialver Werkzeugquelle verfügbar from OpenJDK. Es kommt alles auf diesen Aufruf:

ObjectStreamClass c = ObjectStreamClass.lookup(MyClass.class); 
long serialID = c.getSerialVersionUID(); 
System.out.println(serialID); 

In JDK 6 zumindest ist es die gleiche Anzahl mit serialver Werkzeug zurückgibt.

2

Probieren Sie den Hashcode des Klassennamens der generierten Klasse.

Es könnte Kollisionen geben, da ein Hashcode nicht eindeutig ist, aber diese Kollisionen sind statistisch unwahrscheinlich.

Here's documentation wie serialVersionUID-Werte generiert werden. Es ist viel komplexer als ich es erwartet hätte.

Wegen seiner Komplexität, ich würde entweder den Benutzer geben Sie die UID selbst, oder verwenden Sie einfach einen einfachen Hash der vollständigen Klassennamen.

3

Von ObjectStreamClass:

/** 
* Computes the default serial version UID value for the given class. 
*/ 
private static long computeDefaultSUID(Class<?> cl) { 
    if (!Serializable.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl)) 
    { 
     return 0L; 
    } 

    try { 
     ByteArrayOutputStream bout = new ByteArrayOutputStream(); 
     DataOutputStream dout = new DataOutputStream(bout); 

     dout.writeUTF(cl.getName()); 

     int classMods = cl.getModifiers() & 
      (Modifier.PUBLIC | Modifier.FINAL | 
      Modifier.INTERFACE | Modifier.ABSTRACT); 

     /* 
     * compensate for javac bug in which ABSTRACT bit was set for an 
     * interface only if the interface declared methods 
     */ 
     Method[] methods = cl.getDeclaredMethods(); 
     if ((classMods & Modifier.INTERFACE) != 0) { 
      classMods = (methods.length > 0) ? 
       (classMods | Modifier.ABSTRACT) : 
       (classMods & ~Modifier.ABSTRACT); 
     } 
     dout.writeInt(classMods); 

     if (!cl.isArray()) { 
      /* 
      * compensate for change in 1.2FCS in which 
      * Class.getInterfaces() was modified to return Cloneable and 
      * Serializable for array classes. 
      */ 
      Class<?>[] interfaces = cl.getInterfaces(); 
      String[] ifaceNames = new String[interfaces.length]; 
      for (int i = 0; i < interfaces.length; i++) { 
       ifaceNames[i] = interfaces[i].getName(); 
      } 
      Arrays.sort(ifaceNames); 
      for (int i = 0; i < ifaceNames.length; i++) { 
       dout.writeUTF(ifaceNames[i]); 
      } 
     } 

     Field[] fields = cl.getDeclaredFields(); 
     MemberSignature[] fieldSigs = new MemberSignature[fields.length]; 
     for (int i = 0; i < fields.length; i++) { 
      fieldSigs[i] = new MemberSignature(fields[i]); 
     } 
     Arrays.sort(fieldSigs, new Comparator<MemberSignature>() { 
      public int compare(MemberSignature ms1, MemberSignature ms2) { 
       return ms1.name.compareTo(ms2.name); 
      } 
     }); 
     for (int i = 0; i < fieldSigs.length; i++) { 
      MemberSignature sig = fieldSigs[i]; 
      int mods = sig.member.getModifiers() & 
       (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | 
       Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE | 
       Modifier.TRANSIENT); 
      if (((mods & Modifier.PRIVATE) == 0) || 
       ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) 
      { 
       dout.writeUTF(sig.name); 
       dout.writeInt(mods); 
       dout.writeUTF(sig.signature); 
      } 
     } 

     if (hasStaticInitializer(cl)) { 
      dout.writeUTF("<clinit>"); 
      dout.writeInt(Modifier.STATIC); 
      dout.writeUTF("()V"); 
     } 

     Constructor[] cons = cl.getDeclaredConstructors(); 
     MemberSignature[] consSigs = new MemberSignature[cons.length]; 
     for (int i = 0; i < cons.length; i++) { 
      consSigs[i] = new MemberSignature(cons[i]); 
     } 
     Arrays.sort(consSigs, new Comparator<MemberSignature>() { 
      public int compare(MemberSignature ms1, MemberSignature ms2) { 
       return ms1.signature.compareTo(ms2.signature); 
      } 
     }); 
     for (int i = 0; i < consSigs.length; i++) { 
      MemberSignature sig = consSigs[i]; 
      int mods = sig.member.getModifiers() & 
       (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | 
       Modifier.STATIC | Modifier.FINAL | 
       Modifier.SYNCHRONIZED | Modifier.NATIVE | 
       Modifier.ABSTRACT | Modifier.STRICT); 
      if ((mods & Modifier.PRIVATE) == 0) { 
       dout.writeUTF("<init>"); 
       dout.writeInt(mods); 
       dout.writeUTF(sig.signature.replace('/', '.')); 
      } 
     } 

     MemberSignature[] methSigs = new MemberSignature[methods.length]; 
     for (int i = 0; i < methods.length; i++) { 
      methSigs[i] = new MemberSignature(methods[i]); 
     } 
     Arrays.sort(methSigs, new Comparator<MemberSignature>() { 
      public int compare(MemberSignature ms1, MemberSignature ms2) { 
       int comp = ms1.name.compareTo(ms2.name); 
       if (comp == 0) { 
        comp = ms1.signature.compareTo(ms2.signature); 
       } 
       return comp; 
      } 
     }); 
     for (int i = 0; i < methSigs.length; i++) { 
      MemberSignature sig = methSigs[i]; 
      int mods = sig.member.getModifiers() & 
       (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | 
       Modifier.STATIC | Modifier.FINAL | 
       Modifier.SYNCHRONIZED | Modifier.NATIVE | 
       Modifier.ABSTRACT | Modifier.STRICT); 
      if ((mods & Modifier.PRIVATE) == 0) { 
       dout.writeUTF(sig.name); 
       dout.writeInt(mods); 
       dout.writeUTF(sig.signature.replace('/', '.')); 
      } 
     } 

     dout.flush(); 

     MessageDigest md = MessageDigest.getInstance("SHA"); 
     byte[] hashBytes = md.digest(bout.toByteArray()); 
     long hash = 0; 
     for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) { 
      hash = (hash << 8) | (hashBytes[i] & 0xFF); 
     } 
     return hash; 
    } catch (IOException ex) { 
     throw new InternalError(); 
    } catch (NoSuchAlgorithmException ex) { 
     throw new SecurityException(ex.getMessage()); 
    } 
} 
+0

@ Jeffrey ...Ziemlich Crafty +1 –

2

Wenn Ihr Werkzeug Marke neuen Code zu erzeugen Sie haben keine Notwendigkeit, es den Weg serialver tut zu berechnen. Verwenden Sie einfach 1 oder -1 oder was auch immer Sie mögen.

+0

Könnten Sie erklären, warum? Ist das, weil es eine neue Klasse ist? –

+1

@bn. Da es sich um eine neue Klasse handelt, gibt es keine existierenden Serialisierungen, daher gibt es nichts, womit man kompatibel wäre. – EJP

1

Dies ist ein alter Thread, aber ich denke, serialVersionUID ist immer noch eines der heißen Themen.

Als ich anfing, Java-Codes zu schreiben, war es zu schwierig, einen eindeutigen Wert für serlialVersionUID Variable für mich zu finden und zuzuweisen. Nach einer Weile gab mir das einfache Format der Datumszeit (JJJJ-MM-TTHH: MM: SS) die Idee: Warum erzeuge ich keinen Wert vom aktuellen Datum und Uhrzeit?

Also begann ich, (natürlich manuell) Werte nach aktuellem Datum und Uhrzeit zu generieren.

Sagen wir aktuelles Datum und Uhrzeit ist 01/09/2015 11:00 pm. Ich würde der serialVersionUID den Wert 201601091100L zuweisen.

Ich hoffe, diese Idee hilft Ihnen für weitere Verbesserungen in Ihren Projekten.

+0

Dieses Format für Datum-Uhrzeit-Zeichenfolgen ist übrigens durch den sehr nützlichen [ISO 8601] (https://en.wikipedia.org/wiki/ISO_8601) Standard definiert. Die Bindestriche und Doppelpunkte sind optional, wie Sie gezeigt haben, die "grundlegende" Variation genannt. Das Framework [java.time] (http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html), das in Java 8 und höher integriert ist, verwendet beim Analysieren standardmäßig ISO 8601-Formate/Zeichenkettenrepräsentationen von Datum-Uhrzeit-Werten erzeugen. –