ホームページ  >  記事  >  Java  >  Java の transient キーワードについて詳しく学ぶ

Java の transient キーワードについて詳しく学ぶ

青灯夜游
青灯夜游転載
2019-11-27 16:28:042104ブラウズ

Java の transient キーワードについて詳しく学ぶ

transient キーワードは非常に馴染みがなく、あまり使用されていないかもしれませんが、transient キーワードは Java で重要な役割を果たします。状態!

javatransientこのキーワードが珍しい理由は、実際にはその機能と切り離せません。transientキーワードの主な機能は、いくつかの機能を作成することです。 transient キーワードによって変更されたメンバー属性変数はシリアル化されません。実際、まさにこの理由から、シリアル化操作は学習プロセス (通常は実際の開発) で使用されることはほとんどありません。連載に関して、初心者の方は戸惑ったり、具体的な概念がなかったりする方も多いと思いますので、本記事では以下で紹介させていただきます。

#1. シリアル化とは何ですか?

シリアル化と言えば、それに付随するもう 1 つの概念が逆シリアル化です。パニックにならないでください、小さな白い子供用の靴。シリアル化を思い出すことは、逆シリアル化を思い出すことと同じです。なぜなら、逆シリアル化はその逆だからです。したがって、ブロガーは、混乱を避けるために、シリアル化の概念だけを覚えておくことをお勧めします。

専門用語によるシリアル化の定義:

Java は、オブジェクトのシリアル化のメカニズムを提供します。オブジェクトは、オブジェクトのデータ、オブジェクトのタイプ、オブジェクトに格納されている属性などの情報を含むバイト シーケンスで表すことができます。バイト シーケンスがファイルに書き込まれた後は、ファイル内のオブジェクトの情報を永続化することと同じになります。逆に、バイト シーケンスをファイルから読み戻し、オブジェクトを再構築し、逆シリアル化することもできます。オブジェクトのデータ、オブジェクトのタイプ、およびオブジェクトに格納されているデータ情報はすべて、メモリ内にオブジェクトを作成するために使用できます。

Yichun の用語定義 シリアル化:

シリアル化: Byte——> オブジェクト

実は、私がまとめたのが上記の結論です。わからない場合は専門用語の定義を参照してください。理解できたら、私の言葉を思い出してください。覚えていない場合は、私を殴り殺してください(私は私のmを蹴っているだけです 彼は天才です)

図で理解する連載:


Java の transient キーワードについて詳しく学ぶえ?バイトって何なのか理解してないの?実は、IO フローに関する記事で連載を紹介しました。とても詳しいので、心配しないでください。記事名を見ればすぐにわかります。

最も生意気な、初心者でもわかる、歴史に残る総合的かつ詳細なIOフローチュートリアル!

2. なぜシリアル化するのか?

シリアル化の概念については前のセクションで説明しましたが、その概念を理解した後は、なぜシリアル化する必要があるのか​​を知る必要があります。

連載が必要な理由を話す前に、ブロガーとして栗を贈らせてください。

街で食べ物を買いに行くときと同じように、一般的な操作は家に帰って料理したいときに食器を取り出します。そして、この一連の操作はシリアル化とデシリアル化のようなものです。

Java におけるオブジェクトのシリアル化とは、オブジェクトをバイト シーケンス形式の表現に変換することを指します。これらのバイト シーケンスにはオブジェクトのデータと情報が含まれており、シリアル化されたオブジェクト

データベースまたはファイル に書き込まれ、通常 キャッシュ キャッシュ # を使用する場合、 ネットワーク送信 にも使用できます。 ## (メモリ スペースが不十分な場合、ローカル ストレージがハードディスクに保存される可能性があります) または rpc をリモート (ネットワーク送信) で呼び出す場合、多くの場合、エンティティ クラスを実装する必要がありますSerializable インターフェイス。その目的は、シリアル化可能にすることです。 #開発プロセスで

一時的な

キーワード修正クリを使用するには:

ユーザーがパスワードやその他の情報を持っている場合、セキュリティ上の理由から、ネットワーク操作中に送信されたくない場合は、この情報に対応する変数を transient キーワードを使用して追加できます。 言い換えれば、このフィールドのライフサイクルは呼び出し元のメモリ内にのみ存在し、永続化のためにディスクに書き込まれることはありません。 ##●開発プロセスでは

一時的なものは必要ありません
キーワード修正クリ:

1. クラス内のフィールド値は、ベースに基づいて推定できます。他の分野でも出てきます。

2. 特定のビジネス要件に応じて、どのフィールドをシリアル化したくないのか;


なぜシリアル化すべきではないのか考えたことがあるでしょうか?実際、それは主にストレージスペースを節約するためです。プログラムを最適化しましょう!

PS: 以前 HashMap のソース コードを見たとき、フィールドが transient で変更されていたことを思い出しました。それは理にかなっていると思います。本当にこれを変更する必要はありません modCount フィールドは意味がないためシリアル化されています modCount は主に HashMap が変更されたかどうかを判断するために使用されます (put および delete 操作中など、modCount は自動的に増加します)。この種の変数では、最初は任意の値にすることができますが、値はもちろん 0 (新規、逆シリアル化、またはクローンの場合は 0 になります) であり、その値を保持する必要はありません。

もちろん、シリアル化後の最終的な目標は、シリアル化を解除して元の Java オブジェクトに復元することです。そうでなければ、シリアル化後に他に何をするでしょうか?食料品を買うのと同じように、最後の部分をビニール袋。ビニール袋を取り出す前に帰宅する際の利便性と安全性を考慮して、シリアライズされたバイト シーケンスを Java オブジェクトに復元できます。このプロセスはデシリアライズです。

3. シリアル化と一時的な使用

1. シリアル化する必要があるオブジェクトのクラスシリアル化インターフェイスを実装する必要があります: Java.lang.Serializable インターフェイス (抽象メソッドのないフラグ インターフェイス) Java のほとんどのクラスは、次のようなこのインターフェイスを実装します: String,Integer クラスなど、このインターフェイスを実装していないクラスは、状態をシリアル化または逆シリアル化せず、NotSerializableException 例外をスローします。

2. 最下層は、現在のオブジェクトが Serializable のインスタンスである場合、シリアル化が許可され、Java オブジェクト instanceof Serializable が使用されると判断します。裁判官。

3, Java でオブジェクト ストリーム ObjectOutputStream を使用してシリアル化と ObjectInputStreamストリーム逆シリアル化を完了します

==ObjectOutputStream: writeObject() メソッドによるシリアル化操作==

==ObjectInputStream: readObject() メソッドによる逆シリアル化操作==

4. このクラスのすべてのプロパティはシリアル化可能である必要があります。シリアル化する必要のない属性がある場合は、その属性を一時的としてマークし、transient キーワードを使用して変更する必要があります。
Java の transient キーワードについて詳しく学ぶ
バイトであるため、ストリームのオペレーションが関与する必要があり、オブジェクト ストリームはシリアル化ストリーム ObjectOutputstream とも呼ばれます。さまざまな状況でシリアル化オペレーション コードを分析してみましょう。

ここで、宜春ブログを読んでいる読者に、特に小さな白い子供の靴の場合は、ノックしてみて、一目見て忘れずに持ち歩くか、コピーして実行してください。 、 私を信じて !きっと違う何かが得られるはずです。時間の無駄だと思わないでください。遅い方が速い場合もあります。イーチュンはそれを直接体験しました。

#3.1. Serializable インターフェイスはシリアル化用に実装されていません

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

実行結果

Java の transient キーワードについて詳しく学ぶ

##3.2. Serializable インターフェイスのシリアル化

Serializable インターフェイスを追加して再度実行すると、次の

userinfo.txt

ファイルの内容が次のように表示されることがわかります。

Java の transient キーワードについて詳しく学ぶ実際には、これは重要ではなく、重要なのはシリアル化操作が成功したということです。

3.3. 通常のシリアル化状況

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();
        }
    }
}
実行結果:

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

3.4. 一時的なシリアル化状況

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();
        }
    }
}
実行結果:

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

結果に特に注意して、一時的に変更された属性値をデフォルト値

null

に追加してください。 transient によって変更された属性が int 型の場合、シリアル化後の値は 0 でなければなりません。もちろん、試してみることもできます。これは何を意味しますか?これは、transient としてマークされた属性は、オブジェクトがシリアル化されるときに保存されない (または変数が永続化されない) ことを意味します。

3.5、静的シリアル化の状況

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();
        }
    }
}
実行結果:

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

このとき、静的変更もシリアル化されていると誤解されますが、実際はそうではありません。実際、混乱しやすいです。ここ!明らかに、

null

(デフォルト値) を削除すると、シリアル化されないことを示すことができます。ここでは明らかにデフォルト値に変更されていません。なぜ、static はシリアル化されないと言うのですか?シリーズ化?

実際には、逆シリアル化後のクラスの静的変数名の値は、実際には現在の JVM の対応する静的変数の値です。この値は JVM 内にあり、実際には存在しません。逆シリアル化、派生。 つまり、static によって変更された変数はシリアル化に参加しません。しかし、証拠がなければそれを言うことはできません。はい、2 つのプログラムを比較してみましょう。そうすれば理解できるでしょう。 最初のプログラム: 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 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修饰的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。

Java の transient キーワードについて詳しく学ぶ
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入门 栏目,欢迎学习!

以上がJava の transient キーワードについて詳しく学ぶの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。