目录

(九)File流与IO流

3、IO流(增强流部分)

(4)转换流:

(5)对象流:

<1>概述:

<2>序列化与反序列化:

<3>ObjectOutputStream:实现序列化

<4>ObjectInputStream: 反序列化

<5>集合序列化:

(6)数据流:

(7)随机访问流:

4、propertie类:


(九)File流与IO流

3、IO流(增强流部分)
(4)转换流:

<1>字符编码与字符集:

        计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。

        按照某种规则,将字符存储到计算机中,称为编码 。然后将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码

        比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。但如果按照A规则存储,按照B规则解析,就会导致乱码现象。

        字符编码 Character Encoding : 一套自然语言字符与二进制数(码点)之间的对应规则。

        字符集 Charset :也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。

        计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。

可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。

<2>字符转换时用的流:

        当我们读取的文件的编码形式与程序中的编码形式不同时,就会出现乱码的问题。

OutputStreamWriter 将字节输出流转换为字符输出流,并指明编码      

public class OutputStreamWriter extends Writer {
    //使用默认编码转换
    public OutputStreamWriter(OutputStream in);
    //使用指定编码转换
    public OutputStreamWriter(OutputStream in, String charsetName);
    //....
}

        构造方法:由上面的源码可知,有两个构造方法,通过new创建,参数是字节输出流的对象或者是它子类的对象。

        主要方法:与Writer的主要的方法一致。

InputStreamReader  将字节输入流转换为字符输入流,并指定编码

public class InputStreamReader extends Reader {
    //使用默认编码转换
    public InputStreamReader(InputStream in);
    //使用指定编码转换
    public InputStreamReader(InputStream in, String charsetName);
    //省略...
}

        构造方法:由上面的源码可知,有两个构造方法,通过new创建,参数是字节输入流的对象或者是它子类的对象。

        主要方法:与Reader的主要方法一致

注意:(1)读取和写入要与文件的编码一致

           (2)明确指定的编码,并保证它存在,否则报错。

           (3)转换流一般与缓冲流结合使用

public class Test028_InputStreamReader {
    public static void main(String[] args) throws IOException {
        // 1.创建转换流对象,指定使用GKB编码
        File file = new File("D:\\test\\File_GBK.txt");
        InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "GBK");
        // 2.读取文件内容并输出
        char[] buff = new char[8];
        int len;
        while ((len = isr.read(buff)) != -1) {
            System.out.print(new String(buff,0,len));
        }
        //3.关闭流 释放资源
        isr.close();
    }
}

案例展示:按照GBK编码读取 D:\\test\\File_GBK.txt 文件内容,然后写入UTF-8编码文件 D:\\test\\File_UTF8.txt 。注意拷贝效率,注意新文件中不要出现多余的空行。

public class Test028_Copy {
    //优化转换流 读取,实现整行读取
    // 可以对流 进行 反复增强
    // FileInputStream --> InputStreamReader --> BufferedReader
    public static void main(String[] args) throws Exception {
        String file1 = "D:\\test\\File_GBK.txt";
        String file2 = "D:\\test\\File_UTF8.txt";
        InputStream is = new FileInputStream(file1);
        InputStreamReader isr = new InputStreamReader(is,"gbk");
        //准备缓冲字符流 进一步增强 性能
        BufferedReader br = new BufferedReader(isr);
        //简化写法,获取缓冲字符流对象(指定使用utf-8编码)
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file2), "utf-8"));
        //2.借助缓冲流 进行 逐行读取
        String line;
        while((line = br.readLine()) != null) {
            bw.write(line);
            if(br.ready())
                bw.newLine();
        }
        System.out.println("输出完成!");
        //3.只需要关闭 最后的增强流对象即可
        bw.close();
        br.close();
    }
}
(5)对象流:
<1>概述:

        直接读写java对象的流,核心是实现对象的序列化与反序列化,解决“对象数据持久化的问题”

<2>序列化与反序列化:

        对象和字节序列之间进行转换的机制

        序列化:程序中,可以用一个字节序列来表示一个对象,该字节序列包含了对象的类型、对象中的数据等。如果这个字节序列写出到文件中,就相当于在文件中持久保存了这个对象的信息,实现类是ObjectOutputStream。  

        反序列化:相反的过程,从文件中将这个字节序列读取回来,在内存中重新生成这个对象,对象的类型、对象中的数据等,都和之前的那个对象保持一致。(注意,这时候的对象和之前的对象,内存地址可能是不同的),实现类是ObjectInputStream。

<3>ObjectOutputStream:实现序列化
public class ObjectOutputStream extends OutputStream implements ObjectOutput,
ObjectStreamConstants
{
    //省略...
    public ObjectOutputStream(OutputStream out) throws IOException;
    //序列化方法
    public final void writeObject(Object obj) throws IOException;
}

主要方法:源码中展示了构造方法与主要的将对象序列化的方法。该流还支持写入基本数据类型与字符串(writeInt/writeUTF(String s)),注意要抛出异常。

序列化要求:

  1. 即将被序列化的类必须实现 Serializable
    未实现该接口的类,序列化时会抛 NotSerializableException

  2. serialVersionUID 的作用

    • 若类中未显式声明,JVM 会根据类结构(成员变量、方法等)自动生成一个版本号。
    • 类结构微调(如新增无关成员变量)后,自动生成的版本号会变,导致旧数据反序列化失败。
    • 建议显式声明(如 private static final long serialVersionUID = 1L;),固定版本号,这样即使后期类结构发生变化,也可以成功序列化
  3. transient 关键字

    • 修饰的成员变量不参与序列化,反序列化后为默认值(引用类型为 null,基本类型为 0/false 等)。
    • 常用于敏感数据(如密码)或临时数据(无需持久化的数据)。
  4. 静态成员变量不序列化
    序列化仅处理 “对象的实例数据”,静态变量属于类,不参与序列化。

  5. 父子类序列化规则

    • 父类实现 Serializable,子类自动可序列化(无需再实现)。
    • 父类未实现 Serializable,子类实现时:父类需有无参构造器(反序列化时用于初始化父类部分),否则抛 InvalidClassException

案例展示:

//基础类
public class Student implements Serializable{
    private String name;
    private int age;
    public Student() {}
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    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;
    }
    @Override
    public String toString() {
        return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

//测试类
public class Test029_WriteObject {
    public static void main(String[] args) throws IOException {
        //把对象保存文件
        ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("src/com/briup/chap11/test/stu.txt"));
        Student stu = new Student("tom",20);
        oos.writeObject(stu);
        System.out.println("writeObject success!");
        //操作完成后,关闭流
        oos.close();
    }
}
<4>ObjectInputStream: 反序列化
public class ObjectInputStream extends InputStream implements ObjectInput,
ObjectStreamConstants {
    //省略...
    public ObjectInputStream(InputStream in) throws IOException;
    //反序列化方法
    public final Object readObject()throws IOException, ClassNotFoundException;
}

主要方法:源码中展示了构造方法与主要的将对象反序列化的方法。该流还支持读取基本数据类型与字符串(readInt/readUTF(String s)),注意要抛出异常。

注意:(1)读取的字节必须是对象序列化之后得字节

           (2)目标类的序列化id要与序列化时的版本号一致

案例展示:

public class Test029_ReadObject {
    public static void main(String[] args) throws IOException,ClassNotFoundException {
        //读取文件中对象
        ObjectInputStream ois = new ObjectInputStream( new FileInputStream("src/com/briup/chap11/test/stu.txt"));
        Student s1 = (Student) ois.readObject();
        System.out.println("readObject: " + s1);
        //操作完成后,关闭流
        ois.close();
    }
}
<5>集合序列化:

        序列化多个对象时,先将所有对象添加到一个集合中,然后序列化集合对象;

        反序列化时,从文件中读取单个集合对象,再从集合中获取所有对象。

案例展示:

public class Test029_WriteList {
    public static void main(String[] args) throws Exception {
        //1.实例化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/com/briup/chap11/test/list.txt"));
        //2.准备多个Student对象并加入List集合
        Student s1 = new Student("tom",20);
        Student s2 = new Student("zs",21);
        Student s3 = new Student("jack",19);
        List<Student> list = new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        //3.将集合写入文件
        oos.writeObject(list);
        System.out.println("write list success!");
        //4.操作完成后,关闭流
        oos.close();
    }
}
//运行结果:
write list success!
public class Test029_ReadList {
    public static void main(String[] args) throws IOException,ClassNotFoundException {
        ObjectInputStream oos = new ObjectInputStream(new FileInputStream("src/com/briup/chap11/test/list.txt"));
        //2.读取集合对象
        List<Student> list = (List<Student>) oos.readObject();
        if(list == null) {
            System.out.println("read null");
            return;
        }
        System.out.println("read list size: " + list.size());
        //3.遍历输出
        for (Student stu : list) {
            System.out.println(stu);
        }
        //4.关闭资源
        oos.close();
    }
}
//运行结果:
read list size: 3
Student{name='tom', age=20}
Student{name='zs', age=21}
Student{name='jack', age=19}
(6)数据流:

        在 Java 中,数据流(Data Streams)提供了一种方便、高效和一致的方式来读写基本数据类型和字符串。虽然 Java 提供了字符流和字节流来处理输入和输出,但字符流主要用于处理文本数据,而字节流则用于处理二进制数据。而数据流则提供了一种专门用于读写基本数据类型和字符串的流。

数据流仅有两个核心类,均继承自FilterInputStream/FilterOutputStream,需包装字节流(如FileInputStream/FileOutputStream)使用:

  • DataInputStream:数据输入流,用于读取基本数据类型和字符串(从字节流中按特定格式解析数据)。

  • DataOutputStream:数据输出流,用于写入基本数据类型和字符串(将基本类型按统一格式转换为字节后写入)

(7)随机访问流:

java.io.RandomAccessFile 是JavaAPI中提供的对文件进行随机访问的流

核心特性与作用:

  1. 双向操作:同时实现DataInputDataOutput接口,可直接读写基本数据类型(int、double 等)、字符串(readUTF()/writeUTF()),兼具 “数据流” 功能。

  2. 随机定位:通过seek(long pos)方法跳转到文件任意字节位置(pos 为距离文件开头的偏移量),支持 “读一部分→跳转到其他位置写→再跳转回原位置读”。

  3. 直接操作文件:构造方法需传入文件路径和操作模式,无需依赖其他字节流 / 字符流,直接与文件交互。

  4. 支持文件截断:通过setLength(long newLength)可缩短或扩展文件长度(扩展部分填充空字节)。

public class RandomAccessFile implements DataOutput,DataInput, Closeable {
    //省略...
    //传入文件以及读写方式
    public RandomAccessFile(File file, String mode) throws FileNotFoundException;
    //跳过pos字节
    public void seek(long pos) throws IOException;
}

主要方法:由上可知构造方法,可以指明读写的方式:

  • long getFilePointer():获取当前文件指针的位置(距离文件开头的字节数)。

  • void seek(long pos):将指针跳转到pos字节处(pos 必须≥0,否则抛IllegalArgumentException)。

public class Test0211_RandomAccessFile {
    public static void main(String[] args) throws IOException {
        // 1.实例化随机流对象,设为读写模式
        File file = new File("src/com/briup/chap11/test/a.txt");
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        // 文件中要替换数据的位置
        int replacePos = 6;
        // 文件中要插入的内容
        String replaceContent = "briup";
        // 2.定位到要替换数据的位置
        randomAccessFile.seek(replacePos);
        // 在指定位置,写入需要替换的内容,覆盖原来此位置上的内容
        randomAccessFile.write(replaceContent.getBytes());
        System.out.println("替换成功!");
        // 3.定位到文件的开始位置,读文件中的所有内容,并输出到控制台
        randomAccessFile.seek(0);
        byte[] buf = new byte[8];
        int len = -1;
        while ((len = randomAccessFile.read(buf)) != -1) {
            System.out.print(new String(buf,0,len));
        }
        //4.关闭资源
        randomAccessFile.close();
    }
}
//运行结果:
hello briup123
4、propertie类:

在 Java 中, Properties 类是一个用于处理配置文件的工具类。配置文件通常以 .properties 扩展名,用于存储配置信息,如应用程序的设置、数据库连接参数等。

Properties 类继承自 Hashtable 类,因此它具有 Hashtable 的所有功能,同时还提供了一些特定于属性文件的方法

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐