CodeGym/Java-blogg/Tilfeldig/Eksternaliserbart grensesnitt i Java
John Squirrels
Nivå
San Francisco

Eksternaliserbart grensesnitt i Java

Publisert i gruppen
Hei! I dag vil vi fortsette å bli kjent med serialisering og deserialisering av Java- objekter. I den siste leksjonen ble vi kjent med Serializable markør-grensesnittet, gjennomgikk eksempler på bruken, og lærte også hvordan du kan bruke det forbigående nøkkelordet til å kontrollere serialiseringsprosessen. Vel, å si at vi 'kontrollerer prosessen' kan være å overdrive det. Vi har ett nøkkelord, en versjonsidentifikator, og det er omtrent det. Resten av prosessen er skjult inne i Java, og vi får ikke tilgang til den. Selvfølgelig, med tanke på bekvemmelighet, er dette bra. Men en programmerer bør ikke bare styres av sin egen komfort, ikke sant? :) Det er andre faktorer du må vurdere. Det er derfor Serialiserbarer ikke den eneste mekanismen for serialisering-deserialisering i Java. I dag skal vi bli kjent med Externalizable- grensesnittet. Men før vi begynner å studere det, har du kanskje et rimelig spørsmål: hvorfor trenger vi en annen mekanisme? Serializablegjorde jobben sin, og hva er ikke å elske med den automatiske implementeringen av hele prosessen? Og eksemplene vi så på var også ukompliserte. Så hva er problemet? Hvorfor trenger vi et annet grensesnitt for i hovedsak de samme oppgavene? Faktum er at det Serializablehar flere mangler. Vi lister opp noen av dem:
  1. Opptreden. Grensesnittet Serializablehar mange fordeler, men høy ytelse er tydeligvis ikke en av dem.

    Introduserer grensesnittet som kan eksternaliseres - 2

    For det første Serializable genererer den interne implementeringen en stor mengde tjenesteinformasjon og alle slags midlertidige data.

    For det andre, Serializable er avhengig av Reflection API (du trenger ikke dykke dypt på dette akkurat nå; du kan lese mer når det passer deg, hvis du er interessert). Denne tingen lar deg gjøre de tilsynelatende umulige tingene i Java: for eksempel endre verdiene til private felt. CodeGym har en utmerket artikkel om Reflection API . Du kan lese om det der.

  2. Fleksibilitet. Vi kontrollerer ikke serialisering-deserialiseringsprosessen når vi bruker grensesnittet Serializable.

    På den ene siden er det veldig praktisk, for hvis vi ikke er spesielt opptatt av ytelse, så virker det fint å slippe å skrive kode. Men hva om vi virkelig trenger å legge til noen av våre egne funksjoner (vi gir et eksempel nedenfor) til serialiseringslogikken?

    I utgangspunktet er alt vi trenger for å kontrollere prosessen nøkkelordet transientfor å ekskludere noen data. Det er det. Det er hele verktøykassen vår :/

  3. Sikkerhet. Dette elementet stammer delvis fra forrige element.

    Vi har ikke brukt mye tid på å tenke på dette før, men hva om noe informasjon i klassen din ikke er beregnet på andres nysgjerrige øyne og ører? Et enkelt eksempel er et passord eller andre personlige brukerdata, som i dagens verden er styrt av en haug med lover.

    Hvis vi bruker Serializable, kan vi egentlig ikke gjøre noe med det. Vi serialiserer alt som det er.

    Men hvis vi gjør det på riktig måte, må vi kryptere denne typen data før vi skriver dem til en fil eller sender dem over et nettverk. Men Serializablegjør ikke dette mulig.

Vi introduserer grensesnittet som kan eksternaliseres - 3Vel, la oss endelig se hvordan klassen ville sett ut hvis vi bruker grensesnittet 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 ser har vi betydelige endringer! Den viktigste er åpenbar: når du implementerer grensesnittet Externalizable, må du implementere to nødvendige metoder: writeExternal()ogreadExternal(). Som vi sa tidligere, vil ansvaret for serialisering og deserialisering ligge hos programmereren. Men nå kan du løse problemet med ingen kontroll over prosessen! Hele prosessen programmeres direkte av deg. Naturligvis tillater dette en mye mer fleksibel mekanisme. I tillegg er problemet med sikkerhet løst. Som du ser har klassen vår et persondatafelt som ikke kan lagres ukryptert. Nå kan vi enkelt skrive kode som tilfredsstiller denne begrensningen. For eksempel kan vi legge til klassen vår to enkle private metoder for å kryptere og dekryptere sensitive data. Vi vil skrive dataene til filen og lese dem fra filen i kryptert form. Resten av dataene vil bli skrevet og lest som de er :) Som et resultat ser klassen vår omtrent slik ut:
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 implementerte to metoder som bruker de samme ObjectOutputog ObjectInputparametere som vi allerede møtte i leksjonen om Serializable. I rett øyeblikk krypterer eller dekrypterer vi de nødvendige dataene, og vi bruker de krypterte dataene til å serialisere objektet vårt. La oss se hvordan dette ser ut 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 metodene encryptString()og decryptString()la vi spesifikt til konsollutdata for å bekrefte formen som de hemmelige dataene vil bli skrevet og lest i. Koden ovenfor viste følgende linje: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Krypteringen lyktes! Hele innholdet i filen ser slik ut: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx La oss nå prøve å bruke vår deserialiseringslogikk.
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();

   }
}
Vel, ingenting virker komplisert her. Det burde fungere! Vi kjører det og får... Unntak i tråden "main" java.io.InvalidClassException: UserInfo; ingen gyldig konstruktør Introduserer grensesnittet som kan eksternaliseres - 4 Oops! :( Det er tilsynelatende ikke så lett! Deserialiseringsmekanismen ga et unntak og krevde at vi skulle lage en standardkonstruktør. Jeg lurer på hvorfor. Med , Serializableklarte vi oss uten en... :/ Her har vi møtt en annen viktig nyanse. forskjellen mellom Serializableog Externalizableligger ikke bare i programmererens "utvidede" tilgang og evnen til å kontrollere prosessen mer fleksibelt, men også i selve prosessen. Fremfor alt i deserialiseringsmekanismen . Ved brukSerializable, minne er ganske enkelt allokert for objektet, og deretter leses verdier fra strømmen og brukes til å angi objektets felt. Hvis vi bruker Serializable, kalles ikke objektets konstruktør! Alt arbeidet skjer gjennom refleksjon (Reflection API, som vi kort omtalte i forrige leksjon). Med Externalizableer deserialiseringsmekanismen annerledes. Standardkonstruktøren kalles først. Først etter det kalles metoden til det opprettede UserInfoobjektet readExternal(). Den er ansvarlig for å sette objektets felt. Det er derfor enhver klasse som implementerer Externalizablegrensesnittet må ha en standard konstruktør . La oss legge til en i UserInfoklassen vår og kjøre koden på nytt:
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();
   }
}
Konsollutgang: Paul Pipers passdata UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'Paul Pipers passdata' } Nå er det noe helt annet! Først ble den dekrypterte strengen med hemmelig informasjon vist på konsollen. Deretter ble objektet vi gjenopprettet fra filen vist som en streng! Så vi har løst alle problemene med hell :) Temaet serialisering og deserialisering virker enkelt, men som du kan se, har leksjonene vært lange. Og det er så mye mer vi ikke har dekket! Det er fortsatt mange finesser involvert når du bruker hvert av disse grensesnittene. Men for å unngå å eksplodere hjernen din fra overdreven ny informasjon, vil jeg kort liste opp noen flere viktige punkter og gi deg lenker til ytterligere lesing. Så hva mer trenger du å vite? Først , under serialisering (uavhengig av om du bruker Serializableeller Externalizable), ta hensyn til staticvariabler. Når du bruker Serializable, serialiseres ikke disse feltene i det hele tatt (og følgelig endres ikke verdiene deres, fordi staticfeltene tilhører klassen, ikke objektet). Men når du brukerExternalizable, kontrollerer du prosessen selv, så teknisk sett kan du serialisere dem. Men vi anbefaler det ikke, siden det sannsynligvis vil skape mange subtile feil. For det andre bør du også ta hensyn til variabler med finalmodifikatoren. Når du bruker Serializable, blir de serialisert og deserialisert som vanlig, men når du bruker Externalizable, er det umulig å deserialisere en finalvariabel ! Årsaken er enkel: alle finalfelt initialiseres når standardkonstruktøren kalles - etter det kan ikke verdien endres. Derfor, for å serialisere objekter som har finalfelt, bruk standard serialisering levert av Serializable. For det tredje , når du bruker arv, vil alle etterkommerklasser som arver noenExternalizableklasse må også ha standard konstruktører. Her er lenke til en god artikkel om serialiseringsmekanismer: Til neste gang! :)
Kommentarer
  • Populær
  • Ny
  • Gammel
Du må være pålogget for å legge igjen en kommentar
Denne siden har ingen kommentarer ennå