Ciao! Nell'articolo di oggi esamineremo il modificatore transitorio in Java. Parliamo del motivo per cui è necessario questo modificatore e di come utilizzarlo correttamente. Andare!
atomi . Scriviamo un metodo la vergogna spagnola Modificatore (finalmente)
Qualcuno era confuso dal fatto che abbiamo salvato la password dell'utente? Soprattutto una password del genere... Sì, sì, l'abbiamo inventata noi stessi, ma comunque... A volte ci sono situazioni in cui alcuni campi non possono essere serializzati, oppure è meglio non farlo. Nell'esempio sopra, vorrei salvare tutti i campi tranne la password. Come raggiungere questo obiettivo? Risposta: usa il modificatore
Alcune classi talvolta dispongono di campi calcolati in base ad altri campi o ad altre informazioni. Vengono calcolati, per così dire, al volo. Per fare un esempio di tale campo, immaginiamo un ordine in un negozio online o in un servizio di consegna di cibo. Ogni ordine, tra le altre informazioni, consiste in un elenco di merci e un costo totale. A sua volta, consiste nel costo totale di ciascun prodotto. Si scopre che il costo finale non dovrebbe essere fissato “a mano”: deve essere calcolato in modo programmatico, sommando il costo di tutti i beni. Campi come questi che devono essere calcolati a livello di codice non devono essere serializzati. Pertanto, li contrassegniamo con un modificatore
Esistono anche alcune classi che memorizzano informazioni private. Abbiamo visto un esempio di tale classe all'inizio dell'articolo. Non dovresti consentire che tali informazioni fuoriescano dalla JVM. Pertanto, i campi con tali dati devono essere contrassegnati con un modificatore
A volte una classe contiene campi-oggetto di altre classi che non implementano l'interfaccia
Bene, un'ultima cosa. Non è necessario serializzare i campi che non fanno parte delle informazioni sullo stato dell'oggetto. Gli esempi sopra riportati rientrano in questa regola. Ma puoi anche includere qui tutti gli altri campi aggiunti per il debug o per eseguire qualche tipo di funzione di servizio che non riporta informazioni sullo stato dell'oggetto.
Ricordiamo la serializzazione
Il modificatoretransient
viene utilizzato nel processo di serializzazione e deserializzazione degli oggetti. Quindi parliamo brevemente di questo prima. Supponiamo di avere un oggetto con campi, ognuno dei quali ha un valore. Tutto questo è chiamato lo stato dell'oggetto. La serializzazione è la conversione dello stato di un oggetto in una sequenza di byte. Questi byte sono solitamente memorizzati in qualche file. La deserializzazione è il processo inverso. Immaginiamo di aver serializzato un oggetto in byte e di aver memorizzato questo insieme di byte in un file. Durante la deserializzazione, il programma necessita di:
- Legge un insieme di byte da un file.
- Costruisci un oggetto iniziale da questo insieme di byte e imposta ciascun campo sul valore che l'oggetto aveva al momento della serializzazione.
Ricordiamo la serializzazione in pratica
Bene, ora diamo un'occhiata alla serializzazione in pratica. Se vuoi comprendere meglio l'argomento ti consigliamo di leggere il materiale Serializzazione e deserializzazione in Java . Bene, in questo articolo andremo oltre e andremo direttamente agli esempi. Diciamo di avere una classeUser
con un insieme di alcuni campi, getter e setter e un metodo 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 + '\'' +
'}';
}
}
Vogliamo serializzare gli oggetti di questa classe in futuro. Scriviamo un metodo che accetta un oggetto User
e una stringa path
: il percorso del file in cui salveremo i byte:
static void serialize(User user, String path) throws IOException {
FileOutputStream outputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
//create 2 threads to serialize the object and save it to a file
outputStream = new FileOutputStream(path);
objectOutputStream = new ObjectOutputStream(outputStream);
// сохраняем an object в файл
objectOutputStream.writeObject(user);
} finally {
// Закроем потоки в блоке finally
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
Scriveremo anche un metodo per la deserializzazione. Il metodo prende una stringa path
(il percorso del file da cui verrà “caricato” l'oggetto) e restituisce un oggetto di tipo User
:
static User deserialize(String path) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try {
//создаем 2 потока для десериализации an object из file
fileInputStream = new FileInputStream(path);
objectInputStream = new ObjectInputStream(fileInputStream);
//загружаем an object из file
return (User) objectInputStream.readObject();
} finally {
if (fileInputStream != null) {
fileInputStream.close();
}
if (objectInputStream != null) {
objectInputStream.close();
}
}
}
Tutti gli strumenti sono pronti per l'uso. È ora di dividere i byte in main
in cui creiamo un oggetto classe User
e lo serializziamo. Quindi lo caricheremo e lo confronteremo con quello che era originariamente:
public static void main(String[] args) throws IOException, ClassNotFoundException {
// вставьте свой путь до file
final String path = "/home/zor/user.ser";
// create our object
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");
}
Se eseguiamo il metodo, vedremo il seguente output:
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'}
Come puoi vedere dall'output, gli oggetti sono identici. Ma c’è un piccolo ma… Ed è proprio qui che entra in gioco transient
.
Modificatore (finalmente)transient
Qualcuno era confuso dal fatto che abbiamo salvato la password dell'utente? Soprattutto una password del genere... Sì, sì, l'abbiamo inventata noi stessi, ma comunque... A volte ci sono situazioni in cui alcuni campi non possono essere serializzati, oppure è meglio non farlo. Nell'esempio sopra, vorrei salvare tutti i campi tranne la password. Come raggiungere questo obiettivo? Risposta: usa il modificatore transient
. transient
è un modificatore posizionato prima di un campo di classe (simile ad altri modificatori come public
, final
ecc.) per indicare che il campo non deve essere serializzato. I campi contrassegnati con la parola chiave transient
non vengono serializzati. Ora modifichiamo l'esempio con il nostro utente per correggere una piccola confusione e non salvare la password dell'utente. Per fare ciò, contrassegna il campo corrispondente nella classe con la parola chiave 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...
*/
}
Se eseguiamo nuovamente il metodo dell'esempio precedente main
, vedremo che la password non viene salvata:
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'}
Ottimo, abbiamo raggiunto il nostro obiettivo e non conserviamo informazioni riservate. Soprattutto questo tipo di informazioni... (scusa)
Quando utilizzare il transitorio?
Era necessario un esempio con un utente per immergersi nel contesto della serializzazione. Ora parliamo più nello specifico di quando utilizzare il modificatoretransient
.
- Campi calcolati a livello di codice
Alcune classi talvolta dispongono di campi calcolati in base ad altri campi o ad altre informazioni. Vengono calcolati, per così dire, al volo. Per fare un esempio di tale campo, immaginiamo un ordine in un negozio online o in un servizio di consegna di cibo. Ogni ordine, tra le altre informazioni, consiste in un elenco di merci e un costo totale. A sua volta, consiste nel costo totale di ciascun prodotto. Si scopre che il costo finale non dovrebbe essere fissato “a mano”: deve essere calcolato in modo programmatico, sommando il costo di tutti i beni. Campi come questi che devono essere calcolati a livello di codice non devono essere serializzati. Pertanto, li contrassegniamo con un modificatore transient
.
class Order implements Serializable {
private List- items;
private transient BigDecimal totalAmount; //вычисляется на ходу
}
- Campi con informazioni private
Esistono anche alcune classi che memorizzano informazioni private. Abbiamo visto un esempio di tale classe all'inizio dell'articolo. Non dovresti consentire che tali informazioni fuoriescano dalla JVM. Pertanto, i campi con tali dati devono essere contrassegnati con un modificatore transient
se si intende serializzare tale classe.
- Campi che non implementano l'interfaccia
Serializable
A volte una classe contiene campi-oggetto di altre classi che non implementano l'interfaccia Serializable
Serializable
. Esempi di tali campi sono logger, flussi I/O, oggetti che memorizzano connessioni al database e altre classi di utilità. Se provi a serializzare un oggetto che contiene campi non serializzabili, riceverai un errore java.io.NotSerializableException
. Per evitare ciò, tutti i campi che non implementano l'interfaccia Serializable
devono essere contrassegnati con un modificatore transient
.
public class FileReader implements Serializable {
// Первые 2 поля не реализуют Serializable
// Помечаем их How 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();
}
}
}
}
- Campi con informazioni sullo stato dell'oggetto
Bene, un'ultima cosa. Non è necessario serializzare i campi che non fanno parte delle informazioni sullo stato dell'oggetto. Gli esempi sopra riportati rientrano in questa regola. Ma puoi anche includere qui tutti gli altri campi aggiunti per il debug o per eseguire qualche tipo di funzione di servizio che non riporta informazioni sullo stato dell'oggetto.
transient
Efinal
Risultati
È tutto. Oggi abbiamo parlato del modificatoretransient
:
- Abbiamo ricordato la serializzazione in teoria e in pratica.
- Ci siamo accorti che per non serializzare alcuni campi della classe è necessario contrassegnarli con un modificatore
transient
. - Abbiamo discusso in quali situazioni dovrebbe essere utilizzato questo modificatore. C'erano quattro situazioni simili:
- campi calcolati a livello di codice;
- campi che contengono informazioni segrete;
- campi che non implementano l'interfaccia
Serializable
; - campi che non fanno parte dello stato dell'oggetto.
GO TO FULL VERSION