Java-BIO深入剖析【韩顺平老师版】

本文详细介绍了Java中的IO流,包括文件的创建与读写,IO流的分类(字节流、字符流、输入流、输出流),节点流与处理流的概念,以及装饰者模式在IO流中的应用。同时讲解了缓冲流的使用,如BufferedReader和BufferedWriter,以及对象流(ObjectInputStream和ObjectOutputStream)的序列化与反序列化。此外,还涉及了标准输入输出流、转换流、Properties配置文件的操作,以及在实际编程中的应用示例。

1、文件/目录 File

文件就是保存数据的地方

比如:word文档,txt文件,excel文件…

文件内容可以是图片、视频、声音…

1)文件流

文件在程序中是以的形式来操作的

在这里插入图片描述

  • :数据在数据源(文件)和程序(内存)之间经历的路径
  • 输入流:数据从数据源(文件)到程序(内存)的路径
  • 输出流:数据从程序(内存)到数据源(文件)的路径

2)创建文件File

步骤:

1、定义File对象(多种方式)

  • 只相当于在内存中创建了File对象

2、调用File对象的createNewFile()

  • 真正把内存中的File对象写入文件
// File实现两个接口:序列化、比较
public class File
    implements Serializable, Comparable<File>

在这里插入图片描述

(1)路径名

File(String pathname)

    public void create01(){
        String filePath = "d:\\a.txt";
        File file = new File(filePath);
        try {
            file.createNewFile();
            System.out.println("文件创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

(2)父目录文件 + 子路径名

File(File parent, String child)

    public void create02(){
        File parent = new File("d:");
        String child = "b.txt";
        File file = new File(parent, child);
        try {
            file.createNewFile();
            System.out.println("文件创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

(3)父目录名 + 子路径名

File(String parent, String child)

    public void create03(){
        String parent = "d:";
        String child = "c.txt";
        File file = new File(parent, child);
        try {
            file.createNewFile();
            System.out.println("文件创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

(4)URI

File(URI uri)

  • file,uri,path相互转化

file (File)

uri (URI)

path (String)

转化方式
uri 转 filenew File(uri)
uri 转 pathuri.getPath()
file 转 urifile.toURI()
file 转 pathfile.getPath()
path 转 urinew URI(path)
path 转 filenew File(path)

3)获取文件信息

在这里插入图片描述

4)目录操作

在这里插入图片描述

在这里插入图片描述

2、IO流分类及体系图

1)概念

  • I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯
  • Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行
  • java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据

在这里插入图片描述

2)分类

  • 按数据单位分:
    • 字节流 (二进制文件;如声音、视频)
    • 字符流 (文本文件;1Byte = 8 bit)
  • 按数据流向分:
    • 输入流
    • 输出流
  • 按流的角色分:
    • 节点流
    • 处理流/包装流
(抽象基类)字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

上面4个类都是抽象类(abstract),Java的IO流有40多个类,都是由这4个基类派生出来的

这些子类名都是以父类名作为后缀的

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3)体系图

在这里插入图片描述

在这里插入图片描述

4)流与文件的关系

网购商品时,商品相当于文件,商品从卖家到用户手中的过程是物流,相当于流

在这里插入图片描述
在这里插入图片描述

3、文件流

1)文件字节流

(1)FileInputStream

在这里插入图片描述
在这里插入图片描述

实例

  • 源文件a.txt
hello,world!
abc
测试
  • read():返回读取的字节ASC码(int)
public void readFile01(){
    String filePath = "d:\\a.txt";
    // 读取到的字节的ASC码
    int readData = 0;
    FileInputStream fi = null;
    try {
        fi = new FileInputStream(filePath);
        while((readData = fi.read()) > -1){
            // 把ASC码转为char输出
            System.out.print((char)readData);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            // 关闭流
            fi.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

  • read(byte[] b):返回实际读到的字节数量(int)
public void readFile02(){
    String filePath = "d:\\a.txt";
    // 字节数组:一次读取5个字节
    byte[] buf = new byte[5];
    // 一次实际读到的字节数
    int readLen = 0;
    //FileInputStream fi = null;
    InputStream fi = null;
    try {
        fi = new FileInputStream(filePath);
        while((readLen = fi.read(buf)) > -1){
            // 把一次读到的数据(从0到readLen)构造成字符串,输出
            System.out.print(new String(buf,0,readLen));
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            // 关闭流
            fi.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

中文不适合字节流

(2)FileOutPutStream

对于FileOutputStream,如果文件不存在,会创建文件(前提是目录已经存在)

在这里插入图片描述
在这里插入图片描述

public void writeFile01(){
    String filePath = "d:\\b.txt";
    OutputStream fo = null;
    try {
        // 新写入的数据覆盖原来数据
        //fo = new FileOutputStream(filePath);

        // 新写入的数据不覆盖原来数据,在结尾添加
        fo = new FileOutputStream(filePath,true);

        // 写入单个字符;用单引号
        //fo.write('a');

        // 写入多个字符;用String,getBytes()转化
        String str = "Hello world!";
        fo.write(str.getBytes());

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            // 关闭流
            fo.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
aHello world!

(3)文件拷贝

思路分析

  1. 创建文件输入流,将文件读入到程序
  2. 创建文件的输出流,将读取到的文件数据写入到指定位置

【边读边写】读取部分数据就写入到指定文件

在这里插入图片描述

public void copy(){
    // 源文件
    //String filePath1 = ".\\file\\IMG_20150503_094805_2.jpg";
    String filePath1 = ".\\file\\l.wav";
    // 拷贝文件
    //String filePath2 = ".\\file\\_2.jpg";
    String filePath2 = ".\\file\\_3.wav";
    // 输入流
    InputStream ips = null;
    // 输出流
    OutputStream ops = null;
    try {
        ips = new FileInputStream(filePath1);
        ops = new FileOutputStream(filePath2);
        // 读取的字节数
        int readLen = 0;
        // 字节数组
        byte[] buf = new byte[1024];
        // 循环读取
        while((readLen = ips.read(buf)) > -1){
            // 写入输出流
            ops.write(buf,0,readLen);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭流
        try{
            if(ips != null){
                ips.close();
            }
            if(ops != null){
                ops.close();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

在流未关闭之前,都是以追加的方式输出

在这里插入图片描述

2)文件字符流

(1)FileReader

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

getEncoding() 是InputStreamReader的方法,可以InputStreamReader、FileReader的对象调用,不用能Reader的对象调用

  • read() 读取单个字符,返回字符编码值(int)
public void readFile01(){
    // 源文件:项目根目录下的file文件夹中
    String filePath = ".\\file\\a.txt";
    FileReader reader = null;
    // 读取到的字符编码值(int)
    int readData = 0;
    try {
        reader = new FileReader(filePath);
        // 循环读取
        while ((readData = reader.read()) > -1){
            System.out.print((char)readData);
        }
        // 输出流字符编码
        System.out.println("\n字符编码:"+reader.getEncoding());
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            // 关闭流
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • read(char[] buf) 读取多个字符到缓冲数组(char数组),返回读取到的字符数
public void readFile02(){
    // 源文件:项目根目录下的file文件夹中
    String filePath = ".\\file\\a.txt";
    FileReader reader = null;
    // 一次实际读取到的字符数量
    int readLen = 0;
    // 缓冲数组(字符数组)
    char[] buf = new char[1024];
    try {
        // 建立流
        reader = new FileReader(filePath);
        // 循环读取
        while ((readLen = reader.read(buf)) > -1){
            // 通过String构造器输出
            System.out.print(new String(buf,0,readLen));
        }
        // 得到字符编码
        System.out.println("\n字符编码:"+reader.getEncoding());
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            // 关闭流
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

(2)FileWriter

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五种写入方法

写入后要调用flush(),才能真正写入文件

==关闭流close()==时,会自动flush()

  • write(int c) 写入单个字符
  • write(char[] buf) 写入多个字符(字符数组)
  • write(char[] buf,int off,int len) 写入多个字符(字符数组),从off开始,共len个
  • write(String str) 写入字符串
  • write(String str,int off,int len) 写字符串,从off开始,共len个
public void writeFile01(){
    // 目标文件:项目根目录下的file文件夹中
    String filePath = ".\\file\\b.txt";
    Writer writer = null;
    try {
        // 输出流
        writer = new FileWriter(filePath);

        // 写入单个字符
        //writer.write('A');
        //writer.flush();

        // 写入多个字符
        //char[] buf = {'测','试','B'};
        //writer.write(buf);

        // 写入多个字符中的部分
        //char[] buf = {'测','试','B'};
        //writer.write(buf,0,2);

        // 写入字符串
        //String str = "abc测试";
        //writer.write(str);

        // 写入字符串中的部分
        String str = "abc测试";
        writer.write(str,0,4);

    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            // 关闭流;自动刷新flush
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4、节点流

节点流可以从一个特定的数据源(节点)读写数据

文件流就是一种节点流

如:FileReader、FileWriter等

在这里插入图片描述

5、处理流

1)概念

处理流(也叫包装流)是“附着”于已存在的流(节点流或其它处理流)之上,为原来的流提供附加的功能

如:FilterInputStream和FilterOutputStream及其子类、BufferedReader、BufferedWriter、FilterReader、FilterWriter等

在这里插入图片描述

2)装饰者模式

处理流属于设计模式中的装饰者模式(也叫包装器模式)

在这里插入图片描述
在这里插入图片描述

  • BufferedReader内部有一个Reader,它就是附着于Reader之上(对Reader进行装饰/包装),提供附加功能,之后可以转化为Reader的任意子类,这就是装饰器模式
  • BufferedWriter类似

3)缓冲流

(1)BufferedReader

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public void read(){
    // 目标文件路径:相对路径
    String filePath = ".\\file\\c.txt";

    BufferedReader bufferedReader = null;
    try {
        // 创建缓冲流
        bufferedReader = new BufferedReader(new FileReader(filePath));
        // 接收读取的行数据
        String strLine;
        // 如果读取的行不为null,就循环读取
        while((strLine = bufferedReader.readLine()) != null){
            // 输出
            System.out.println(strLine);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭流;只需关闭外层流,底层节点流会自动关闭
        try {
            if(bufferedReader != null){
                bufferedReader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

关闭流时,只需要关闭外层流即可,底层流会自动关闭

在这里插入图片描述

(2)BufferedWriter

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public void write(){
    // 目标文件
    String filePath = ".\\file\\d.txt";

    BufferedWriter writer = null;
    try {
        // 缓冲输出流
        writer = new BufferedWriter(new FileWriter(filePath));
        // 写字符串
        writer.write("测试abc1");
        // 换行
        writer.newLine();
        writer.write("测试abc2");
        writer.newLine();
        writer.write("测试abc3");
        writer.newLine();
        writer.write("测试abc4");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭流
        try {
            if(writer != null){
                writer.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(3)Buffered字符拷贝

BufferedReader和BufferedWriter是字符缓冲流;只适合字符数据(如文本文件)

对于声音、视频等二进制文件应选用字节缓冲流,BufferedInputStream和BufferedOutputStream


用字符流,选用ISO8859-1编码,也可以处理声音、视频等二进制文件,需结合转换流(InputStreamReader、OutputStreamWriter)

public void copy01(){
    // 目标文件
    String filePath1 = ".\\file\\a.txt";
    // 拷贝文件
    String filePath2 = ".\\file\\a_.txt";

    // 输入缓冲流
    BufferedReader reader = null;
    // 输出缓冲流
    BufferedWriter writer = null;

    try {
        // 创建缓冲流
        reader = new BufferedReader(new FileReader(filePath1));
        writer = new BufferedWriter(new FileWriter(filePath2));
        // 接收读取到的行数据
        String strLine;
        // 如果读取到的行数据不为null,就循环读取
        while ((strLine = reader.readLine()) != null){
            // 写【边读边写】
            writer.write(strLine);
            // 换行
            writer.newLine();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if(reader != null){
                reader.close();
            }
            if(writer != null){
                writer.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(4)Buffered字节拷贝

既可以拷贝字节数据(二进制数据),也可以拷贝字符数据(文件数据)

public void copy02() {
    // 目标文件
    String filePath1 = ".\\file\\1.wav";
    // 拷贝文件
    String filePath2 = ".\\file\\1_.wav";

    // 输入缓冲流
    // FilterInputStream reader = null;
    BufferedInputStream reader = null;
    // 输出缓冲流
    // FilterOutputStream writer = null;
    BufferedOutputStream writer = null;

    try {
        // 创建缓冲流
        reader = new BufferedInputStream(new FileInputStream(filePath1));
        writer = new BufferedOutputStream(new FileOutputStream(filePath2));
        // 接收读取到的数据
        byte[] buf = new byte[8192];
        // 单次读取到的字节数量
        int readLen = 0;
        // 如果读取到的字节数量不为-1,就循环读取
        while ((readLen = reader.read(buf)) > -1) {
            // 写【边读边写】
            writer.write(buf, 0, readLen);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭流
        try {
            if (reader != null) {
                reader.close();
            }
            if (writer != null) {
                writer.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4)对象流

(1)概述

  • 对象流包括 ObjectInputStream 和 ObjectOutputStream,专门用来处理对象的

  • 作用:可以将 基本数据类型 或者 对象 进行序列化 和 反序列化

    如:int num = 100,可以把值100和类型int保存到文件,并且恢复

    Dog dog = new Dog(“小黄”,3),可以把对象dog以及属性值:“小黄”和3保存到文件,并且恢复

  • 序列化 与 反序列化

    • 就是在保存 或者 恢复时,即包含数据的值,也包含数据类型(或者是对象及对象属性值)
    • 对象要支持序列化,必须实现接口(下面两个接口之一
      • Serializable:这是一个标记接口,仅仅是序列化的标记,没有方法和属性
      • Externalizable:这是自定义接口,有两个方法需要实现。(一般不用)
  • 装饰者模式

    • ObjectInputStream 和 ObjectOutputStream都有一个可以接受其它流(一般为节点流)的构造器,在这个流的基础上进行处理,所以也是一种装饰者模式

在这里插入图片描述

在这里插入图片描述

  • 重要使用场景

    在原型设计模式中,进行深拷贝时,把对象先序列化,再反序列化,就实现了深拷贝

(2)ObjectOutputStream

序列化

保存的文件类型为 .dat

public class ObjectOutputStreamDemo {
    @Test
    public void write() {
        String filePath = ".\\file\\e.dat";

        ObjectOutputStream oos = null;
        try {
            // 创建对象处理流
            oos = new ObjectOutputStream(new FileOutputStream(filePath));
            // int
            oos.writeInt(50);
            // 单字节
            oos.writeByte('A');
            // 单字符
            oos.writeChar(520);
            // 布尔值
            oos.writeBoolean(false);
            // Double
            oos.writeDouble(3.8);
            // 字符串
            oos.writeUTF("测试");
            // 对象
            oos.writeObject(new Dog("小黄",3));

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            try {
                if (oos != null) {
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

class Dog implements Serializable {
    String name;
    int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在这里插入图片描述

(3)ObjectInputStream

反序列化

反序列化顺序要和序列化时的顺序一致

@Test
public void read() {
    String filePath = ".\\file\\e.dat";

    ObjectInputStream ois = null;
    try {
        // 创建对象处理流
        ois = new ObjectInputStream(new FileInputStream(filePath));
        // int
        //oos.writeInt(50);
        System.out.println(ois.readInt());
        // 单字节
        //oos.writeByte('A');
        System.out.println((char) ois.readByte());
        // 单字符
        //oos.writeChar(520);
        System.out.println((int)ois.readChar());
        // 布尔值
        //oos.writeBoolean(false);
        System.out.println(ois.readBoolean());
        // Double
        //oos.writeDouble(3.8);
        System.out.println(ois.readDouble());
        // 字符串
        //oos.writeUTF("测试");
        System.out.println(ois.readUTF());
        // 对象
        //oos.writeObject(new Dog("小黄",3));
        try {
            Object dog = ois.readObject();
            System.out.println("dog类型:" + dog.getClass());
            System.out.println(dog);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭流
        try {
            if (ois != null) {
                ois.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

(4)对象处理流使用细节

  • 读写顺序要一致
  • 要求实现序列化/反序列化的对象,要实现Serializable接口
  • 序列化的类中建议添加SerialVersionUID,提高版本的兼容性
  • statictransient 修饰的属性不序列化
  • 需要序列化的属性类型,也需要实现Serializable接口
  • 序列化具备可继承性;如果某类实现了序列化,子类也可以序列化

5)标准输入输出流

(1)System.in

  • 编译类型:InputStream
  • 运行类型:BufferedInputStream
  • 对应设备:键盘

(2)System.out

  • 编译类型:PrintStream
  • 运行类型:PrintStream
  • 对应设备:显示器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

(3)Scanner 扫描类

public static void main(String[] args) {
    System.out.println("in类型:"+System.in.getClass());

    System.out.println("out类型:"+System.out.getClass());

    // 创建扫描器:扫描键盘输入(System.in)
    Scanner scanner = new Scanner(System.in);
    System.out.println("请输入...");

    // next() 查找并返回来自此扫描器的下一个完整标记
    // 在一行中输入“abc  lmn  xyz”时,只返回“abc”
    //String next = scanner.next();

    // nextLine() 查找并返回当前行
    String next = scanner.nextLine();

    System.out.println(next);

    // 关闭扫描器
    scanner.close();
}
in类型:class java.io.BufferedInputStream
out类型:class java.io.PrintStream
请输入...
abc lmn xyz

// next()
abc

// nextLine()
abc lmn xyz

关闭扫描器时,会自动关闭System.in

在这里插入图片描述

6)转换流

转换流包含:InputStreamReader 和 OutputStreamWriter

(1)乱码问题

public static void main(String[] args) throws IOException {
    String filePath = ".\\file\\a.txt";
    // 默认情况下,读取文件是按照 utf-8 编码
    BufferedReader br = new BufferedReader(new FileReader(filePath));

    String line = br.readLine();

    System.out.println(line);

    br.close();
}

在这里插入图片描述

在这里插入图片描述

ANSI码是国标码,对应系统就是GBK码,国标码是一个统称,每个国家都有自己的国标码,国标码是根据系统来确定的

  • 出现乱码是因为文件的编码方式与读取时的编码方式不一致
  • 转换流可以把字节流转换成字符流,字节流是可以指定编码方式

(2)InputStreamReader

InputStreamReader 是 Reader 的子类,有可以接受 InputStream 字节流 以及 编码格式的构造器,属于装饰者设计模式;可以将字节流(InputStream)包装成字符流(Reader)

在这里插入图片描述

public static void main(String[] args) throws IOException {
    String filePath = ".\\file\\a.txt";
    // 文件编码格式GBK,指定读取时所用编码格式也为GBK,就不会出现乱码了
    // 包装缓冲流(转换流(节点流))
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath),"GBK"));

    // ======= 代码分解 ======
    // 字节 节点流
    // InputStream inputStream = new FileInputStream(filePath);
    
    // 转换流:把字节流转为字符流;用GBK编码
    // Reader reader = new InputStreamReader(inputStream, "GBK");
    
    // 包装缓冲处理流:提高读取效率
    // BufferedReader br = new BufferedReader(reader);
    // =======================

    String line = br.readLine();

    System.out.println(line);

    br.close();
}

在这里插入图片描述

(3)OutputStreamWriter

OutputStreamWriter 是 Writer 的子类,有可以接受 OutputStream 字节流 以及 编码格式的构造器,属于装饰者设计模式;可以将字节流(OutputStream)包装成字符流(Writer)

在这里插入图片描述

以指定编码(GBK)保存文件

public static void main(String[] args) throws IOException {
    // 文件保存路径
    String filePath = ".\\file\\e.txt";
    // 字符编码:GBK/UTF-8/UTF8/utf-8/utf8
    String charSet = "GBK";
    // 输出字节流
    OutputStream os = new FileOutputStream(filePath);
    // 转换处理流
    Writer osw = new OutputStreamWriter(os, charSet);
    // 缓冲处理流
    BufferedWriter bw = new BufferedWriter(osw);

    bw.write("ABxmy!测试数据");

    bw.close();
}

在这里插入图片描述

7)打印输出流

(1)PrintStream(字节)

PrintStream 是 FilterOutputStream 的子类,有可以接受 OutputStream 字节流 的构造器,属于装饰者设计模式;也有可以接受文件(File/String)及字符编码 的构造器,可以把输出保存到文件

在这里插入图片描述

  • 标准输出:显示器
public static void main(String[] args) throws IOException {
    // 标准输出:显示器
    t1();
    // 指定输出:文件
    //t2();
}

public static void t1() throws IOException {
    // System.out底层类型就是PrintStream
    PrintStream out = System.out;

    // 默认情况下,PrintStream是标准输出,输出位置是:显示器
    out.println("Hello!你好!");

    // print内部调用的是write(),可以直接用write()输出
    // 由于PrintStream是字节流,所以参数是字节或字节数组
    out.write("Abc,北京".getBytes());

    // 关闭输出流
    out.close();
}

在这里插入图片描述

  • 输出至文件
public static void t2() throws IOException {
    // 改变输出位置:输出到文件
    String filePath = ".\\file\\f.txt";
    // 字符编码:GBK/UTF-8/UTF8/utf-8/utf8
    String charSet = "utf8";

    // 改变输出位置:保存到文件
    // 通过System.setOut()方法
    System.setOut(new PrintStream(filePath,charSet));

    // System.out底层类型就是PrintStream
    PrintStream out = System.out;
    // PrintStream out = new PrintStream(filePath,charSet);

    // 输出至文件
    out.println("Hello!你好!");

    // print内部调用的是write(),可以直接用write()输出
    out.write("Abc,北京".getBytes());

    out.println("测试!Xyz");

    // 关闭输出流
    out.close();
}

在这里插入图片描述

(2)PrintWriter(字符)

PrintWriter 是 Writer 的子类,有可以接受 OutputStream 字节流 的构造器,属于装饰者设计模式;也有可以接受文件(File/String)及字符编码 的构造器,可以把输出保存到文件

在这里插入图片描述

  • 标准输出:显示器

    关闭流(自动调用flush())后,才会真正输出

public class PrintWriterDemo {
    public static void main(String[] args) throws IOException {
        // 标准输出:显示器
        t1();
        // 指定输出:文件
        //t2();
    }

    public static void t1() throws IOException {
        // System.out底层类型就是PrintStream:字节流
        PrintWriter out = new PrintWriter(System.out);

        // 默认情况下,PrintWriter是标准输出,输出位置是:显示器
        out.println("Hello!你好!");

        // print内部调用的是write(),可以直接用write()输出
        // 由于PrintWriter是字符流,所以参数直接用字符串就可以
        out.write("Abc,北京");

        // 关闭输出流
        out.close();
    }
}    

在这里插入图片描述

在这里插入图片描述

  • 输出至文件
public static void t2() throws IOException {
    // 改变输出位置:输出到文件
    String filePath = ".\\file\\f1.txt";
    // 字符编码:GBK/UTF-8/UTF8/utf-8/utf8
    String charSet = "utf8";

    // 改变输出位置:保存到文件
    PrintWriter out = new PrintWriter(filePath,charSet);

    // 输出至文件
    out.println("Hello!你好!");

    // print内部调用的是write(),可以直接用write()输出
    out.write("Abc,北京");

    out.println("测试!Xyz");

    // 关闭输出流
    out.close();
}

在这里插入图片描述

(3)刷新问题

只有在使用缓冲时才会有刷新的问题

  • PrintStream是字节流,在不使用缓冲流(BufferedOutputStream)写入文件时,不论关不关流close(),都会写入文件;在使用缓冲流(BufferedOutputStream)写入文件时,只有自动刷新autoFlush是true时,才会刷新;为false时(默认),写入文件后,要调用flush()或close(),数据才会真正写入文件

    简要:PrintStream 使用缓冲时,在刷新问题上和 PrintWriter 一致

    刷新autoFlush使用缓冲BufferedOutputStream不用缓冲BufferedOutputStream
    true可以写入文件可以写入文件
    false只有flush()或close()后才写入可以写入文件

在这里插入图片描述

  • PrintWriter是字符流,天生使用了缓冲,所以有刷新的问题;在autoFlush是true时,才会刷新;为false时(默认),写入文件后,要调用flush()或close(),数据才会真正写入文件

6、操作Properties

Properties配置文件

在这里插入图片描述

1)传统方法

public class PropertiesDemo {
    public static void main(String[] args) throws IOException {
        read01();
    }

    /**
     * 传统方法读取
     * @throws IOException
     */
    public static void read01() throws IOException {
        // 文件路径:两种方式都可以
        String filePath = ".\\src\\main\\resources\\db.properties";
        //String filePath = "src\\main\\resources\\db.properties";

        // 缓冲字符读取流
        BufferedReader br = new BufferedReader(new FileReader(filePath));
        String line = "";
        // 循环读取
        while((line = br.readLine()) != null){
            // 对读取的行数据进行拆分
            String[] split = line.split("=");
            System.out.println(split[0] + "值为:"+split[1]);
        }
        // 关闭流
        br.close();
    }
}

2)类Properties介绍

(1)概述

  • Properties是专门用于处理配置文件的集合类

  • 文件的格式:键-值对;不需要空格,也不需要引号

    如:

    ip=192.168.100.100
    user=abc
    ps=123456
    

在这里插入图片描述

(2)常用方法

在这里插入图片描述
在这里插入图片描述

3)用Properties类操作配置文件

(1)读取

 /**
  * 用Properties读取
 */
public static void read02() throws IOException {
    // 创建Properties类
    Properties properties = new Properties();

    // 文件路径:两种方式都可以
    String filePath = ".\\src\\main\\resources\\db.properties";
    //String filePath = "src\\main\\resources\\db.properties";

    // 字节流
    //properties.load(new FileInputStream(filePath));
    // 字符流
    properties.load(new FileReader(filePath));

    String ip = properties.getProperty("ip");
    String user = properties.getProperty("user");
    String ps = properties.getProperty("ps");

    System.out.println("ip = " + ip);
    System.out.println("user = " + user);
    System.out.println("ps = " + ps);

}
ip = 192.168.100.100
user = tuwer
ps = 123456

(2)存储

/**
 * 用Properties存储
 */
public static void write01() throws IOException {
    // 创建Properties类
    Properties properties = new Properties();

    // 配置键值对
    // 如果文件中某个键存在,setProterty()就是修改,不存在时是新建
    properties.setProperty("charSet","uft-8");
    properties.setProperty("user","汤姆");
    properties.setProperty("ps","123456");

    // 文件路径
    String filePath = ".\\src\\main\\resources\\db1.properties";
    // 存储:中文以Unicode码存储
    properties.store(new FileOutputStream(filePath),"注释");
}

在这里插入图片描述

7、练习作业

1)文件创建及写入数据

判断文件夹(file/mytemp)是否存在,如果不存在就创建

判断文件夹下hello.txt文件是否存在,如果不存在就创建,如果存在就不创建,并给出相应提示

向文件hello.txt中写入数据

public class HomeWork {
    public static void main(String[] args) {
        work01();
    }

    public static void work01() {
        // 文件夹路径
        String dirStr = ".\\file\\a\\mytemp";
        // 文件夹对象
        File dir = new File(dirStr);
        // 如果文件夹不存在,就创建
        if (!dir.exists()) {
            // 创建文件夹:可以创建多级目录
            dir.mkdirs();
        }

        // 文件路径
        String fileStr = "hello.txt";
        // 文件对象
        File file = new File(dir, fileStr);
        // 如果文件不存在就创建,然后提示
        if (!file.exists()) {
            try {
                // 创建文件
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("文件" + fileStr + "不存在,已创建成功!");
        }
        // 如果文件存在,给出提示
        else {
            System.out.println("文件" + fileStr + "已经存在!");
        }
        
        // 写入数据
        FileWriter fw = null;
        try {
            // 文件字符输出流
            fw = new FileWriter(file);
            fw.write("Hello world!测试!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            if(fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("数据写入成功!");
    }
}

在这里插入图片描述

2)读取文件并添加行号输出

用BufferedReader

/**
 * 使用BufferedReader读取文件,并加上行号,输出到屏幕
 */
public static void work02() {
    // 目标文件路径
    String filePath = ".\\file\\a\\mytemp\\hello.txt";
    // 创建文件
    File file = new File(filePath);
    // 判断文件是否存在
    if (!file.exists()) {
        System.out.println("文件不存在!");
        return;
    }

    BufferedReader br = null;
    try {
        // 创建读取缓冲流
        br = new BufferedReader(new FileReader(file));
        String lineStr = "";
        int lineNumber = 0;
        // 循环读取并添加行号输出
        while ((lineStr = br.readLine()) != null) {
            System.out.println(++lineNumber + "、 " + lineStr);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭流
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述

3)获取文件数据创建对象

  • 用properties的值作为属性值创建对象;然后对象序列化输出
/**
 * 用properties的值作为属性值创建对象
 */
public static void work03() {
    // =====读取配置文件数据=====
    // 配置文件路径
    String filePath = ".\\src\\main\\resources\\dog.properties";
    File file = new File(filePath);
    if (!file.exists()) {
        System.out.println("文件不存在!");
        return;
    }

    String name = "";
    int age = -1;
    String color = "";

    // 创建Properties类
    Properties properties = new Properties();
    try {
        // 加载配置文件
        properties.load(new BufferedReader(new FileReader(file)));
        name = properties.getProperty("name");
        age = Integer.parseInt(properties.getProperty("age"));
        color = properties.getProperty("color");
    } catch (IOException e) {
        e.printStackTrace();
    }

    // =====创建对象=====
    // 检查是否读取到数据
    if (name == null || name.length() < 1 || age < 0 || color == null || color.length() < 1) {
        System.out.println("属性值读取失败!");
        return;
    }

    Dog dog = new Dog(name, age, color);
    System.out.println(dog);

    // =====序列化对象到文件=====
    String objectFilePath = ".\\src\\main\\resources\\dog.dat";
    ObjectOutputStream oos = null;
    try {
        // 创建对象处理流
        oos = new ObjectOutputStream(new FileOutputStream(objectFilePath));
        // 写入对象
        oos.writeObject(dog);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (oos != null) {
                oos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    System.out.println("对象" + dog + "已输出到文件!");
}
class Dog implements Serializable{
    private String name;
    private int age;
    private String color;

    public Dog(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

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

在这里插入图片描述

  • 反序列化创建对象
/**
 * 反序列化
 * 从文件中读取对象
 */
public static void readObject(){
    // 对象文件路径
    String filePath = ".\\src\\main\\resources\\dog.dat";
    File file = new File(filePath);
    if(!file.exists()){
        System.out.println("文件不存在!");
        return;
    }

    ObjectInputStream ois = null;
    try {
        // 创建对象处理流
        ois = new ObjectInputStream(new FileInputStream(file));
        try {
            // 读对象
            Object dog = ois.readObject();
            // 对象类型
            System.out.println(dog.getClass());
            // 输出Object
            System.out.println(dog);

            // 强转
            Dog d = (Dog)dog;
            // 输出Dog
            System.out.println(d);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if(ois!=null){
                ois.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土味儿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值