CodeGym/Java blog/Tilfældig/Eksternaliserbar grænseflade i Java
John Squirrels
Niveau
San Francisco

Eksternaliserbar grænseflade i Java

Udgivet i gruppen
Hej! I dag vil vi fortsætte med at lære serialisering og deserialisering af Java- objekter at kende. I den sidste lektion lærte vi Serializable markør-grænsefladen at kende, gennemgik eksempler på dens brug og lærte også, hvordan du kan bruge det forbigående nøgleord til at styre serialiseringsprocessen. Tja, at sige, at vi 'kontrollerer processen', kan være at overdrive det. Vi har ét søgeord, én versionsidentifikator, og det er det hele. Resten af ​​processen er skjult inde i Java, og vi kan ikke få adgang til den. Det er selvfølgelig godt med hensyn til bekvemmelighed. Men en programmør bør ikke kun lade sig vejlede af sin egen komfort, vel? :) Der er andre faktorer, du skal overveje. Det er derfor, der kan serialisereser ikke den eneste mekanisme til serialisering-deserialisering i Java. I dag vil vi stifte bekendtskab med den Eksternaliserbare grænseflade. Men før vi begynder at studere det, har du måske et rimeligt spørgsmål: hvorfor har vi brug for en anden mekanisme? Serializablegjorde 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 Serializablehar flere mangler. Vi lister nogle af dem:
  1. Ydeevne. Interfacet Serializablehar mange fordele, men høj ydeevne er tydeligvis ikke en af ​​dem.

    Introduktion til eksternaliserbar grænseflade - 2

    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.

  2. 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 transienttil at udelukke nogle data. Det er det. Det er hele vores værktøjskasse :/

  3. 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 Serializablegør dette ikke muligt.

Introduktion til eksternaliserbar grænseflade - 3Nå, lad os endelig se, hvordan klassen ville se ud, hvis vi bruger grænsefladen 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 ObjectOutputog ObjectInputparametre, 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 Introduktion til den eksternaliserbare grænseflade - 4 Ups! :( Det er tilsyneladende ikke så nemt! Deserialiseringsmekanismen kastede en undtagelse og krævede, at vi oprettede en standardkonstruktør. Jeg undrer mig over hvorfor. Med Serializable, vi klarede os uden en... :/ Her er vi stødt på en anden vigtig nuance. forskellen mellem Serializableog Externalizableligger 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 Externalizableer deserialiseringsmekanismen anderledes. Standardkonstruktøren kaldes først. Først derefter kaldes det oprettede UserInfoobjekts readExternal()metode. Den er ansvarlig for at indstille objektets felter. Det er derfor, enhver klasse, der implementerer Externalizablegrænsefladen, skal have en standardkonstruktør . Lad os tilføje en til vores UserInfoklasse 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 Serializableeller ). 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 brugerExternalizablestaticSerializablestaticExternalizable, 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 finalmodifikatoren. Når du bruger Serializable, bliver de serialiseret og deserialiseret som normalt, men når du bruger Externalizable, er det umuligt at deserialisere en finalvariabel ! Årsagen er enkel: alle finalfelter initialiseres, når standardkonstruktøren kaldes - derefter kan deres værdi ikke ændres. For at serialisere objekter, der har felter, skal du derfor finalbruge standardserialiseringen leveret af Serializable. For det tredje , når du bruger arv, alle efterkommerklasser, der arver nogleExternalizableklasse skal også have standardkonstruktører. Her er et link til en god artikel om serialiseringsmekanismer: Indtil næste gang! :)
Kommentarer
  • Populær
  • Ny
  • Gammel
Du skal være logget ind for at skrive en kommentar
Denne side har ingen kommentarer endnu