>  기사  >  Java  >  Java의 임시 키워드에 대해 자세히 알아보기

Java의 임시 키워드에 대해 자세히 알아보기

青灯夜游
青灯夜游앞으로
2019-11-27 16:28:042104검색

Java의 임시 키워드에 대해 자세히 알아보기

transient 키워드가 낯설고 거의 사용하지 않으실 수도 있지만, transient 키워드는 자바에서 없어서는 안 될 역할을 합니다! transient这个关键字或许很陌生基本没怎么用过,但是transient关键字在java中却起到了不可或缺的地位!

在学习java的过程中transient关键字少见的原因其实离不开它的作用:transient关键字的主要作用就是让某些被transient关键字修饰的成员属性变量不被序列化。实际上也正是因此,在学习过程中很少用得上序列化操作,一般都是在实际开发中!至于序列化,相信有很多小白童鞋一直迷迷糊糊或者没有具体的概念,下面本篇文章就介绍一下。

1、何谓序列化?

说起序列化,随之而来的另一个概念就是反序列化,小白童鞋不要慌,记住了序列化就相当于记住了反序列化,因为反序列化就是序列化反过来,所以博主建议只记住序列化概念即可,省的搞晕自己。

专业术语定义的序列化:

Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。

宜春的术语定义序列化:

序列化: 字节 ——> 对象

其实,我总结的就是上面的结论,如果不理解,直接参照专业术语的定义,理解之后就记住我的话就行了,记不住,请打死我(我踢m简直就是个天才)

图理解序列化:
Java의 임시 키워드에 대해 자세히 알아보기
啥?你不懂啥是字节?其实,我在一篇IO流的文章里就已经介绍了序列化,放心,绝对特别详细~光看文章名字就知道了~

史上最骚最全最详细的IO流教程,小白都能看懂!

2、为何要序列化?

从上一节提到序列化的概念,知道概念之后,我们就必须要知道 为何要序列化了。

讲为何要序列化原因之前,博主我举个栗子:

就像你去街上买菜,一般操作都是用塑料袋给包装起来,直到回家要做菜的时候就把菜给拿出来。而这一系列操作就像极了序列化和反序列化!

Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象 可以被写到数据库或文件中,也可用于 网络传输,一般当我们使用 缓存cache(内存空间不够有可能会本地存储到硬盘)或 远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable

Java를 배우는 과정에서 transient 키워드가 드문 이유는 실제로 그 기능과 분리될 수 없습니다. transient 키워드의 주요 기능은

임시적이어야 합니다. 키워드로 수정된 멤버 속성 변수는 직렬화되지 않습니다

. 실제로 직렬화 작업이 학습 프로세스, 일반적으로 실제 개발에서 거의 사용되지 않는 것이 바로 이러한 이유입니다! 연재에 관해서는 많은 초보자분들이 헷갈리시거나 구체적인 개념이 없으실 거라 생각합니다. 이번 글에서는 아래에서 소개해드리겠습니다.

1. 직렬화란 무엇인가요?

직렬화와 함께 제공되는 또 다른 개념은 직렬화입니다. 당황하지 마세요. 직렬화를 기억하는 것은 직렬화를 기억하는 것과 같습니다. 반면에 블로거는 직렬화라는 개념만 기억할 것을 권장합니다. 혼란을 피하기 위해 직렬화합니다.

전문 용어로 정의된 직렬화:

Java는 객체 직렬화를 위한 메커니즘을 제공합니다. 객체는 객체의 데이터, 객체 유형, 객체에 저장된 속성과 같은 정보를 포함하는 바이트 시퀀스로 표현될 수 있습니다. 바이트 시퀀스가 ​​파일에 기록된 후 이는 파일에 개체 정보를 유지하는 것과 같습니다. 반대로, 파일에서 바이트 시퀀스를 다시 읽고 객체를 재구성하고 역직렬화할 수 있습니다. 객체의 데이터, 객체의 유형, 객체에 저장된 데이터 정보를 모두 사용하여 메모리에 객체를 생성할 수 있습니다.


Yichun의 용어 정의 Serialization:

Serialization: 🎜byte——> object 🎜🎜🎜🎜🎜사실 제가 요약한 내용이 이해가 안 되시면 그냥 위의 결론을 참고하세요. 전문 용어는 직접 이해하신 후 기억해 주세요. 기억나지 않으면 때려죽여 주세요. (나는 m차기 천재입니다.)🎜🎜그림 이해 연재: 🎜여기에 이미지 설명 삽입🎜뭐? 바이트가 무엇인지 이해하지 못하시나요? 사실, IO 흐름에 대한 기사에서 이미 직렬화를 소개했습니다. 기사 이름만 봐도 알 수 있을 것입니다. 🎜🎜가장 뻔뻔하고 포괄적입니다. 상세한 IO 흐름 튜토리얼은 초보자도 할 수 있습니다. 이해하세요! 🎜🎜🎜2. 왜 직렬화하나요? 🎜🎜🎜🎜직렬화의 개념은 이전 섹션에서 언급했습니다. 개념을 알고 나면 왜 직렬화를 해야 하는지 알아야 합니다. 🎜🎜연재가 필요한 이유에 대해 이야기하기 전에 블로거님, 예를 들어보겠습니다. 🎜🎜🎜 길거리에서 음식을 사러 갈 때처럼 집에 도착할 때까지 비닐봉지에 포장하는 것이 일반적인 작업입니다. 요리를 꺼내세요. 그리고 이 일련의 작업은 직렬화 및 역직렬화와 같습니다! 🎜🎜🎜Java에서 객체 직렬화는 객체를 바이트 시퀀스로 변환하는 것을 의미합니다. 이러한 바이트 시퀀스에는 객체의 데이터와 정보가 포함됩니다. 직렬화된 객체 🎜 🎜는 데이터베이스에 기록될 수도 있고 🎜🎜에 기록될 수도 있습니다. 🎜 🎜네트워크 전송🎜🎜에 사용됩니다. 일반적으로 🎜 🎜cache🎜🎜(메모리 공간이 부족하면 하드 디스크에 로컬로 저장될 수 있음) 또는 🎜 🎜원격으로 rpc 호출🎜🎜(네트워크 전송)을 사용할 때 필요합니다. 엔터티 클래스를 직렬화할 수 있도록 Serialize 인터페이스를 구현하도록 합니다. 🎜🎜● 개발 과정에서🎜 임시🎜 키워드 수정 예를 사용하세요: 🎜🎜🎜🎜 🎜사용자가 보안을 위해 일부 비밀번호 및 기타 정보를 가지고 있는 경우 네트워크 운영 중에 변수가 전송되는 것을 원하지 않습니다. 이 정보에 해당하는 임시 키워드를 추가할 수 있습니다. 🎜🎜즉, 이 필드의 수명 주기는 호출자의 메모리에만 존재하며 지속성을 위해 디스크에 기록되지 않습니다. 🎜🎜🎜● 개발 과정에서🎜transient🎜키워드 수정 밤을 사용할 필요가 없습니다.🎜🎜🎜1. 클래스의 필드 값은 다른 필드를 기반으로 파생될 수 있습니다. 🎜2. 특정 비즈니스 요구 사항에 따라 직렬화를 원하지 않는 필드는 무엇입니까? 🎜🎜🎜왜 직렬화하면 안 되는지 생각해 본 적이 있으신가요? 사실 주로 저장 공간을 절약하기 위한 것입니다. 프로그램을 최적화하세요! 🎜

PS: HashMap의 소스 코드를 볼 때 필드가 transient로 수정된 것을 발견한 것을 기억합니다. 정말 말도 안 되는 일입니다. 이 필드를 modCount해야 합니다. 의미가 없기 때문에 modCount는 주로 HashMap이 수정되었는지 확인하는 데 사용됩니다(예를 들어 넣기 및 제거 작업 중에 modCount가 자동으로 증가함). 변수의 경우 처음에는 어떤 값이라도 될 수 있습니다. 물론 0도 허용되며(새 변수이거나 역직렬화되거나 복제된 경우에는 0이 됨) 해당 값을 유지할 필요가 없습니다. HashMap源码的时候,发现有个字段是用transient修饰的,我觉得还是有道理的,确实没必要对这个modCount字段进行序列化,因为没有意义,modCount主要用于判断HashMap是否被修改(像put、remove操作的时候,modCount都会自增),对于这种变量,一开始可以为任何值,0当然也是可以(new出来、反序列化出来、或者克隆clone出来的时候都是为0的),没必要持久化其值。

当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象,要不然序列化后干嘛呢,就像买菜一样,用塑料袋包裹最后还是为了方便安全到家再去掉塑料袋,所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。

3、序列化与transient的使用

1、需要做序列化的对象的类,必须实现序列化接口:Java.lang.Serializable 接口(一个标志接口,没有任何抽象方法),Java 中大多数类都实现了该接口,比如:StringInteger类等,不实现此接口的类将不会使任何状态序列化或反序列化,会抛NotSerializableException异常 。

2、底层会判断,如果当前对象是 Serializable 的实例,才允许做序列化,Java对象 instanceof Serializable 来判断。

3、在 Java 中使用对象流ObjectOutputStream来完成序列化以及ObjectInputStream流反序列化   

==ObjectOutputStream:通过 writeObject()方法做序列化操作== 

==ObjectInputStream:通过 readObject() 方法做反序列化操作==

4、该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。
Java의 임시 키워드에 대해 자세히 알아보기
由于字节嘛所以肯定要涉及流的操作,也就是对象流也叫序列化流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의 임시 키워드에 대해 자세히 알아보기

3.2、实现Serializable接口序列化情况

当我们加上实现Serializable接口再运行会发现,项目中出现的userinfo.txt文件内容是这样的:

Java의 임시 키워드에 대해 자세히 알아보기

其实这都不是重点,重点是序列化操作成功了!

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、transient序列化情况

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'}

特别注意结果,添加transient修饰的属性值为默认值null!如果被transient修饰的属性为int类型,那它被序列化之后值一定是0,当然各位可以去试试,这能说明什么呢?说明被标记为transient的属性在对象被序列化的时候不会被保存(或者说变量不会持久化)

3.5、static序列化情况

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'}

这个时候,你就会错误的认为static修饰的也被序列化了,其实不然,实际上这里很容易被搞晕!明明取出null(默认值)就可以说明不会被序列化,这里明明没有变成默认值,为何还要说static

물론, 직렬화 후의 최종 목적은 직렬화를 해제하여 원래 Java 객체로 복원하는 것입니다. 그렇지 않으면 야채를 구입하는 것처럼 비닐봉지에 포장하는 것이 궁극적으로 편의를 위한 것입니다. 집에 안전하게 도착하면 비닐봉지를 제거하여 직렬화된 바이트 시퀀스를 Java 객체로 복원할 수 있습니다. 이 프로세스가 역직렬화입니다.

3. 직렬화 및 임시 사용

1.

직렬화해야 하는 객체 클래스는 직렬화 인터페이스인 Java.lang을 구현해야 합니다. .직렬화 가능 인터페이스

(추상 메소드가 없는 플래그 인터페이스), Java의 대부분의 클래스(예: String, Integer 클래스 등)는 이 인터페이스를 구현합니다. 클래스 없음 이 인터페이스를 구현하는 인터페이스는 어떤 상태도 직렬화하거나 역직렬화하지 않으며 NotSerializedException 예외를 발생시킵니다.

🎜2. 현재 객체가 Serialized의 인스턴스라면, Java 객체 instanceof Serialized가 허용되는지 판단합니다. 🎜🎜3. 🎜🎜Java에서 객체 스트림 ObjectOutputStream을 사용하여 직렬화를 완료하고 ObjectInputStream 스트림 역직렬화🎜🎜  🎜🎜==ObjectOutputStream: writeObject() 메서드를 통해 직렬화 작업 수행 == 🎜🎜==ObjectInputStream: readObject() 메서드를 통해 역직렬화 작업을 수행합니다==🎜🎜4. 이 클래스의 모든 속성은 직렬화 가능해야 합니다. 직렬화할 필요가 없는 속성이 있는 경우 속성을 임시로 표시하고 transient 키워드를 사용하여 수정해야 합니다.
여기에 이미지 설명을 삽입하세요
바이트의 경우 스트림 작업이 포함되어야 합니다. 즉, 개체 스트림은 직렬화 스트림 ObjectOutputstream이라고도 합니다. 다음은 직렬화 작업 코드에 대한 다중 사례 분석입니다. 🎜🎜🎜여기서 이춘 블로그를 읽는 독자들에게 정말 강력히 추천합니다. 타이핑을 해보고, 한눈에 가져가거나 복사해서 실행하는 것을 잊지 마세요. 특히 작은 흰색 어린이 신발의 경우 저를 믿으세요! 당신은 확실히 뭔가 다른 것을 얻을 것입니다. 시간낭비라고 생각하지 마세요, 때로는 느린 것이 더 빠른 것, 이춘이가 직접 경험해봤습니다! 🎜🎜🎜3.1 직렬화를 위해 직렬화 가능 인터페이스가 구현되지 않았습니다🎜🎜
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();
        }
    }
}
🎜실행 결과🎜🎜여기에 그림 설명 삽입🎜🎜🎜3.2. 직렬화 가능 인터페이스 구현의 직렬화 상황🎜🎜🎜직렬화 가능 인터페이스 구현을 추가하고 다시 실행하면 다음과 같은 결과가 나타납니다. 프로젝트에 나타나는 userinfo.txt 파일의 내용은 다음과 같습니다. 🎜🎜at 여기에 그림 설명 삽입🎜🎜사실 이게 요점이 아닙니다. 요점은 직렬화 작업이 성공했다는 것입니다! 🎜🎜🎜3.3. 일반적인 직렬화 상황 🎜🎜
name=程序员老过, psw=456
name=程序员老过, psw=null
🎜실행 결과: 🎜
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();
        }
    }
}
🎜🎜3.4. 일시적 직렬화 상황 🎜🎜
name=程序员老过, psw=456
name=程序员老改, psw=null
🎜실행 결과: 🎜rrreee🎜결과에 특히 주의하세요. 기본값null! Transient에 의해 수정된 속성이 int 유형인 경우 직렬화된 후 해당 값은 0이어야 합니다. 이것이 의미하는 바는 무엇입니까? 이는 개체가 직렬화될 때 transient로 표시된 속성이 저장되지 않음을 의미합니다(또는 변수가 지속되지 않음)🎜🎜🎜3.5, 정적 직렬화 상황🎜🎜rrreee🎜실행 결과: 🎜rrreee🎜 이때, 정적인 수정도 연재되었다고 착각하게 되실 겁니다. 사실, 여기서는 헷갈리기 쉽습니다. 분명히 null(기본값)을 빼면 직렬화되지 않을 것임을 의미할 수 있습니다. 여기서는 분명히 기본값으로 변경되지 않았는데 왜 여전히 static이라고 말하는 걸까요? 연재는 안되나요? 🎜🎜🎜 🎜사실 deserialization 후 클래스의 정적 변수 이름 값은 실제로 현재 JVM의 해당 정적 변수 값입니다. 이 값은 JVM에 있으며 deserialization에서 파생되지 않습니다. 🎜🎜즉, static으로 수정된 변수는 직렬화에 참여하지 않습니다! 하지만 증거 없이는 말할 수 없습니다. 두 프로그램을 비교해보면 이해가 될 것입니다. 🎜🎜첫 번째 프로그램: static으로 수정되지 않은 이름 속성 프로그램입니다: 🎜rrreee🎜실행 결과: 🎜rrreee🎜프로그램 실행 결과에서 알 수 있듯이 name 값을 이전 프로그램으로 변경해 보세요. deserialization 기존 멤버 개혁이 실패했습니다! 🎜

第二个程序:这是一个被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의 임시 키워드에 대해 자세히 알아보기
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의 임시 키워드에 대해 자세히 알아보기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 cnblogs.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제