CodeGym/Java blogg/Slumpmässig/Externaliserbart gränssnitt i Java
John Squirrels
Nivå
San Francisco

Externaliserbart gränssnitt i Java

Publicerad i gruppen
Hej! Idag kommer vi att fortsätta att lära känna serialisering och deserialisering av Java- objekt. I den förra lektionen lärde vi känna gränssnittet Serializable markör, granskade exempel på dess användning och lärde oss också hur du kan använda det transienta nyckelordet för att styra serialiseringsprocessen. Tja, att säga att vi "kontrollerar processen" kan vara att överdriva det. Vi har ett nyckelord, en versionsidentifierare, och det är ungefär det. Resten av processen är gömd i Java, och vi kan inte komma åt den. Naturligtvis, när det gäller bekvämlighet, är detta bra. Men en programmerare bör inte bara vägledas av sin egen komfort, eller hur? :) Det finns andra faktorer som du måste ta hänsyn till. Det är därför Serialiserbarär inte den enda mekanismen för serialisering-deserialisering i Java. Idag ska vi bekanta oss med det externa gränssnittet. Men innan vi börjar studera det kanske du har en rimlig fråga: varför behöver vi en annan mekanism? Serializablegjorde sitt jobb, och vad är inte att älska med den automatiska implementeringen av hela processen? Och exemplen vi tittade på var också okomplicerade. Så vad är problemet? Varför behöver vi ett annat gränssnitt för i huvudsak samma uppgifter? Faktum är att det Serializablehar flera brister. Vi listar några av dem:
  1. Prestanda. Gränssnittet Serializablehar många fördelar, men hög prestanda är uppenbarligen inte en av dem.

    Introduktion av det externa gränssnittet - 2

    För det första Serializable genererar den interna implementeringen en stor mängd serviceinformation och alla typer av temporär data.

    För det andra, Serializable förlitar sig på Reflection API (du behöver inte dyka djupt på detta just nu, du kan läsa mer när du vill, om du är intresserad). Denna sak låter dig göra de till synes omöjliga sakerna i Java: till exempel ändra värden för privata fält. CodeGym har en utmärkt artikel om Reflection API . Du kan läsa om det där.

  2. Flexibilitet. Vi kontrollerar inte serialisering-deserialiseringsprocessen när vi använder gränssnittet Serializable.

    Å ena sidan är det väldigt bekvämt, för om vi inte är särskilt bekymrade över prestanda, så verkar det skönt att slippa skriva kod. Men vad händer om vi verkligen behöver lägga till några av våra egna funktioner (vi ger ett exempel nedan) till serialiseringslogiken?

    I grund och botten är allt vi behöver för att kontrollera processen nyckelordet transientför att utesluta vissa data. Det är allt. Det är hela vår verktygslåda :/

  3. Säkerhet. Denna post härrör delvis från föregående post.

    Vi har inte ägnat så mycket tid åt att tänka på detta tidigare, men tänk om viss information i din klass inte är avsedd för andras nyfikna ögon och öron? Ett enkelt exempel är ett lösenord eller annan personlig användardata, som i dagens värld styrs av en massa lagar.

    Om vi ​​använder Serializable, kan vi egentligen inte göra något åt ​​det. Vi serialiserar allt som det är.

    Men om vi gör det på rätt sätt måste vi kryptera den här typen av data innan vi skriver den till en fil eller skickar den över ett nätverk. Men Serializablegör inte detta möjligt.

Introduktion av det externa gränssnittet - 3Nåväl, låt oss äntligen se hur klassen skulle se ut om vi använder gränssnittet 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 ni ser har vi betydande förändringar! Den viktigaste är uppenbar: när du implementerar gränssnittet Externalizablemåste du implementera två nödvändiga metoder: writeExternal()ochreadExternal(). Som vi sa tidigare kommer ansvaret för serialisering och deserialisering att ligga hos programmeraren. Men nu kan du lösa problemet med ingen kontroll över processen! Hela processen programmeras direkt av dig. Naturligtvis tillåter detta en mycket mer flexibel mekanism. Dessutom är problemet med säkerhet löst. Som du kan se har vår klass ett persondatafält som inte kan lagras okrypterat. Nu kan vi enkelt skriva kod som uppfyller denna begränsning. Till exempel kan vi lägga till två enkla privata metoder till vår klass för att kryptera och dekryptera känslig data. Vi kommer att skriva data till filen och läsa den från filen i krypterad form. Resten av informationen kommer att skrivas och läsas som den är :) Som ett resultat ser vår klass ut ungefär så här:
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 implementerade två metoder som använder samma ObjectOutputoch ObjectInputparametrar som vi redan träffade på lektionen om Serializable. I rätt ögonblick krypterar eller dekrypterar vi den nödvändiga informationen, och vi använder den krypterade informationen för att serialisera vårt objekt. Låt oss se hur det här ser ut i praktiken:
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 metoderna encryptString()och decryptString()har vi specifikt lagt till konsolutdata för att verifiera i vilken form de hemliga data kommer att skrivas och läsas. Koden ovan visade följande rad: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Krypteringen lyckades! Det fullständiga innehållet i filen ser ut så här: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx Låt oss nu försöka använda vår 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åväl, inget verkar komplicerat här. Det borde fungera! Vi kör det och får... Undantag i tråden "huvud" java.io.InvalidClassException: UserInfo; ingen giltig konstruktor Introduktion av det externa gränssnittet - 4 Hoppsan! :( Tydligen är det inte så lätt! Deserialiseringsmekanismen gjorde ett undantag och krävde att vi skulle skapa en standardkonstruktör. Jag undrar varför. Med , Serializableklarade vi oss utan en... :/ Här har vi stött på en annan viktig nyans. skillnaden mellan Serializableoch Externalizableligger inte bara i programmerarens "utökade" åtkomst och förmågan att mer flexibelt styra processen, utan också i själva processen. Framför allt i deserialiseringsmekanismen . När du använderSerializable, minne tilldelas helt enkelt för objektet, och sedan läses värden från strömmen och används för att ställa in objektets fält. Om vi ​​använder Serializable, anropas inte objektets konstruktor! Allt arbete sker genom reflektion (Reflection API, som vi kort nämnde i förra lektionen). Med Externalizableär deserialiseringsmekanismen annorlunda. Standardkonstruktorn anropas först. Först efter det anropas det skapade UserInfoobjektets readExternal()metod. Den ansvarar för att ställa in objektets fält. Det är därför varje klass som implementerar Externalizablegränssnittet måste ha en standardkonstruktor . Låt oss lägga till en till vår UserInfoklass och köra 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();
   }
}
Konsolutgång: Paul Pipers passdata UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'Paul Pipers passdata' } Nu är det något helt annat! Först visades den dekrypterade strängen med hemlig information på konsolen. Sedan visades objektet vi hämtade från filen som en sträng! Så vi har framgångsrikt löst alla problem :) Ämnet serialisering och deserialisering verkar enkelt, men som du kan se har lektionerna varit långa. Och det finns så mycket mer vi inte har täckt! Det finns fortfarande många subtiliteter involverade när du använder vart och ett av dessa gränssnitt. Men för att undvika att din hjärna exploderar från överdriven ny information, ska jag kort lista några fler viktiga punkter och ge dig länkar till ytterligare läsning. Så, vad mer behöver du veta? Först , under serialisering (oavsett om du använder Serializableeller Externalizable), var uppmärksam på staticvariabler. När du använder Serializableserialiseras dessa fält inte alls (och följaktligen ändras inte deras värden, eftersom staticfält tillhör klassen, inte objektet). Men när du använderExternalizable, kontrollerar du processen själv, så tekniskt sett kan du serialisera dem. Men vi rekommenderar det inte, eftersom det sannolikt kommer att skapa massor av subtila buggar. För det andra bör du också vara uppmärksam på variabler med finalmodifieraren. När du använder Serializable, serialiseras de och deserialiseras som vanligt, men när du använder Externalizable, är det omöjligt att deserialisera en finalvariabel ! Anledningen är enkel: alla finalfält initieras när standardkonstruktorn anropas - efter det kan deras värde inte ändras. För att serialisera objekt som har finalfält, använd därför standardserialiseringen som tillhandahålls av Serializable. För det tredje , när du använder nedärvning, alla avkomliga klasser som ärver någraExternalizableklass måste också ha standardkonstruktorer. Här är länk till bra artikel om serialiseringsmekanismer: Tills nästa gång! :)
Kommentarer
  • Populär
  • Ny
  • Gammal
Du måste vara inloggad för att lämna en kommentar
Den här sidan har inga kommentarer än