Hallo! Im heutigen Artikel werden wir uns den transienten Modifikator in Java ansehen. Lassen Sie uns darüber sprechen, warum dieser Modifikator benötigt wird und wie man ihn richtig verwendet. Gehen!
Es ist Zeit, Bytes in Atome aufzuteilen . Schreiben wir eine Methode die spanische Schande Modifikator (endlich)
War irgendjemand verwirrt darüber, dass wir das Passwort des Benutzers gespeichert haben? Besonders ein solches Passwort... Ja, ja, wir haben es uns selbst ausgedacht, aber trotzdem... Manchmal gibt es Situationen, in denen einige Felder nicht serialisiert werden können, oder es ist besser, dies nicht zu tun. Im obigen Beispiel möchte ich alle Felder außer dem Passwort speichern. Wie erreicht man das? Antwort: Verwenden Sie den Modifikator
Einige Klassen verfügen manchmal über Felder, die auf der Grundlage anderer Felder oder anderer Informationen berechnet werden. Sie werden sozusagen spontan berechnet. Um ein Beispiel für einen solchen Bereich zu geben, stellen wir uns eine Bestellung in einem Online-Shop oder einem Lebensmittellieferdienst vor. Jede Bestellung besteht unter anderem aus einer Warenliste und einem Gesamtpreis. Es setzt sich wiederum aus den Gesamtkosten jedes Produkts zusammen. Es stellt sich heraus, dass die endgültigen Kosten nicht „von Hand“ festgelegt werden sollten: Sie müssen programmgesteuert berechnet werden, indem die Kosten aller Waren summiert werden. Felder wie diese, die programmgesteuert berechnet werden sollten, müssen nicht serialisiert werden. Deshalb kennzeichnen wir sie mit einem Modifikator
Es gibt auch einige Klassen, die private Informationen speichern. Ein Beispiel einer solchen Klasse haben wir uns am Anfang des Artikels angesehen. Sie sollten nicht zulassen, dass solche Informationen außerhalb der JVM durchsickern. Daher müssen Felder mit solchen Daten mit einem Modifikator gekennzeichnet werden,
Manchmal enthält eine Klasse Felder – Objekte anderer Klassen, die die Schnittstelle nicht implementieren
Nun, noch eine letzte Sache. Es besteht keine Notwendigkeit, Felder zu serialisieren, die nicht Teil der Statusinformationen des Objekts sind. Die obigen Beispiele fallen unter diese Regel. Sie können hier aber auch alle anderen Felder einschließen, die zum Debuggen oder zur Ausführung einer Servicefunktion hinzugefügt wurden und keine Informationen über den Zustand des Objekts enthalten.
Erinnern wir uns an die Serialisierung
Der Modifikatortransient
wird beim Serialisieren und Deserialisieren von Objekten verwendet. Lassen Sie uns zunächst kurz darüber sprechen. Angenommen, wir haben ein Objekt und es verfügt über Felder, von denen jedes einen bestimmten Wert hat. All dies wird als Zustand des Objekts bezeichnet. Bei der Serialisierung handelt es sich um die Umwandlung des Zustands eines Objekts in eine Folge von Bytes. Diese Bytes werden normalerweise in einer Datei gespeichert. Die Deserialisierung ist der umgekehrte Vorgang. Stellen wir uns vor, wir hätten ein Objekt in Bytes serialisiert und diesen Satz Bytes in einer Datei gespeichert. Beim Deserialisieren benötigt das Programm:
- Liest eine Reihe von Bytes aus einer Datei.
- Erstellen Sie aus diesem Satz von Bytes ein anfängliches Objekt und setzen Sie jedes Feld auf den Wert, den das Objekt zum Zeitpunkt der Serialisierung hatte.
Erinnern wir uns an die Serialisierung in der Praxis
Schauen wir uns nun die Serialisierung in der Praxis an. Wenn Sie das Thema besser verstehen möchten, empfehlen wir die Lektüre des Materials Serialisierung und Deserialisierung in Java . Nun, in diesem Artikel werden wir es übertreiben und direkt zu den Beispielen gehen. Nehmen wir an, wir haben eine KlasseUser
mit einer Reihe einiger Felder, Getter und Setter sowie einer Methode toString
:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String firstName;
private String lastName;
private String email;
private LocalDate birthDate;
private String login;
private String password;
public User() {}
public User(String firstName, String lastName, String email, LocalDate birthDate, String login, String password) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.birthDate = birthDate;
this.login = login;
this.password = password;
}
/*
Геттеры, Сеттеры
*/
@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", birthDate=" + birthDate +
", login='" + login + '\'' +
", password='" + password + '\'' +
'}';
}
}
Wir wollen in Zukunft Objekte dieser Klasse serialisieren. Schreiben wir eine Methode, die ein Objekt User
und einen String akzeptiert path
– den Pfad zu der Datei, in der wir die Bytes speichern:
static void serialize(User user, String path) throws IOException {
FileOutputStream outputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
//2 Threads erstellen, um das Objekt zu serialisieren und in einer Datei zu speichern
outputStream = new FileOutputStream(path);
objectOutputStream = new ObjectOutputStream(outputStream);
// сохраняем ein Objekt в файл
objectOutputStream.writeObject(user);
} finally {
// Закроем потоки в блоке finally
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
Wir werden auch eine Methode zur Deserialisierung schreiben. Die Methode nimmt einen String path
(den Pfad zu der Datei, aus der das Objekt „geladen“ wird) und gibt ein Objekt des Typs zurück User
:
static User deserialize(String path) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try {
//создаем 2 потока для десериализации ein Objektа из Datei
fileInputStream = new FileInputStream(path);
objectInputStream = new ObjectInputStream(fileInputStream);
//загружаем ein Objekt из Datei
return (User) objectInputStream.readObject();
} finally {
if (fileInputStream != null) {
fileInputStream.close();
}
if (objectInputStream != null) {
objectInputStream.close();
}
}
}
Alle Werkzeuge sind einsatzbereit. main
, in der wir ein Klassenobjekt erstellen User
und es serialisieren. Dann laden wir es und vergleichen es mit dem Original:
public static void main(String[] args) throws IOException, ClassNotFoundException {
// вставьте свой путь до Datei
final String path = "/home/zor/user.ser";
// unser Objekt erstellen
User user = new User();
user.setFirstName("Stefan");
user.setLastName("Smith");
user.setEmail("ssmith@email.com");
user.setBirthDate(LocalDate.of(1991, 7, 16));
user.setLogin("ssmith");
user.setPassword("gemma_arterton_4ever_in_my_heart91");
System.out.println("Initial user: " + user + "\r\n");
serialize(user, path);
User loadedUser = deserialize(path);
System.out.println("Loaded user from file: " + loadedUser + "\r\n");
}
Wenn wir die Methode ausführen, sehen wir die folgende Ausgabe:
Initial user: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}
Loaded user from file: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}
Wie Sie der Ausgabe entnehmen können, sind die Objekte identisch. Aber es gibt ein kleines Aber... Und genau hier kommt transient
ins Spiel .
Modifikator (endlich)transient
War irgendjemand verwirrt darüber, dass wir das Passwort des Benutzers gespeichert haben? Besonders ein solches Passwort... Ja, ja, wir haben es uns selbst ausgedacht, aber trotzdem... Manchmal gibt es Situationen, in denen einige Felder nicht serialisiert werden können, oder es ist besser, dies nicht zu tun. Im obigen Beispiel möchte ich alle Felder außer dem Passwort speichern. Wie erreicht man das? Antwort: Verwenden Sie den Modifikator transient
. transient
ist ein Modifikator, der vor einem Klassenfeld platziert wird (ähnlich wie andere Modifikatoren wie usw.), um anzugeben, dass das public
Feld final
nicht serialisiert werden soll. Mit dem Schlüsselwort gekennzeichnete Felder transient
werden nicht serialisiert. Bearbeiten wir nun das Beispiel mit unserem Benutzer, um eine kleine Verwirrung zu beheben und das Passwort des Benutzers nicht zu speichern. Markieren Sie dazu das entsprechende Feld in der Klasse mit dem Schlüsselwort transient
:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String firstName;
private String lastName;
private String email;
private LocalDate birthDate;
private String login;
private transient String password;
/*
Конструкторы, геттеры, сеттеры, toString...
*/
}
Wenn wir die Methode aus dem obigen Beispiel erneut ausführen main
, werden wir feststellen, dass das Passwort nicht gespeichert wird:
Initial user: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}
Loaded user from file: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='null'}
Großartig, wir haben unser Ziel erreicht und speichern keine vertraulichen Informationen. Besonders diese Art von Informationen... (sorry)
Wann sollte man transient verwenden?
Um in den Kontext der Serialisierung einzutauchen, wurde ein Beispiel mit einem Benutzer benötigt. Lassen Sie uns nun genauer darüber sprechen, wann der Modifikator verwendet werden solltetransient
.
- Felder, die programmgesteuert berechnet werden
Einige Klassen verfügen manchmal über Felder, die auf der Grundlage anderer Felder oder anderer Informationen berechnet werden. Sie werden sozusagen spontan berechnet. Um ein Beispiel für einen solchen Bereich zu geben, stellen wir uns eine Bestellung in einem Online-Shop oder einem Lebensmittellieferdienst vor. Jede Bestellung besteht unter anderem aus einer Warenliste und einem Gesamtpreis. Es setzt sich wiederum aus den Gesamtkosten jedes Produkts zusammen. Es stellt sich heraus, dass die endgültigen Kosten nicht „von Hand“ festgelegt werden sollten: Sie müssen programmgesteuert berechnet werden, indem die Kosten aller Waren summiert werden. Felder wie diese, die programmgesteuert berechnet werden sollten, müssen nicht serialisiert werden. Deshalb kennzeichnen wir sie mit einem Modifikator transient
.
class Order implements Serializable {
private List- items;
private transient BigDecimal totalAmount; //вычисляется на ходу
}
- Felder mit privaten Informationen
Es gibt auch einige Klassen, die private Informationen speichern. Ein Beispiel einer solchen Klasse haben wir uns am Anfang des Artikels angesehen. Sie sollten nicht zulassen, dass solche Informationen außerhalb der JVM durchsickern. Daher müssen Felder mit solchen Daten mit einem Modifikator gekennzeichnet werden, transient
wenn Sie eine solche Klasse serialisieren möchten.
- Felder, die die Schnittstelle nicht implementieren
Serializable
Manchmal enthält eine Klasse Felder – Objekte anderer Klassen, die die Schnittstelle nicht implementieren Serializable
Serializable
. Beispiele für solche Felder sind Logger, I/O-Streams, Objekte, die Datenbankverbindungen speichern und andere Dienstprogrammklassen. Wenn Sie versuchen, ein Objekt zu serialisieren, das nicht serialisierbare Felder enthält, erhalten Sie eine Fehlermeldung java.io.NotSerializableException
. Um dies zu vermeiden, müssen alle Felder, die die Schnittstelle nicht implementieren, Serializable
mit einem Modifikator gekennzeichnet werden transient
.
public class FileReader implements Serializable {
// Первые 2 поля не реализуют Serializable
// Помечаем их Wie transient поля
private transient InputStream is;
private transient BufferedReader buf;
private String fileName;
// Constructors, Getters, Setters
public String readFile() throws IOException {
try {
is = new FileInputStream(fileName);
buf = new BufferedReader(new InputStreamReader(is));
String line = buf.readLine();
StringBuilder sb = new StringBuilder();
while (line != null) {
sb.append(line).append("\n");
line = buf.readLine();
}
return sb.toString();
} finally {
if (buf != null) {
buf.close();
}
if (is != null) {
is.close();
}
}
}
}
- Felder mit Informationen zum Objektzustand
Nun, noch eine letzte Sache. Es besteht keine Notwendigkeit, Felder zu serialisieren, die nicht Teil der Statusinformationen des Objekts sind. Die obigen Beispiele fallen unter diese Regel. Sie können hier aber auch alle anderen Felder einschließen, die zum Debuggen oder zur Ausführung einer Servicefunktion hinzugefügt wurden und keine Informationen über den Zustand des Objekts enthalten.
transient
Undfinal
Ergebnisse
Das ist alles. Heute haben wir über den Modifikator gesprochentransient
:
- Wir erinnerten uns an die Serialisierung in Theorie und Praxis.
- Wir haben festgestellt, dass einige Felder der Klasse mit einem Modifikator markiert werden müssen, damit sie nicht serialisiert werden
transient
. - Wir haben besprochen, in welchen Situationen dieser Modifikator verwendet werden sollte. Es gab vier solcher Situationen:
- Felder, die programmgesteuert berechnet werden;
- Felder, die geheime Informationen enthalten;
- Felder, die die Schnittstelle nicht implementieren
Serializable
; - Felder, die nicht Teil des Objektstatus sind.
GO TO FULL VERSION