Java中的IO流
IO流的分类
按照数据类型划分:字符流和字节流
按照数据流向划分:输入流和输出流
对于纯文本数据,优先使用字符流。除此之外都使用字节流。
File类
java.io.File:不能对文件内容(流)进行读写,只能对文件和文件夹进行操作
绝对路径和相对路径
路径不区分大小写,一个反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠
绝对路径:一个完整的路径,以盘符开头,例如F:\\aaa.txt
相对路径:一个简化的路径,不以盘符开头,例如\\aaa.txt\\b.txt
构造方法
File类构造方法不会给你检验这个文件或文件夹是否真实存在,因此无论该路径下是否存在文件或者目录,都不影响File对象的创建。
// 文件路径名
String path2 = "D:\\1\\2.txt";
File file2 = new File(path2); -------------相当于D:\\1\\2.txt
// 通过父路径和子路径字符串
String parent = "F:\\aaa";
String child = "bbb.txt";
File file3 = new File(parent, child); --------相当于F:\\aaa\\bbb.txt
// 通过父级File对象和子路径字符串
File parentDir = new File("F:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child); --------相当于F:\\aaa\\bbb.txt
常用方法
方法 | 用途 |
---|---|
public String getAbsolutePath() | 获取此File的绝对路径名字符串。 |
public String getPath() | 获取此File转换的路径名字符串。 |
public String getName() | 获取由此File表示的文件或目录的名称。 |
public long length() | 获取由此File表示的文件的长度。 |
public boolean exists() | 判断此File表示的文件或目录是否实际存在。 |
public boolean isDirectory() | 判断此File表示的是否为目录。 |
public boolean isFile() | 判断此File表示的是否为文件。 |
public boolean createNewFile() | 文件不存在,创建一个新的空文件并返回true ,文件存在,不创建文件并返回false |
public boolean delete() | 删除由此File表示的文件或目录。 |
public boolean mkdir() | 创建由此File表示的目录。 |
public boolean mkdirs() | 创建由此File表示的目录,包括任何必需但不存在的父目录。(创建多级目录) |
public String[] list() | 返回一个String数组,表示该File目录中的所有子文件或目录。 |
public File[] listFiles() | 返回一个File数组,表示该File目录中的所有的子文件或目录。 |
练习
递归遍历目录以及子目录下的所有文件(见同目录下的IDEA文件)
输入输出流
字节流概述
IO分类之后的父类
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流 InputStream | 字节输出流 OutputStream |
字符流 | 字符输入流 Reader | 字符输出流 Writer |
OutputStream与InputStream的继承关系
字节流基流
3.1.1)InputStream:
字节输入流基类,抽象类是表示字节输入流的所有类的超类。
方法 | 功能 |
---|---|
abstract int read() | //每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1 |
int read(byte[] b) | // 每次读取b的长度个字节到数组b中,返回读取到的有效字节个数,读取到末尾时,返回-1 |
int read(byte[] b, int off, int len) | // 将输入流中从 off 开始最多 len 个数据字节读入 byte 数组 |
long skip(long n) | // 跳过和丢弃此输入流中数据的 n个字节 |
void close() | // 关闭此输入流并释放与该流关联的所有系统资源 |
3.1.2)OutputStream
OutputStream:字节输出流基类,抽象类是表示输出字节流的所有类的超类。
方法 | 功能 |
---|---|
void write(byte[] b) | // 将 b.length 个字节从指定的 byte 数组写入此输出流 |
void write(byte[] b, int off, int len) | // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流 |
abstract void write(int b) | // 将指定的字节写入此输出流 |
void close() | // 关闭此输出流并释放与此流有关的所有系统资源 |
void flush() | // 刷新此输出流并强制写出所有缓冲的输出字节 |
字节流文件操作流
字节文件输入流 (FileInputStream)
FileInputStream:字节文件输入流,从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流。
①构造函数
构造方法 | 解释 |
---|---|
FileInputStream(File file) | // 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定 |
FileInputStream(String name) | // 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径name指定 |
②代码示例
常用方法:覆盖和重写了父类的的常用方法。
//构造方法1
//InputStream inputStream = new FileInputStream(new File("f://hello//test.txt"));
//构造方法2
InputStream inputStream2 = new FileInputStream("f://hello/test.txt");
// 字节数组
byte[] b = new byte[2];
int i2 = 0;
// 一次读取一个字节数组 -->int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中
while ((i2 = inputStream2.read(b)) != -1) {
//表示从缓冲区数组中输出从0开始的1个字节,也就是一个字母,如果改成2,就输出AB CD
System.out.print(new String(b, 0, 1) + " ");// A B C D
} //关闭IO流
inputStream2.close();
字节文件输出流 (FileOutputStream)
FileOutputStream:字节文件输出流是用于将数据写入到File,从程序中写入到其他位置。
①构造函数
构造方法 | 解释 |
---|---|
FileOutputStream(File file) | // 创建一个向指定File对象表示的文件中写入数据的文件输出流 |
FileOutputStream(File file, boolean append) | // 创建一个向指定File对象表示的文件中追加数据的文件输出流 |
FileOutputStream(String name) | // 创建一个向具有指定名称的文件中写入数据的输出文件流 |
FileOutputStream(String name, boolean append) | // 创建一个向具有指定name的文件中追加数据的输出文件流 |
②代码示例
OutputStream outputStream = new FileOutputStream(new File("test.txt"));
// 写出数据
outputStream.write("ABCD".getBytes());
// 关闭IO流
outputStream.close();
// 当布尔值为真,表示进行内容追加。当布尔值为假,表示清空原数据再添加
OutputStream outputStream2 = new FileOutputStream("test.txt", true);
// 输出换行符
outputStream2.write("\r\n".getBytes());
// 输出追加内容
outputStream2.write("hello".getBytes());
// 关闭IO流
outputStream2.close();
注:输出的目的地文件不存在,则会自动创建,不指定盘符的话,默认创建在项目目录下;输出换行符时一定要写\r\n不能只写\n,因为不同文本编辑器对换行符的识别存在差异性。
练习:使用字节流文件操作流进行文件复制
public static void copy() {
//不能把两个对象写在try中,否则finally无法调用这两个对象
InputStream inputStream = null;
OutputStream fileOutputStream = null;
try {
inputStream = new FileInputStream("F:\\FileTest.txt");
fileOutputStream = new FileOutputStream("F:\\Copy.txt");
byte[] b = new byte[1024];
int len;
//len表明当前字节长度,b是用于存储读到的数据
while ((len = inputStream.read(b)) != -1) {
fileOutputStream.write(b,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流概述
为什么要用字符流
字节流读取中文字符时,可能不会显示完整字符,那是因为一个中文字符占用多个字节存储。因此,当需要高效处理字符时,最佳的选择往往是字符流操作
当文件内含有中文时,下面的使用字节流的代码将输出乱码。
//FileInputStream为操作文件的字符输入流
FileInputStream inputStream = new FileInputStream("a.txt");
int len;
while ((len=inputStream.read())!=-1){
System.out.print((char)len);
}
虽然可以通过字节组来缓存文件的数组大小个字节,以达到一次读出多个字节的目的,但是仍然比较麻烦。
FileInputStream inputStream = new FileInputStream("a.txt");
byte[] bytes = new byte[1024];
int len;
while ((len=inputStream.read(bytes))!=-1){
//利用了String构造方法的解码功能
System.out.print(new String(bytes,0,len));
}
字符流Reader和Writer的关系
字符流基类
读取字符流(Reader )
Reader:读取字符流的抽象类.
①常用方法
方法 | 作用 |
---|---|
int read() | // 读取单个字符 |
int read(char[] cbuf) | // 将字符读入数组 |
abstract int read(char[] cbuf, int off, int len) | // 将字符读入数组的某一部分 |
long skip(long n) | // 跳过字符 |
abstract void close() | // 关闭该流并释放与之关联的所有资源 |
②构造方法
FileReader类
java.io.FileReader
类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
方法 | 作用 |
---|---|
FileReader(File file) | 创建一个新的 FileReader ,给定要读取的File对象。 |
FileReader(String fileName) | 创建一个新的 FileReader ,给定要读取的文件的字符串名称。 |
③代码示例
/**
* 字符流:读
*/
public class FRRead {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileReader fr = new FileReader("a.txt");
// 定义变量,保存数据
int b ;
// 一次可以读出一个字符,不需要再设定字节数组
while ((b = fr.read())!=-1) {
System.out.println((char)b);
}
// 关闭资源
fr.close();
}
}
写入字符流(Writer)
Writer:写入字符流的抽象类
①常用方法
方法 | 作用 |
---|---|
void write(char[] cbuf) | // 写入字符数组 |
abstract void write(char[] cbuf, int off, int len) | // 写入字符数组的某一部分 |
void write(int c) void write(String str) void write(String str, int off, int len) |
// 写入单个字符 // 写入字符串 // 写入字符串的某一部分 |
Writer append(char c) Writer append(CharSequence csq) Writer append(CharSequence csq, int start, int end) |
// 将指定字符添加到此 writer // 将指定字符序列添加到此 writer // 将指定字符序列的子序列添加到此 writer.Appendable |
abstract void close() | // 关闭此流,但要先刷新它 |
abstract void flush() | // 刷新该流的缓冲 |
②构造方法
FileWriter类
java.io.FileWriter
类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
方法 | 作用 |
---|---|
FileWriter(File file) | 创建一个新的 FileWriter,给定要读取的File对象。 |
FileWriter(String fileName) | 创建一个新的 FileWriter,给定要读取的文件的名称。 |
③代码示例
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter("fw.txt");
// 写出数据
fw.write(97); // 写出第1个字符
fw.write('b'); // 写出第2个字符
fw.write('C'); // 写出第3个字符
//关闭资源时,与FileOutputStream不同。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
// fw.close();
}
}
输出结果:
abC
【注意】关闭资源时,与FileOutputStream不同。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
关闭close和刷新flush
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要
flush
方法了。
flush
:刷新缓冲区,流对象可以继续使用。
close
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
练习:FileReader和FileWriter类完成文本文件复制
public class CopyFile {
public static void main(String[] args) throws IOException {
//创建输入流对象
FileReader fr=new FileReader("F:\\新建文件夹\\aa.txt");//文件不存在会抛出java.io.FileNotFoundException
//创建输出流对象
FileWriter fw=new FileWriter("C:\\copyaa.txt");
//文本文件复制,一次读一个字符
copyMethod1(fr, fw);
//文本文件复制,一次读一个字符数组
copyMethod2(fr, fw);
fr.close();
fw.close();
}
public static void copyMethod1(FileReader fr, FileWriter fw) throws IOException {
int ch;
while((ch=fr.read())!=-1) {//读数据
fw.write(ch);//写数据
}
//写一个字符刷新一次
fw.flush();
}
public static void copyMethod2(FileReader fr, FileWriter fw) throws IOException {
char chs[]=new char[1024];
int len=0;
while((len=fr.read(chs))!=-1) {//读数据
fw.write(chs,0,len);//写数据
}
//写一个字符数组刷新一次
fw.flush();
}
}
`
IO异常的处理
我们在学习的过程中可能习惯把异常抛出,而实际开发中并不能这样处理,建议使用
try...catch...finally
代码块,处理异常部分
缓冲流
创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
字节缓冲流
构造方法
//构造方式一: 创建字节缓冲输入流【但是开发中一般常用下面的格式申明】
FileInputStream fps = new FileInputStream(b.txt);
BufferedInputStream bis = new BufferedInputStream(fps)
//构造方式一: 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("b.txt"));
///构造方式二: 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
字符缓冲流
构造函数
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("b.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
总结:使用缓冲流对文件进行读写操作,效率将会快很多!
转换流
避免因编码问题导致的乱码现象。字符流=字节流+编码表
字符集
字符集也叫编码表,是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
InputStreamReader类
字节流到字符流的桥梁,是
Reader
的子类。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
使用转换流解决编码问题的例子
public class ReaderDemo2 {
public static void main(String[] args) throws IOException {
// 定义文件路径,文件为gbk编码
String FileName = "C:\\A.txt";
// 创建流对象,默认UTF8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
// 创建流对象,指定GBK编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
// 定义变量,保存字符
int read;
// 使用默认编码字符流读取,乱码
while ((read = isr.read()) != -1) {
System.out.print((char)read); // �����ʺ
}
isr.close();
// 使用指定编码字符流读取,正常解析
while ((read = isr2.read()) != -1) {
System.out.print((char)read);// 叶斯摩拉
}
isr2.close();
}
}
OutputStreamWriter类
字符流到字节流的桥梁,是Writer的子类.使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("a.txt")); //默认为UTF-8编码方式
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("b.txt") , "GBK"); //指定文件写出为GBK编码
指定编码构造代码的一个例子
public class OutputDemo {
public static void main(String[] args) throws IOException {
// 定义文件路径
String FileName = "C:\\s.txt";
// 创建流对象,默认UTF8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
// 写出数据
osw.write("加油"); // 保存为6个字节
osw.close();
// 定义文件路径
String FileName2 = "D:\\A.txt";
// 创建流对象,指定GBK编码
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
// 写出数据
osw2.write("哥们");// 保存为4个字节
osw2.close();
}
}
为了达到最高效率,可以考虑在 BufferedReader
内包装 InputStreamReader
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
打印流
println
方法都来自于java.io.PrintStream
类。PrintStream是OutputStream的子类,PrintWriter是Writer的子类,打印操作效率最高
字节输出打印流PrintStream复制文本文件
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
public class PrintStreamDemo {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("copy.txt"));
PrintStream ps=new PrintStream("printcopy.txt");
String line;
while((line=br.readLine())!=null) {
ps.println(line);
}
br.close();
ps.close();
}
}
字符输出打印流PrintWriter复制文本文件
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 使用打印流复制文本文件
*/
public class PrintWriterDemo {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("aa.txt"));
PrintWriter pw=new PrintWriter("printcopyaa.txt");
String line;
while((line=br.readLine())!=null) {
pw.println(line);
}
br.close();
pw.close();
}
}