一篇文章了解 Java IO 流

news2024/12/26 9:31:42

一:IO 流的概述

1. 什么是 IO 流?

存储和读取数据的解决方法

I:input O:output

流:像水流一样传输数据

2. IO 流的作用?

用于读写数据(本地文件,网络)

3. IO 流按照流向可以分类哪两种流?

输出流:程序 ----> 文件

输入流:文件 ----> 程序

4. IO 流按照操作文件的类型可以分为哪两种流?

字节流:可以操作所有类型的文件

字符流:只能操作纯文本文件

5. 什么是纯文本文件?

用 windows 系统自带的记事本打开并且能读得懂的文件 txt 文件、md 文件、xml 文件、lrc 文件等

6. IO 流基本体系

C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230507200646836.png

二:IO 流基本用法

1. 字节流

1.1 fileOutputStream

**作用:**可以把程序中的数据写到本地文件上,是字节流的基本流。

书写步骤

        // 1. 创建对象
        FileOutputStream fos = new FileOutputStream("hmio\\a.txt");
        // 2. 写入数据
        fos.write(88);
        // 3. 释放资源
        fos.close();

字节输出流书写细节

① 创建字节输出流对象

​ 细节一:参数是字符串表示的路径或者 File 对象都可以

​ 细节二:如果文件不存在会创建一个新文件,但是要保证父级路径是存在的

​ 细节三:如果文件已经存在,则会清空文件

② 写数据

​ 细节:write 方法的参数是整数,但实际上写到本地文件中的是整数在 ASCII 上对应的字符

③ 释放资源

​ 细节:每次使用完之后都要释放资源

​ 如果没有释放资源,那么这个资源 / 文件就会一直被占用着。无法进行删除等操作。

三种写出多个数据的方式

        // 1. 创建对象
        FileOutputStream fos = new FileOutputStream("hmio\\a.txt");
        // 2. 写出数据

//        fos.write(97);  // a
//        fos.write(98);  // b

        byte[] bytes = {97, 98, 99 ,100, 101};
        /*fos.write(bytes);*/

        fos.write(bytes, 1, 2);  // b c

        // 3. 释放资源
        fos.close();

换行与续写

		// 换行与续写
        // 1. 换行直接写个换行符就好了
        // 2. 续写在创建 FileOutputStream 的时候开启续写开关 true(默认为 false)

        // 1. 创建对象
        FileOutputStream fos = new FileOutputStream("hmio\\a.txt", true);
        // 2. 写出数据
        String str = "hello";
        byte[] bytes = str.getBytes();
        fos.write(bytes);

        // 再次写入一个换行符就好了
        // 换行符:windows:\r\n    Linux:\n    Mac:\r
        // Java 中只写 \r 或者 \n 也可以起到换行作用
        String wrap = "\r\n";
        byte[] bytes2 = wrap.getBytes();
        fos.write(bytes2);

        String str2 = "666";
        byte[] bytes3 = str2.getBytes();
        fos.write(bytes3);
        // 3. 释放资源
        fos.close();

1.2 fileInputStream

基本用法

        // 1. 创建对象
        FileInputStream fis = new FileInputStream("hmio\\a.txt");
        // 2. 读取资源

        int b1 = fis.read();
        System.out.println((char)b1);

        // 3. 释放资源
        fis.close();

FileInputStream 书写细节

① 创建字节输入流对象

​ 细节一:如果文件不存在,就直接报错

② 读取数据

​ 细节二:一次读一个字节。读出来的是数据在 ASCII 上对应的数字

​ 细节二:读到文件末尾了,read 方法返回 -1。

③ 释放资源

​ 细节一:每次使用完必须要释放资源。

循环读取整个文件

        // 1. 创建对象
        FileInputStream fis = new FileInputStream("hmio\\a.txt");
        // 2. 读取资源
        int b;
        while ((b = fis.read()) != -1) {
            System.out.println((char) b);
        }
        // 3. 释放资源
        fis.close();

3. 文件拷贝

        // 1. 创建对象
        FileInputStream fis = new FileInputStream("D:\\a.txt");
        FileOutputStream fos = new FileOutputStream("hmio\\copy.txt");
        // 2. 拷贝
        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b);
        }
        // 3. 释放资源
        // 规则:先开的流最后关闭
        fos.close();
        fis.close();

这只适用于小文件的拷贝,如果是大文件的拷贝会相当耗时,因为这个拷贝的方式是每拷贝一个字节建立一次连接,拷贝的文件越大,建立的连接数量越多。

我们可以使用 read(byte[] buffer) 的方法来读取数据,这是一次读取一个字节组的数据,这样就可以做到只需要建立一次连接就可以拷贝一批量的字节。

大文件的拷贝原理:

// 1. 创建对象
FileInputStream fis = new FileInputStream("D:\\a.txt");
// 2. 拷贝
byte[] bytes = new byte[2];
// 一次读取多个字节数据,具体读多少,跟数据的长度有关。
int len = fis.read(bytes);
System.out.println(len);
String str = new String(bytes, 0, len);
System.out.println(str);

int len2 = fis.read(bytes);
System.out.println(len2);
String str2 = new String(bytes, 0, len2);
System.out.println(str2);

int len3 = fis.read(bytes);
System.out.println(len3);
String str3 = new String(bytes, 0, len3);
System.out.println(str3);
fis.close();

大文件拷贝示例:

// 1. 创建对象
FileInputStream fis = new FileInputStream("D:\\ProMaterisals\\idea\\HMIO\\hmio\\a.txt");
FileOutputStream fos = new FileOutputStream("hmio\\copy2.txt");
// 2. 拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];  // 5m
while ((len = fis.read(bytes)) != -1) {
    fos.write(bytes, 0, len);
}
// 3. 释放资源
fos.close();
fis.close();

利用 try…catch… finally 捕获拷贝文件中代码出现的异常

// 1. 创建对象
FileInputStream fis = null;
FileOutputStream fos = null;
try {
    fis = new FileInputStream("D:\\ProMaterisals\\idea\\HMIO\\hmio\\a.txt");
    fos = new FileOutputStream("hmio\\copy2.txt");
    // 2. 拷贝
    int len;
    byte[] bytes = new byte[1024 * 1024 * 5];  // 5m
    while ((len = fis.read(bytes)) != -1) {
        fos.write(bytes, 0, len);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    // 3. 释放资源
    if (fos != null) {
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4. 字符集

ASCII、GBK 详解

1. 在计算机中,任意数据都是以二进制的形式存储的。
2. 计算机中最小的存储单位是一个字节。
3. ASCII 字符集中,一个英文占一个字节。
4. 简体中文版 Windows,默认使用 GBK 字符集。
5. GBK 字符集完全兼容 ASCII 字符集。
   	1. 一个英文占一个字节,二进制第一位是 0
   	2. 一个中文占两个字节,二进制高位字节的第一位是 1

Unicode 详解

1. Unicode 字符集的 UTF-8 编码格式
   	1. 一个英文占一个字节,二进制第一位是 0,转成十进制是正数
   	2. 一个中文占三个字节,二进制第一位是 1,第一个字节转成十进制是负数

乱码出现的原因:

1.  使用字节流读取数据的时候未读完整整个汉字(字节流一次读取一个字节)。

2.  编码和解码时的方式不统一。

5. 字符流

5.1 FileReader

read (空参)方法详解:

// 1. 创建对象并关联本地文件
FileReader fr = new FileReader("hmio\\a.txt");
// 2. 读取数据
// 字符流的底层也是字节流,默认也是一个字节一个字节的读取的。
// 如果遇到中文就会一次读取多个,GBK 一个读两个字节,UTF-8(IDEA 默认) 一次读取三个字节。

// read() 细节:
// 1. read():默认也是一个字节一个字节的读取,如果遇到中文就会一次读取多个
// 2. 在读取之后,方法的底层还会进行解码并转成十进制。
//    最终把这个十进制作为返回值
//    这个十进制的数据也表示在字符集上的数字
//    英文:文件里面二进制的数据 0110 0001
//          read 方法进行提取,解码并转成十进制 97
//    中文:文件里面的二进制数据:11100110 10110001 10001001
//          read 方法进行提取,解码并转成十进制 27721

int ch;
while ((ch = fr.read()) != -1) {
    System.out.print((char) ch);
}
// 3. 释放资源
fr.close();

read(有参)方法详解

// 1. 创建对象并关联本地文件
FileReader fr = new FileReader("hmio\\a.txt");
// 2. 读取数据
char[] chars = new char[2];
int len;
// read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数据当中
// 空参 read + 强转类型转换
while ((len = fr.read(chars)) != -1) {
    // 把数据中的数据编程字符串再打印
    System.out.println(new String(chars, 0, len));
}
// 3. 释放资源
fr.close();

5.2 FileWriter

书写细节

① 创建字符输出流对象

​ 细节1:参数是字符串表示的路径或者 File 对象都可以的

​ 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的

​ 细节3:如果文件已经存在,则会清空文件,如果不想被清空可以打开续写

② 写数据

​ 细节:如果 writer 方法的参数是整数,但是实际上写道本地文件中的是整数在字符集上对应的字符

③ 释放资源

​ 细节:每次使用完之后释放资源

5.3 字符流原理解析

缓冲区

① 创建字符输入流对象

​ 底层:关联文件,并创建缓冲区(长度为 8129 的字节数组)

② 读取数据

​1.判断缓冲区中是否有数据可以读取
2. 缓冲区中没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区,如果文件也没有数据,返回 -1
3. 缓冲区有数据:就从缓冲区中读取。
(1) 空参 read 方法:一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回
(2) 有参 read 方法:把读取字节,解码,强转三步合并了,强转之后的字节放到数组中

以下验证是否有缓冲区(a 文件中要多于 8129 字节的数据,程序会把存入程序中的数据读取出来,大于 8129 字节的数据无法读取)

FileReader fr = new FileReader("hmio\\a.txt");
fr.read();  // 会把文件中的数据读取到缓冲区

// 清空文件
FileWriter fw = new FileWriter("hmio\\a.txt");

// 会把缓冲区的数据全部读取完毕
// 但是也只能打印缓冲区中的数据,文件剩余的数据不能被打印出来
int ch;
while ((ch = fr.read()) != -1) {
    System.out.println((char) ch);
}

fw.close();
fr.close();

缓冲区中的数据什么时候会写入目标文件中?

① 缓冲区满了之后

FileWriter fw = new FileWriter("hmio\\a.txt");
for (int i = 0; i < 8193; i++) {
    fw.write(97);
}

② 执行 flush 或者 close 方法之后

FileWriter fw = new FileWriter("hmio\\a.txt");
fw.write("阿巴1");
fw.write("阿巴2");
fw.write("阿巴3"); 
fw.flush();
fw.write("阿巴4");
FileWriter fw = new FileWriter("hmio\\a.txt");
fw.write("阿巴4");
fw.write("阿巴5");
fw.write("阿巴6");
fw.write("阿巴7");
fw.close();

flush 跟 close 的区别:

​ flush 执行完之后可以继续在后面写数据。close 方法执行完之后,连接就断开可。

6. 练习

6.1 拷贝文件夹

public class Test01 {
    public static void main(String[] args) throws IOException {
        // 拷贝一个文件夹,考虑子文件夹
        // 1. 创建对象表示数据源
        File src = new File("D:\\ProMaterisals\\idea\\HMIO\\testDir");
        // 2. 创建对象表示目的地
        File dest = new File("D:\\ProMaterisals\\idea\\HMIO\\testDirCopy");

        // 调用方法开始拷贝
        copyDir(src, dest);
    }

    /**
     * 拷贝文件夹
     * @param src
     * @param dest
     */
    private static void copyDir(File src, File dest) throws IOException {
        dest.mkdirs();
        // 递归
        // 1. 进入数据源
        File[] files = src.listFiles();
        // 2. 遍历数组
        for (File file : files) {
            if (file.isFile()) {
                // 3. 判断文件,拷贝
                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(new File(dest, file.getName()));
                byte[] bytes = new byte[1024];
                int len;
                while ((len = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
                fos.close();
                fis.close();
            } else {
                // 4. 判断文件夹,递归
                copyDir(file, new File(dest, file.getName()));
            }
        }
    }
}

6.2 文件加密与解密

加密解密的原理:

​ 在 Java 代码中,当一个数字两次异或(^) 同一个数字的时候,得到的结果是原来那个数字。

System.out.println(100 ^ 10);  // 110
System.out.println(110 ^ 10);  // 100
FileInputStream fis = new FileInputStream("hmio\\encj.jpg");
FileOutputStream fos = new FileOutputStream("hmio\\redu.jpg");
// 加密处理
int b;
while ((b = fis.read()) != -1) {
    fos.write(b ^ 2);
}
// 释放资源
fos.close();
fis.close();

解密的时候只需要把已加密的文件重新异或一次加密时异或的数字就好了。

6.3 修改文件中的数据

// 1. 读取数据
        FileReader fr = new FileReader("hmio\\a.txt");
        StringBuilder sb = new StringBuilder();
        int ch;
        while ((ch = fr.read()) != -1) {
            sb.append((char) ch);
        }
        fr.close();
        System.out.println(sb);
        // 排序
        Integer[] arr = Arrays.stream(sb.toString()
                        .split("-"))
                .map(Integer::parseInt)
                .sorted()
                .toArray(Integer[]::new);
//        System.out.println(Arrays.toString(arr));
        FileWriter fw = new FileWriter("hmio\\a.txt");
        String s = Arrays.toString(arr).replace(", ", "-");
        s = s.substring(1, s.length() - 1);
        System.out.println(s);
        fw.write(s);
        fw.close();

三:高级流

1. 缓冲流

在这里插入图片描述

1.1 字节缓冲流拷贝文件

字节缓冲流创建的时候也会创建一个长度为 8192 的缓冲区

一次读取单个字节

// 1. 创建缓冲流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("hmio\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("hmio\\copy.txt"));
// 2. 循环读取并写道目的地
int b;
while ((b = bis.read()) != -1) {
    bos.write(b);
}
// 3. 释放资源
bos.close();
bis.close();

一次读取多个字节

// 1. 创建缓冲流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("hmio\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("hmio\\copy2.txt"));
// 2. 拷贝
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
    bos.write(bytes, 0, len);
}
// 3. 释放资源
bos.close();
bis.close();

1.2 字符缓冲流

输入

// 1. 创建缓冲流对象
BufferedReader br = new BufferedReader(new FileReader("hmio\\a.txt"));
// 2. 读取数据
// readLine 方法一次会读取一整行,遇到回车换行结束
//      但是它不会把回车换行读取到内存当中
/*String line = br.readLine();
System.out.println(line);*/
String line;
while ((line = br.readLine()) != null) {
    System.out.println(line);
}
br.close();

输出

BufferedWriter bw = new BufferedWriter(new FileWriter("hmio\\b.txt", true));
bw.write("123");
bw.newLine();
bw.write("456");
bw.newLine();
bw.close();

缓冲流总结

1. 缓冲流有几种?
   	1. 字节缓冲输入流:BufferedInputStream
   	2. 字节缓冲输出流:BufferedOutputStream
   	3. 字节缓冲输入流:BufferedReader
   	4. 字节缓冲输出流:BufferedWriter
2. 缓冲流为什么能提高性能?
        	1. 缓冲流自带长度为 8192 的缓冲区
        	2. 可以显著提高字节流的读写性能
        	3. 对于字符流提升不显著,对于字符缓冲流而言关键点是两个特有的方法
   3. 字符缓冲流两个特有的方法是什么?
     4. 字符缓冲输入流 BufferedReader:readLine() (一次读取一行
             	2. 字符缓冲输出流 BufferedWriter:newLine() (换行,可跨平台使用

缓冲流综合练习

对文件中的首列序号进行排序

8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。
4.将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
/**
         * 将乱序的《出师表》进行排序
         */
        // 1. 读取数据
        BufferedReader br = new BufferedReader(new FileReader("hmio\\csb.txt"));
        String line;
        TreeMap<Integer, String> tm = new TreeMap<>();
        while ((line = br.readLine()) != null) {
            String[] arr = line.split("\\.");
            tm.put(Integer.parseInt(arr[0]), arr[1]);
        }
        br.close();
//        System.out.println(tm);
        // 2. 写出数据
        BufferedWriter bw = new BufferedWriter(new FileWriter("hmio\\result.txt"));
        Set<Map.Entry<Integer, String>> entries = tm.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            String value = entry.getValue();
            bw.write(value);
            bw.newLine();
        }
        bw.close();

2. 转换流

转换流是字符流和字节流之间的桥梁
在这里插入图片描述

2.1 转换流读取与写出数据

利用转换流按照指定的字符编码读取数据

/*// 1. 创建对象并指定字符编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("hmio\\gbkfile.txt"), "GBK");
// 2. 读取数据
int ch;
while ((ch = isr.read()) != -1) {
    System.out.println((char) ch);
}
// 3. 释放资源
isr.close();*/

/**
 * 需要使用 JDK11+
 */
FileReader fr = new FileReader("hmio\\\\gbkfile.txt", Charset.forName("GBK"));
// 2. 读取数据
int ch;
while ((ch = fr.read()) != -1) {
    System.out.println((char) ch);
}
// 3. 释放资源
fr.close();

利用转换流按照指定字符编码写出

/**
 * 利用转换流按照指定字符编码写出
 */
/*// 1. 创建转换流对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("hmio\\b.txt"), "GBK");
// 2. 写出数据
osw.write("你好你好");
// 3. 关闭连接
osw.close();*/

/**
 * 替代方案 JDK11 +
 */
FileWriter fw = new FileWriter("hmio\\c.txt", Charset.forName("GBK"));
fw.write("你好你好");
fw.close();

转换流练习

使用转换流将读取的 GBK 文件转换为 UTF-8

/**
 * 将本地文件中的 GBK 文件,转换成 UTF-8
 */
// 1. JDK11 以前的方案
/*InputStreamReader isr = new InputStreamReader(new FileInputStream("hmio\\b.txt"), "GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("hmio\\d.txt"), "UTF-8");

int b;
while ((b = isr.read()) != -1) {
    osw.write(b);
}*/

// 2. 替代方案
FileReader fr = new FileReader("hmio\\b.txt", Charset.forName("GBK"));
FileWriter fw = new FileWriter("hmio\\e.txt", Charset.forName("UTF-8"));
int b;
while ((b = fr.read()) != -1) {
    fw.write(b);
}
fw.close();
fr.close();

利用字节流读取文件的数据,每次读取一整行,而且不能乱码

/**
         * 利用字节流读取文件的数据,每次读取一整行,而且不能乱码
         *
         * 1. 字节流读取中文的时候,是会出现乱码的,但是字符流可以搞定
         * 2. 字节流里面是没有读取一整行的方法的,只有字符缓冲流可以搞定
         */
//        FileInputStream fis = new FileInputStream("hmio\\a.txt");
//        InputStreamReader isr = new InputStreamReader(fis);
//        BufferedReader br = new BufferedReader(isr);
//
//        String str = br.readLine();
//        System.out.println(str);
//
//        br.close();

        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("hmio\\a.txt")));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();

转换流总结

1. 转换流名字
   	1. 字符转换输入流:InputStreamReader
   	2. 字符转换输入流:OuputStreamWriter
2. 转换流的作用:
   	1. 指定字符集读写数据(JDK11 之后就被淘汰)
   	2. 字节流想要使用字符流中的方法了

3. 序列化流

3.1 序列化流读取/输出对象

序列化流写出对象

/**
 * 利用序列化流/对象操作流,把一个对象写到本地中
 */
// 1. 创建对象
Student stu = new Student("zhangsan", 10);
// 2. 创建序列化流的对象/对象操作输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hmio\\a.txt"));
// 3. 写出数据
oos.writeObject(stu);
// 4. 释放资源
oos.close();

反序列化流读取对象

/**
 * 利用反序列化流,把文件中的对象读取到程序中。
 */
// 1. 创建反序列化流的对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hmio\\a.txt"));
// 2. 读取数据
Object o = ois.readObject();
System.out.println(o);
// 3. 释放资源
ois.close();

3.2 序列化流细节汇总

  1. 使用序列化流将对象写道文件时,需要让 Javabean 类实现 Serializable 接口。否则,会出现 NotSerializableException 异常。

  2. 序列化流写到文件中的数据不能修改,一旦修改就无法再次读回来了。

  3. 序列化对象后,修改了 Javabean 类,再次反序列化,会不会有问题?

    ​ 会出问题,会抛出 InvalidClassException 异常。

    ​ 解决方案:给 Javabean 类添加 serialVersionUID(序列号、版本号)

    1. 如果一个对象的某个成员变量的值不想被序列化,又如何实现?

    ​ 解决方案:给该成员变量加 transient 关键字修饰,该关键字标记的成员变量不参与序列化过程。

序列化流练习

序列化读写多个对象

序列化的 Javabean 对象:Student.java

import java.io.Serializable;

public class Student implements Serializable {

    private static final long serialVersionUID = -7424513845312147863L;

    private String name;
    private int age;
    private String address;

    public Student() {
    }

    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

序列化写出对象

/**
 * 将多个自定义对象序列化到文件中,但是对象的个数不确定,该如何操作呢?
 */
// 1. 序列化多个对象
Student s1 = new Student("zhangsan", 12, "魔都");
Student s2 = new Student("lisi", 13, "帝都");
Student s3 = new Student("wangwu", 14, "妖都");

ArrayList<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hmio\\a.txt"));
oos.writeObject(list);

oos.close();

序列化读取对象

// 1. 创建反序列化流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hmio\\a.txt"));

// 2. 读取数据
ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
for (Student student : list) {
    System.out.println(student);
}

// 3. 释放资源
ois.close();

4. 打印流

在这里插入图片描述

打印流

​ 分类:打印流一般是指:PrintStream,PrintWriter 两个类

​ 特点一:打印流只操作文件目的地,不操作数据源

​ 特点二:特有的写出方法可以实现,数据原样写出

​ 例如:打印:97 文件中:97

​ 特点三:特有的写出方法,可以实现自动刷新,自动换行

​ 打印一次数据 = 写出 + 换行 + 刷新

4.1 字节打印流

字节打印流底层没有缓冲区

// 1. 创建字节打印流对象
PrintStream ps = new PrintStream(new FileOutputStream("hmio\\a.txt"), true, "UTF-8");
ps.println(97);  // 写出 + 自动刷新 + 自动换行
ps.print(true);
ps.println();
ps.printf("%s爱上了%s", "阿珍","阿强");
ps.close();

4.2 字符打印流

字节打印流底层有缓冲区,想要自动刷新需要开启

// 1. 创建字符打印流对象
PrintWriter pw = new PrintWriter(new FileWriter("hmio\\a.txt"), true);
// 2. 写出数据
pw.println("你今天终于叫我名字了,虽然叫错了,但是没关系,我马上改");
pw.print("你好你好");
pw.printf("%s爱上了%s", "阿珍","阿强");
// 3. 释放资源
pw.close();

打印流小结

​ 打印流有几种?各有什么特点?

​ 有字节打印流和字符打印流两种。

​ 打印流不操作数据源,只能操作目的地。

​ 字节打印流:默认自动刷新,特有的 println 自动换行。

5. 解压缩流

5.1 解压流

public static void main(String[] args) throws IOException {
    // 1. 创建一个 File 表示要解压的压缩包
    File src = new File("D:\\ProMaterisals\\idea\\HMIO\\testZip.zip");
    // 2. 创建一个 File 表示要解压的目的地
    File dest = new File("D:\\ProMaterisals\\idea\\HMIO\\");
    unzip(src, dest);
}

/**
 * 解压的方法
 * @param src 要解压的压缩包地址
 * @param dest 要解压的目的地地址
 */
public static void unzip(File src, File dest) throws IOException {
    // 创建一个解压缩流
    ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
    // 先获取到压缩包里面的每一个 zipentry 对象

    // 表示当前在压缩包当中获取到的文件或者文件夹
    ZipEntry entry;
    while ((entry = zip.getNextEntry()) != null) {
        System.out.println(entry);
        if (entry.isDirectory()) {
            // 文件夹:需要在目的地 dest 处创建一个相同的文件夹
            File file = new File(dest, entry.toString());
            file.mkdirs();
        } else {
            // 文件:需要读取到压缩包中的文件,并把它存放在目的地 dest 文件夹中(按照层级目录进行存放)
            FileOutputStream fos = new FileOutputStream(new File(dest, entry.toString()));
            int b;
            while ((b = zip.read()) != -1) {
                // 写到目的地
                fos.write(b);
            }
            fos.close();
            // 表示在压缩包中的文件处理完毕了
            zip.closeEntry();
        }
    }
    zip.close();
}

5.2 压缩流

压缩单个文件

public class ZipStreamDemo2 {
    public static void main(String[] args) throws IOException {
        /**
         * 压缩流
         *
         */
        // 1. 创建 File 对象表示要压缩的文件
        File src = new File("D:\\ProMaterisals\\idea\\HMIO\\ziptest.txt");
        // 2. 创建 File 对象表示压缩包的位置
        File dest = new File("D:\\ProMaterisals\\idea\\HMIO\\");
        // 3. 调用方法压缩
        toZip(src, dest);
    }

    /**
     * 压缩文件
     * @param src 要压缩的文件
     * @param dest 压缩包的位置
     */
    public static void toZip(File src, File dest) throws IOException {
        // 1. 创建压缩流,关联压缩包
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest, "ziptest.zip")));
        // 2. 创建 ZipEntry 对象,表示压缩包里面的每一个文件和文件夹
        // 参数:压缩包里面的路径
        ZipEntry entry = new ZipEntry("ziptest.txt");
        // 3. 把 ZipEntry 对象放到压缩包中
        zos.putNextEntry(entry);
        // 4. 把 src 文件中的数据写到压缩包当中
        FileInputStream fis = new FileInputStream(src);
        int b;
        while ((b = fis.read()) != -1) {
            zos.write(b);
        }
        zos.closeEntry();
        zos.close();
    }
}
public class ZipStreamDemo3 {
    public static void main(String[] args) throws IOException {
        /**
         * 把文件夹压缩成一个压缩包
          */
        // 1. 创建 File 对象表示要压缩文件夹的
        File src = new File("D:\\ProMaterisals\\idea\\HMIO\\testZip1");
        // 2. 创建 File 对象表示压缩包放在那里
        File destParent = src.getParentFile(); // src 的 testZip 文件的父级目录
        // 3. 创建 File 文件表示压缩包的路径
        File dest = new File(destParent, src.getName() + ".zip");
        // 4. 创建压缩流关联压缩包
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
        // 5. 获取 src 里面的每个文件
        toZip(src, zos, src.getName());
        // 6. 释放资源
        zos.close();
    }

    /**
     * 获取 src 的每个文件,变成 ZipEntry 对象,放入压缩包中
     * 但是这个方法目前有 BUG,如果要压缩的文件夹里面没有文件,不会被压缩进压缩包
     * @param src 数据源
     * @param zos 压缩流
     * @param name 压缩包内部的路径
     */
    public static void toZip(File src, ZipOutputStream zos, String name) throws IOException {
        // 1. 进入 src 方法
        File[] files = src.listFiles();
        // 2. 遍历数据
        for (File file : files) {
            if (file.isFile()) {
                // 3.判断-文件,变成 ZipEntry 对象,放入压缩包中
                ZipEntry entry = new ZipEntry(name + "\\" + file.getName());
                zos.putNextEntry(entry);
                // 读取文件中的数据,写道压缩包中
                FileInputStream fis = new FileInputStream(file);
                int b;
                while ((b = fis.read()) != -1) {
                    zos.write(b);
                }
                fis.close();
                zos.closeEntry();
            } else {
                // 4. 判断-文件夹,递归
                toZip(file, zos, name + "\\" + file.getName());
            }
        }

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/514379.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux系统 数组 的运用

这里写目录标题 数组数组的定义数组的定义方式输出数组的方式数组包括的数据类型数组的删除与追加数组的删除数组的追加 数组切片与替换切片替换 冒泡排序 数组 数组的定义 数组是存放相同类型数据的集合&#xff0c;在内存中开辟了连续的空间&#xff0c;通常配合循环使用 …

爬虫逆向——某建筑市场监管平台的滑块验证码分析

目录 网址链接&#xff1a; 正文&#xff1a; 一、思路分析 二、图片处理 三、完整代码 网址链接&#xff1a; aHR0cHM6Ly9nY3htLmh1bmFuanMuZ292LmNuL2RhdGFzZXJ2aWNlLmh0bWw &#xff08;bs64解密可见&#xff09; 正文&#xff1a; 注&#xff1a;分步的代码为示例…

C语言中数据结构——带头双向循环链表

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;数据结构 &#x1f525;座右铭&#xff1a;“不要等到什么都没…

如何删除打印机任务?三种快速删除打印机任务的方法

打印机用户可能会经常遇到添加错的打印任务&#xff0c;或是一不小心重复选择过多的打印任务&#xff0c;环保人人有责&#xff0c;杜绝纸张浪费&#xff0c;驱动人生就为大家带来快速删除打印任务的方法。 方法一&#xff1a;使用Windows自带的清理功能 在Windows操作系统中…

谷歌全线反击!PaLM 2部分性能已经超越GPT-4

ChatGPT横空出世&#xff0c;所有人都能够明确感知到AI的惊人潜力&#xff0c;瞬间改变了整个AI行业的节奏&#xff0c;不紧不慢的谷歌也开始紧张了。 ChatGPT舆论热潮仍未消退&#xff0c;红色警报又拉响 北京时间5月11日凌晨1点&#xff0c;Google I/O 2023开发者大会上发布…

C++ | 深拷贝和浅拷贝

C 深拷贝和浅拷贝 当类的函数成员存在__指针成员__时会产生深拷贝和浅拷贝和问题。 在进行对象拷贝时会使用默认拷贝构造函数&#xff0c;默认进行浅拷贝&#xff0c;即只会拷贝指针的值&#xff0c;新拷贝的指针与原来的指针指向同一内存&#xff1b; 浅拷贝带来的问题是&…

stm32裸机开发下利用MultiTimer多任务时间片询

stm32裸机开发下利用MultiTimer多任务时间片询 &#x1f4cc;MultiTimerGithub地址&#xff1a;https://github.com/0x1abin/MultiTimer ✨这是一个类似Arduino平台上的Ticker库&#xff0c;如需阅读懂源码&#xff0c;起码需要有链表知识的储备&#xff0c;如果仅仅只是拿来使…

docker学习笔记(二)

目录 启动Docker ​编辑 建立 Docker 用户 ​编辑 测试 Docker 是否正常工作 卸载Docker Docker镜像加速器配置 配置镜像 检查加速器是否生效 如何在Linux中的.json文件下保存并退出 如果我是使用vi操作进来的&#xff0c;我该如何保存并退出呢&#xff1f; 如何在Li…

3 步集成 Terraform + 极狐GitLab CI ,实现基础设施自动化管理

本文来自&#xff1a;极狐GitLab 开发者社区 作者&#xff1a;KaliArch 利用极狐GitLab CI 实现基础设施编排自动化后&#xff0c;用户就可以使用极狐GitLab 进行基础设施管理&#xff1a;提交基础设施变更后&#xff0c;会触发 MR 进行极狐GitLab CI 流水线执行&#xff0c;从…

电视盒子什么牌子好?数码小编分享2023热销电视盒子排行榜

整理私信的时候有朋友希望我能分享电视盒子排行榜&#xff0c;对不了解电视盒子什么牌子好的朋友们来说&#xff0c;选购电视盒子时很容易踩雷&#xff0c;因此我根据各大电商平台的销量数据整理了最新热销电视盒子排行榜&#xff0c;对电视盒子感兴趣的朋友们可以了解一下。 ●…

时序预测 | Python实现AR、ARMA、ARIMA时间序列预测

时序预测 | MATLAB实现VAR和GARCH时间序列预测 目录 时序预测 | MATLAB实现VAR和GARCH时间序列预测预测效果基本介绍模型原理程序设计参考资料预测效果 基本介绍 Python实现AR、ARMA、ARIMA时间序列预测 模型原理 AR、ARMA、ARIMA都是常用的时间序列预测方法,它们的主要区别在…

功能优化升级,添加更丰富的刷题练习设置|会员权益、答题后显示数据、切题按钮、进入小组必设昵称、小组统计筛选条件、考试排行榜、优化缓存机制

土著刷题微信小程序v1.14&#xff0c;主要是针对用户在使用期间提的一些优化建议&#xff0c;经过评估后进行优化升级的一次版本&#xff0c;下面将逐条介绍一下这一版的优化点。 01 会员权益页面 通过对用户提出的问题进行归纳总结&#xff0c;部分用户在购买VIP时&#xff0c…

【git】修改作者和提交者信息

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 修改git作者和提交者信息 1. 正文 1.1 前提 1.1.1 作者和提交者 作者就是我们git log看到的信息&#xff0c;如下&#xff1a; 其修改方式参考&…

linux应用编程

项目内容 开发板内部使用c语言调用硬件驱动实现各种测试功能&#xff0c;保存测试结果。 外部程序通过socket接口使用tcp协议与开发板通信进行信息传输&#xff0c; 最后使用python GUI构造一个界面按照测试顺序逐步显示出各个模块的测试结果 测试包括&#xff1a;485-232uart、…

(十四)地理数据库创建——进一步定义数据库②

&#xff08;十四&#xff09;地理数据库创建——进一步定义数据库② 目录&#xff08;接上篇&#xff09; &#xff08;十四&#xff09;地理数据库创建——进一步定义数据库② 4.创建注释类4.1建立注释类4.2产生连接要素注释 5.创建几何网络5.1几何网络概述5.2建立几何网络 6…

亿级大表毫秒关联,荔枝微课基于Apache Doris 统一实时数仓建设实践

本文导读&#xff1a; Apache Doris 助力荔枝微课构建了规范的、计算统一的实时数仓平台&#xff0c;目前 Apache Doris 已经支撑了荔枝微课内部 90% 以上的业务场景&#xff0c;整体可达到毫秒级的查询响应&#xff0c;数据时效性完成 T1 到分钟级的提升&#xff0c;开发效率更…

设备树的相关概念

.dts相当于.c DTS的源码文件 DTC工具相当于gcc编译器 将dts 编译成 dtb dtb相当于bin文件 或可执行文件 编译dtb 文件的方法 在linux内核文件夹中 make imx6ull-alientek-emmc.dtb在执行上述代码之前 要把 imx6ull-alientek-emmc.dtb删除 否则会提示已经存在 dts的结构 层层…

【Python文本处理】基于GPX文件的心率、速度、时间等参数更改

【Python文本处理】基于GPX文件的心率、速度、时间等参数更改 GPX文件本身其实就是坐标、海拔、时间、心率等综合性的xml文件 如图&#xff1a; 海拔&#xff1a;ele 时间&#xff1a;time 心率&#xff1a;heartrate 在不改变坐标和距离的情况下 缩短时间即可提高速度&#…

使用rsync和inotify实时备份CentOS服务器数据(详解)

简介 在日常运维中&#xff0c;确保服务器上的数据安全是至关重要的。数据丢失或损坏可能会导致灾难性后果&#xff0c;因此定期备份数据是一个明智的做法。本文LZ将向您展示如何使用 rsync 和 inotify-tools 工具在 CentOS 系统上设置实时备份&#xff0c;以确保您的数据始终…

Google I/O:谷歌AR看似不紧不慢,实则暗藏玄机

在今天举行的Google I/O大会上&#xff0c;尽管AI是全场最大的关注点&#xff0c;也还是有一系列AR相关技术和应用更新&#xff0c;比如&#xff1a;ARCore进行更新、推出新的Geospatial Creator等等。 ARCore面世已有5年时间&#xff0c;谷歌每年都在持续推动AR技术的发展。相…