Java 基础 07. Java I/O 流

1. 流的概念

  • 内存与存储设备之间传输数据的通道
  • 数据借助流传输

2. 流的分类

按方向(重点)

  • 输入流:将 存储设备 中的内容读到 内存 中;
  • 输出流:将 内存 中的内容写到 存储设备 中;

按单位

  • 字节流:以字节为单位,可以读写 所有数据
  • 字符流:以字符为单位,只能读写 文本数据

按功能

  • 节点流:具有 实际传输数据读写功能
  • 过滤流:在节点流的基础之上 增强功能

3. 字节流

  • 字节流的父类(抽象类)
// InputStream 字节输入流 (读取)
public int read(){}
public int read(byte[] b){}
public int read(byte[] b, int off, int len){}

// OutputStream 字节输出流 (写入)
public void write(int n){}
public void write(byte[] b){}
public void write(byte[] b, int off, int len){}

文件字节流

  • 文件字节输入流:FileInputStream
public static void main(String[] args) throws Exception{
  // 1 创建FileInputStream 并指定文件路径
  FileInputStream fis = new FileInputStream("d:\\abc.txt");
  // 2 读取文件
  // fis.read();
  // 2.1单字节读取
  int data = 0;
  while((data = fis.read()) != -1){
    System.out.println((char)data);
  }
  // 2.2 一次读取多个字节
  byte[] buf = new byte[3]; // 大小为3的缓存区
  int count = fis.read(buf); // 一次读3个
  System.out.println(new String(buf));
  System.out.println(count);
  int count2 = fis.read(buf); // 再读3个
  System.out.println(new String(buf));
  System.out.println(count2);
  
  // 上述优化后
  int count = 0;
  while((count = fis.read(buf)) != -1){
    System.out.println(new String(buf, 0, count));
  }
  
  // 3 关闭
  fis.close();
}
  • 文件字节输出流:FileOutputStream
public static void main(String[] args) throws Exception{
  // 1 创建文件字节输出流
  // true 表示不覆盖 接着写 
  FileOutputStream fos = new FileOutputStream("路径", true);
    // 2 写入文件
  fos.write(97);
  fos.write('a');
  // String string = "hello world";
  fos.write(string.getBytes());
  // 3 关闭
  fos.close();
} 

图片复制案例

// 1 创建流
// 1.1 文件字节输入流
FileInputStream fis = new FileInputStream("路径");
// 1.2 文件字节输出流
FileInputStream fos = new FileOutpuStream("路径");
// 2 边读边写
byte[] buf = new byte[1024];
int count = 0;  // 计数器
while((count = fis.read(buf)) != -1){
  fos.write(buf, 0, count);
}
// 3 关闭
fis.close();
fos.close();

字节缓冲流(过滤流):提高 IO 效率

  • 缓冲流:BufferedInputStream/ BufferedOutputStream
    • 提高 IO 效率,减少访问磁盘次数;
    • 数据存储在缓冲区中(8K),flush 是将缓冲区的内容写入文件中,也可以直接 close。
  • BufferedInputStream:读取文件
// 使用字节缓冲流 读取 文件
public static void main(String[] args) throws Exception{  // 抛出异常
  // 1 创建BufferedInputStream
  FileInputStream fis = new FileInputStream("路径");
  BufferedInputStream bis = new BufferedInputStream(fis);
  // 2 读取  BufferedInputStream内部自定义了8K缓冲区
  int data = 0;
  while((data = bis.read()) != -1){
    System.out.print((char)data);
  }
  // 用自己创建的缓冲流
  byte[] buf = new byte[1024];
  int count = 0;
  while((count = bis.read(buf)) != -1){
    System.out.println(new String(buf, 0, count));
  }
  
  // 3 关闭,同时也关闭了 fis
  bis.close();
}
  • BufferedOutputStream:写入文件
// 使用字节缓冲流 写入 文件
public static void main(String[] args) throws Exception{
  // 1. 创建BufferedInputStream
  FileOutputStream fos = new FileOutputStream("路径");
  BufferedOutputStream bis = new BufferedOutputStream(fos);
  // 2. 写入文件
  for(int i = 0; i < 10; i ++){
    bos.write("hello".getBytes());// 写入内部 8k 缓冲区
    bos.flush(); // 刷新缓冲区,写入到硬盘
  }
  // 3. 关闭(内部调用flush())
  bos.close();
}

4. 对象流:ObjectOutputStream / ObjectInputStream

  • 增强了缓冲区功能
  • 增强了读写 8 种基本数据类型和字符串的功能
  • 增强了读写对象的功能
    • readObject(); :从流中读取一个对象(反序列化
    • writeObject(Object obj); :向流中写入一个对象(序列化
  • 使用流传输对象的过程,称为序列化、反序列化

5. 序列化与反序列化

序列化

  • 实例:创建 Student 类实现 Serializable 接口
package com.base.demo06;

import java.io.Serializable;

// 要求:序列化类,必须要实现 Serializable 接口
public class Student implements Serializable {
    // serialVersionUID 序列化版本号,保证序列化、反序列化为同一个类
    private static final long serialVersionUID = -3115151823789216775L;
    private String name;
    // transient(内存中,瞬时的) 不被序列化
    private transient int age;
    // 静态属性,不被序列化
    private static String country = "China";

    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 +
                '}';
    }
}
  • 序列化
package com.base.demo06;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

public class Demo06 {
    public static void main(String[] args) throws Exception {
        /*
        1. 使用 ObjectOutputStream 实现对象的序列化(写入)
        2. 要求:序列化类,必须要实现 Serializable 接口
        */
        // 1. 创建对象流
        FileOutputStream fos = new FileOutputStream("d:\\stu.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        // 2. 序列化(写入操作)
        Student s1 = new Student("Liu", 20);
        // 创建多个对象
        Student s2 = new Student("Chen", 21);
        // 集合方式
        ArrayList list = new ArrayList<>();
        list.add(s1);
        list.add(s2);

        /*
        普通方式,写入多个对象
        oos.writeObject(s1);
        oos.writeObject(s2);
        */

        // 集合写入
        oos.writeObject(list);

        // 3. 关闭
        oos.close();
        System.out.println("序列化完华");
    }
}

反序列化

  • 实例:
package com.base.demo06;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;

public class Demo07 {
    public static void main(String[] args) throws Exception {
        // 使用 ObjectInputStream 实现反序列化(读取重构成对象)
        // 1.创建对象流
        FileInputStream fis = new FileInputStream("d:\\stu.bin");
        ObjectInputStream ois = new ObjectInputStream(fis);

        // 2.读取文件(反序列化)
//        Student s = (Student) ois.readObject();    // 强制转换成 Student 类型
//        Student s1 = (Student) ois.readObject();
        // 集合方式读取
        ArrayList list = (ArrayList) ois.readObject();    // 强制转换
        // 3.关闭
        ois.close();
        System.out.println("反序列化,执行完华");
//        System.out.println(s.toString());
//        System.out.println(s1.toString());
        System.out.println(list.toString());
    }
}
  • 运行结果:
  • readObject(),读取完后,再次读取时,会出现异常

注意事项:

  1. 类要想序列化,必须实现 Serializable 接口;
  2. 序列化类中,对象属性要求实现 Serializable 接口;
  3. 序列化版本号 ID(serialVersionUID),保证序列化的类和反序列化的类是同一个类;
  4. 使用 transient(内存中,瞬时的)修饰属性,这个属性就不能被序列化;
  5. 静态属性(static)不能被序列化;
  6. 序列化多个对象,可以借助集合来实现。

6. 编码方式

  • UTF-8 等
    编码方式要一致:当编码方式和解码方式不一致时,会出现乱码。

7. 字符流

// 传统字节流读取
public static void main(String[] args){
  // 1. 创建FileInputStream 对象
  FileInputSteam fis = new FileInputStream("路径");
  // 2. 读取
  int data = 0;
  // 3. 字节流按一个字节为单位来读取
  while((data = fis.read()) != -1){
    System.out.print((char)data); 
  }
  // 3. 关闭
  fis.close();
}
  • 字节流读取汉字时乱码:(一般一个汉字 2 个字节),字节流按一个字节为单位读取

字符流的父类(抽象类)

  • reader 字符输入流
    • public int read(){}
    • public int read(char[] c){}
    • public int read(char[] b, int off, int len){}
  • writer 字符输出流
    • public void write(int n){}
    • public void write(String str){}
    • public void write(char[] c){}

FileReader 文件字符输入流:(读取)

// 1. 创建FileReader 文件字符输入流
FileReader fr = new FileReader("路径");
// 2. 读取
// 2.1 单个字符读取
int data = 0;
while((data = fr.read()) != -1){
  System.out.print((char)data);// 读取一个字符
}
// 2.2 字符缓冲区读取
char[] buf = new char[2];
int count = 0;  // 计数器
while((count = fr.read(buf) != -1)){
  System.out.println(new String(buf, 0, count));
}
// 3. 关闭
fr.close();

FileWriter 文件字符输出流:(写入)

// 1. 创建FileWriter对象
FileWriter fw = new FileWriter("路径");
// 2. 写入
for(int i = 0; i < 10; i ++){
  fw.write("写入的内容\r\n");  // \r\n 换行
  fw.flush();  // 刷新,写入
}
// 3. 关闭
fw.close();
System.out.println("执行完毕");

案例:文本文件复制

  • 字符流:不能复制图片或二进制文件;字节流:可以复制任意文件
    字符流读取文件时,会将文件转化成字符
public static void main(String[] args) throws Exception{
  // 1. 创建流
  FileReader fr = new FileReader("路径");
  FileWriter fw = new FileWriter("路径");
  // 2. 读写
  int data = 0;
  while((data = fr.read()) != -1){
    fw.write(data);
    // 刷新,写入
    fw.flush();
  }
  // 3. 关闭
  fw.close();
  fr.close();
}

字符缓冲流:BufferedReader / BufferedWriter

  • 高效读写;
  • 支持输入换行符;
  • 可一次写一行读一行。

BufferedReader:读取缓冲流

public static void main(String[] args) throws Exception{
  // 创建缓冲流
  FileReader fr = new FileReader("路径");
  BufferedReader br = new BufferedReader(fr);
  // 读取
  // 1. 第一种方式
  char[] buf = new char[1024];  // 自定义缓冲区
  int count = 0;
  while((count = br.read(buf)) != -1){
    System.out.print(new String(buf, 0, count));
  }
  // 2. 第二种方式:readLine() 一行一行读取
  String line = null;
  while((line = br.readLine()) != null){
    System.out.println(line);
  }
  // 3.关闭
  br.close();
}

BufferedWriter:写入缓冲流

public static void main(String[] args) throws Exception{
  // 1. 创建BufferedWriter对象
  FileWriter fw = new FileWriter("路径");
  BufferedWriter bw = new BufferedWriter(fw);
  // 2. 写入
  for(int i = 0; i < 10; i ++){
    bw.write("写入的内容");
    bw.newLine();  // 写入一个换行符,根据操作系统,自动调整
    bw.flush();
  }
  // 3. 关闭
  bw.close(); // 此时会自动关闭fw
}

PrintWrite:打印流

  • 封装了print() / println() 方法,支持写入后换行
  • 支持数据原样打印
public static void main(String[] args) throws Exception{
  // 1 创建打印流
  PrintWriter pw = new PrintWriter("路径");
  // 2 打印
  pw.println(97);  // 97 原样打印,非字母 a
  pw.println(true);
  pw.println(3.14);
  pw.println('a');
  // 3 关闭
  pw.close();
}

转换流

  • 又称桥转换流: InputStreamReader / OutputStreamWriter
  • 可将字节流转换为字符流
  • 可设置字符的编码方式

InputStreamReader:读取

public static void main(String[] args) throws Exception{
  // 1 创建InputStreamReader对象
  FileInputStream fis = new FisInputStream("路径");
  // 指定读取编码方式,要与文件编码方式统一,否则乱码
  InputStreamReader isr = new InputStreamReader(fis, "utf-8");
  // 2 读取文件
  int data = 0;
  while((data = isr.read()) != -1){
    System.out.print((char)data);
  }
  // 3 关闭
  isr.close();
}

OutputStreamWriter:写入

public static void main(String[] args) throws Exception{
  // 1 创建OutputStreamReader对象
  FileOutputStream fos = new FisOutputStream("路径");
  OutputStreamWriter osw = new OutputStreamReader(fos,"utf-8");
  // 2 写入
  for(int i = 0; i < 10; i ++){
    osw.write("写入内容\r\n");
    osw.flush();
  }
  // 3 关闭
  osw.close();
}

8. File 类

概念:代表物理盘符中的一个 文件 或者 文件夹

方法名 说明
createNewFile() 创建一个新文件,需要声明异常
mkdir() 创建一个新目录
delete() 删除文件或空目录
exists() 判断文件或目录是否存在
getAbsolutePath() 获取文件的绝对路径
getName() 获取文件或目录的名称
getParent() 获取文件或目录所在的目录名称
isDirectory() 判断是否是一个目录
isFile() 判断是否是文件
length() 获取文件的长度
ListFiles() 列出目录中的所有内容
renameTo() 修改文件名称
  • 分隔符、文件操作、文件夹操作
/*
File类的使用
1. 分隔符
2. 文件操作
3. 文件夹操作
*/
public class Demo01 {
    public static void main(String[] args) throws Exception {
        // 1.分隔符
        // separator();
        // 2.文件操作
        fileOpe();
        // 3.文件夹操作
        // directoryOpe();
    }
  // 1、分隔符
  public static void separator(){
    System.out.println("路径分隔符" + File.pathSeparator);
    System.out.println("名称分隔符" + File.separator);
  }
  // 2、文件操作
  public static void fileOpe() throws Exception{
    // 1. 创建文件
    if(!file.exists()){ // 是否存在
        File file = new File("路径");
        boolean b = file.creatNewFile();
        System.out.println("创建结果:" + b);
    }
    
    // 2. 删除文件
    // 2.1 直接删除
    file.delete(); // 成功 true
    // 2.2 使用 jvm 退出时删除
    file.deleteOnExit();
    
    // 3. 获取文件信息
    System.out.println("获取绝对路径" + file.getAbsolutePaht());
    System.out.println("获取路径" + file.getPath());
    System.out.println("获取文件名称" + file.getName());
    System.out.println("获取父目录" + file.getParent());
    System.out.println("获取文件长度" + file.length());
    System.out.println("文件创建时间" + new Date(file.lashModified()).toLocalString());
    
    // 4. 判断
    System.out.println("是否可写:" + file.canWrite());
    System.out.println("是否是文件:" + file.isFile());
    System.out.println("是否隐藏:" + file.isHidden());
  }
 
  // 3、文件夹操作
  public static void directoryOpe() throws Exception{
    // 1. 创建文件夹
    File dir = new File("路径");
    System.out.println(dir.toString());
    if(!dir.exists()){
      //dir.mkdir(); // 只能创建单级目录
      dir.mkdirs(); // 创建多级目录
    }
    
    // 2. 删除文件夹
    // 2.1 直接删除
    dir.delete(); // 只能删除最底层空目录
    // 2.2 使用 jvm 删除
    dir.deleteOnExit();
    
    // 3. 获取文件夹信息
    System.out.println("获取绝对路径" + dir.getAbsolutePaht());
    System.out.println("获取路径" + dir.getPath());
    System.out.println("获取文件名称" + dir.getName());
    System.out.println("获取夫目录" + dir.getParent());
    System.out.println("获取文件长度" + dir.length());
    System.out.println("文件夹创建时间" + new Date(dir.lashModified()).toLocalString());
    
    // 4. 判断
    System.out.println("是否是文件夹" + dir.isFile());
    System.out.println("是否隐藏" + dir.isHidden());
    
    // 5. 遍历文件夹
    File dir2 = new File("路径");
    String[] files = dir2.list();
    for(String string : files){
      System.out.println(string);
    }
    
    // FileFilter 接口的使用    
    File[] files2 = dir2.listFiles(new FileFilter(){      
      @Override
      public boolean accept(File pathname){
        if(pathname.getName().endsWith(".jpg")){
          return true;
        }
        return false;
      }
    });
    for(File file : files2){
      System.out.println(file.getName());
    }    
  }
}

FileFilter 接口:文件过滤器

  • 用来过滤文件(File对象);
  • public interface FileFilter
    • boolean accept(File pathname)
  • 当调用 File 类中的 listFiles() 方法时,支持传入 FileFilter 接口实现类,对获取文件进行过滤,只有满足条件的文件才可以出现在 listFiles() 的返回值中。
File[] files2 = dir2.listFiles(new FileFilter(){      
      @Override
      public boolean accept(File pathname){
        if(pathname.getName().endsWith(".jpg")){
          return true;
        }
        return false;
      }
    });

递归遍历文件夹

public static void main(String[] args){
  listDir(new File("d:\\myfiles"));
}
public static void listDir(File dir){
  File[] files = dir.listFiles();
  // 打印文件夹下文件
  System.out.println(dir.getAbsolutePath());
  // 判断文件夹是否为空
  if(files != null && files.length > 0){
    for(File file : files){
      // 判断是否是文件夹
      if(file.isDirectory()){
        // 如果是文件夹,递归调用listDir,遍历文件
        listDir(file); // 递归
      }else {
        // 打印子文件夹内文件
        System.out.println(file.getAbsolutePath());
      }
    }
  }
}

递归删除文件夹

public static void deleteDir(File dir){
  File[] files = dir.listFiles();
  // 判断是否为空
  if(files != null && files.length > 0){
    for(File file : files){
      // 判断是否是文件夹
      if(file.idDirectory()){
        deleteDir(file); // 递归
      }else{
        // 删除子文件夹文件
        System.out.println(file.getAbsolutePath() + " 删除:" + file.delete());
      }
    }
  }
  // 删除文件
  System.out.println(dir.getAbsolutePath() + " 删除:" + dir.delete());
}

补充:Properties

  • Properties:属性集合
  • 特点:
    1. 存储属性名和属性值
    2. 属性名和属性值都是字符串类型
    3. 没有泛型
    4. 和流有关
package com.base.demo08;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Properties;
import java.util.Set;

/*
 * Properties 集合的使用
 * */
public class Demo03 {
    public static void main(String[] args) throws Exception {
        // 1.创建集合
        Properties properties = new Properties();
        // 2.添加数据
        properties.setProperty("username", "Liu");
        properties.setProperty("age", "20");
        System.out.println(properties.toString());

        // 3.遍历
        // 3.1--------------keySet---------------
        // 3.2--------------entrySet---------------
        // 3.3--------------stringPropertyNames()---------------
        Set pronames = properties.stringPropertyNames();
        for (String pro : pronames) {
            // 打印键、值
            System.out.println(pro + "=========" + properties.getProperty(pro));
        }

        // 4.和流有关的方法
        // ---------1.list方法----------
        // 创建打印流
        PrintWriter pw = new PrintWriter("d:\\print.txt");
        properties.list(pw);
        pw.close();

        // ---------2.store方法(保存)----------
        // 创建流
        FileOutputStream fos = new FileOutputStream("d:\\store.properties");
        properties.store(fos, "注释");
        fos.close();

        // ---------3.load方法(加载)----------
        // 创建对象
        Properties properties2 = new Properties();
        // 创建流
        FileInputStream fis = new FileInputStream("d:\\store.properties");
        properties2.load(fis);
        fis.close();
        System.out.println(properties2.toString());
    }
}

你可能感兴趣的:(Java 基础 07. Java I/O 流)