Serializable
gjorde sit arbejde, og hvad er der ikke at elske ved den automatiske implementering af hele processen? Og de eksempler, vi så på, var også ukomplicerede. Så hvad er problemet? Hvorfor har vi brug for en anden grænseflade til i det væsentlige de samme opgaver? Faktum er, at det Serializable
har flere mangler. Vi lister nogle af dem:
-
Ydeevne. Interfacet
Serializable
har mange fordele, men høj ydeevne er tydeligvis ikke en af dem.For det første
Serializable
genererer den interne implementering en stor mængde serviceinformation og alle mulige midlertidige data.For det andet
Serializable
er afhængig af Reflection API (du behøver ikke dykke dybt i dette lige nu; du kan læse mere i ro og mag, hvis du er interesseret). Denne ting lader dig gøre de tilsyneladende umulige ting i Java: for eksempel ændre værdierne for private felter. CodeGym har en fremragende artikel om Reflection API . Du kan læse om det der. -
Fleksibilitet. Vi kontrollerer ikke serialisering-deserialiseringsprocessen, når vi bruger grænsefladen
Serializable
.På den ene side er det meget praktisk, for hvis vi ikke er særligt bekymrede for ydeevne, så virker det rart at slippe for at skrive kode. Men hvad hvis vi virkelig har brug for at tilføje nogle af vores egne funktioner (vi giver et eksempel nedenfor) til serialiseringslogikken?
Grundlæggende er alt, hvad vi skal kontrollere processen, nøgleordet
transient
til at udelukke nogle data. Det er det. Det er hele vores værktøjskasse :/ -
Sikkerhed. Dette punkt stammer delvist fra det foregående punkt.
Vi har ikke brugt meget tid på at tænke over dette før, men hvad nu hvis nogle oplysninger i din klasse ikke er beregnet til andres nysgerrige øjne og ører? Et simpelt eksempel er en adgangskode eller andre personlige brugerdata, som i dagens verden er styret af en masse love.
Hvis vi bruger
Serializable
, kan vi ikke rigtig gøre noget ved det. Vi serialiserer alt, som det er.Men hvis vi gør det på den rigtige måde, skal vi kryptere denne form for data, før vi skriver dem til en fil eller sender dem over et netværk. Men
Serializable
gør dette ikke muligt.

Externalizable
.
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class UserInfo implements Externalizable {
private String firstName;
private String lastName;
private String superSecretInformation;
private static final long SERIAL_VERSION_UID = 1L;
// ...constructor, getters, setters, toString()...
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
}
Som du kan se, har vi væsentlige ændringer! Den vigtigste er indlysende: Når du implementerer grænsefladen Externalizable
, skal du implementere to nødvendige metoder: writeExternal()
ogreadExternal()
. Som vi sagde tidligere, vil ansvaret for serialisering og deserialisering ligge hos programmøren. Men nu kan du løse problemet med ingen kontrol over processen! Hele processen programmeres direkte af dig. Dette tillader naturligvis en meget mere fleksibel mekanisme. Derudover er problemet med sikkerhed løst. Som du kan se, har vores klasse et persondatafelt, der ikke kan opbevares ukrypteret. Nu kan vi nemt skrive kode, der opfylder denne begrænsning. For eksempel kan vi tilføje to simple private metoder til vores klasse til at kryptere og dekryptere følsomme data. Vi vil skrive dataene til filen og læse dem fra filen i krypteret form. Resten af dataene vil blive skrevet og læst som de er :) Som et resultat ser vores klasse sådan ud:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Base64;
public class UserInfo implements Externalizable {
private String firstName;
private String lastName;
private String superSecretInformation;
private static final long serialVersionUID = 1L;
public UserInfo() {
}
public UserInfo(String firstName, String lastName, String superSecretInformation) {
this.firstName = firstName;
this.lastName = lastName;
this.superSecretInformation = superSecretInformation;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.getFirstName());
out.writeObject(this.getLastName());
out.writeObject(this.encryptString(this.getSuperSecretInformation()));
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
firstName = (String) in.readObject();
lastName = (String) in.readObject();
superSecretInformation = this.decryptString((String) in.readObject());
}
private String encryptString(String data) {
String encryptedData = Base64.getEncoder().encodeToString(data.getBytes());
System.out.println(encryptedData);
return encryptedData;
}
private String decryptString(String data) {
String decrypted = new String(Base64.getDecoder().decode(data));
System.out.println(decrypted);
return decrypted;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getSuperSecretInformation() {
return superSecretInformation;
}
}
Vi implementerede to metoder, der bruger de samme ObjectOutput
og ObjectInput
parametre, som vi allerede mødte i lektionen om Serializable
. På det rigtige tidspunkt krypterer eller dekrypterer vi de nødvendige data, og vi bruger de krypterede data til at serialisere vores objekt. Lad os se, hvordan det ser ud i praksis:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
UserInfo userInfo = new UserInfo("Paul", "Piper", "Paul Piper's passport data");
objectOutputStream.writeObject(userInfo);
objectOutputStream.close();
}
}
I metoderne encryptString()
og decryptString()
tilføjede vi specifikt konsoloutput for at bekræfte den form, hvori de hemmelige data vil blive skrevet og læst. Koden ovenfor viste følgende linje: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Krypteringen lykkedes! Det fulde indhold af filen ser sådan ud: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx Lad os nu prøve at bruge vores deserialiseringslogik.
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
UserInfo userInfo = (UserInfo) objectInputStream.readObject();
System.out.println(userInfo);
objectInputStream.close();
}
}
Nå, intet virker kompliceret her. Det burde virke! Vi kører det og får... Undtagelse i tråden "main" java.io.InvalidClassException: UserInfo; ingen gyldig konstruktør 
Serializable
, vi klarede os uden en... :/ Her er vi stødt på en anden vigtig nuance. forskellen mellem Serializable
og Externalizable
ligger ikke kun i programmørens 'udvidede' adgang og evnen til mere fleksibelt at styre processen, men også i selve processen. Frem for alt i deserialiseringsmekanismen . Ved brugSerializable
, tildeles der blot hukommelse til objektet, og derefter læses værdier fra strømmen og bruges til at indstille objektets felter. Hvis vi bruger Serializable
, kaldes objektets konstruktør ikke! Alt arbejdet foregår gennem refleksion (Reflection API, som vi kort omtalte i sidste lektion). Med Externalizable
er deserialiseringsmekanismen anderledes. Standardkonstruktøren kaldes først. Først derefter kaldes det oprettede UserInfo
objekts readExternal()
metode. Den er ansvarlig for at indstille objektets felter. Det er derfor, enhver klasse, der implementerer Externalizable
grænsefladen, skal have en standardkonstruktør . Lad os tilføje en til vores UserInfo
klasse og køre koden igen:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
UserInfo userInfo = (UserInfo) objectInputStream.readObject();
System.out.println(userInfo);
objectInputStream.close();
}
}
Konsoloutput: Paul Pipers pasdata UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'Paul Pipers pasdata' } Nu er det noget helt andet! Først blev den dekrypterede streng med hemmelig information vist på konsollen. Så blev det objekt, vi gendannet fra filen, vist som en streng! Så vi har med succes løst alle problemerne :) Emnet serialisering og deserialisering virker simpelt, men som du kan se, har lektionerne været lange. Og der er så meget mere, vi ikke har dækket! Der er stadig mange finesser involveret, når du bruger hver af disse grænseflader. Men for at undgå at eksplodere din hjerne fra overdreven ny information, vil jeg kort liste nogle flere vigtige punkter og give dig links til yderligere læsning. Så hvad har du ellers brug for at vide? Først skal du være opmærksom på variabler under serialisering (uanset om du bruger Serializable
eller ). Når du bruger , serialiseres disse felter slet ikke (og deres værdier ændres derfor ikke, fordi felter tilhører klassen, ikke objektet). Men når du brugerExternalizable
static
Serializable
static
Externalizable
, styrer du selv processen, så teknisk set kunne du serialisere dem. Men vi anbefaler det ikke, da det sandsynligvis vil skabe masser af subtile fejl. For det andet bør du også være opmærksom på variabler med final
modifikatoren. Når du bruger Serializable
, bliver de serialiseret og deserialiseret som normalt, men når du bruger Externalizable
, er det umuligt at deserialisere en final
variabel ! Årsagen er enkel: alle final
felter initialiseres, når standardkonstruktøren kaldes - derefter kan deres værdi ikke ændres. For at serialisere objekter, der har felter, skal du derfor final
bruge standardserialiseringen leveret af Serializable
. For det tredje , når du bruger arv, alle efterkommerklasser, der arver nogleExternalizable
klasse skal også have standardkonstruktører. Her er et link til en god artikel om serialiseringsmekanismer:
Indtil næste gang! :)
GO TO FULL VERSION