Maison  >  Article  >  Java  >  Explication détaillée du mot-clé transitoire en Java

Explication détaillée du mot-clé transitoire en Java

angryTom
angryTomavant
2019-11-26 16:14:581736parcourir

Pour être honnête, les amis qui étudient Java depuis un certain temps connaissent encore très peu le mot-clé transient et ne l'ont pratiquement pas utilisé, mais le mot-clé transient joue un rôle indispensable en Java ! Si je veux en parler, je pense que l'endroit le plus susceptible d'apparaître est lorsqu'il s'agit de flux d'objets (également appelés flux sérialisés) dans les flux IO !

Explication détaillée du mot-clé transitoire en Java

Je crois que beaucoup de gens ne se soucieront pas de ce mot-clé jusqu'à ce qu'ils le rencontrent. Je me souviens que la première fois que les blogueurs ont rencontré le mot-clé transitoire, c'était lors de la lecture du code source du JDK. Dans le processus d'apprentissage de Java, la raison pour laquelle le mot-clé transient est rare est en fait indissociable de sa fonction : la fonction principale du mot-clé transient est d'empêcher la sérialisation de certaines variables d'attributs membres modifiées par le mot-clé transient. En fait, c’est précisément pour cette raison que les opérations de sérialisation sont rarement utilisées dans le processus d’apprentissage, généralement dans le développement réel ! Quant à la sérialisation, je pense que de nombreux novices sont confus ou n'ont pas de concept précis. Ce n'est pas un problème. Le blogueur suivant vous rappellera clairement ce qu'est la sérialisation, garantissant que vous ne l'oublierez jamais de cette vie (cela semble être un problème). un peu Exagéré, un peu prétentieux, j'ai l'impression que je vais être battu)

1. Qu'est-ce que la sérialisation ?

En parlant de sérialisation, un autre concept qui l'accompagne est la désérialisation des chaussures pour enfants, ne paniquez pas. Se souvenir de la sérialisation équivaut à se souvenir de la désérialisation, car la désérialisation est l'inverse de la sérialisation. Le blogueur recommande de simplement se rappeler le concept de sérialisation pour éviter toute confusion.

(Vidéo recommandée : Tutoriel vidéo Java)

Sérialisation des définitions de terminologie professionnelle :

Java fournit un mécanisme de sérialisation des objets. Un objet peut être représenté par une séquence d'octets contenant des informations telles que les données de l'objet, le type de l'objet et les attributs stockés dans l'objet. Une fois la séquence d'octets écrite dans le fichier, cela équivaut à conserver les informations d'un objet dans le fichier. À l'inverse, la séquence d'octets peut être relue à partir du fichier, l'objet reconstruit et désérialisé. Les données de l'objet, le type de l'objet et les informations de données stockées dans l'objet peuvent toutes être utilisées pour créer des objets en mémoire.

Sérialisation : Octet——> Objet

En fait, ce que j'ai résumé est la conclusion ci-dessus. Si vous ne comprenez pas, référez-vous directement à la définition des termes professionnels, et souvenez-vous-en après l'avoir compris. Gardez simplement mes mots, si vous ne vous en souvenez pas, s'il vous plaît, tuez-moi

Graphique comprenant la sérialisation :

Explication détaillée du mot-clé transitoire en Java

2. Pourquoi la sérialisation ?

Dans la section précédente, nous avons mentionné le concept de sérialisation. Après avoir connu le concept, nous devons savoir pourquoi nous devons sérialiser.

Avant de parler des raisons pour lesquelles la sérialisation est nécessaire, laissez-moi vous donner une châtaigne en tant que blogueur :

Tout comme lorsque vous allez dans la rue pour faire l'épicerie, l'opération générale est de emballez-les dans des sacs en plastique jusqu'à votre retour à la maison. Au moment de cuisiner, sortez la vaisselle. Et cette série d'opérations s'apparente à la sérialisation et à la désérialisation !

La sérialisation d'objets en Java fait référence à la conversion d'objets en séquences d'octets. Ces séquences d'octets contiennent les données et les informations de l'objet. Un objet sérialisé peut être écrit dans des bases de données ou des fichiers, il peut également être utilisé pour le réseau. Transmission. Généralement, lorsque nous utilisons le cache (pas assez d'espace mémoire peut être stocké localement sur le disque dur) ou appelons rpc à distance (transmission réseau), nous devons souvent faire en sorte que nos classes d'entité implémentent l'interface Serialisable. il sérialisable.

Chestnuts modifiés avec le mot-clé transient pendant le processus de développement :

Si un utilisateur dispose de certains mots de passe et autres informations, par souci de sécurité, ils ne souhaitent pas être transmis lors des opérations réseau, cette information correspond à Vous pouvez ajouter le mot clé transient à la variable. En d'autres termes, le cycle de vie de ce champ n'existe que dans la mémoire de l'appelant et ne sera pas écrit sur le disque à des fins de persistance.

Exemples qui ne nécessitent pas la modification transitoire du mot-clé pendant le processus de développement :

1. Les valeurs de champ dans une classe peuvent être dérivées en fonction d'autres champs.

2. En fonction des exigences spécifiques de l'entreprise, quels champs ne souhaitent pas être sérialisés

Je me demande si vous avez déjà réfléchi aux raisons pour lesquelles ils ne devraient pas être sérialisés ? En fait, il s’agit principalement d’économiser de l’espace de stockage. Optimisez le programme !

PS : je me souviens qu'en regardant le code source de HashMap, j'ai découvert qu'un champ était modifié avec un transitoire. Je pense que cela a du sens. Il n'est vraiment pas nécessaire de sérialiser ce champ modCount car il n'a aucun sens. ModCount est principalement utilisé pour déterminer si le HashMap a été modifié (modCount augmentera automatiquement lors des opérations de mise et de suppression). Pour ce type de variable, il peut s'agir de n'importe quelle valeur au début, et 0 est également possible (nouveau, désérialisé,). ou cloné). Il est toujours 0 à la sortie), il n'est pas nécessaire de conserver sa valeur.

Bien sûr, le but ultime après la sérialisation est de le désérialiser et de le restaurer dans l'objet Java d'origine. Sinon, que feriez-vous après la sérialisation, tout comme acheter des produits d'épicerie, les emballer dans des sacs en plastique est pour faciliter le retour à la maison en toute sécurité ? . Retirez le sac en plastique afin que la séquence d'octets sérialisée puisse être restaurée en un objet Java.

3. L'utilisation de la sérialisation et du transitoire

1. La classe d'objets qui doivent être sérialisés doit implémenter l'interface de sérialisation : Java.lang.Serialised interface ( Une interface de drapeau sans aucune méthode abstraite), la plupart des classes en Java implémentent cette interface, telles que : la classe String, Integer, etc. Les classes qui n'implémentent pas cette interface ne sérialiseront ni ne désérialiseront aucun état et lèveront une exception NotSerializingException.

2. La couche inférieure jugera. Si l'objet actuel est une instance de Serialisable, la sérialisation est autorisée.

3. Utilisez le flux d'objets ObjectOutputStream en Java pour terminer la sérialisation et la désérialisation du flux ObjectInputStream

==ObjectOutputStream : opération de sérialisation via la méthode writeObject() == 

==ObjectInputStream : Opération de désérialisation via la méthode readObject() ==

4 Toutes les propriétés de cette classe doivent être sérialisables. S'il existe un attribut qui n'a pas besoin d'être sérialisable, l'attribut doit être marqué comme transitoire et modifié à l'aide du mot-clé transient.

Explication détaillée du mot-clé transitoire en Java

En raison des octets, cela doit impliquer des opérations de flux, c'est-à-dire que le flux d'objets est également appelé flux de sérialisation ObjectOutputstream. Analysons la sérialisation dans diverses situations. .Code d'action !

Ici, je recommande vraiment fortement aux lecteurs qui lisent le blog Yichun d'essayer de le frapper, n'oubliez pas de l'emporter avec vous d'un coup d'œil ou de le copier et de l'exécuter, surtout pour les petites chaussures blanches pour enfants, croyez-moi ! Vous gagnerez certainement quelque chose de différent. !

3.1. L'interface sérialisable n'est pas implémentée pour la sérialisation

package TransientTest;
import java.io.*;

class UserInfo { //================================注意这里没有实现Serializable接口
    private String name;
    private transient String password;

    public UserInfo(String name, String psw) {
        this.name = name;
        this.password = psw;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
            "name='" + name + '\'' +
            ", password='" + password + '\'' +
            '}';
    }
}

public class TransientDemo {
    public static void main(String[] args) {

        UserInfo userInfo = new UserInfo("老王", "123");
        System.out.println("序列化之前信息:" + userInfo);

        try {
            ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("userinfo.txt"));
            output.writeObject(new UserInfo("老王", "123"));
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Exécuter les résultats

Explication détaillée du mot-clé transitoire en Java

3.2.

Lorsque nous ajoutons l'implémentation de l'interface Serialisable et l'exécutons, nous constaterons que le contenu du fichier userinfo.txt qui apparaît dans le projet ressemble à ceci :

Explication détaillée du mot-clé transitoire en Java

En fait, là n’est pas la question. Le fait est que l’opération de sérialisation a réussi !

3.3. Sérialisation ordinaire

package TransientTest;
import java.io.*;

class UserInfo implements Serializable { //第一步实现Serializable接口
    private String name;
    private String password; //都是普通属性==============================

    public UserInfo(String name, String psw) {
        this.name = name;
        this.password = psw;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
            "name='" + name + '\'' +
            ", password='" + password + '\'' +
            '}';
    }
}

public class TransientDemo {
    public static void main(String[] args) throws ClassNotFoundException {

        UserInfo userInfo = new UserInfo("程序员老王", "123");
        System.out.println("序列化之前信息:" + userInfo);

        try {
            ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
            output.writeObject(new UserInfo("程序员老王", "123"));
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            ObjectInputStream input = new ObjectInputStream(new FileInputStream("userinfo.txt")); //第三步开始反序列化操作
            Object o = input.readObject(); //ObjectInputStream的readObject方法会抛出ClassNotFoundException
            System.out.println("序列化之后信息:" + o);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Résultat en cours d'exécution :

序列化之前信息:UserInfo{name='程序员老王', password='123'}
序列化之后信息:UserInfo{name='程序员老王', password='123'}

3.4. Sérialisation transitoire

package TransientTest;
import java.io.*;

class UserInfo implements Serializable { //第一步实现Serializable接口
    private String name;
    private transient String password; //特别注意:属性由transient关键字修饰===========

    public UserInfo(String name, String psw) {
        this.name = name;
        this.password = psw;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
            "name='" + name + '\'' +
            ", password='" + password + '\'' +
            '}';
    }
}

public class TransientDemo {
    public static void main(String[] args) throws ClassNotFoundException {

        UserInfo userInfo = new UserInfo("程序员老王", "123");
        System.out.println("序列化之前信息:" + userInfo);

        try {
            ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
            output.writeObject(new UserInfo("程序员老王", "123"));
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            ObjectInputStream input = new ObjectInputStream(new FileInputStream("userinfo.txt")); //第三步开始反序列化操作
            Object o = input.readObject(); //ObjectInputStream的readObject方法会抛出ClassNotFoundException
            System.out.println("序列化之后信息:" + o);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Résultat en cours d'exécution :

序列化之前信息:UserInfo{name='程序员老王', password='123'}
序列化之后信息:UserInfo{name='程序员老王', password='null'}

Payer spécial attention au résultat. La valeur d'attribut ajoutée avec la modification transitoire est la valeur par défaut null ! Si l'attribut modifié par transient est de type int, alors sa valeur doit être 0 après avoir été sérialisé. Bien sûr, vous pouvez l'essayer. Qu'est-ce que cela signifie ? Cela signifie que les attributs marqués comme transitoires ne seront pas enregistrés lorsque l'objet est sérialisé (ou les variables ne seront pas conservées)

3.5, situation de sérialisation statique

package TransientTest;
import java.io.*;

class UserInfo implements Serializable { //第一步实现Serializable接口
    private String name;
    private static String password; //特别注意:属性由static关键字修饰==============

    public UserInfo(String name, String psw) {
        this.name = name;
        this.password = psw;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
            "name='" + name + '\'' +
            ", password='" + password + '\'' +
            '}';
    }
}

public class TransientDemo {
    public static void main(String[] args) throws ClassNotFoundException {

        UserInfo userInfo = new UserInfo("程序员老王", "123");
        System.out.println("序列化之前信息:" + userInfo);

        try {
            ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
            output.writeObject(new UserInfo("程序员老王", "123"));
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            ObjectInputStream input = new ObjectInputStream(new FileInputStream("userinfo.txt")); //第三步开始反序列化操作
            Object o = input.readObject(); //ObjectInputStream的readObject方法会抛出ClassNotFoundException
            System.out.println("序列化之后信息:" + o);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Résultats en cours d'exécution :

序列化之前信息:UserInfo{name='程序员老王', password='123'}
序列化之后信息:UserInfo{name='程序员老王', password='123'}

À ce moment-là, vous penserez à tort que la modification statique a également été sérialisée. En fait, ce n'est pas le cas. En fait, il est facile de se tromper ici ! Évidemment, supprimer null (valeur par défaut) peut montrer qu'il ne sera pas sérialisé. Il est évident qu'il n'est pas devenu la valeur par défaut. Pourquoi dites-vous toujours que static ne sera pas sérialisé ?

En fait, la valeur du nom de la variable statique dans la classe après désérialisation est en fait la valeur de la variable statique correspondante dans la JVM actuelle. Cette valeur est dans la JVM et n'est pas dérivée de la désérialisation. Autrement dit, les variables modifiées par static ne participent pas à la sérialisation ! Mais on ne peut pas le dire sans preuve, oui, alors comparons les deux programmes et nous comprendrons !

Le premier programme : il s'agit d'un programme d'attributs de nom qui n'a pas été modifié par statique :

package Thread;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class UserInfo implements Serializable {
    private String name;
    private transient String psw;

    public UserInfo(String name, String psw) {
        this.name = name;
        this.psw = psw;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPsw() {
        return psw;
    }

    public void setPsw(String psw) {
        this.psw = psw;
    }

    public String toString() {
        return "name=" + name + ", psw=" + psw;
    }
}
public class TestTransient {
    public static void main(String[] args) {
        UserInfo userInfo = new UserInfo("程序员老过", "456");
        System.out.println(userInfo);
        try {
            // 序列化,被设置为transient的属性没有被序列化
            ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.txt"));
            o.writeObject(userInfo);
            o.close();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        try {
            //在反序列化之前改变name的值 =================================注意这里的代码
            userInfo.setName("程序员老改");
            // 重新读取内容
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.txt"));
            UserInfo readUserInfo = (UserInfo) in .readObject();
            //读取后psw的内容为null
            System.out.println(readUserInfo.toString());
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

Résultats en cours d'exécution :

name=程序员老过, psw=456name=程序员老过, psw=null

Exécution des résultats de le programme Comme le montre la figure, la tentative de modification de la valeur de name avant la désérialisation a échoué !

Le deuxième programme : Il s'agit d'un programme d'attribut de nom modifié par statique :

package Thread;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class UserInfo implements Serializable {
    private static final long serialVersionUID = 996890129747019948 L;
    private static String name;
    private transient String psw;

    public UserInfo(String name, String psw) {
        this.name = name;
        this.psw = psw;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPsw() {
        return psw;
    }

    public void setPsw(String psw) {
        this.psw = psw;
    }

    public String toString() {
        return "name=" + name + ", psw=" + psw;
    }
}
public class TestTransient {
    public static void main(String[] args) {
        UserInfo userInfo = new UserInfo("程序员老过", "456");
        System.out.println(userInfo);
        try {
            // 序列化,被设置为transient的属性没有被序列化
            ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.txt"));
            o.writeObject(userInfo);
            o.close();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        try {
            //在反序列化之前改变name的值
            userInfo.setName("程序员老改");
            // 重新读取内容
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.txt"));
            UserInfo readUserInfo = (UserInfo) in .readObject();
            //读取后psw的内容为null
            System.out.println(readUserInfo.toString());
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

Résultat de l'exécution :

name=程序员老过, psw=456name=程序员老改, psw=null

À partir du résultat en cours d'exécution du programme, il peut on voit qu'essayer de changer la valeur de name avant la désérialisation est effectué par les programmeurs, et le résultat est réussi ! Est-ce très clair maintenant si l’on compare les deux programmes ?

Les attributs membres modifiés par le mot-clé static sont mieux chargés en mémoire que les attributs membres non statiques. En même temps, static est également meilleur que les objets entrant en mémoire. Les variables membres modifiées par static ne peuvent pas être sérialisées, et tous les objets le sont. sérialisé. Une variable statique ne fait pas partie de l’état de l’objet, elle ne participe donc pas à la sérialisation. Il est donc inutile de déclarer des variables statiques comme variables transitoires. Par conséquent, la valeur du nom de la variable statique dans la classe après la désérialisation est en fait la valeur de la variable statique correspondante dans la JVM actuelle. Cette valeur se trouve dans la JVM et n'est pas dérivée de la désérialisation.

3.6. Situation de sérialisation finale

Pour le mot-clé final, la variable finale participera directement à la sérialisation via la valeur Quant au programme de code, je ne le posterai plus. Vérifiez la modification finale !

La principale chose à noter est que final et transitoire peuvent modifier la même variable en même temps, et le résultat est le même. Cela n'a aucun impact sur le transitoire. J'espère que vous le mentionnerez principalement ici. ne soyez pas confus lorsque vous rencontrerez ces situations dans le développement de l'eau à l'avenir !

4. Le rôle de SerialVersionUID dans les classes Java

Puisque le mot-clé transient a été mentionné, la sérialisation doit être mentionnée. Puisque la sérialisation a été mentionnée, SerialVersionUID doit être mentionné. ? Du drap de laine ? Fondamentalement, ce SerialVersionUID existera s'il y a une sérialisation.

Explication détaillée du mot-clé transitoire en Java

serialVersionUID convient au mécanisme de sérialisation de Java. En termes simples, le mécanisme de sérialisation de Java vérifie la cohérence des versions en jugeant le serialVersionUID de la classe. Lors de la désérialisation, la JVM comparera le SerialVersionUID dans le flux d'octets transmis avec le SerialVersionUID de la classe d'entité locale correspondante, s'ils sont identiques, ils sont considérés comme cohérents et peuvent être désérialisés. Sinon, une version sérialisée apparaîtra. à savoir InvalidCastException, peut parfois être écrit ou non lors du développement. Il est recommandé de l'écrire.

5. Récapitulatif des mots-clés transitoires

1 Si la variable est modifiée par transitoire, la variable ne sera pas sérialisée
2. mot-clé uniquement Les variables peuvent être modifiées, mais les méthodes et les classes ne peuvent pas être modifiées.
3. Les variables modifiées par le mot-clé static ne participent pas à la sérialisation. Une variable statique ne peut pas être sérialisée, qu'elle soit modifiée ou non par un transitoire.
4. La valeur de la variable finale participe à la sérialisation, et final transient modifie la variable en même temps. Final n'affectera pas le transitoire et ne participera pas non plus à la sérialisation.

Le deuxième point. Remarque : les variables locales ne peuvent pas être modifiées par le mot-clé transient. Si la variable est une variable de classe définie par l'utilisateur, la classe doit implémenter l'interface Serialisable

Le troisième point à noter est : la valeur de la variable statique dans la classe après désérialisation est en fait la variable statique correspondante dans la JVM actuelle La valeur se trouve dans la JVM et n'est pas dérivée de la désérialisation.

Conclusion : Modifié par le mot-clé transient, il ne sera pas sérialisé. L'avantage est qu'il permet d'économiser de l'espace de stockage. Optimisez le programme ! Il s'ensuit que les champs modifiés par transitoire seront recalculés et initialisés !

Cet article provient du site Web php chinois, colonne tutoriel Java, bienvenue pour apprendre !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer