本文章内容主要摘选自《Java核心技术卷II》

是一种概念,也可以看作一种管道,连接着内存与硬盘(或其他媒介)

相对程序而言,输入流是把外部媒介的数据传输到内存的对象,而输出则是到内存中的数据传输到外部媒介的对象

IO流


通过IO完成设备中文件的读和写。API中,可以从其中读入一个字节序列的对象称为输入流,而可以从其中写入一个字节序列的对象称为输出流。IO中的 I 为Input(输入),O 为Output(输出)。这些字节序列的目的地和来源地可以是一个文件,或者是一个网络设备,甚至可以是内存块

分类

字节序列流

InputStream(输入流) ,OutputStream(输出流),java.io包下的抽象类,所有字节序列输入/出流的超类。

按照字节,每次读取/写入一个字节。这类流可以操作的类型包括但不限于:文本,视频,音乐,图片等

字符序列流

Reader (输入流), Writer(输出流),java.io包下的抽象类,所有字符序列输入/出流的超类。

按照字符,每次读取/写入一个字符。这类流可以操作的类型:Unicode文本等普通文本文件。

操作系统使用的编码表会使字符占用的字节不同,如:ascii中一个中文字符占二个字节,UTF-8中一个中文字符等于三个字节。所以,使用同一流对不同编码表的文件读取/写入会有时间上的差异

扩展接口

Interface Closeable

所有的流都实现了java.io.Closeable接口,用于关闭流管道。每次在使用完流后必须要调用close方法关闭流,只有把流关闭后垃圾回收器才会对其回收,如果程序打开了多个流没有关闭,那么系统资源将被耗尽

Interface Flushable

关闭一个输出流的同时还会刷新输出流的管道,输出的字节会先存放在缓冲区中,等字节数到一定量时再同时输出。如果不关闭流,那么最后堆积的字节可能不会得到输出。也可以调用flush方法去刷新流,每个输出流都会实现Flushable接口

流对象

操作文件的流对象

FileInputStreamFileOutputStreamFileReaderFileWriter

带缓冲区的流对象

BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter

文件输入输出流


可以对磁盘(或其他媒介)中文件数据操作的流对象,如向文件中写入数据,或将文件中的数据读取到程序中

字节流

FileInputStream

文件输入流,将文件中的数据读取到内存

public FileInputStream(String name) throws FileNotFoundException {}
public FileInputStream(File file) throws FileNotFoundException {}

创建对象时可以选择绑定文件路径或File对象。

操作系统中文件路径如果是反斜扛字符\在java字符中是转义字符,要确保在Windows风格中要使用\\两个反斜扛,还可以使用单个斜扛/。但我们的程序要做到与时俱进、程序可移植应该使用程序运行平台的文件分隔符,通过java.io.File.separator获得

路径可以是相对路径或绝对路径,相对路径是基于某个文件夹下的路径,比如:目前处在G:\这个路径,当要打开当前路径下的文件或文件夹时,可以直接用文件名打开。绝对路径就是文件的全限定名,要包含有路径和文件名,比如:G:\gbk.txtC:\Demo\classTest.java,这种在任何目录下都可以定位文件

注:创建FileInputStream流时,会报FileNotFoundException异常。

读取数据

public int read() throws IOException {}

向文件中读取一个字节,并返回读取的字节。如果到达文件的末尾则返回-1

文件中的空格和换行也是有对应的字节。

public int read(byte b[]) throws IOException {}

向文件中读取最多b.length个字节到byte数组。如果到达文件的末尾则返回-1

每次读取都是覆盖byte数组。如果最后读取的有效字节个数少于b.length,除有效字节个数的索引会被覆盖,其余索引会保留上一次读取的字节

public int read(byte b[], int off, int len) throws IOException {}

向文件中读取len个字节,并将数据插入到byte数组中从off处开始的索引

其他方法

public int available() throws IOException {}

返回文件中还没有读取的字节数

public long skip(long n)throws IOException

跳过n个字节后输出

FileOutputStream

文件输出流,将内存中的数据写入到文件

public FileOutputStream(File file) throws FileNotFoundException
public FileOutputStream(String name) throws FileNotFoundException
public FileOutputStream(String name, boolean append) throws FileNotFoundException

创建对象时可以选择绑定文件路径或File对象。当File不存在就自动创建。

注:如果被写入的文件存在,那么append参数决定写入的方式,如果append参数为true那么数据会被追加到原文件的末尾;如果为false文件会被清空再写入。其他没有此参数的构造方法是将文件清空再写入,要谨慎使用因为清掉的数据无法找回

写入数据

public void write(byte[] b) throws IOException
public void write(int b) throws IOException
public void write(byte[] b, int off, int len) throws IOException

将指定的字节或字节数组写入此文件输出流。也可以将数组中某段位置的字节写到文件中

其他方法

public void flush() throws IOException

刷新此输出流并强制任何缓冲的输出字节被写出

File Hello:
    hello world!!!
    
public static void main(String[] args){
    FileInputStream input = null;
    FileOutputStream output = null;
	try{
        input = new FileInputStream("Hello.txt");
        output = new FileOutputStream("CopyHello.txt");
        
        byte[] bytes = new byte[1024];
        int readCount;
        
        while ((readCount = input.read(bytes)) != -1)
            output.write(bytes,0,readCount);
        
    }catch(FileNotFoundException e){
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finlly{
        //关闭流和刷新流前要做非空判断
        if(input != null)
        	try{
                input.close;
            }catch(IOException e){
                e.printStackTrace();
            }
        if(output != null)
        	try{
                output.flush();
                output.close;
            }catch(IOException e){
                e.printStackTrace();
            }
    }
}
	

关闭流和刷新流要在finlly语句块中,这样可以无论语句是否抛出异常,流都可以正常关闭

字符流

此类流更合适操作普通文本

FileReader

与FileInputStream基本一致,只是在读取时,按字符读取

public FileReader(String fileName) throws FileNotFoundException
public FileReader(File file) throws FileNotFoundException
public FileReader(File file, boolean append) throws IOException

创建对象时可以选择绑定文件路径或File对象。

方法也是同FileInputStream基本一致

FileWriter

与FileOutputStream基本一致,在写入时,按字符写入

public FileWriter(String fileName) throws FileNotFoundException
public FileWriter(File file) throws FileNotFoundException
public FileWriter(File file, boolean append) throws IOException

如果File不存在则自动创建File。

其他默认没有追加此append参数的构造方法是将文件清空再写入,要谨慎使用因为清掉的数据无法找回

其他方法也与FileOutputStream一致

File Hello:
    您好,很高兴见到您!!

public static void main(String[] args){
    FileReader reader = null;
    FileWriter writer = null;
    try{
        reader = new FileReader("Hello.txt");
        writer = new FileWriter("CotyHello.txt");
        
        char[] chars = new char[16];
        int readCount;
        
        while ((readCount = reader.read(bytes)) != -1)
            writer.write(bytes,0,readCount);
    }catch(IOException e){
        e.printStackTrace();
    }catch(FileNotFoundException e){
        e.printStackTrace();
    }finally{
        if(reader != null){
            try{
                reader.close();
            }catch(IOExcetpion e){
                e.printStackTrace();
            }
        }
        if(writer != null){
            try{
                writer.flush();
                writer.close();
            }catch(IOExcetpion e){
                e.printStackTrace();
            }
        }
    }
}

缓冲流


在使用此类流时不用指定接受数据的容器,其底层自带缓冲数组,可以在创建缓冲流时指定数组的长度。

在创建此类流还要传递一个节点流,因为缓冲流内部只声明了流对象,并未对其初始化,也就是缓冲流只是在对节点流的封装,内部实际还是节点流在操作。关闭流的时候只需要做一次,在调用缓冲流close方法关闭流时,底层是通过节点流去调用close,所以在关闭缓冲流后,不需要再去关闭节点流。

close方法

private Reader in;
private char cb[];

public void close() throws IOException {
    synchronized (lock) {
        if (in == null)
            return;
        try {
            in.close();
        } finally {
            in = null;
            cb = null;
        }
    }
}

综合上述代码,在关闭流时,只需关闭缓冲流,无需关闭节点流

其他方法与FileInputStream的方法无异

BufferedReader

private Reader in;
private static int defaultCharBufferSize = 8192;
private char cb[];

底层字符数组默认大小为8192。

构造方法

public BufferedReader(Reader in, int sz) 
public BufferedReader(Reader in) {
    this(in, defaultCharBufferSize);
}

创建缓冲字符输入流,需要传递Reader对象,可以使用默认大小的输入缓冲区或指定缓冲区大小

特有方法

public String readLine() throws IOException

可以读一整行文本,该方法是以换行符('\ n')或回车符('\ r')为结束读取标签。读取的结果不包含换行和回车符。如果到达文件末尾则返回null。

BufferedWriter

private Writer out;
private char cb[];
private static int defaultCharBufferSize = 8192;

底层字符数组默认大小也是8192。

public BufferedWriter(Writer out)
public BufferedWriter(Writer out, int sz)

创建字符输出流,也可以指定缓冲区大小

String file = "bufferedTemp";
BufferedReader br = null;
BufferedWriter bw = null;
try {
    bw = new BufferedWriter(new FileWriter(file,false));
    br = new BufferedReader( new FileReader(file));
    //写数据
    bw.write("hello word!!\n");
    bw.write("2022-09-08 13:25:04 457: hello\n");
    bw.flush();//刷新输入流后数据才会写入到文件,才能被输出流读取
    //读文件
    String s = null;
    while ((s = br.readLine()) != null) {
        System.out.print(s+"\n");
    }
}catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}finally{
    if (bw != null) {
        try {
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    if (br != null) {
        try {
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

转换流


一个从字节流到字符流的桥梁,当方法需要一个缓冲流操作时,而调用者只提供了一个字节流时,可以使用转换流将字节流转为字符流,再把转换后的字符流传给缓冲流。

InputStreamReader

继承 Reader ,字节输入转字符输入流

public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in, String charsetName)

提供一个字节输入,创建一个转换流,也可以自定义字符集

BufferedWriter bw = null;
try{
    bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("Copy.txt",true)));
    bw.write("Hello\n");
    bw.write("World\n");
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally{
    if (bw != null) {
        try{
            bw.flush();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

数据流


数据流让应用程序以可移植的方式将原始的Java数据类型写到流。使用此类流输出的文件是加密的,只有再次使用此类输出流才能正常读取到输入的数据。而且输入的顺序必须是已知的,不然输出时会导致存储数据的数据类型不一致而出现数据乱码

public DataOutputStream(OutputStream out)
public DataInputStream(InputStream in)

创建此类流时还需传递一个节点流

操作方法与File**Stream流一致,但要记住存储顺序与取出顺序的一致

DataOutputStream ds = null;
DataInputStream di = null;
String file = "Data";
try {
    ds = new DataOutputStream(new FileOutputStream("file"));
    di = new DataInputStream(new FileInputStream("file"));
    //读取顺序与写入顺序必须一致
    ds.writeChar('B');
    ds.writeInt(1024);
    ds.writeByte('C');
    char b = di.readChar();
    int i = di.readInt();
    byte c = di.readByte();
    
    System.out.println(b);
    System.out.println(i);
    System.out.println(c);
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (ds != null){
        try{
            ds.close();
            ds.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    if (di != null){
        try{
            di.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

标准输入/输出流


与其它流不同,该类流不需要手动关闭流,且标准输出流可以控制”目的地“,可以是控制台或者文件

常用的控制台输出System.out返回的是一个标准输出流PrintStream

PrintStream

标准输出流,与其他输出流不同,PrintStream不抛会出IOException。日常工作中一般用于打印日志

public PrintStream(OutputStream out){this(out, false);}
public PrintStream(OutputStream out, boolean autoFlush)

创建标准输出流需要一个OutputStream对象。如果参数autoFlushtrue时,每当写下一个字节数组,调用一个println方法,或写下一个换行符或字节('/n'),输出缓冲区就会被刷新。

PrintStream printStream;
try {
    printStream = new PrintStream(new FileOutputStream("log"));
    //设置 System.out 输出方向,不再是向控制台输出
    System.setOut(printStream);
    //向log文件输出
    System.out.println("2022-7-12 14:00:45 145: Hello World!!");
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

PrintStream标准输出流不用手动关闭流

posted @   hello_12153_98  阅读(180)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示