IO流

File与IO流

Author: Msuenb

Date: 2023-02-21


File类

  • java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关
  • File 能新建、删除、重命名文件和目录,但不能访问文件内容。 访问文件内容,需要使用输入/输出流
  • 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象;但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录
  • File对象可以作为参数传递给流的构造器

File常用构造方法:

序号 方法 描述
1 public File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
2 public File(String parent, String child) 父路径名字符串和子路径名字符串创建新的 File实例。
3 public File(File parent, String child) 父抽象路径名和子路径名字符串创建新的 File实例。
File file1 = new File("D:\\aaa.txt");

FIle file2 = new File("D:\\ddd\bbb.txt");

File parentDir = new File("D:\\ddd");
File file3 = new File(parentDir, "ccc.txt");

File常用方法:

序号 方法 描述
1 public String getAbsolutePath() 获取绝对路径
2 public String getName() 获取名称
3 public String getParent() 获取上层文件目录路径。若无,返回null
4 public long length() 获取文件长度
5 public boolean isDirectory() 判断是否是文件目录
6 public boolean isFile() 判断是否是文件
7 public boolean exists() 判断是否存在
8 public boolean createNewFile() 创建文件
9 public boolean mkdirs() 创建文件目录。如果上层文件目录不存在,一并创建
10 public boolean delete() 删除文件或者文件夹
@Test
public void test01() throws IOException {
    File dir1 = new File("E:\\iotest\\dir1");	// \ 为转义
    if (!dir1.exists()) // 如果 E:\iotest\dir1 不存在 就创建为目录
        dir1.mkdir();

    // 创建dir2目录 以dir1为父目录
    File dir2 = new File(dir1, "dir2");
    if (!dir2.exists()) dir2.mkdirs();

    // 在dir2目录下创建文件 a.txt
    File file = new File(dir2, "a.txt");
    if (!file.exists()) file.createNewFile();
}
@Test
public void test02() {
    File file = new File("E:\\iotest\\dir1\\dir2\\a.txt");
    File absoluteFile = file.getAbsoluteFile(); // 获取绝对路径
    String name = file.getName();
    String parent = file.getParent();
    boolean isDirectory = file.isDirectory();
    boolean isFile = file.isFile();

    if (file.exists()) file.delete();  // 删除文件
}

IO流分类

根据数据的流向分为:输入流和输出流。

  • 输入流:把数据从其他设备上读取到内存中的流。以InputStream,Reader结尾
  • 输出流 :把数据从内存 中写出到其他设备上的流。以OutputStream、Writer结尾

根据数据的类型分为:字节流和字符流。

  • 字节流 :以字节为单位,读写数据的流。以Stream结尾
  • 字符流 :以字符为单位,读写数据的流。以Reader和Writer结尾

根据IO流的角色不同分为:节点流和处理流。

  • 节点流:直接从数据源或目的地读写数据。常用的节点流:

    • 文件IO流: FileInputStream、FileOutputStream、FileReader、FileWriter 。

    • 字符串IO流: StringReader、StringWriter。

    • 数 组IO流: ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter。

  • 处理流:是对一个已存在的流进行连接和封装,通过所封装的流的功能调用实现数据读写。 常用处理流:

    • 缓冲流:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter。增加缓冲功能,避免频繁读写硬盘。

    • 转换流:InputStreamReader、OutputStreamReader。实现字节流和字符流之间的转换。

    • 数据流:DataInputStream、DataOutputStream。提供读写Java基础数据类型功能

    • 对象流:ObjectInputStream、ObjectOutputStream。提供直接读写Java对象功能

    • 打印流:PrintStream、PrintWriter。提供各种print、println方法输出各种类型的数据

    • 管道IO流:PipedInputStream、PipedOutputStream、PipedReader、PipedWriter。负责两个线程之间的数据交互

    装饰者设计模式:

    IO流的设计使用了装饰模式(Decorator Pattern)也称为包装模式(Wrapper Pattern)。装饰模式是使用一种对客户端透明的方式来动态地扩展对象的功能,它是通过继承扩展功能的替代方案之一。

输入流 输出流
字节流 InputStream OutputStream
字符流 Reader Writer

文件流

  • FIleInputStream和FileOutputStream是字节输入流和字节输出流。
  • FileReader和FileWriter是字符输入流和字符输出流。

FIleInputStream和FileOutputStream

FileOutputStream 用于写出非文本数据之类的原始字节流;FileOutputStream 从文件系统中的某个文件中获得输出字节。

读入字节数据:

@Test
public void input() {
    File file = new File("E:\\iotest\\dir\\test.txt");
    // 1 建立一个字节流对象,将已存在的一个文件加载进流
    FileInputStream fis = null;

    try {
        fis = new FileInputStream(file);
        int len;
        // 2 创建一个临时存放数据的数组
        byte[] buf = new byte[1024];
        // 3 调用字节流对象的读取方法将流中的数据读入到数组
        while ((len = fis.read(buf)) != -1) {
            System.out.println(new String(buf, 0, len));
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fis != null) fis.close();    // 4 关闭资源
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

写出字节数据:

@Test
public void output() {
    File file = new File("E:\\iotest\\dir\\test.txt");
    FileOutputStream fos = null;

    try {
        fos = new FileOutputStream(file, true); // 追加写   如果file不存在会自动创建
        // fos = new FileOutputStream(file);   // 覆盖写
        String str = "hello, world!\r\n";
        fos.write(str.getBytes());	// 写入数据
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            if (fos != null) fos.close();	// 关闭资源
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

FileReader和FileWriter

FileReader用于写出非文本数据之类的原始字符流;FileWriter从文件系统中的某个文件中获得输出字符。

读入字符数据:

@Test
public void reader() throws IOException {
    File file = new File("E:\\iotest\\dir\\test.txt");
    FileReader fr = new FileReader(file);

    int len;
    char[] cbuf = new char[1024];
    while ((len = fr.read(cbuf)) != -1) {
        System.out.println(new String(cbuf, 0, len));
    }

    fr.close();
}

写出字符数据:

@Test
public void writer() throws IOException {
    File file = new File("E:\\iotest\\dir\\test.txt");
    FileWriter fw = new FileWriter(file, true);

    String str = "hello, java!";
    fw.write(str);
    fw.flush(); // 刷新缓冲区 fw还可以用

    fw.close(); // 刷新缓冲区 并关闭资源
}

FileWriter与FileOutputStream不同。因为内置缓冲区的原因,需要flush 或 close才能写出字符到文件中

缓冲流

缓冲流,也叫高效流,按照数据类型分类:

  • 字节缓冲流:BufferedInputStream,BufferedOutputStream
  • 字符缓冲流:BufferedReader,BufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组(默认8K字节),通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

使用缓冲流复制文件:

public class BufferStreamTest {
    public static void main(String[] args) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            bis = new BufferedInputStream(new FileInputStream("E:\\iotest\\dir\\test.txt"));
            bos = new BufferedOutputStream(new FileOutputStream("E:\\iotest\\dest.txt"));
            byte[] buf = new byte[1024];
            int len;
            while ((len = bis.read(buf)) != -1) {
                bos.write(buf, 0, len);
            }
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bis != null) bis.close();
                if (bos != null) bos.close();	// 会自动关闭它包装的底层节点流
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

转换流

转换流提供了在字节流和字符流之间的转换

Java API提供了两个转换流:

  • InputStreamReader:将InputStream转换为Reader,需要和InputStream“套接”。
  • OutputStreamWriter:将Writer转换为OutputStream,需要和OutputStream“套接”

字节流中的数据都是字符时,转成字符流操作更高效。很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。

public static void main(String[] args) throws IOException {
    FileInputStream fis = new FileInputStream("E:\\iotest\\dir\\test.txt");
    FileOutputStream fos = new FileOutputStream("E:\\iotest\\dest2.txt");

    InputStreamReader isr = new InputStreamReader(fis, "GBK");
    OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");

    BufferedReader br = new BufferedReader(isr);
    BufferedWriter bw = new BufferedWriter(osw);
    String str = null;
    while ((str = br.readLine()) != null) {
        bw.write(str);
        bw.newLine();
        bw.flush();
    }
    bw.close();
    br.close();
}

对象流

ObjectInputStream 和 OjbectOutputSteam 用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

  • 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
  • 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制

ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量

某个类的对象需要序列化输出时,该类必须实现java.io.Serializable 接口。如果对象的某个属性也是引用数据类型,那么如果该属性也要序列化的话,也要实现Serializable接口

class User implements Serializable {
    private String username;
    private String password;
    //  该版本号的目的在于验证序列化的对象和对应类是否版本匹配
    private static final long serialVersionUID = -2991413533082785204L;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
@Test
public void serializable() throws IOException {
    User u = new User("zhangsan", "6934y8234");
    FileOutputStream fos = new FileOutputStream("E:\\iotest\\user.dat");
    ObjectOutputStream oos = new ObjectOutputStream(fos);

    oos.writeObject(u);
    oos.close();
}

@Test
public void deserializable() throws IOException, ClassNotFoundException {
    FileInputStream fis = new FileInputStream("E:\\iotest\\user.dat");
    ObjectInputStream ois = new ObjectInputStream(fis);
    Object o = ois.readObject();
    System.out.println(o);

    ois.close();
}

输入输出流和打印流

System.out对象是PrintStream类型的。它也是IO流对象。

PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream永远不会抛出 IOException;另外,PrintStream可以设置自动刷新。

import java.io.FileNotFoundException;
import java.io.PrintStream;

public class TestPrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        PrintStream ps = new PrintStream("io.txt");
        ps.println("hello");
        ps.println(1);
        ps.println(1.5);
        ps.close();
    }
}

实现键盘输入都是通过Scanner类的对象来完成的,Scanner类不只是键盘输入。

import org.junit.Test;
import java.io.*;
import java.util.Scanner;

public class TestScanner {
    @Test
    public void test01() throws IOException {
        Scanner input = new Scanner(System.in);
        PrintStream ps = new PrintStream("1.txt");
        while(true){
            System.out.print("请输入一个单词:");
            String str = input.nextLine();
            if("stop".equals(str)){
                break;
            }
            ps.println(str);
        }
        input.close();
        ps.close();
    }
    
    @Test
    public void test2() throws IOException {
        Scanner input = new Scanner(new FileInputStream("1.txt"));
        while(input.hasNextLine()){
            String str = input.nextLine();
            System.out.println(str);
        }
        input.close();
    }
}

数据流

为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。

数据流有两个类:DataInputStream 和 DataOutputStream,分别“套接”在 InputStream 和 OutputStream 子类的流上

@Test
public void output() throws IOException {
    DataOutputStream dos = new DataOutputStream(new FileOutputStream("E:\\iotest\\dir\\data.txt"));
    dos.writeUTF("写入UTF字符串");	// 
    dos.writeBoolean(true);
    dos.writeInt(123455);
    System.out.println("写入文件成功");

    dos.close();
}
public void input() throws IOException {
    DataInputStream dis = new DataInputStream(new FileInputStream("E:\\iotest\\dir\\data.txt"));
    String info = dis.readUTF();
    boolean flag = dis.readBoolean();
    int num = dis.readInt();
    System.out.println(info);
    System.out.println(flag);
    System.out.println(num);

    dis.close();
}

try...resource

语法格式:

try(需要关闭的资源对象的声明){
    业务逻辑代码
}catch(异常类型 e){
    处理异常代码
}catch(异常类型 e){
    处理异常代码
}
....

它没有finally,也不需要程序员去关闭资源对象,无论是否发生异常,都会关闭资源对象。

需要指出的是,为了保证try语句可以正常关闭资源,这些资源实现类必须实现AutoCloseable或Closeable接口,实现这两个接口就必须实现close方法。Closeable是AutoCloseable的子接口。Java7几乎把所有的“资源类”(包括文件IO的各种类、JDBC编程的Connection、Statement等接口…)进行了改写,改写后资源类都是实现了AutoCloseable或Closeable接口,并实现了close方法。

写到try()中的资源类的变量默认是final声明的,不能修改。

示例代码:

@Test
public void test03() {
    //从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
    try(
        FileInputStream fis = new FileInputStream("d:/1.txt");
        InputStreamReader isr = new InputStreamReader(fis,"GBK");
        BufferedReader br = new BufferedReader(isr);

        FileOutputStream fos = new FileOutputStream("1.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
        BufferedWriter bw = new BufferedWriter(osw);
    ){
        String str;
        while((str = br.readLine()) != null){
            bw.write(str);
            bw.newLine();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
posted @ 2023-02-21 21:02  msuenb  阅读(12)  评论(0编辑  收藏  举报