Bonjour! Dans l'article d'aujourd'hui, nous examinerons le modificateur transitoire en Java. Parlons de pourquoi ce modificateur est nécessaire et comment l'utiliser correctement. Aller!
atomes . Écrivons une méthode la honte espagnole Modificateur (enfin)
Quelqu'un a-t-il pensé que nous avions enregistré le mot de passe de l'utilisateur ? Surtout un tel mot de passe... Oui, oui, nous l'avons trouvé nous-mêmes, mais quand même... Parfois, il y a des situations où certains champs ne peuvent pas être sérialisés, ou il vaut mieux ne pas le faire. Dans l'exemple ci-dessus, je souhaite enregistrer tous les champs sauf le mot de passe. Comment y parvenir ? Réponse : utilisez le modificateur
Certaines classes comportent parfois des champs calculés en fonction d'autres champs ou d'autres informations. Ils sont calculés, pour ainsi dire, à la volée. Pour donner un exemple d’un tel domaine, imaginons une commande dans une boutique en ligne ou dans un service de livraison de nourriture. Chaque commande comprend, entre autres informations, une liste de marchandises et un coût total. Il correspond à son tour au coût total de chaque produit. Il s'avère que le coût final ne doit pas être fixé « à la main » : il doit être calculé par programme, en résumant le coût de toutes les marchandises. Les champs comme ceux-ci qui doivent être calculés par programme n'ont pas besoin d'être sérialisés. Par conséquent, nous les marquons avec un modificateur
Certaines classes stockent également des informations privées. Nous avons examiné un exemple d'une telle classe au début de l'article. Vous ne devez pas permettre à ces informations de fuir en dehors de la JVM. Par conséquent, les champs contenant de telles données doivent être marqués d'un modificateur
Parfois, une classe contient des champs-objets d'autres classes qui n'implémentent pas l'interface
Eh bien, une dernière chose. Il n'est pas nécessaire de sérialiser les champs qui ne font pas partie des informations sur l'état de l'objet. Les exemples ci-dessus relèvent de cette règle. Mais vous pouvez également inclure ici tous les autres champs ajoutés pour le débogage ou pour exécuter une sorte de fonction de service qui ne contiennent pas d'informations sur l'état de l'objet.
Rappelons-nous la sérialisation
Le modificateurtransient
est utilisé dans le processus de sérialisation et de désérialisation d'objets. Parlons donc brièvement de cela d’abord. Supposons que nous ayons un objet et qu’il comporte des champs dont chacun a une valeur. Tout cela s’appelle l’état de l’objet. La sérialisation est la conversion de l'état d'un objet en une séquence d'octets. Ces octets sont généralement stockés dans un fichier. La désérialisation est le processus inverse. Imaginons que nous sérialisons un objet en octets et stockions cet ensemble d'octets dans un fichier. Lors de la désérialisation, le programme a besoin de :
- Lit un ensemble d'octets à partir d'un fichier.
- Construisez un objet initial à partir de cet ensemble d'octets et définissez chaque champ sur la valeur que l'objet avait au moment de la sérialisation.
Rappelons la sérialisation en pratique
Eh bien, regardons maintenant la sérialisation en pratique. Si vous souhaitez mieux comprendre le sujet, nous vous recommandons de lire le matériel Sérialisation et désérialisation en Java . Eh bien, dans cet article, nous allons aller plus loin et passer directement aux exemples. Disons que nous avons une classeUser
avec un ensemble de champs, de getters et de setters, et une méthodetoString
:
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 + '\'' +
'}';
}
}
Nous souhaitons sérialiser les objets de cette classe à l'avenir. Écrivons une méthode qui prend un objet User
et une chaîne path
- le chemin d'accès au fichier dans lequel nous allons enregistrer les octets :
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();
}
}
}
Nous écrirons également une méthode de désérialisation. La méthode prend une chaîne path
(le chemin d'accès au fichier à partir duquel l'objet sera « chargé ») et renvoie un objet de type 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();
}
}
}
Tous les outils sont prêts à l'emploi. Il est temps de diviser les octets en main
dans laquelle nous créons un objet de classe User
et le sérialisons. Ensuite, nous allons le charger et le comparer avec ce qui était à l'origine :
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");
}
Si nous exécutons la méthode, nous verrons le résultat suivant :
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'}
Comme vous pouvez le voir sur le résultat, les objets sont identiques. Mais il y a un petit mais... Et c'est exactement là que transient
entre en jeu .
Modificateur (enfin)transient
Quelqu'un a-t-il pensé que nous avions enregistré le mot de passe de l'utilisateur ? Surtout un tel mot de passe... Oui, oui, nous l'avons trouvé nous-mêmes, mais quand même... Parfois, il y a des situations où certains champs ne peuvent pas être sérialisés, ou il vaut mieux ne pas le faire. Dans l'exemple ci-dessus, je souhaite enregistrer tous les champs sauf le mot de passe. Comment y parvenir ? Réponse : utilisez le modificateur transient
. transient
est un modificateur placé avant un champ de classe (similaire à d'autres modificateurs tels que public
, final
etc.) pour indiquer que le champ ne doit pas être sérialisé. Les champs marqués du mot-clé transient
ne sont pas sérialisés. Modifions maintenant l'exemple avec notre utilisateur pour corriger une petite confusion et ne pas enregistrer le mot de passe de l'utilisateur. Pour cela, marquez le champ correspondant dans la classe avec le mot-clé 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...
*/
}
Si nous exécutons à nouveau la méthode de l'exemple ci-dessus main
, nous verrons que le mot de passe n'est pas enregistré :
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'}
Génial, nous avons atteint notre objectif et ne stockons pas d’informations confidentielles. Surtout ce genre d'informations... (désolé)
Quand utiliser le transitoire ?
Un exemple avec un utilisateur était nécessaire afin de plonger dans le contexte de la sérialisation. Parlons maintenant plus spécifiquement du moment où utiliser le modificateurtransient
.
- Champs calculés par programme
Certaines classes comportent parfois des champs calculés en fonction d'autres champs ou d'autres informations. Ils sont calculés, pour ainsi dire, à la volée. Pour donner un exemple d’un tel domaine, imaginons une commande dans une boutique en ligne ou dans un service de livraison de nourriture. Chaque commande comprend, entre autres informations, une liste de marchandises et un coût total. Il correspond à son tour au coût total de chaque produit. Il s'avère que le coût final ne doit pas être fixé « à la main » : il doit être calculé par programme, en résumant le coût de toutes les marchandises. Les champs comme ceux-ci qui doivent être calculés par programme n'ont pas besoin d'être sérialisés. Par conséquent, nous les marquons avec un modificateur transient
.
class Order implements Serializable {
private List- items;
private transient BigDecimal totalAmount; //вычисляется на ходу
}
- Champs avec informations privées
Certaines classes stockent également des informations privées. Nous avons examiné un exemple d'une telle classe au début de l'article. Vous ne devez pas permettre à ces informations de fuir en dehors de la JVM. Par conséquent, les champs contenant de telles données doivent être marqués d'un modificateur transient
si vous souhaitez sérialiser une telle classe.
- Champs qui n'implémentent pas l'interface
Serializable
Parfois, une classe contient des champs-objets d'autres classes qui n'implémentent pas l'interface Serializable
Serializable
. Des exemples de tels champs sont les enregistreurs, les flux d'E/S, les objets qui stockent les connexions à la base de données et d'autres classes d'utilitaires. Si vous essayez de sérialiser un objet contenant des champs non sérialisables, vous recevrez une erreur java.io.NotSerializableException
. Pour éviter cela, tous les champs qui n'implémentent pas l'interface Serializable
doivent être marqués d'un modificateur 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();
}
}
}
}
- Champs contenant des informations sur l'état de l'objet
Eh bien, une dernière chose. Il n'est pas nécessaire de sérialiser les champs qui ne font pas partie des informations sur l'état de l'objet. Les exemples ci-dessus relèvent de cette règle. Mais vous pouvez également inclure ici tous les autres champs ajoutés pour le débogage ou pour exécuter une sorte de fonction de service qui ne contiennent pas d'informations sur l'état de l'objet.
transient
Etfinal
Résultats
C'est tout. Aujourd'hui nous avons parlé du modificateurtransient
:
- Nous nous sommes souvenus de la sérialisation en théorie et en pratique.
- Nous avons réalisé que pour ne pas sérialiser certains champs de la classe, ils doivent être marqués d'un modificateur
transient
. - Nous avons discuté dans quelles situations ce modificateur devrait être utilisé. Il y avait quatre situations de ce type :
- les champs calculés par programme ;
- les champs contenant des informations secrètes ;
- les champs qui n'implémentent pas l'interface
Serializable
; - champs qui ne font pas partie de l’état de l’objet.
GO TO FULL VERSION