Maison  >  Article  >  Java  >  En savoir plus sur le mot-clé transient en Java

En savoir plus sur le mot-clé transient en Java

青灯夜游
青灯夜游avant
2019-11-27 16:28:042104parcourir

En savoir plus sur le mot-clé transient en Java

Vous n'êtes peut-être pas familier avec le mot-clé transient et l'avez à peine utilisé, mais le mot-clé transient joue un rôle indispensable en java !

Dans le processus d'apprentissage de Java, la raison pour laquelle le mot-clé transient est rare est en réalité indissociable de sa fonction : la fonction principale du mot-clé transient est de s'assurer que soit utilisé par le mot-clé transitoire Les variables d'attribut de membre modifié ne sont pas sérialisées . 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 ont été confus ou n'ont pas de concept précis. Cet article le présentera ci-dessous.

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

En parlant de sérialisation, un autre concept qui l'accompagne est la désérialisation. Pas de panique, petits enfants blancs, 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 donc de simplement se souvenir du concept de sérialisation pour éviter de se tromper.

Sérialisation définie par des termes professionnels :

Java fournit un mécanisme de sérialisation d'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.

Définition du terme Yichun Sérialisation :

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 simplement à la définition des termes professionnels, rappelez-vous simplement mes paroles. Si vous ne vous en souvenez pas, s'il vous plaît, battez-moi à mort (. Je suis juste en train de botter le m C'est un génie)

Graphique comprenant la sérialisation :
En savoir plus sur le mot-clé transient en Java
Quoi ? Vous ne comprenez pas ce qu'est un octet ? En fait, j'ai déjà introduit la sérialisation dans un article sur le flux IO. Ne vous inquiétez pas, c'est absolument très détaillé. Il suffit de regarder le nom de l'article et vous le saurez

Le plus impertinent. ​​Tutoriel complet et détaillé sur les flux IO dans l'histoire. Même les novices peuvent le comprendre !

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, blogueur je vais vous donner un exemple :

Tout comme lorsque vous allez acheter de la nourriture dans la rue, le fonctionnement général est de je l'emballe dans des sacs en plastique. Quand je rentre à la maison et que je veux cuisiner, je sors la vaisselle. Et cette série d'opérations s'apparente à la sérialisation et à la désérialisation !

La sérialisation des objets en Java fait référence à la conversion d'objets en séquences d'octets contenant les données et les informations de l'objet peut être écrit dans une base de données ou. fichier , et peut également être utilisé pour la transmission réseau , généralement lorsque l'on utilise le cache (Espace mémoire insuffisant peut entraîner un stockage local sur le disque dur) ou Lors d'un appel à distance rpc (transmission réseau), nous avons souvent besoin que nos classes d'entité implémententSerializableInterface, le but est de rendez-le sérialisable.

Quantity Dans le processus de développement utilisez le mot-clé transitoire pour modifier la châtaigne :

Si un utilisateur a un mot de passe et d'autres informations , Pour des raisons de sécurité, si vous ne souhaitez pas être transmis lors des opérations réseau, les variables correspondant à ces informations peuvent être ajoutées avec le mot clé transient. 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 pour des raisons de persistance.

Quantity Pendant le processus de développement, ne nécessite pas de modifications transitoires de mots-clés :

1. Les valeurs de champ dans la classe peuvent être déduites. basé sur d'autres domaines sortent.
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 que lorsque j'ai regardé le code source de HashMap auparavant, j'ai découvert qu'un champ avait été modifié avec transient. Je pense que cela a du sens. Il n'est vraiment pas nécessaire de sérialiser ce champ modCount. , comme il n'y a pas de signification, modCount est principalement utilisé pour déterminer si le HashMap a été modifié (comme lors de l'insertion et de la suppression d'opérations, modCount augmentera tout seul). Pour ce type de variable, il peut s'agir de n'importe quelle valeur). début, et 0 est bien sûr également possible (un nouveau sort, se désérialise. Il est toujours 0 lorsqu'il est transformé ou cloné), 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 d'autre après la sérialisation ? Tout comme faire l'épicerie, envelopper la dernière partie dans un fichier. sac en plastique. C'est toujours pour la commodité et la sécurité de rentrer à la maison avant de retirer le sac en plastique, afin que la séquence d'octets sérialisée puisse être restaurée dans un objet Java. Ce processus est une désérialisation.

3. Utilisation de la sérialisation et du transitoire

1 La classe de l'objet qui doit être sérialisée doit. be Implémenter l'interface de sérialisation : Java.lang.Seriallessly interface (une interface de drapeau sans aucune méthode abstraite). La plupart des classes en Java implémentent cette interface, telles que : String, Integer classes, 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 NotSerializableException.

2. La couche inférieure jugera. Si l'objet actuel est une instance de Serializable, l'objet Java instanceof Serializable jugera.

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

==ObjectOutputStream : opération de sérialisation via writeObject() method== 

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

4. Tous les attributs de cette classe doivent être accessibles en série. S'il existe un attribut qui n'a pas besoin d'être sérialisable, l'attribut doit être marqué comme transitoire et modifié avec le mot-clé transient.
En savoir plus sur le mot-clé transient 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 le code de l'opération de sérialisation dans diverses situations !

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, en particulier pour les petites chaussures blanches pour enfants , crois-moi ! Vous gagnerez certainement quelque chose de différent. Ne pensez pas que c'est une perte de temps, parfois lent est plus rapide, Yichun l'a vécu par lui-même !

3.1. L'interface Serialisable 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écution des résultats

En savoir plus sur le mot-clé transient en Java

3.2. Situation de sérialisation de l'implémentation de l'interface Serialisable

Lorsque nous ajoutons l'implémentation de l'interface Serialisable et la réexécutons, nous constaterons que le contenu du fichier userinfo.txt qui apparaît dans le Le projet est comme ça :

En savoir plus sur le mot-clé transient en Java

En fait, ce 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ésultats 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 d'exécution :

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

Portez une attention particulière au résultat et ajoutez la valeur de l'attribut modifié transitoire à la valeur par défautnull ! 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 transient ne seront pas enregistrés lorsque l'objet est sérialisé (ou la variable ne sera pas conservée)

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ésultat 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, retirer null (valeur par défaut) peut indiquer qu'il ne sera pas sérialisé. Il n'est clairement pas modifié à la valeur par défaut ici, alors pourquoi dites-vous encore 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 ne l'est pas. désérialisé. C'est-à-dire que 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'attribut 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ésultat de l'exécution :

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

Comme le montre le résultat de l'exécution du programme , avant la désérialisation, j'ai essayé de modifier la valeur de name pour le programmeur, mais sans succès !

第二个程序:这是一个被static修饰的name属性程序:

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 = 996890129747019948L;
    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();
        }
    }
}

运行结果:

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

从程序运行结果中可以看出,在反序列化之前试着改变name的值为程序员老改,结果是成功的!现在对比一下两个程序是不是就很清晰了?

static关键字修饰的成员属性优于非静态成员属性加载到内存中,同时静态也优于对象进入到内存中,被static修饰的成员变量不能被序列化,序列化的都是对象,静态变量不是对象状态的一部分,因此它不参与序列化。所以将静态变量声明为transient变量是没有用处的。因此,反序列化后类中static型变量name的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。

如果对static关键字还是不太清楚理解的童鞋可以参考这篇文章,应该算是不错的:深入理解static关键字

3.6、final序列化情况

对于final关键字来讲,final变量将直接通过值参与序列化,至于代码程序我就不再贴出来了,大家可以试着用final修饰验证一下!

主要注意的是final 和transient可以同时修饰同一个变量,结果也是一样的,对transient没有影响,这里主要提一下,希望各位以后在开发中遇到这些情况不会满头雾水!

4、java类中serialVersionUID作用

既然提到了transient关键字就不得不提到序列化,既然提到了序列化,就不得不提到serialVersionUID了,它是啥呢?基本上有序列化就会存在这个serialVersionUID。

En savoir plus sur le mot-clé transient en Java
serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException,在开发中有时候可写可不写,建议最好还是写上比较好。

5、transient关键字小结

1、变量被transient修饰,变量将不会被序列化
2、transient关键字只能修饰变量,而不能修饰方法和类。
3、被static关键字修饰的变量不参与序列化,一个静态static变量不管是否被transient修饰,均不能被序列化。
4、final变量值参与序列化,final transient同时修饰变量,final不会影响transient,一样不会参与序列化

第二点需要注意的是:本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口

第三点需要注意的是:反序列化后类中static型变量的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。

结语:被transient关键字修饰导致不被序列化,其优点是可以节省存储空间。优化程序!随之而来的是会导致被transient修饰的字段会重新计算,初始化!

本文来自 java入门 栏目,欢迎学习!

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