当前位置: 首页 > news >正文

Java基础-IO流

目录

一、IO流的概念

二、字节操作(字节流)

1.InputStream输入流(读)

1.1 方法

1.2 实现类

1.2.1 FileInputStream类

1.2.2 BufferedInputStream类

1.2.3 ObjectInputStream类

2.OutputStream输出流(写)

2.1 方法

2.2 实现类

2.2.1 FileOutStream类

2.2.2 BufferedOutStream类

2.2.3 ObjectOutputStream类

三、字符操作(字符流)

1. Writer输出流(写)

1.1 方法

1.2 实现类

1.2.1 FileWriter类

1.2.2 BufferedWriter类

2. Reader输入流(读)

2.1 方法

2.2 实现类

2.2.1 FileReader类

2.2.2 BufferedReader类

四、读取classpath资源文件

1.Properties类简介

五、Serializable序列化

1.概述

2.序列化

3.反序列化

4.安全性

5.小结


一、IO流的概念

I表示Input,把硬件文件的数据读入到内存的过程,称为输入,负责读。

O表示output,把内存中的数据写出到硬盘文件的过程,称之输出,负责写。

二、字节操作(字节流)

1.InputStream输入流(读)

InputStream就是Java标准库提供的最基本的输入流。它位于java.io这个包里。java.io包提供了所有同步IO的功能。要特别注意的一点是,InputStream并不是一个接口,而是一个抽象类,它是所有输入流的超类。

1.1 方法

(1)int read():读取输入流的下一个字节。/读取一个8位的字节,把它转换为0~255之间的整数,并返回这个整数。/读到末尾,返回-1,遇到-1代表文件中的数据已经被读取完毕。

(2)int read(byte b[]):每次读取N个字节,存入byte[]字节数组中。/int的返回值是读取字节的长度。/读到末尾,返回-1。

(3)public void close():关闭此输入流,并释放与此流相关联的任何系统。

原因:在IO流进行操作时,当前IO流占用到一定的内存,由于系统资源宝贵,因此在进行IO操作结束后,调用close方法关闭流,从而释放资源。

public class Demo02 {public static void main(String[] args) throws IOException {try (InputStream is=new FileInputStream("E:\\YuanJiuYuan\\a.txt");){//1.int read()一个字节一个字节读取,赋值给返回值,直至末尾返回-1
//        int data1=is.read();
//        System.out.println(data1);
//        int data2=is.read();
//        System.out.println(data2);
//        int data3=is.read();
//        System.out.println(data3);
//        int data4=is.read();
//        System.out.println(data4);//循环读取
//        int data;
//        while ((data=is.read())!=-1){
//            System.out.println((char)data);
//        }//2.int read(byte b[]) 每次读取N个字节,把字节放到字节数组中,读取到末尾返回值-1byte[] bytes=new byte[10];//存放每次读取到的字节信息int len;//每次读取的长度//读取操作while((len=is.read(bytes))!=-1){System.out.println("读取到的长度为:"+len+"读取到的内容:"+new String(bytes,0,len));}} catch (IOException e) {e.printStackTrace();}}
}

1.2 实现类

1.2.1 FileInputStream类

是基于磁盘中字节内容的输入流。

构造方法:

(1)FileInputStream(File file); 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。

(2)FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

public class Demo01 {public static void main(String[] args) throws FileNotFoundException {//注意路径:一定是一个文件路径,路径一定要存在,如果不存在则出异常//public FileInputStream(String name)InputStream is1=new FileInputStream("E:\\YuanJiuYuan\\a.txt");System.out.println(is1);//FileInputStream(File file)InputStream is2=new FileInputStream(new File("E:\\YuanJiuYuan\\a.txt"));System.out.println(is2);}
}
1.2.2 BufferedInputStream类

BufferedInputStream是缓冲输入流。它继承于FilterInputStream。BufferedInputStream的作用是为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持“mark()标记”和“reset()重置方法”。

BufferedInputStream本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。

作用:没有缓冲区,就好像逛超市没有推车,一次只拿一样东西去结账,缓冲区可以暂时放一堆数据直到满为止。

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class Demo03 {public static void main(String[] args) {//缓冲字节输入流依然是字节对象,缓冲流的底层帮助我们维护了一个数组-8192个字节//缓冲流不具备读写功能,真正读写的还是FileInputStreamtry(InputStream is=new FileInputStream("E:\\YuanJiuYuan\\a.txt");BufferedInputStream bis=new BufferedInputStream(is)){byte[] bytes=new byte[10];int len;while ((len=bis.read(bytes))!=-1){System.out.println(new String(bytes,0,len));}}catch (IOException e){}}
}
1.2.3 ObjectInputStream类

是对象输入流,进行反序列化操作。

2.OutputStream输出流(写)

2.1 方法

(1)write(int b):写入一个字节。

(2)write(byte b[]):把这个b字节数组中的所有数据写到关联的设备中(设备包括文件、网络或者其他任何地方)。

(3) write(byte b[],int off,int len):把数组b字节中的数据从下标off位置开始往出写,共计写len个元素。

(4)public void close():关闭从输出流并释放与此流相关联的任何系统资源。

close方法,当完成流的操作时,必须调用此方法,释放系统资源。

public class Demo05 {public static void main(String[] args) {try {OutputStream os=new FileOutputStream("E:\\YuanJiuYuan\\IoTest\\a.txt");//1.write(int data)写入一个字节os.write(97);os.write(98);os.write(99);//2.write(byte b[])把这个b字节数组中的所有数据写到关联的设备中(设备包括文件、网络或者其他任何地方)。os.write("庆祝反法西斯胜利80周年".getBytes());//3.write(byte b[],int offset,int len)从指定下标开始,写出指定个字节信息os.write("ajsiwhdewbdeu".getBytes(),1,5);System.out.println("end");} catch (IOException e) {e.printStackTrace();}}
}

2.2 实现类

2.2.1 FileOutStream类

是基于磁盘中字节内容的输出流。

public class Demo04 {public static void main(String[] args) throws FileNotFoundException {//1.字节输出流路径一定要是一个文件路径//2.输出流文件对象可以不存在,但是父路径一定要存在//FileOutputStream(String name)如果路径中有内容,会将原路径中的内容给覆盖掉OutputStream os=new FileOutputStream("E:\\YuanJiuYuan\\IoTest\\a.txt");System.out.println(os);//FileOutputStream(File file)OutputStream os1=new FileOutputStream(new File("E:\\YuanJiuYuan\\IoTest\\a.txt"));System.out.println(os1);}
}
public class Demo06 {public static void main(String[] args) throws IOException {//使用追加写的方式进行字节流的输出,不会对原文件信息进行覆盖OutputStream os=new FileOutputStream("E:\\YuanJiuYuan\\IoTest\\a.txt",true);os.write("字节输出流的学习".getBytes());//使用追加写的方式进行字节流的输出,不会对原文件信息进行覆盖OutputStream os1=new FileOutputStream(new File("E:\\YuanJiuYuan\\IoTest\\a.txt"),true);os.write("你好中国".getBytes());}
}
2.2.2 BufferedOutStream类

是带有缓冲区的输入流。

public class Demo07 {public static void main(String[] args) throws IOException {//使用追加写的方式进行字节流的输出,不会对原文件信息进行覆盖try (OutputStream os = new FileOutputStream("D:\\IOTest\\a.txt", true);BufferedOutputStream bos = new BufferedOutputStream(os);){//window换行写:\r\n//linux换行:\r//mac换行:\nbos.write('a');//写出一个字节信息bos.write("\r\n".getBytes());//写出一个换行信息bos.write("我本将心向明月\r\n".getBytes());//写出一个字节数组信息//获取系统换行符String str = System.lineSeparator();for (int i = 0; i < 5; i++) {String s1 = UUID.randomUUID().toString().substring(0, 8);bos.write((s1 + str).getBytes());}//bos.flush();//刷新缓冲流//bos.close(); 关闭流bos.write("站着还能睡着吗".getBytes());} catch (IOException e) {e.printStackTrace();}}
}
2.2.3 ObjectOutputStream类

对象输出流,进行序列化操作。

操作的最小单元:包含音频视频图片等。

三、字符操作(字符流)

1. Writer输出流(写)

1.1 方法

(1)void write(int data);//写出一个字符的数据。

(2)void write(char[] array);

(3)void write(char[] array,int offset,int len);

(4)void write(String str);

(5)void write(String str,int offset,int len);

(6)void flush();刷新输出流并强制任何缓冲数据的字符被输出到目标位置。

(7)void close();调用close方法的时候,底层会把缓冲区中的数据写出到目标位置。

import java.io.IOException;
import java.io.Writer;public class Demo02 {public static void main(String[] args) {try {Writer w1=new FileWriter("E:\\YuanJiuYuan\\IoTest\\a.txt");//1.w1.write('')写出一个字符信息w1.write('你');w1.write('好');w1.write('a');w1.write('!');String separator=System.lineSeparator();w1.write(separator);//写出换行符//2.write(char cbuf[])写出字符数组w1.write("我本将心向明月".toCharArray());w1.write(separator);//3.write(char cbuf[], int off, int len) 写出字符数组中指定的下标的元素w1.write("奈何明月照沟渠".toCharArray(),4,3);w1.write(separator);//4.write(String str)写出字符串w1.write("白日依山尽");w1.write(separator);//5.write(String str, int off, int len)写出字符串中指定范围的字符w1.write("黄河入海流",0,4);w1.write(separator);w1.flush();System.out.println("写出结束");} catch (IOException e) {e.printStackTrace();}}
}

1.2 实现类

1.2.1 FileWriter类

构造方法:

(1)public FileWriter(String path);

(2)public FileWriter(File path);

(3)public FileWriter(String path,boolean append);

(4)public FileWriter(File path,boolean append);

//调用下面构造方法的时候,底层会创建一个长度是 8192的byte数组(缓冲区) //写出的字符要先转换成byte数组,存入缓冲区中 //当缓冲区 存满 或者 显示的调用flush方法,会把缓冲区中的数据写出到目标。

public class Demo01 {public static void main(String[] args) throws IOException {//FileWriter(String fileName) 覆盖写Writer w1=new FileWriter("E:\\YuanJiuYuan\\IoTest\\a.txt");System.out.println(w1);//FileWriter(File file) 覆盖写Writer w2=new FileWriter(new File("E:\\YuanJiuYuan\\IoTest\\b.txt"));System.out.println(w1);//FileWriter(String fileName) 追加写Writer w3=new FileWriter("E:\\YuanJiuYuan\\IoTest\\c.txt",true);System.out.println(w3);//FileWriter(File file) 追加写Writer w4=new FileWriter(new File("E:\\YuanJiuYuan\\IoTest\\d.txt"),true);System.out.println(w4);}
}
1.2.2 BufferedWriter类

方法:

(1)void write(String str) 写入一整行。

(2)void newLine( ) 创建新行,输出换行符(根据操作系统不同,输出不同的换行符,windows是\r\n,Linux和Mac OS是\n)。

public class Demo03 {public static void main(String[] args) {try (BufferedWriter bw=new BufferedWriter(new FileWriter("E:\\YuanJiuYuan\\IoTest\\d.txt"))){bw.write("你好");bw.newLine();//换行操作bw.write("白日依山尽",0,3);bw.newLine();bw.flush();//刷新bw.close();//关闭,用了try-with-resource,所以不用关了} catch (IOException e) {e.printStackTrace();}}
}
public class Demo04 {public static void main(String[] args) throws IOException {//缓冲区大小验证FileWriter f1=new FileWriter("E:\\YuanJiuYuan\\IoTest\\c.txt",true);for (int i = 0; i <8192 ; i++) {f1.write('a');}f1.write('b');System.out.println("写出结束");}
}

2. Reader输入流(读)

2.1 方法

(1)int read()读取一个字符的数据,如果没有数据则返回-1。

(2)int read(char buf[]) 读取数据存入char数组中,返回读取的长度,如果没有数据则返回-1。

(3)void close(); 关闭此流并释放与此流相关联的任何系统资源。

public class Demo06 {public static void main(String[] args) {try (Reader r1=new FileReader("E:\\YuanJiuYuan\\IoTest\\a.txt");){//读取单个字符,返回字符对应码表值,当为-1读取结束
//            int data=r1.read();
//            System.out.println((char)data);//循环读取
//            int data;
//            while ((data=r1.read())!=-1){
//                System.out.println((char)data);
//            }//int read(char buf[]) 读取数据存入char数组中,如果没有数据则返回-1char[] chars=new char[10];int len;while ((len=r1.read(chars))!=-1){System.out.print(new String(chars,0,len));}} catch (IOException e) {e.printStackTrace();}}
}

2.2 实现类

2.2.1 FileReader类

构造方法:

(1)public FileReader(String path);

(2)public FileReader(File path);

public class Demo05 {public static void main(String[] args) throws FileNotFoundException {//创建字符输入流//FileReader(String fileName)Reader r1=new FileReader("E:\\YuanJiuYuan\\IoTest\\a.txt");System.out.println(r1);//FileReader(File file)Reader r2=new FileReader(new File("E:\\YuanJiuYuan\\IoTest\\a.txt"));System.out.println(r2);}
}
2.2.2 BufferedReader类

方法:

String readLine():读取一整行。/读取至末尾,返回null。

public class Demo08 {public static void main(String[] args) {try (BufferedReader br=new BufferedReader(new FileReader("E:\\YuanJiuYuan\\IoTest\\a.txt"))){//按行读取内容,当读取到末尾时返回值为nullString str;while ((str=br.readLine())!=null){System.out.println(str);}} catch (IOException e) {e.printStackTrace();}}
}

操作的最小单元:txt文本文件或者java文件。

四、读取classpath资源文件

很多Java程序或框架启动的时候,都需要读取配置文件。

1.Properties类简介

Properties 是 Hashtable 的子类,专门用于处理属性文件(以 .properties 为后缀的文件,不过这里是直接在代码里操作),它的键和值都是 String 类型,常用于存储配置信息。

import java.util.Properties;
import java.util.Set;public class Demo01 {public static void main(String[] args) {Properties p1=new Properties();//setProperty(String key, String value)给对象赋值p1.setProperty("姓名","张三");p1.setProperty("年龄","18");System.out.println(p1);//getProperty(String key)获取键对应的值String str=p1.getProperty("姓名");System.out.println(str);//getProperty(String key, String defaultValue)获取键对应的值,若不存在,返回默认值String str1=p1.getProperty("姓名1","不知道");System.out.println(str1);//Set<String> stringPropertyNames()获取所有的键Set<String> set=p1.stringPropertyNames();System.out.println(set);}
}

代码分析:

创建一个 Properties 对象 p1,用于存储键值对。

使用 setProperty 方法向 Properties 对象 p1 中添加键值对,这里添加了 “姓名 - 张三” 和 “年龄 - 18” 两组键值对。setProperty 方法内部其实是调用了 Hashtable 的 put 方法,不过对参数做了 String 类型的限制 。

直接打印 Properties 对象,会按照类似 {键1=值1, 键2=值2} 的格式输出,这里会输出 {姓名=张三, 年龄=18} 。

获取指定键的值:使用 getProperty 方法根据键 “姓名” 获取对应的值,会输出 张三 。

获取指定键的值(带默认值):使用 getProperty 方法获取键 “姓名 1” 对应的值,因为 p1 中不存在该键,所以返回指定的默认值 “不知道” 。

获取所有键的集合:通过 stringPropertyNames 方法获取 p1 中所有键的集合,这里会输出 [姓名, 年龄] ,返回的 Set 集合可以用于遍历等操作,方便获取所有配置项的键 。

public class Demo02 {public static void main(String[] args) throws IOException {Properties p1=new Properties();p1.setProperty("姓名","张三");p1.setProperty("年龄","18");p1.setProperty("性别","男");Writer w1=new FileWriter("b.txt");//将输出流和p1对象结合,进行p1对象的内容输出p1.store(w1,"helloworld");w1.close();}
}

这段代码的功能是将 Properties 对象中的键值对数据写入到 b.txt 文件中,以下是详细解析:

核心逻辑

        创建 Properties 对象并添加数据
        通过 setProperty 方法向 Properties 对象 p1 中添加了三组键值对(姓名 = 张三、年龄 = 18、性别 = 男)。

        将数据写入文件

        使用 FileWriter 创建一个指向 b.txt 的字符输出流 w1

        调用 p1.store(w1, "helloworld") 方法,将 Properties 中的键值对通过输出流写入文件:

        第二个参数 "helloworld":注释信息,会被写入文件的第一行(以 # 开头)。

        第一个参数 w1:用于写入数据的输出流。

输出文件b.txt的内容示例:

#helloworld
#Mon Jul 28 15:30:00 CST 2025
性别=男
姓名=张三
年龄=18
  • 第一行是注释信息 #helloworld
  • 第二行是自动生成的时间戳(格式为 #星期 月 日 时:分:秒 时区 年)。
  • 后续行是 Properties 中的键值对,顺序可能与添加顺序不一致(因为 Properties 底层是哈希表实现)。

在 Java 的 Properties 类中,store() 方法用于将 Properties 对象中存储的键值对数据写入到输出流(如文件、网络流等),通常用于将配置信息持久化到文件中(如 .properties 文件)。

store() 方法的核心作用

        (1)持久化配置:将内存中 Properties 对象的键值对(如程序中的配置参数)写入到外部存储(如本地文件),便于下次程序启动时读取复用。

        (2)格式规范:写入的数据会遵循 .properties 文件的格式(键值对用 = 或 : 分隔,注释用 # 开头),并自动添加时间戳注释。

关键特点

        (1)写入文件时,会自动在第一行添加注释(若指定),第二行添加当前时间戳(格式如 #Mon Jul 28 16:00:00 CST 2025)。

        (2)键值对的顺序不保证与添加顺序一致(因 Properties 底层基于哈希表)。

        (3)支持中文,但通过 store(OutputStream, ...) 写入时会自动转为 Unicode 编码(如 张三 转为 \u5F20\u4E09),而 store(Writer, ...) 则依赖 Writer 的编码。

public class Demo03 {public static void main(String[] args) throws IOException {Properties p1=new Properties();Reader r1=new FileReader("b.txt");//将输入流获取到的所有信息传递给p1对象p1.load(r1);Set<String> set=p1.stringPropertyNames();for (String s1:set) {System.out.println(s1+"__"+p1.getProperty(s1));}}
}

这段代码的功能是从 b.txt 文件中读取键值对数据,并通过 Properties 对象进行解析和打印。以下是详细解析:

核心逻辑

        (1)创建 Properties 对象Properties p1 = new Properties() 用于存储从文件中读取的键值对。

        (2)读取文件内容
通过 FileReader 创建字符输入流 r1,关联到 b.txt 文件,然后调用 p1.load(r1) 将文件中的数据加载到 Properties 对象中。

        load() 方法会自动解析文件中的键值对(遵循 .properties 格式,如 key=value),忽略注释行(# 开头)和空行。

        (3)遍历并打印数据
通过 p1.stringPropertyNames() 获取所有键的集合,然后遍历集合,通过 getProperty(s1) 获取对应的值并打印。

执行代码后,输出结果会是:

性别__男
姓名__张三
年龄__18

关键说明

        文件路径new FileReader("b.txt") 表示读取项目根目录下的 b.txt,若文件不存在会抛出 FileNotFoundException

  load() 方法特性

                自动忽略注释行(# 或 ! 开头)和空行。

                支持 key=valuekey:value 或 key value(空格分隔)三种键值对格式。

                若文件中有中文,FileReader 默认使用平台编码,可能导致乱码,建议指定编码。

这段代码是 Properties 类 “读文件” 功能的典型应用,与之前的 store() 方法(写文件)形成对应,共同实现了配置信息的持久化存储与读取。

public class Demo04 {public static void main(String[] args) throws IOException {//Class对象的getResourceAsStream()可以从classpath中读取指定资源InputStream is=Demo04.class.getResourceAsStream("/jdbc.properties");Properties p1=new Properties();p1.load(is);//输入流和p1结合//打印键值对for (String key:p1.stringPropertyNames()) {System.out.println(key+"__"+p1.getProperty(key));}is.close();}
}

这段代码的功能是从类路径(classpath)中读取 jdbc.properties 配置文件,并通过 Properties 类解析和打印其中的键值对,以下是详细说明:

核心逻辑解析

    读取类路径资源
通过 Demo04.class.getResourceAsStream("/jdbc.properties") 从类路径根目录获取 jdbc.properties 文件的输入流:

                路径中的 / 表示类路径的根目录(通常对应项目的 src/main/resources 目录,编译后会被打包到 classes 目录)。

                若文件存在,返回 InputStream 对象;若不存在,返回 null(这也是之前可能出现空指针异常的原因)。

      加载配置到 Properties
p1.load(is) 方法将输入流中的配置内容(键值对)加载到 Properties 对象中,自动解析 .properties 格式的内容(忽略注释和空行)。

      遍历并打印配置
通过 p1.stringPropertyNames() 获取所有键的集合,遍历后使用 getProperty(key) 获取对应值并打印,实现配置信息的读取展示。

      关闭资源
手动调用 is.close() 关闭输入流,释放系统资源。

注意事项

        文件位置:确保 jdbc.properties 确实在类路径根目录(如 src/main/resources),且文件名大小写正确(区分大小写)。

        路径写法

                带 /(如 /jdbc.properties):从类路径根目录查找。

                不带 /:从当前类(Demo04)所在的包路径下查找。

        资源关闭优化:推荐使用 try-with-resources 自动关闭流,避免遗漏关闭导致的资源泄漏。

        异常处理:若文件不存在,is 会为 null,调用 p1.load(is) 会抛出 NullPointerException,可添加判空逻辑提前处理。

五、Serializable序列化

1.概述

序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。

为什么要把Java对象序列化呢?因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络远程传输。这样,就相当于把Java对象存储到文件或者通过网络传输出去了。有序列化,就有反序列化,即把一个二进制内容(也就是byte[]数组)变回Java对象。有了反序列化,保存到文件中的byte[]数组又可以“变回”Java对象,或者从网络上读取byte[]并把它“变回”Java对象。

 一个Java对象要能序列化,必须实现一个特殊的java.io.Serializable接口,它的定义如下:

public interface Serializable {}

Serializable接口没有定义任何方法,它是一个空接口。我们把这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。

2.序列化

把一个Java对象变为byte[]数组,需要使用ObjectOutputStream。它负责把一个Java对象写入一个字节流。ObjectOutputStream既可以写入基本类型,如intboolean,也可以写入String(以UTF-8编码),还可以写入实现了Serializable接口的Object。因为写入Object时需要大量的类型信息,所以写入的内容很大。

import java.io.*;
import java.util.Arrays;public class Main {public static void main(String[] args) throws IOException {ByteArrayOutputStream buffer = new ByteArrayOutputStream();try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {// 写入int:output.writeInt(12345);// 写入String:output.writeUTF("Hello");// 写入Object:output.writeObject(Double.valueOf(123.456));}System.out.println(Arrays.toString(buffer.toByteArray()));}
}

3.反序列化

ObjectOutputStream相反,ObjectInputStream负责从一个字节流读取Java对象:

try (ObjectInputStream input = new ObjectInputStream(...)) {int n = input.readInt();String s = input.readUTF();Double d = (Double) input.readObject();
}

除了能读取基本类型String类型外,调用readObject()可以直接返回一个Object对象。要把它变成一个特定类型,必须强制转型。

readObject()可能抛出的异常有:

    • ClassNotFoundException:没有找到对应的Class
    • InvalidClassExceptionClass不匹配。

对于ClassNotFoundException,这种情况常见于一台电脑上的Java程序把一个Java对象。例如:Person对象序列化以后,通过网络传给另一台电脑上的另一个Java程序,但是这台电脑的Java程序并没有定义Person类,所以无法反序列化。

对于InvalidClassException,这种情况常见于序列化的Person对象定义了一个int类型的age字段,但是反序列化时,Person类定义的age字段被改成了long类型,所以导致class不兼容。

为了避免这种class定义变动导致的不兼容,Java的序列化允许class定义一个特殊的serialVersionUID静态变量,用于标识Java类的序列化“版本”,通常可以由IDE自动生成。如果增加或修改了字段,可以改变serialVersionUID的值,这样就能自动阻止不匹配的class版本:

public class Person implements Serializable {private static final long serialVersionUID = 2709425275741743919L;
}

要特别注意反序列化的几个重要特点:反序列化时,由JVM直接构造出Java对象,不调用构造方法,构造方法内部的代码,在反序列化时根本不可能执行。

4.安全性

因为Java的序列化机制可以导致一个实例能直接从byte[]数组创建,而不经过构造方法,因此,它存在一定的安全隐患。一个精心构造的byte[]数组被反序列化后可以执行特定的Java代码,从而导致严重的安全漏洞。

实际上,Java本身提供的基于对象的序列化和反序列化机制既存在安全性问题,也存在兼容性问题。更好的序列化方法是通过JSON这样的通用数据结构来实现,只输出基本类型(包括String)的内容,而不存储任何与代码相关的信息。

5.小结

(1)可序列化的Java对象必须实现java.io.Serializable接口,类似Serializable这样的空接口被称为“标记接口”(Marker Interface);

(2)反序列化时不调用构造方法,可设置serialVersionUID作为版本号(非必需);

(3)Java的序列化机制仅适用于Java,如果需要与其它语言交换数据,必须使用通用的序列化方法,例如JSON

http://www.dtcms.com/a/303525.html

相关文章:

  • Python day27
  • GoLand 项目从 0 到 1:第三天 —— 图数据库版本管理方案调研与中间件部署
  • 064_不可变集合与同步集合
  • python列表与元组--python005
  • 《中小学音乐教育》是什么级别的期刊?是正规期刊吗?能评职称吗?
  • c++: 尾置返回类型(Trailing Return Type)
  • 深度解析Manus:从多智能体架构到通用AI Agent的技术革命
  • Unity教程(二十五)技能系统 掷剑技能(下)冻结时间实现
  • PostgreSQL 详解
  • java每日精进 7.28【流程设计6.0(泳池和泳道)】
  • V-Ray 7.00.08 for 3ds Max 2021-2026 安装与配置教程(含语言补丁)
  • HTML5 `<figure>` 标签:提升网页语义化与可访问性的利器
  • 【2025/07/28】GitHub 今日热门项目
  • Solidity基础(教程①-简单数字存储)
  • 第二十一章:AI的“视觉压缩引擎”与“想象力温床”
  • AIBOX硬件设计概述
  • 什么是 LoRA 学习笔记
  • 项目执行标准流程是什么样的,如何制定
  • Java 接口入门学习笔记:从概念到简单实践
  • ts学习3
  • Microsoft 365中的Compromised User Detection功能深度解析:智能识别与防护用户账户安全的利器
  • 极速保鲜+ERP数字化,深圳“荔枝出海”驶入外贸订单管理快车道
  • 2023.2.2版IDEA安装教程(ideaIU-2023.2.2.win.exe详细步骤)Windows电脑一键安装指南
  • 二层环路与三层环路:原理、区别与解决方案全解析
  • MacBook IOS操作系统格式化U盘FAT32
  • 铜金矿数据分组优化系统设计与实现
  • 前端基础之《Vue(25)—Vue3简介》
  • Go 原理之 GMP 并发调度模型
  • it is not annotated with @ClientEndpoint“
  • 【学习路线】Android开发2025:从入门到高级架构师