第 15 章IO流

第 15 章IO流

15.1 文件

15.1.1 什么是文件

文件:对我们并不陌生,文件是保存数据的地方,比如大家经常使用的word文档,txt文件,excel文件...都是文件。它既可以保存一张图片,也可以保持视频,声音...

15.1.2 文件流

image-20250831195831126

15.2 常用的文件操作

15.2.1 创建文件对象相关构造器和方法

image-20250831195933714
package com.ming.file;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;

/**
 * @author 明
 * @version 1.0
 */
public class FileCreate {
    public static void main(String[] args) {

    }
    //方式1 new File(String pathname)
    @Test
    public void create01(){
        String filePath = "D:/JAVA/韩顺平/代码/chapter19/1.txt";
        File file = new File(filePath);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //方式2 new File(File parent,String child) //根据父目录文件+子路径构建
    //D:/JAVA/韩顺平/代码/chapter19/2.txt
    @Test
    public void create02(){
        File parent =  new File("D:/JAVA/韩顺平/代码/chapter19/");
        String fileName = "2.txt";
        //这里的file对象,在java程序中,只是一个对象
        //只有执行了createNewFile 方法,才会真正的,在磁盘创建该文件
        File file = new File(parent, fileName);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    //方式3 new File(String parent,String child) //根据父目录+子路径构建
    public void create03(){
        String parent = "D:/JAVA/韩顺平/代码/chapter19/";
        String fileName = "3.txt";
        File file = new File(parent,fileName);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

15.2.2 获取文件的相关信息

getNamegetAbsolutePathgetParentlengthexistsisFileisDirectory

image-20250831213857892

15.2.3 应用案例演示 FileInformation.java

如何获取到文件的大小,文件名,路径,父File,是文件还是目录(目录本质也是文件,一种特殊的文件),是否存在。

package com.ming.file;

import org.junit.jupiter.api.Test;

import java.io.File;

/**
 * @author 明
 * @version 1.0
 */
public class FileInformation {
    public static void main(String[] args) {

    }

    //获取文件的信息
    @Test
    public void info() {
        //先创建文件对象
        File file = new File("D:/JAVA/韩顺平/代码/chapter19/1.txt");
        //调用相应的方法,得到对应信息
        System.out.println("文件名字=" + file.getName());
        //getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
        System.out.println("文件绝对路径=" + file.getAbsolutePath());
        System.out.println("文件父级目录=" + file.getParent());
        System.out.println("文件大小(字节)=" + file.length());
        System.out.println("文件是否存在=" + file.exists());//T
        System.out.println("是不是一个文件=" + file.isFile());//T
        System.out.println("是不是一个目录=" + file.isDirectory());//F
    }
}

15.2.4 目录的操作和文件删除

image-20250831214156637
public class Directory_ {
    public static void main(String[] args) {

    }

    @Test
    public void test01() {
        String filePath = "D:/JAVA/韩顺平/代码/chapter19/3.txt";
        File file = new File(filePath);
        if(file.exists()){
            if(file.delete()){
            System.out.println(filePath + "删除成功");
            } else {
                System.out.println(filePath + "删除失败");
            }
        } else {
            System.out.println("该文件不存在...");
        }
    }

    //判断 D:\\demo02 是否存在,存在就删除,否则提示不存在
    //这里我们需要体会到,在java编程中,目录也被当做文件
    @Test
    public void m2() {

        String filePath = "D:\\demo02";
        File file = new File(filePath);
        if (file.exists()) {
            if (file.delete()) {
                System.out.println(filePath + "删除成功");
            } else {
                System.out.println(filePath + "删除失败");
            }
        } else {
            System.out.println("该目录不存在...");
        }

    }

    //判断 D:\\demo\\a\\b\\c 目录是否存在,如果存在就提示已经存在,否则就创建
    @Test
    public void m3() {

        String directoryPath = "D:\\demo\\a\\b\\c";
        File file = new File(directoryPath);
        if (file.exists()) {
            System.out.println(directoryPath + "存在..");
        } else {
            if (file.mkdirs()) { //创建一级目录使用mkdir() ,创建多级目录使用mkdirs()
                System.out.println(directoryPath + "创建成功..");
            } else {
                System.out.println(directoryPath + "创建失败...");
            }
        }
    }
}

15.3 IO 流原理及流的分类

15.3.1 Java IO 流原理

  1. I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。
  2. Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行。
  3. java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
  4. 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
  5. 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

15.3.2 流的分类

  • 按操作数据单位不同分为:字节流(8 bit) 二进制文件,字符流(按字符) 文本文件
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流/包装流
(抽象基类) 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
  1. Java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的。
  2. 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

15.4 IO 流体系图-常用的类

1. IO 流体系图

image-20250831220004725

2. 文件 VS 流

image-20250831215944466

15.4.1 FileInputStream 介绍

15.4.2 FileInputStream 应用实例 FileInputStream_.java

package com.ming.inputstream_;

import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
 * @author 明
 * @version 1.0
 */
public class FileInputStream_ {
    public static void main(String[] args) {

    }

    /**
     * 演示读取文件...
     * 单个字节的读取,效率比较低
     * -> 使用 read(byte[] b)
     */
    @Test
    public void readFile01() {
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\1.txt";
        int readData = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
            //如果返回-1 , 表示读取完毕
            while ((readData = fileInputStream.read()) != -1) {
                System.out.print((char)readData);//转成char显示
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 使用 read(byte[] b) 读取文件,提高效率
     */
    @Test
    public void readFile02() {
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\1.txt";
        //字节数组
        byte[] buf = new byte[8]; //一次读取8个字节.
        int readLen = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
            //如果返回-1 , 表示读取完毕
            //如果读取正常, 返回实际读取的字节数
            while ((readLen = fileInputStream.read(buf)) != -1) {
                System.out.print(new String(buf, 0, readLen));//显示
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

15.4.3 FileOutputStream 介绍

image-20250831220452943

15.4.4 FileOutputStream 应用实例 1 FileOutputStream01.java

package com.ming.outputstream_;

import org.junit.jupiter.api.Test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author 明
 * @version 1.0
 */
public class FileOutputStream_ {
    public static void main(String[] args) {
    }

    @Test
    public void f(){
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\3.txt";
        FileOutputStream fileOutputStream = null;
        try {
            //得到 FileOutputStream对象 对象
            //老师说明
            //1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容
            //2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面
            fileOutputStream = new FileOutputStream(filePath);
            //写入一个字节
            //fileOutputStream.write('H');//
            //写入字符串
            String str = "ming,world!";
            //str.getBytes() 可以把 字符串-> 字节数组
            //fileOutputStream.write(str.getBytes());
            /*
            write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流
             */
            fileOutputStream.write(str.getBytes(), 0, 3);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

15.4.5 FileOutputStream应用实例2 FileCopy.java

package com.ming.outputstream_;

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

/**
 * @author 明
 * @version 1.0
 */
public class FileCopy {
    //思路分析
    //1. 创建文件的输入流 , 将文件读入到程序
    //2. 创建文件的输出流, 将读取到的文件数据,写入到指定的文件.
    public static void main(String[] args) {
        String infilePath = "C:/Users/yemin/Pictures/5.png";
        String outfilePath = "C:/Users/yemin/Pictures/555.png";
        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            fis = new FileInputStream(infilePath);
            fos = new FileOutputStream(outfilePath);
//            int len = 0;
//            while((len = fis.read()) != -1){
//                fos.write(len);
//            }
            //读取到后,就写入到文件 通过 fileOutputStream
            //即,是一边读,一边写
            byte[] buffer = new byte[1024];
            int readlen = 0;
            while((readlen = fis.read(buffer)) != -1){
                fos.write(buffer,0,readlen);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

15.4.6 FileReader 和 FileWriter 介绍

image-20250831221925839

15.4.7 FileReader 相关方法:

  1. new FileReader(File/String)
  2. read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
  3. read(char[]): 批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1

相关API:

  1. new String(char[]):将char[]转换成String
  2. new String(char[],off,len):将char[]的指定部分转换成String

15.4.8 FileWriter 常用方法

  1. new FileWriter(File/String): 覆盖模式,相当于流的指针在首端
  2. new FileWriter(File/String,true): 追加模式,相当于流的指针在尾端
  3. write(int):写入单个字符
  4. write(char[]):写入指定数组
  5. write(char[],off,len):写入指定数组的指定部分
  6. write (string): 写入整个字符串
  7. write(string,off,len):写入字符串的指定部分

相关API:String类:toCharArray:将String转换成char[]

注意:
FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!

15.4.9 FileReader 和 FileWriter 应用案例 FileReader_.java

要求:

  1. 使用FileReaderstory.txt 读取内容,并显示
package com.ming.reader_;

import org.junit.jupiter.api.Test;

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

/**
 * @author 明
 * @version 1.0
 */
public class FileReader_ {
    public static void main(String[] args) {

    }
    /**
     * 单个字符读取文件
     */
    @Test
    public void readFile01() throws IOException {
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\1.txt";
        FileReader fileReader = null;
        int data = 0;
        //1. 创建FileReader对象
        try {
            fileReader = new FileReader(filePath);
            while((data = fileReader.read()) != -1){
                System.out.print((char)data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            fileReader.close();
        }
    }


    /**
     * 字符数组读取文件
     */
    @Test
    public void readFile02() throws IOException {
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\1.txt";
        FileReader fileReader = null;
        int readlen = 0;
        char[] chars = new char[1024];
        //1. 创建FileReader对象
        try {
            fileReader = new FileReader(filePath);
            while((readlen = fileReader.read(chars)) != -1){
                System.out.print(new String(chars,0,readlen));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            fileReader.close();
        }
    }
}

15.5 节点流和处理流

15.5.1 基本介绍

image-20250831223115435

15.5.2 节点流和处理流一览图

分 类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
访问字符串 StringReader StringWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流 InputStreamReader OutputStreamWriter
对象流 ObjectInputStream ObjectOutputStream
抽象基类 FilterInputStream FilterOutputStream FilterReader FilterWriter
打印流 PrintStream PrintWriter
推回输入流 PushbackInputStream PushbackReader
特殊流 DataInputStream DataOutputStream

15.5.3 节点流和处理流的区别和联系

  1. 节点流是底层流/低级流,直接跟数据源相接。
  2. 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入、输出。[源码理解]
  3. 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连[模拟修饰器设计模式=> 小伙伴就会非常清楚.]

15.5.4 处理流的功能主要体现在以下两个方面:

  1. 性能的提高: 主要以增加缓冲的方式来提高输入输出的效率。
  2. 操作的便捷: 处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据, 使用更加灵活方便

15.5.5 处理流-BufferedReader 和 BufferedWriter

  • BufferedReaderBufferedWriter 属于字符流,是按照字符来读取数据的
  • 关闭时处理流,只需要关闭外层流即可[后面看源码]

应用案例

  1. 使用BufferedReader 读取文本文件,并显示在控制台 BufferedReader_.java
package com.ming.reader_;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

/**
 * @author 明
 * @version 1.0
 */
public class BuffererReader_ {
    public static void main(String[] args) throws Exception {
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\2.txt";

        //创建bufferedReader
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
        //读取
        String line;//按行读取效率高
        //说明
        //1. bufferedReader.readLine() 是按行读取文件
        //2. 当返回null 时,表示文件读取完毕
        while((line = bufferedReader.readLine()) != null){
            System.out.println(line);
        }
        //关闭流, 这里注意,只需要关闭 BufferedReader ,因为底层会自动的去关闭 节点流
        //FileReader。
        /*
            public void close() throws IOException {
                synchronized (lock) {
                    if (in == null)
                        return;
                    try {
                        in.close();//in 就是我们传入的 new FileReader(filePath), 关闭了.
                    } finally {
                        in = null;
                        cb = null;
                    }
                }
            }

         */
        bufferedReader.close();
    }
}

15.5.6 处理流-BufferedInputStream 和 BufferedOutputStream

image-20250901150151238

15.5.7 介绍BufferedOutputStream

image-20250901151135691

package com.ming.outputstream_;

import java.io.*;

/**
 * @author 明
 * @version 1.0
 */
public class BufferedCopy02 {
    public static void main(String[] args) throws IOException {
        String srcFilePath = "C:/Users/yemin/Pictures/1.jpg";
        String destFilePath = "C:/Users/yemin/Pictures/111.jpg";
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        bis = new BufferedInputStream(new FileInputStream(srcFilePath));
        bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = bis.read(buffer)) != -1){
            bos.write(buffer,0,len);
        }
        bis.close();
        bos.close();
        System.out.println("拷贝完毕");
    }
}

15.5.8 对象流-ObjectInputStream 和 ObjectOutputStream

看一个需求

  1. int num = 100这个int数据保存到文件中,注意不是 100 数字,而是 int 100,并且,能够从文件中直接恢复 int 100
  2. Dog dog = new Dog(“小黄”, 3) 这个 dog 对象保存到文件中,并且能够从文件恢复。
  3. 上面的要求,就是能够将 基本数据类型 或者 对象 进行 序列化和反序列化操作

序列化和反序列化

  1. 序列化就是在保存数据时,保存数据的值和数据类型
  2. 反序列化就是在恢复数据时,恢复数据的值和数据类型
  3. 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
    • Serializable:这是一个标记接口,没有方法
    • Externalizable:该接口有方法需要实现,因此我们一般实现上面的 Serializable 接口

15.5.9 对象流介绍

功能:提供了对基本类型或对象类型的序列化和反序列化的方法
ObjectOutputStream 提供 序列化功能
ObjectInputStream 提供 反序列化功能

image-20250901151920146

应用案例

  1. 使用ObjectOutputStream 序列化 基本数据类型和一个 Dog对象(name, age), 并保存到 data.dat 文件中 ObjectOutStream_.java

    package com.ming.outputstream_;
    import java.io.FileOutputStream;
    import java.io.ObjectOutputStream;
    /**
     * @author 明
     * @version 1.0
     */
    public class ObjectOutStream_ {
        public static void main(String[] args) throws Exception{
            //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
            String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\5.dat";
    
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
    
            oos.writeInt(100);
            oos.writeBoolean(true);
            oos.writeChar('a');
            oos.writeDouble(9.5);
            oos.writeUTF("解放的快");
            //保存一个dog对象
            oos.writeObject(new Dog("旺财", 10, "日本", "白色"));
            oos.close();
            System.out.println("数据保存完毕(序列化形式)");
    
        }
    }
    
    class  Dog implements Serializable {
        private String name;
        private int age;
        //序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
        private static String nation;
        private transient String color;
        //序列化对象时,要求里面属性的类型也需要实现序列化接口
        private Master master = new Master();
    
        //serialVersionUID 序列化的版本号,可以提高兼容性
        private static final long serialVersionUID = 1L;
    
        public Dog(String name, int age, String nation, String color) {
            this.name = name;
            this.age = age;
            this.color = color;
            this.nation = nation;
        }
    
        @Override
        public String toString() {
            return "Dog{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", color='" + color + '\'' +
                    '}' + nation + " " +master;
        }
    
        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;
        }
    }
     class Master implements Serializable {
    }
    
  2. 使用ObjectInputStream 读取 data.dat 并反序列化恢复数据

package com.ming.inputstream_;

import com.ming.outputstream_.Dog;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * @author 明
 * @version 1.0
 */
public class ObjectInputStream_ {
    public static void main(String[] args) throws Exception {
        //指定反序列化的文件
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\5.dat";

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));

        //读取
        //1. 读取(反序列化)的顺序顺序和你保存数据(序列化)的顺序一致
        //2. 否则会出现异常

        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());

        System.out.println(ois.readChar());
        System.out.println(ois.readDouble());
        System.out.println(ois.readUTF());

        //dog 的编译类型是 Object , dog 的运行类型是 Dog
        Object dog = ois.readObject();
        System.out.println("运行类型=" + dog.getClass());
        System.out.println("dog信息=" + dog);//底层 Object -> Dog

        //这里是特别重要的细节:

        //1. 如果我们希望调用Dog的方法, 需要向下转型
        //2. 需要我们将Dog类的定义,放在到可以引用的位置
        Dog dog2 = (Dog)dog;
        System.out.println(dog2.getName()); //旺财..

        //关闭流, 关闭外层流即可,底层会关闭 FileInputStream 流
        ois.close();
    }
}

15.6打印流-PrintStream和PrintWriter

image-20250901152321665

image-20250901152349434

package com.ming.printstream_;

import java.io.IOException;
import java.io.PrintStream;

/**
 * @author ming
 * @version 1.0
 * 演示PrintStream (字节打印流/输出流)
 */
public class PrintStream_ {
    public static void main(String[] args) throws IOException {

        PrintStream out = System.out;
        //在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
        /*
             public void print(String s) {
                if (s == null) {
                    s = "null";
                }
                write(s);
            }

         */
        out.println("我发到付的");
        out.write("666".getBytes());
        //因为print底层使用的是write , 所以我们可以直接调用write进行打印/输出
        out.close();

        //我们可以去修改打印流输出的位置/设备
        //1. 输出修改成到 "D:\JAVA\韩顺平\代码\chapter19\1.txt"
        //2. "hello, ming~" 就会输出到 D:\JAVA\韩顺平\代码\chapter19\1.txt
        //3. public static void setOut(PrintStream out) {
        //        checkIO();
        //        setOut0(out); // native 方法,修改了out
        //   }
        System.setOut(new PrintStream("D:/JAVA/韩顺平/代码/chapter19/1.txt"));
        System.out.println("hello, ming~");


    }
}

15.7 Properties 类

15.7.1 看一个需求

com.ming.properties_ Properties01.java
如下一个配置文件 mysql.properties

ip=192.168.0.13
user=root
pwd=12345

请问编程读取 ipuserpwd 的值是多少

√ 分析

  1. 传统的方法
  2. 使用Properties类可以方便实现
public class Properties01 {
    public static void main(String[] args) throws IOException {


        //读取mysql.properties 文件,并得到ip, user 和 pwd
        BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
        String line = "";
        while ((line = br.readLine()) != null) { //循环读取
            String[] split = line.split("=");
            //如果我们要求指定的ip值
            if("ip".equals(split[0])) {
                System.out.println(split[0] + "值是: " + split[1]);
            }
        }

        br.close();
    }
}

15.7.2 基本介绍

  1. 专门用于读写配置文件的集合类
    配置文件的格式:
    键=值
    键=值
  2. 注意:键值对不需要有空格,值不需要用引号一起来。默认类型是String
  3. Properties的常见方法
    • load:加载配置文件的键值对到Properties对象
    • list:将数据显示到指定设备
    • getProperty(key):根据键获取值
    • setProperty(key,value):设置键值对到Properties对象
    • store:将Properties中的键值对存储到配置文件,在Idea中,保存信息到配置文件,如果含有中文,会存储为unicode
      http://tool.chinaz.com/tools/unicode.aspx unicode码查询工具

15.7.3 应用案例

  1. 使用Properties类完成对 mysql.properties 的读取,看演示
  2. 使用Properties类添加key-val到新文件 mysql2.properties
  3. 使用Properties类完成对 mysql2.properties 的读取,并修改某个key-val
package com.ming.properties;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

/**
 * @author ming
 * @version 1.0
 */
public class Properties02 {
    public static void main(String[] args) throws IOException {
        //使用Properties 类来读取mysql.properties 文件

        //1. 创建Properties 对象
        Properties properties = new Properties();
        //2. 加载指定配置文件
        properties.load(new FileReader("src/mysql.properties"));
        //3. 把k-v显示控制台
        properties.list(System.out);
        //4. 根据key 获取对应的值
        String user = properties.getProperty("user");
        String pwd = properties.getProperty("pwd");
        System.out.println("用户名=" + user );
        System.out.println("密码是="  + pwd);



    }
}
package com.ming.properties;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

/**
 * @author ming
 * @version 1.0
 */
public class Properties03 {
    public static void main(String[] args) throws IOException {
        //使用Properties 类来创建 配置文件, 修改配置文件内容

        Properties properties = new Properties();
        //创建
        //1.如果该文件没有key 就是创建
        //2.如果该文件有key ,就是修改
        /*
            Properties 父类是 Hashtable , 底层就是Hashtable 核心方法
            public synchronized V put(K key, V value) {
                // Make sure the value is not null
                if (value == null) {
                    throw new NullPointerException();
                }

                // Makes sure the key is not already in the hashtable.
                Entry<?,?> tab[] = table;
                int hash = key.hashCode();
                int index = (hash & 0x7FFFFFFF) % tab.length;
                @SuppressWarnings("unchecked")
                Entry<K,V> entry = (Entry<K,V>)tab[index];
                for(; entry != null ; entry = entry.next) {
                    if ((entry.hash == hash) && entry.key.equals(key)) {
                        V old = entry.value;
                        entry.value = value;//如果key 存在,就替换
                        return old;
                    }
                }

                addEntry(hash, key, value, index);//如果是新k, 就addEntry
                return null;
            }

         */
        properties.setProperty("charset","utf-8");
        properties.setProperty("username","汤姆");
        properties.setProperty("password","540088");

        //将k-v 存储文件中即可
        properties.store(new FileOutputStream("src\\mysql2.properties"),null);
        System.out.println("保存配置文件成功~");

    }
}

15.8 转换流(InputStreamReaderOutputStreamWriter

转换流是字节流与字符流之间的桥梁,能在字节流操作过程中指定字符编码,有效解决中文乱码等问题。

15.8.1 InputStreamReader(字节流转字符流)

InputStreamReader 可将字节输入流(如 FileInputStream)转换为字符输入流,并指定字符编码(如 GBKUTF-8 等),方便按指定编码读取字符数据。

示例代码(解决中文读取乱码问题)

package com.ming.transformation;

import java.io.*;

/**
 * 演示使用 InputStreamReader 转换流解决中文乱码问题
 * 将字节流 FileInputStream 转成字符流 InputStreamReader,指定编码 UTF-8
 */
public class InputStreamReader_ {
    public static void main(String[] args) throws IOException {
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\1.txt";
        // 将 FileInputStream 转为 InputStreamReader,指定编码为 UTF-8
        // 再将 InputStreamReader 传入 BufferedReader,利用缓冲提高读取效率
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"));
        // 按行读取文件内容
        String s = br.readLine();
        System.out.println("读取内容=" + s);
        // 关闭外层缓冲流,会自动关闭内层的转换流和字节流
        br.close();
    }
}

代码说明

  • 通过 new InputStreamReader(new FileInputStream(filePath), "UTF-8"),把字节输入流 FileInputStream 转换为指定 UTF-8 编码的字符输入流 InputStreamReader
  • 结合 BufferedReader 进行缓冲读取,提升读取大文件时的性能,最后调用 readLine() 按行读取内容。

15.8.2 OutputStreamWriter(字符流转字节流)

OutputStreamWriter 能把字符输出流转换为字节输出流,同时指定字符编码,确保字符按指定编码写入到字节流(如文件)中。

示例代码(指定编码写入文件)

package com.ming.transformation;

import java.io.*;

/**
 * 演示 OutputStreamWriter 使用
 * 把 FileOutputStream 字节流,转成字符流 OutputStreamWriter,指定处理的编码为 UTF-8
 */
public class OutputStreamWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\1.txt";
        String charSet = "UTF-8";
        // 将 FileOutputStream 转为 OutputStreamWriter,指定编码为 UTF-8
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
        // 结合 BufferedWriter 进行缓冲写入,提升写入性能
        BufferedWriter bw = new BufferedWriter(osw);
        bw.write("hi, 123346密密麻麻的");
        bw.newLine(); // 写入换行符
        bw.write("ming");
        // 关闭缓冲流,会自动刷新并关闭内层的转换流和字节流
        bw.close();
        System.out.println("按照 " + charSet + " 保存文件成功~");
    }
}

代码说明

  • 利用 new OutputStreamWriter(new FileOutputStream(filePath), charSet),将字节输出流 FileOutputStream 转换为指定 UTF-8 编码的字符输出流 OutputStreamWriter
  • 借助 BufferedWriter 实现缓冲写入,newLine() 方法用于写入系统相关的换行符,让换行更具兼容性。

15.8.3 字符流默认编码问题与转换流的必要性

当直接使用字符流(如 FileReader)读取文件时,默认采用系统的字符编码(通常为 UTF-8,但不同环境可能有差异)。若文件编码与默认编码不一致,就会出现乱码。而转换流可显式指定编码,避免该问题。

示例代码(直接用 FileReader 可能出现乱码的情况)

package com.ming.transformation;

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

/**
 * 演示直接使用 FileReader 可能出现的编码问题
 */
public class CodeQuestion {
    public static void main(String[] args) throws IOException {
        String filePath = "D:\\JAVA\\韩顺平\\代码\\chapter19\\2.txt";
        // 直接使用 FileReader,默认采用系统编码(若文件编码非系统编码,可能乱码)
        BufferedReader br = new BufferedReader(new FileReader(filePath));
        String s = br.readLine();
        System.out.println("读取到的内容: " + s);
        br.close();
    }
}

说明
2.txt 文件的编码不是系统默认编码(比如文件是 GBK 编码,而系统默认是 UTF-8),使用 FileReader 读取就会出现中文乱码。此时,就需要用 InputStreamReader 并指定 GBK 编码来正确读取。

15.8.4 转换流的扩展应用场景

  1. 多编码文件处理:在需要处理不同编码的文件(如同时处理 GBKUTF-8 编码的配置文件)时,可通过转换流灵活指定编码,统一处理逻辑。
  2. 网络流编码转换:当从网络获取字节流数据(如 HTTP 响应体),且已知数据编码时,用 InputStreamReader 转换为字符流并指定编码,能方便地处理文本内容。
  3. 日志文件编码统一:若应用生成的日志文件需要按特定编码(如 UTF-8)存储,可通过 OutputStreamWriter 指定编码,将日志内容正确写入文件。
posted @ 2025-09-01 15:43  *珍惜当下*  阅读(10)  评论(0)    收藏  举报