java基础-chapter15(io流)

io流:存储和读取数据的解决方案

I:input         O:output

io流的作用:用于读写数据(本地文件,网络)

io流按照流向可以分为:

输出流:程序->文件

输入流:文件->程序

io流按照操作文件的类型可分类为:

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

字符流:只能操作纯文本文件(能用电脑自带记事本打开并且能读懂的文件)

File:表示系统中的文件或者文件夹的路径

获取文件信息(大小,文件名,修改时间) 判断文件的类型

创建文件/文件夹         删除文件/文件夹

注意:File类只能对文件本身进行操作,不能读写文件里面存储的数据

字节流

FileOutputStream

操作本地文件的字节输出流,可以把程序中的数据写在本地文件中

步骤:

1.创建字节输出流对象

2.写数据

3.释放资源

FileOutputStream原理

通过创建对象 传入路径 使程序和文件产生数据传输通道

 FileOutputStream fos = new FileOutputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");

通过调用write方法写入数据 

fos.write(97);

调用close方法释放资源(断开程序和文件之间的连接)

 fos.close();

FileOutputStream书写细节

创建字节输出流对象 

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

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

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

写出数据

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

释放资源

每次使用完流之后都要释放资源

FileOutputStream写数据的3种方式

一次写一个字节数组数据

public class ByteStreamDemo2 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        //一次写一个字节数组数据
        byte[] bytes = {97,98,99,100,101,102};
        //调用write方法将数组写入文件
        fos.write(bytes);
        //释放资源
        fos.close();
    }
}

一次写一个字节数组的部分数据

public class ByteStreamDemo3 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        /*
        void write(byte[] b,int off,int len)
        参数一:数组  参数二:起始索引  参数三:个数
         一次写一个字节数组的部分数据
         */
        byte[] bytes = {97,98,99,100,101,102};
        //调用write方法将数组写入文件
        fos.write(bytes,2,2); //cd
        //释放资源
        fos.close();
    }
}

FileOutputStream写数据的两个小问题

换行写

public class ByteStreamDemo4 {
    public static void main(String[] args) throws IOException {
        /*
        换行写
        写出一个换行符
        Windows: \r\n
        Linux:   \n
        Mac:     \r
         */
        FileOutputStream fos = new FileOutputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        //写出数据 huluyazhenshuai
        String str = "huluyazhenshuai";
        //调用getBytes方法把字符串变成字节数组
        byte[] bytes = str.getBytes();
//        System.out.println(Arrays.toString(bytes));
        /*
        变成字节数组
        [104, 117, 108, 117, 121, 97, 122, 104, 101, 110, 115, 104, 117, 97, 105]
         */
        //调用write方法写入数据
        fos.write(bytes);

        String str2= "\r\n"; //写出换行符
        fos.write(str2.getBytes());

        String str3 = "666";
        fos.write(str3.getBytes());

        /*
        文件中的数据:
        huluyazhenshuai
        666
         */

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

续写

如果想要续写,打开续写开关即可

开关位置:创建对象的第二个参数

默认false:表示续写,此时创建对象就可以清空文件

手动传递true;表示打开续写,此时创建对象不会清空文件

 FileOutputStream fos = new FileOutputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt",true);

FileOutputStream小结

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

书写步骤:创建对象        写出数据        释放资源

三步操作的细节:

创建对象: 文件存在(清空文件)  文件不存在(创建文件)  追加写入(true)

写出数据:写出整数  写出字节数组  换行写

释放资源:关闭通道

字节输入流的基本用法

FileInputStream

操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来

书写步骤

1.创建对象

2.读数据

3.释放资源

FileInputStream书写细节

1.创建对象

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

2.读数据

细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字

细节2:读到文件末尾,read方法返回-1

3.释放资源

FileInputStream循环读取

public class FileInputStreamDemo2 {
    public static void main(String[] args) throws IOException {
        //字节输入流循环读取
        FileInputStream fis = new FileInputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        //循环读取
        int b;
        while ((b = fis.read()) != -1) { //read:表示读取数据 并且读取一次数据就会移动一次指针
            System.out.println((char) b);
        }
        //释放资源
        fis.close();
    }
}

文件拷贝

public class FileInputStreamDemo3 {
    public static void main(String[] args) throws IOException {
        /*
        练习:
        文件拷贝
         */
        //创建对象
        FileInputStream fis = new FileInputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        FileOutputStream fos = new FileOutputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\copy.txt");

        //拷贝  边读边写
        int b;
        while ((b = fis.read()) != -1){ 
            fos.write(b);
        }

        //释放资源
        fos.close();
        fis.close();
    }
}

FileInputStream读取的问题

一次读写一个字节

public class FileInputStreamDemo4 {
    public static void main(String[] args) throws IOException {
        //一次读一个字节数组数据
        //创建对象
        FileInputStream fis = new FileInputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        //创建数组
        //表示一次读两个数据
        byte [] bytes = new byte[2];
        //返回值len:表示本次读取到了多少个字节数据
        int len1 = fis.read(bytes);
        System.out.println(len1); //2
        String str1 = new String(bytes,0,len1);
        System.out.println(str1);

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

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

    }
}

GBK和Unicode

  1. GBK:在GBK字符集中,每个中文字符占据两个字节。

  2. Unicode:Unicode字符集中,在UTF-8字符集中,中文字符通常占用3个字节的空间。UTF-8是一种可变长度的编码方案,用于表示Unicode字符集中的字符。对于常见的汉字,UTF-8编码通常占用3个字节,但对于罕见的汉字,也可能会占用更多的字节。

public class CharSetDemo1 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        /*
        java中编码的方法
        public byte[] getBytes() 默认方式进行编码
        public byte[] getBytes(String charsetName) 指定方式进行编码

        java中解码的方法
        String (byte[] bytes) 默认方式进行解码
        String (byte[] bytes,String charsetName) 指定方式进行解码
         */

        //1.编码
        String str1 = "ai你哟";
        byte[] bytes1 = str1.getBytes();
        System.out.println(Arrays.toString(bytes1)); //[97, 105, -28, -67, -96, -27, -109, -97]

        byte[] bytes2 = str1.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2)); //[97, 105, -60, -29, -45, -76]

        //解码
        String str2 = new String(bytes1);
        System.out.println(str2); //ai你哟

        String str3 = new String(bytes1,"GBK");
        System.out.println(str3); //ai浣犲摕

    }
}

字符流

FileReader

import java.io.FileReader;
import java.io.IOException;

/**
 * @author hyk~
 */
public class FileReaderDemo1 {
    public static void main(String[] args) throws IOException {
        //创建对象并关联本地文件
        FileReader fr = new FileReader("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        //读取数据read()
        //字符流的底层也是字节流,默认也是一个字节一个字节的读取
        //但是遇到中文会一次读多个字节 GBK读2个 utf-8读3个
        int ch;
        while((ch = fr.read()) != -1){
            System.out.print((char) ch);
            /*
            read () 细节:
            1.read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
            2.在读取之后,方法的底层还会进行解码并转成十进制。
            最终把这个十进制作为返回值 这个十进制的数据也表示在字符集上的数字
            英文: 文件里面二进制数据 0110 0001
            read方法进行读取,解码并转成十进制97
            中文: 文件里面的二进制数据 11100110 10110001 1001001
            read方法进行读取,解码并转成十进制27721
            如果想看到中文汉字,就把这些十进制数据,再进行强转就可以了 (char) ch
             */
        }

        //释放资源
        fr.close();
    }
}

FileWriter

1.创建字符输出流对象

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

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

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

2.写数据

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

3.释放资源

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

public class FileReaderDemo3 {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        fw.write("大家好,我是学生");
        fw.close();
    }
}

字节流和字符流使用场景

字节流

拷贝任意类型的文件

package itemIO;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author hyk~
 */
public class Test1 {
    public static void main(String[] args) throws IOException {
        //拷贝一个文件夹,考虑子文件夹

        //创建对象表示数据源
        File src = new File("D:\\ideaProject\\src");
        //创建对象表示目的地
        File dest = new File("D:\\ideaProject\\src10086");

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

    /*
    作用:拷贝文件夹
    参数一:数据源
    参数二:目的地
    */
    private static void copyDir(File src, File dest) throws IOException {
        dest.mkdirs();
        //递归
        //进入数据源
        File[] files = src.listFiles();
        //遍历数组
        for (File file : files) {
            //判断文件,拷贝
            if (file.isFile()) {
                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);
                    //bytes数组中从0索引开始,一共len个元素拷贝
                }
                fos.close();
                fis.close();
            }else {
                //判断文件夹,递归
                copyDir(file,new File(dest,file.getName())); //要拷贝的文件夹,目的地
            }
        }
    }
}

文件加密

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author hyk~
 */
public class Test2 {
    /*
    为了保证文件的安全性,就需要对原始文件进行加密存储再使用的时候再对其进行解密处理
    加密原理:
    对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中
    解密原理:
    读取加密之后的文件,按照加密的规则反向操作,变成原始文件
     */
    public static void main(String[] args) throws IOException {
        //创建对象关联原始文件
        FileInputStream fis = new FileInputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\1.png");
        //创建对象关联加密文件
        FileOutputStream fos = new FileOutputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\ency.png");
        //加密处理
        int b;
        while((b = fis.read()) != -1){
            fos.write(b ^ 10);
        }
        fos.close();
        fis.close();
    }
}

解密操作:

修改文件中的数据

package itemIO;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

/**
 * @author hyk~
 */
public class Test3 {
    /*
    文本文件中有以下的数据:
    2-1-9-4-7-8
    将文件中的数据进行排序,变成以下的数据:
    1-2-4-7-8-9
     */
    public static void main(String[] args) throws IOException {
        //读取数据
        FileReader fr = new FileReader("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        StringBuilder sb = new StringBuilder();
        int ch;
        while((ch = fr.read()) != -1){
            sb.append((char) ch);
        }
        fr.close();

        //排序
        String str = sb.toString();
        String [] arr = str.split("-");
        ArrayList list = new ArrayList();
        for (String s : arr) {
            int i = Integer.parseInt(s);
            list.add(i);
        }
        Collections.sort(list);
        //写出
        FileWriter fw = new FileWriter("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt");
        for (int i = 0; i < list.size(); i++){
            if (i == list.size() -1){
                fw.write(list.get(i) + "");
            }else {
                fw.write(list.get(i)+"-");
            }
        }
        fw.close();

    }
}

字节缓冲流

import java.io.*;

/**
 * @author hyk~
 */
public class BufferedStreamDemo2 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
            利用字节缓冲流拷贝文件

            字节缓冲输入流的构造方法:
                public BufferedInputStream(Inputstream is)
            字节缓冲输出流的构造方法:
                public BufferedOutputstream(Outputstream os)
         */

        //创建缓冲流的对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\copy.txt"));

        //拷贝 一次读写多个字节
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes))!= -1){
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
    }
}

字符缓冲流

public class BufferedStreamDemo3 {
    public static void main(String[] args) throws IOException {
        /*
        字符缓冲输入流:
        构造方法:
        public BufferedReader(Reader r)
        特有方法:
        public String readLine()读一整行
         */

        //创建字符缓冲流的对象
        BufferedReader br = new BufferedReader(new FileReader("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemIO\\a.txt"));
        //读取数据
        String line;
        while (((line = br.readLine()) != null)){
            System.out.println(line);
        }

        //释放资源
        br.close();

    }
}

字符流

读取纯文本文件中的数据

往纯文本文件中写出数据

转换流

转换流主要有两种类型:InputStreamReader OutputStreamWriter

它们的作用是在字节流和字符流之间建立桥梁,使得字节流能够以字符流的形式被读取,或者字符流能够以字节流的形式被写入。

创建转换流:

1. 创建 InputStreamReader
FileInputStream fis = new FileInputStream("input.txt"); // 创建字节输入流
InputStreamReader isr = new InputStreamReader(fis); // 创建转换流
2. 创建 OutputStreamWriter
FileOutputStream fos = new FileOutputStream("output.txt"); // 创建字节输出流
OutputStreamWriter osw = new OutputStreamWriter(fos); // 创建转换流

在创建转换流时,你可以选择是否指定字符集。如果不指定字符集,将会使用系统默认的字符集。例如,可以这样指定字符集:

InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); // 指定字符集为UTF-8
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8"); // 指定字符集为UTF-8

使用转换流:

1. 使用 InputStreamReader 读取文件内容:
BufferedReader br = new BufferedReader(isr); // 创建缓冲字符流
String line;
while ((line = br.readLine()) != null) {
    System.out.println(line); // 输出每一行内容
}
2. 使用 OutputStreamWriter 写入数据:
BufferedWriter bw = new BufferedWriter(osw); // 创建缓冲字符流
bw.write("Hello, world!"); // 写入字符串
bw.newLine(); // 写入换行符
bw.close(); // 关闭流

序列化流

序列化流在Java中是用来将对象转换为字节流的一种方式。主要用于对象的持久化存储或网络传输。Java提供了两种序列化流:ObjectOutputStream 用于将对象写入到流中,ObjectInputStream 用于从流中读取对象。

创建序列化流:

1. 创建 ObjectOutputStream
FileOutputStream fos = new FileOutputStream("object.dat"); // 创建字节输出流
ObjectOutputStream oos = new ObjectOutputStream(fos); // 创建序列化流
2. 创建 ObjectInputStream
FileInputStream fis = new FileInputStream("object.dat"); // 创建字节输入流 
ObjectInputStream ois = new ObjectInputStream(fis); // 创建反序列化流

使用序列化流:

1. 使用 ObjectOutputStream 写入对象:
MyObject obj = new MyObject(); // 创建一个自定义对象
oos.writeObject(obj); // 将对象写入流中
2. 使用 ObjectInputStream 读取对象:
MyObject obj = (MyObject) ois.readObject(); // 从流中读取对象,并进行类型转换

注意事项:

  1. 被写入流中的对象必须实现 Serializable 接口,否则会抛出 java.io.NotSerializableException 异常。
  2. 对象中的静态变量不会被序列化,因为静态变量属于类而不是对象。
  3. 有时候需要对某些敏感信息进行序列化时,可以使用 transient 关键字来标记不需要序列化的字段。
  4. 序列化的版本号可以通过 serialVersionUID 显式声明,以控制序列化对象的版本。
import java.io.*;

class MyObject implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;

    public MyObject(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            // 创建对象输出流
            FileOutputStream fos = new FileOutputStream("object.dat");
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            // 写入对象
            MyObject obj1 = new MyObject("Object 1");
            oos.writeObject(obj1);

            // 关闭对象输出流
            oos.close();

            // 创建对象输入流
            FileInputStream fis = new FileInputStream("object.dat");
            ObjectInputStream ois = new ObjectInputStream(fis);

            // 读取对象
            MyObject obj2 = (MyObject) ois.readObject();
            System.out.println("Name: " + obj2.getName());

            // 关闭对象输入流
            ois.close();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

这段代码演示了如何创建和使用序列化流来序列化和反序列化对象。请确保在处理异常时进行适当的处理,比如打印错误信息或者进行其他的异常处理操作。

上一篇:2024最新智能优化算法:常春藤算法(Ivy algorithm,LVYA)求解23个函数,提供MATLAB代码


下一篇:解决Vscode Copilot连不上网问题