IO流
IO流
本篇整理 Java IO 的基础内容:File 类的常用操作、IO 流的分类与处理流程、字节流与字符流的使用,以及缓冲流的读写与文件拷贝。
1. File 类
File 类被定义为文件和目录路径名的抽象表示形式。这是因为 File 类既可以表示文件,也可以表示目录,二者都通过对应的路径来描述。
通过构造函数创建一个 File 类对象后,该对象就是指定文件(或目录)的引用,可以通过该对象对文件进行操作。
//创建一个File类对象f,可通过f对temp.txt进行操作
File f = new File("D:\\temp.txt");路径分隔符:
- Windows 下路径分隔符使用反斜线
\,而 Java 程序中的反斜线表示转义字符。如果需要在 Windows 路径下使用反斜线,则应写成两条反斜线,如C:\\mytest.txt; - 使用斜线
/时,Java 程序会把它当作平台无关的路径分隔符处理。
File 类常见方法:
| 方法 | 说明 |
|---|---|
boolean isHidden() | 判断是否是隐藏文件 |
boolean canRead() | 是否可读 |
boolean canWrite() | 是否可写 |
String getAbsolutePath() | 获取文件的绝对路径 |
String getName() | 获取文件名 |
boolean isDirectory() | 是否是目录 |
boolean isFile() | 是否是文件 |
boolean mkdir() | 创建目录 |
long lastModified() | 最后修改时间 |
long length() | 文件长度 |
File[] listFiles() | 返回目录下的所有文件和目录 |
public class MyTest1 {
/*
* I---input
* O---output
* IO--输入输出(键盘 显示器 硬盘 网络)
* |---File类---文件和目录(文件夹)的抽象表示形式---File类将文件当成一个整体,不能对文件的内容进行操作,如果对文件的内容操作,需要使用IO流
* 通过File类的对象和文件或目录建立关联,操作File类的对象,就是操作相应的文件或者目录
*
* |---IO流
* */
public static void main(String[] args) {
//创建File类的对象 java.io....
/*
* 文件的路径有两种写法
* C:\\Users\\Raymond\\Desktop\\test\\a.txt
* C:/Users/Raymond/Desktop/test/a.txt
*
* \ 转义字符----和其后的第一个字符结合,表示特殊的含义 \n \t
* 如果要表示\,必须写两条\
*
* */
//File f = new File("C:\\Users\\Maxwell\\Desktop\\a.txt");
File f = new File("C:/Users/Maxwell/Desktop/a.txt");
//File类中常见的方法
//判断文件是否是隐藏文件
System.out.println("是否隐藏:" + f.isHidden());
//大小 Byte
System.out.println("文件的大小:" + f.length());
//文件名
System.out.println("文件名:" + f.getName());
//绝对路径
System.out.println("绝对路径:" + f.getAbsolutePath());
//是否可读
System.out.println("是否可读:" + f.canRead());
//是否可写
System.out.println("是否可写:" + f.canWrite());
/*
* 时间戳---->表示时间的字符串(xxxx年xx月xx日 xx:xx:xx)
*
* SimpleDateFormat
* |
* Date()
* |
* 时间戳
* 时间戳---->Date()
* */
//文件最后修改时间
long lastMdate = f.lastModified();
Date date = new Date(lastMdate);
//Date---String
SimpleDateFormat sft = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String dateStr = sft.format(date);
System.out.println("最后修改时间:" + dateStr);
//判断是否是文件夹
System.out.println("是否是文件夹:" + f.isDirectory());
File f1 = new File("C:/Users/Maxwell/Desktop/test123/");
//判断文件或文件夹是否存在
//System.out.println("是否存在:" + f1.exists());
if(!f1.exists()) { //不存在创建该文件夹
//创建文件夹 Linux mkdir
f1.mkdir();
}
File[] fArr = f1.listFiles();
for (File file : fArr) {
System.out.println(file.getName());
}
}
}File 类中没有提供对文件的读写操作,如果要实现对文件的读写,需要通过 IO 流来进行操作。
下面通过一个案例演示 File 类的递归用法:列出一个目录下所有的文件和目录(包括子目录中的目录和文件)。
public class MyTest2 {
/*
* f表示需要被遍历的文件夹
* */
public static void listFiles(File f) {
File[] farr = f.listFiles();
for(File subf : farr) {
System.out.println(subf.getAbsolutePath());
if(subf.isDirectory()) {
listFiles(subf);
}
}
}
public static void main(String[] args) {
File file = new File("C:/Users/Maxwell/Desktop/test123/");
listFiles(file);
}
}2. IO 流概述
2.1 什么是 IO 流
I/O 是 Input/Output 的缩写,I/O 技术是非常实用的技术,用于处理设备之间的数据传输,例如读/写文件、网络通讯等。
IO 流是一组有序的、有起点和终点的数据集合,是对数据传输的总称和抽象。
IO 的作用主要有:
- 人机交互;
- 文件数据的读取与写入,实现数据持久化保存。
IO 流的源和目的地可以是:内存、控制台、磁盘文件、网络端点。
关于 Input 和 Output:
Input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中;Output:将程序(内存)中的数据输出到磁盘、光盘等存储设备中。
2.2 IO 流分类
按照处理的数据单元不同,IO 流可分为:
- 字节流:操作的数据单元是 8 位字节,对应
InputStream、OutputStream。适用于二进制文件(声音、图片、视频)和文本文件; - 字符流:操作的数据单元是 16 位字符,对应
Reader、Writer,通常用于处理文本文件。
按照数据流流向不同,IO 流可分为:
- 输入流:只能从中读取数据,而不能向其写入数据,对应
InputStream、Reader; - 输出流:只能向其写入数据,而不能从中读取数据,对应
OutputStream、Writer; - 输入、输出都是从内存的角度进行划分的:内存 → 硬盘是输出流,硬盘 → 内存是输入流。
四种基类的组合关系如下:
| 字节流 | 字符流 | |
|---|---|---|
| 输入流 | InputStream | Reader |
| 输出流 | OutputStream | Writer |
Java 的 IO 流共涉及 40 多个类,但结构非常规整,都是从上述 4 个抽象基类派生出来的。由这四个类派生出来的子类名称,都以父类名作为子类名的后缀。
2.3 IO 流处理流程
IO 流的处理流程分为三步:
- 打开流;
- 在流中传输数据(输入/输出,即读/写);
- 关闭流。
注意
程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以必须显式关闭文件 IO 资源。
3. 字节流
字节流操作的数据单元是 8 位字节,主要涉及两个抽象类:InputStream 和 OutputStream。
3.1 InputStream
InputStream 的主要方法:
int read():从输入流中读取数据的下一个字节,返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回 -1;int read(byte[] b):从此输入流中将最多b.length个字节的数据读入一个byte数组中。如果已经到达流末尾而没有可用的字节,则返回 -1,否则以整数形式返回实际读取的字节数;int read(byte[] b, int off, int len):将输入流中最多len个数据字节读入byte数组。尝试读取len个字节,但实际读取的字节数也可能小于该值,以整数形式返回实际读取的字节数;如果流位于文件末尾而没有可用的字节,则返回 -1;void close():关闭此输入流并释放与该流关联的所有系统资源。
import java.io.FileInputStream;
import java.io.IOException;
public class MyTest3 {
/*
* IO流
*
* 操作步骤:
* 1.打开流
* 2.读/写
* 3.关闭流
* 分类------->能够解决什么情况下选择什么样的IO流的问题
* |---处理数据单元的不同
* |---字节流(8) InputStream OutputStream
* |---字符流 (16) Reader Writer
* |---流向---方向 物理 参考系---内存
* |---输入流 InputStream Reader
* |---输出流 OutputStream Writer
* 1.InputStream OutputStream Reader Writer都是抽象类,我们学习的是他们的子类;
* 2.任何一个IO流同时属于上面两个分类体系;
* 3.处理的内容是二进制文件(声音,图片,视频)使用字节流进行处理
* 4.处理的内容是文本文件(txt)使用字符流进行处理
* */
public static void main(String[] args) throws IOException {
/*
* 使用字节流读取文件并在控制台打印--InputStream--FileInputStream
* */
//打开流
FileInputStream in = new FileInputStream("C:\\Users\\Maxwell\\Desktop\\a.txt");
/*
* 读取文件中的内容
* 1.为了提升读取效率,要一次尽可能读取多个字节的数据----读到数组里面
* 2.文件有可能特别大,数组如果设置特别大会导致程序崩溃----数组的大小不能特别大,不能和被读取的文件一样大,数组要小一点----多次调用读取的方法进行读取----循环读取
* 3.read方法返回-1表示读到文件末尾
*
* read(byte[] arr)
* read(byte[] arr, int off, int len); ***
*/
byte[] arr = new byte[1024];//1KB
int len = 0;
while((len = in.read(arr, 0, 1024)) != -1) {
//内容在数组中----String
//byte[]----String
/*
* 根据数组中的有效信息转换成字符串
* */
String str = new String(arr, 0, len);
//打印转换的内容
System.out.print(str);
}
//关闭流
in.close();
}
}3.2 OutputStream
OutputStream 的主要方法:
void write(int b):将指定的字节写入此输出流。write的常规协定是向输出流写入一个字节,要写入的字节是参数b的八个低位,b的 24 个高位将被忽略,即写入 0~255 范围的值;void write(byte[] b):将b.length个字节从指定的byte数组写入此输出流。write(b)的效果应与调用write(b, 0, b.length)完全相同;void write(byte[] b, int off, int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流;void flush():刷新此输出流并强制写出所有缓冲的输出字节,调用此方法表示应将这些字节立即写入它们预期的目标;void close():关闭此输出流并释放与该流关联的所有系统资源。
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
public class MyTest4 {
public static void main(String[] args) throws IOException {
/*
* 从控制台获取输入,将输入的内容写入到文件当中(使用字节流)
* OutputStream
* */
Scanner sc = new Scanner(System.in);
/*
* 日志文件
*
* */
//打开流
FileOutputStream out = new FileOutputStream("C:\\Users\\Maxwell\\Desktop\\a.txt");
//读取键盘数据
String str = sc.next();
str += "\r\n";
//String--->byte[]
byte[] arr = str.getBytes();
//写入数据
out.write(arr);
//关闭流
out.close();
}
}注
在写入一个文件时,如果使用构造器 FileOutputStream(file),则目录下的同名文件将被覆盖;如果使用构造器 FileOutputStream(file, true),则目录下的同名文件不会被覆盖,而是在文件内容末尾追加内容。
3.3 字节流实现文件拷贝
public class MyTest5 {
public static void main(String[] args) throws IOException {
//拷贝操作---复制---读取一个文件的内容,写入另外的一个文件
//打开流
FileInputStream in = new FileInputStream("C:\\Users\\Maxwell\\Desktop\\pic.jpg");
FileOutputStream out = new FileOutputStream("C:\\Users\\Maxwell\\Desktop\\pic1.jpg");
byte[] arr = new byte[1024];
int len = 0;
//读写操作
while((len = in.read(arr, 0, 1024)) != -1) {
//读取多少内容,就写入多少内容
out.write(arr, 0, len);
}
//关闭
out.close();
in.close();
}
}4. 字符流
字符流操作的数据单元是 16 位字符,主要涉及两个抽象类:Reader 和 Writer,通常用于处理文本文件。
4.1 Reader
Reader 的主要方法:
int read():读取单个字符。作为整数读取的字符,范围在 0 到 65535 之间(2 个字节的 Unicode 码);如果已到达流的末尾,则返回 -1;int read(char[] cbuf):将字符读入数组。如果已到达流的末尾,则返回 -1,否则返回本次读取的字符数;int read(char[] cbuf, int off, int len):将字符读入数组的某一部分,存到数组cbuf中,从off处开始存储,最多读len个字符。如果已到达流的末尾,则返回 -1,否则返回本次读取的字符数;void close():关闭此输入流并释放与该流关联的所有系统资源。
import java.io.FileReader;
import java.io.IOException;
public class MyTest6 {
public static void main(String[] args) throws IOException {
/*
* 字符流(16)---Reader Writer
* */
//打开流
FileReader in = new FileReader("C:\\Users\\Maxwell\\Desktop\\a.txt");
char[] arr = new char[1024];
int len = 0;
//读取
while((len = in.read(arr, 0, 1024)) != -1) {
//打印String
//char[]---->String
String str = new String(arr, 0, len);
System.out.println(str);
}
//关闭
in.close();
}
}4.2 Writer
Writer 的主要方法:
void write(int c):写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 个高位被忽略,即写入 0 到 65535 之间的 Unicode 码;void write(char[] cbuf):写入字符数组;void write(char[] cbuf, int off, int len):写入字符数组的某一部分,从off开始,写入len个字符;void write(String str):写入字符串;void write(String str, int off, int len):写入字符串的某一部分;void flush():刷新该流的缓冲,立即将它们写入预期目标;void close():关闭此输出流并释放与该流关联的所有系统资源。
import java.io.FileWriter;
import java.io.IOException;
public class MyTest7 {
public static void main(String[] args) throws IOException {
//在文件中写入数据---Writer
FileWriter out = new FileWriter("C:\\Users\\Maxwell\\Desktop\\a.txt");
//写入
String str = "abcdefg....";
out.write(str);
//关闭
out.close();
}
}注
在写入一个文件时,如果使用构造器 FileWriter(file),则目录下的同名文件将被覆盖;如果使用构造器 FileWriter(file, true),则目录下的同名文件不会被覆盖,而是在文件内容末尾追加内容。
4.3 字符流实现文件拷贝
public class MyTest8 {
public static void main(String[] args) throws IOException {
//使用字符流进行文件的拷贝
//打开流
FileReader in = new FileReader("C:\\Users\\Maxwell\\Desktop\\a.txt");
FileWriter out = new FileWriter("C:\\Users\\Maxwell\\Desktop\\b.txt");
char[] arr = new char[1024];
int len = 0;
//读写
while((len = in.read(arr, 0, 1024)) != -1) {
out.write(arr, 0, len);
}
//关闭
out.close();
in.close();
}
}5. 缓冲流
为了提高数据读写的速度,Java API 提供了带缓冲功能的流类。使用这些流类时,会创建一个内部缓冲区数组,默认使用 8192 个字节或字符的缓冲区。缓冲流要套接在相应的节点流之上。
根据数据操作单位,缓冲流可以分为:
- 字节缓冲流:
BufferedInputStream和BufferedOutputStream; - 字符缓冲流:
BufferedReader和BufferedWriter。
以 BufferedInputStream 为例:读取字节文件时,它会一次性从文件中读取 8192 个(8KB)字节存入缓冲区,直到缓冲区装满,才重新从文件中读取下一个 8192 个字节。
向流中写入字节时,不会直接写到文件,而是先写到缓冲区中;直到缓冲区写满,BufferedOutputStream 才会把缓冲区中的数据一次性写到文件里。使用 flush() 方法可以强制将缓冲区的内容全部写入输出流。
重要
关闭流的顺序和打开流的顺序相反,且只要关闭最外层流即可——关闭最外层流时会相应关闭内层节点流。带缓冲区的流对象调用 close() 方法时,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出。
5.1 使用缓冲流实现文件拷贝
import java.io.*;
public class MyTest9 {
public static void main(String[] args) throws IOException {
BufferedReader rd = new BufferedReader(new FileReader("C:\\Users\\Maxwell\\Desktop\\a.txt"));
BufferedWriter wr = new BufferedWriter(new FileWriter("C:\\Users\\Maxwell\\Desktop\\b.txt"));
char[] temp = new char[1024];
int len = 0;
while ((len = rd.read(temp)) != -1) {
wr.write(temp, 0, len);
}
wr.close();
rd.close();
}
}5.2 按行读取
public class MyTest10 {
public static void main(String[] args) throws IOException {
//字符过滤流---缓冲区---BufferdReader
//打开流
FileReader rd = new FileReader("C:\\Users\\Maxwell\\Desktop\\a.txt");
/*
* Reader in = rd;向上转型 多态
* BufferedReader in = new BufferedReader(in);
* */
BufferedReader in = new BufferedReader(rd);
//读---控制台
String line = null;
while((line = in.readLine()) != null) {
System.out.println(line);
System.out.println("-----------------------------------------");
}
//关闭
in.close();
}
}