文章目录
- 1. 流概述
- 2. 输入/输出流
- 2.1 输入流
- 2.2 输出流
- 3. File 类
- 3.1 文件的创建与删除
- 3.2 获取文件信息
- 4. 文件输入/输出流
- 4.1 FileInputStream 与 FileOutputStream 类
- 4.2 FileReader 和 FIleWriter 类
- 5. 带缓存的输入/输出流
- 5.1 BufferedInputStream 类与 BufferedOutputStream 类
- 5.2 BufferedReader 与 BufferedWriter 类
- 6. 数据输入/输出流
1. 流概述
流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流两种。I/O(Input/Output)流提供了一条通道程序,可以使用这条通道把源中的字节序列送到目的地。虽然I/O流经常与磁盘文件存取有关,但是程序的源和目的地也可以是键盘、鼠标、内存或显示器窗口等。
2. 输入/输出流
2.1 输入流
InputStream
类是字节输入流的抽象类,是所有字节输入流的父类。InputStream类的具体层次结构如图所示:
该类中所有方法遇到错误时都会引发IOException异常。下面是对该类中的一些方法的简要说明如下表所示
Java中的字符是Unicode编码,是双字节的。InputStream
是用来处理字节的,在处理字符文本时不是很方便。Java为字符文本的输入提供了专门一套单独的类Reader,但Reader类并不是InputStream类的替换者,只是在处理字符串时简化了编程。Reader类是字符输入流的抽象类,所有字符输入流的实现都是它的子类,Reader类的具体层次结构如下图所示:
2.2 输出流
OutputStream
类是字节输入流的抽象类,此抽象类是表示输出字节流的所有类的超类。OutputStream类的具体层次如图所示:
OutputStream类中的所有方法均返回void,在遇到错误时会引发IOException异常。下面对OutputStream类中的方法作一简单的介绍,如下表所示:
Writer类是字符输出流的抽象类,所有字符输出类的实现都是它的子类,Writer类的层次结构如下图所示:
3. File 类
3.1 文件的创建与删除
可以使用File类创建一个文件对象,通常使用以下3种构造方法来创建文件对象。
import java.io.File;
public class Demo {
public static void main(String[] args) {
/*
* 项目下的路径(默认路径): word.txt
* 包中的文件路径"src/mr/word.txt
* 绝对路径: C:/test/word.txt
*/
File f1 = new File("D:/test/word.txt");// 第一种构造方法
File f2 = new File("D:/test", "word.txt");// 第二种构造方法
File dir = new File("D:/test");// 文件夹
File f3 = new File(dir, "word.txt");// 第三种构造方法
System.out.println(f1.getAbsolutePath());// 输出文件绝对路径
System.out.println(f2.getAbsolutePath());// 输出文件绝对路径
System.out.println(f3.getAbsolutePath());// 输出文件绝对路径
System.out.println(f1 == f2);// f1、f2只是文件的代表
System.out.println(f1.equals(f2));
}
}
运行结果:
D:\test\word.txt
D:\test\word.txt
D:\test\word.txt
false
true
3.2 获取文件信息
File类提供了很多方法用于获取文件本身的一些信息,File类的常用方法如下表所示。
我们在D盘test文件夹中新建一个word.txt文件,然后运行以下代码:
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo {
public static void main(String[] args) {
File f1 = new File("D:/test/word.txt");// 第一种构造方法
System.out.println("文件是否存在:" + f1.exists());// 判断文件是否存在
System.out.println("文件名:" + f1.getName());// 输出文件名
System.out.println("文件绝对路径:" + f1.getAbsolutePath());// 输出文件绝对路径
System.out.println("文件是否隐藏:" + f1.isHidden());// 是否为隐藏文件
System.out.println("文件的字节数:" + f1.length());// 输出文件大小,单位为字节
Date date = new Date(f1.lastModified());// 通过毫秒值创建日期类
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/ HH:mm:ss");
System.out.println("文件最后修改时间:" + sdf.format(date));// 文件最后修改日期
}
}
得到结果:
文件是否存在:true
文件名:word.txt
文件绝对路径:D:\test\word.txt
文件是否隐藏:false
文件的字节数:9
文件最后修改时间:2020/12/14/ 22:37:26
然后,对文件进行删除和新建的操作:
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo {
public static void main(String[] args) {
File f1 = new File("D:/test/word.txt");// 第一种构造方法
boolean del = f1.delete();// 删除文件
System.out.println("删除文件是否成功:" + del);
try {
// 已存在的文件,不能重新创建(不能覆盖已有文件)
boolean create = f1.createNewFile();// 创建新的空文件
System.out.println("创建文件是否成功:" + create);
} catch (IOException e) {
e.printStackTrace();
}
}
}
得到结果:
删除文件是否成功:true
创建文件是否成功:true
下面是新建文件夹的例子:
import java.io.File;
public class Demo {
public static void main(String[] args) {
File dir = new File("dir/dir1/dir2/dir3");// 只能创建1层文件夹
boolean flag = dir.mkdir();// 创建文件夹
boolean flag2 = dir.mkdirs();// 创建文件夹及父文件夹
System.out.println("创建文件夹是否成功:"+flag);
System.out.println("创建多层文件夹是否成功:"+flag2);
}
}
运行结果:
创建文件夹是否成功:false
创建多层文件夹是否成功:true
删除文件夹:
import java.io.File;
public class Demo {
public static void main(String[] args) {
File dir = new File("dir/dir1/dir2/dir3");// 只能创建1层文件夹
// 删除文件路径最后一个文件夹
boolean del = dir.delete();// 删除文件夹
System.out.println("删除文件夹是否成功:"+del);
}
}
运行结果:
删除文件夹是否成功:true
查看文件夹及文件名:
import java.io.File;
public class Demo {
public static void main(String[] args) {
File f = new File("C:/Windows/");// C盘Windows文件夹
File files[] = f.listFiles();// 返回文件夹下所有子文件及子文件夹
for(File tmp:files) {
if(tmp.isFile()) {// 判断是否为文件
System.out.println("文件:"+tmp.getName());
}else if(tmp.isDirectory()) {
System.out.println("文件夹:"+tmp.getName());
}
}
}
}
运行结果:
文件夹:addins
文件夹:appcompat
文件夹:apppatch
文件夹:AppReadiness
文件夹:assembly
文件夹:bcastdvr
文件:bfsvc.exe
文件夹:Boot
文件:bootstat.dat
文件夹:Branding
……略
4. 文件输入/输出流
程序运行期间,大部分数据都在内存中进行操作,当程序结束或关闭时,这些数据将消失。如果需要将数据永久保存,可以使用文件输入/输入流与指定的文件建立连接,将需要的数据永久保存到文件中。
4.1 FileInputStream 与 FileOutputStream 类
FileInputStream
类与FileOutputStream
类都是用来操作磁盘文件。如果用户的文件读取需求比较简单,则可以使用FileInputStream类。该类继承自InputStream类。FileOutputStream类与FileInputStream类对应,提供了基本的文件写入能力。FileOutputStream类是OutoputStream类的子类。
例子:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
File f = new File("word.txt");
FileOutputStream out = null;
try {
out = new FileOutputStream(f, false);// 文件输出流,为true,在文件末尾添加;为false,替换掉文件内容。
String str = "你见过洛杉矶凌晨4点的样子吗?";
byte b[] = str.getBytes();// 字符串转换为字节数组
out.write(b);// 将字节数组中的数据写入到文件当中
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileInputStream in = null;
try {
in = new FileInputStream(f);// 输入流读文件
byte b2[] = new byte[2014];// 缓冲区
int len = in.read(b2);// 读入缓冲区的总字节数
System.out.println("文件中的数据是:" + new String(b2, 0, len));// 可以去掉1024字节中的空格
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
文件中的数据是:你见过洛杉矶凌晨4点的样子吗?
4.2 FileReader 和 FIleWriter 类
使用FileOutputStream
类向文件中写入数据与使用FileInputStream类从文件中将内容读出来,存在一点不足,即这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占用两个字节,如果使用字节流,读取不好可能会出现乱码现象。此时采用字符流Reader或Writer类即可避免这种现象。
FileReader
、FileWriter
字符流对应了FileInputStream
、FileOutputStream
类。FileReader流顺序地读取文件,只要不关闭流,每次调用read()方法就顺序地读取源中其余的内容,直到源的末尾或流被关闭。
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
File f = new File("word.txt");
FileWriter fw = null;
try {
fw = new FileWriter(f, false);// true为在原文件后追加新内容
String str = "天行健,君子当自强不息,地势坤,君子当厚德载物";
fw.write(str);// 将字符串写入文本文档
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileReader fr = null;
try {
fr = new FileReader(f);
char ch[] = new char[2014];// 缓冲区
int count;// 已读出的字符数
while ((count = fr.read(ch)) != -1) {// 循环读取文件中的数据直到所有字符都读完
System.out.println("文件中的内容为:" + new String(ch, 0, count));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
文件中的内容为:天行健,君子当自强不息,地势坤,君子当厚德载物
5. 带缓存的输入/输出流
缓存是I/O的一种性能优化。缓存流为I/O流增加了内存缓存区。有了缓存区,使得在流上执行skip()
、mark()
和reset()
方法都称为可能。
5.1 BufferedInputStream 类与 BufferedOutputStream 类
BufferedInputStream
类可以对任何的InputStream
类进行带缓存区的包装以达到性能的优化。
使用BufferedOutputStream输出信息和往OutputStream输出信息完全一样,只不过BufferedOutputStream有一个flush()
方法用来将缓存区的数据强制输出完。
无缓冲字节流情况下读取数据需要的时间:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
File f = new File("D:/Android/jdk1.8中文百度.CHM");
FileInputStream in = null;
long start = System.currentTimeMillis();// 数据流开始时毫秒值
try {
in = new FileInputStream(f);
byte b[] = new byte[2014];// 缓冲区自己数组(这个缓冲区与Buffered不同)
while (in.read(b) != -1) {
}
long end = System.currentTimeMillis();// 数据流结束时毫秒值
System.out.println("运行经历的毫秒数:" + (end - start));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
运行经历的毫秒数:124
使用缓冲字节流读取数据:
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
File f = new File("D:/Android/jdk1.8中文百度.CHM");
BufferedInputStream bi = null;
FileInputStream in = null;
long start = System.currentTimeMillis();// 数据流开始时毫秒值
try {
in = new FileInputStream(f);
bi = new BufferedInputStream(in);// 将文件字节流包装成缓冲流
byte b[] = new byte[2014];// 缓冲区自己数组(这个缓冲区与Buffered不同)
while (bi.read(b) != -1) {// 使用缓冲流读取数据
}
long end = System.currentTimeMillis();// 数据流结束时毫秒值
System.out.println("运行经历的毫秒数:" + (end - start));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (bi != null) {
try {
bi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
运行经历的毫秒数:62
使用缓冲字节流可以大大提升效率。
同理,缓冲输出流例子:
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo2 {
public static void main(String[] args) {
File f = new File("word.txt");
FileOutputStream out = null;
BufferedOutputStream bo = null;
try {
out = new FileOutputStream(f);
bo = new BufferedOutputStream(out);// 将文件输出流包装
String str = "天生我材必有用,千金散尽还复来。";
byte b[] = str.getBytes();
bo.write(b);
// 使用缓冲字节流时,要多进行刷新操作
bo.flush();// 刷新,可强制将缓冲区数据写入文件中,即使缓冲区没有写满
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bo != null) {
try {
bo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行生成了word.txt文档。
5.2 BufferedReader 与 BufferedWriter 类
BufferedReader
类与BufferedWriter
类分别继承Reader
类与Writer
类。这两个类同样具有内部缓存机制,并可以以行为单位进行输入输出。
在使用BufferedWriter类的Write()方法时,数据并没有立刻被写入至输出流中,而是首先进入缓存区中。如果想立刻将缓存区中的数据写入输出流中,一定要调用flush()方法。
缓冲输入流例子:
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
File f = new File("word.txt");
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(f);
bw = new BufferedWriter(fw);// 将文件字符输出流包装成缓冲字符流
String str1 = "世界那么大";
String str2 = "我想去看看";
bw.write(str1);// 第一行数据
bw.newLine();// 创建一个新行
bw.write(str2);// 第二行数据
} catch (IOException e) {
e.printStackTrace();
} finally {// 要注意流的关闭顺序,先创建的后关闭
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行生成word.txt文件,显示内容:
世界那么大
我想去看看
下面是BufferedReader实例,先将word.txt中的内容修改为“会当凌绝顶 一览众山小”,分为两行写入:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
File f = new File("word.txt");
FileReader fr = null;
BufferedReader br = null;
try {
fr = new FileReader(f);
br = new BufferedReader(fr);// 将文件字符输入流包装成缓冲字符输入流
String tmp = null;// 创建临时变量
int i = 1;// 计数器
while ((tmp = br.readLine()) != null) {// 循环读取文件中的内容
System.out.println("第" + i + "行内容:" + tmp);
i++;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
第1行内容:会当凌绝顶
第2行内容:一览众山小
6. 数据输入/输出流
数据输入输出流(DataInputStream
类与DataOutputStream
类)允许应用程序以与机器无关的方式从底层输入流中读取基本Java数据类型。也就是说,当读取一个数据时,不必再关心这个数值应当是什么字节。
DataInputStream类只提供了一个readUTF()
方法返回字符串。这是因为要在一个连续的字节流读取一个字符串,如果没有特殊的标记作为一个字符串的结尾,并且事先也不知道这个字符串的长度,也就无法知道读取到什么位置才是这个字符串的结束。DataOutputStream类中只有writeUTF()方法向目标设备中写入字符串的长度,所以我们也只能准确地读回写入字符串。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
File f = new File("word.txt");
FileOutputStream out = null;
DataOutputStream dos = null;
try {
out = new FileOutputStream(f);
dos = new DataOutputStream(out);// 将文件流包装成数据流
dos.writeUTF("这是写入字符串数据");// 写入字符串数据
dos.writeInt(123);// 写入整型数据
dos.writeDouble(3.14);// 写入浮点型数据
dos.writeBoolean(true);// 写入布尔类型数据
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (dos != null) {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
DataInputStream di = null;
FileInputStream in = null;
try {
in = new FileInputStream(f);
di = new DataInputStream(in);
System.out.println("readUTF()读取数据:" + di.readUTF());
System.out.println("readInt()读取数据:" + di.readInt());
System.out.println("readDouble()读取数据:" + di.readDouble());
System.out.println("readBoolean()读取数据:" + di.readBoolean());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (di != null) {
try {
di.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
readUTF()读取数据:这是写入字符串数据
readInt()读取数据:123
readDouble()读取数据:3.14
readBoolean()读取数据:true
这里,因为程序写入的是字节码,如果我们不使用DataOutputStream读取数据,读出的便会是乱码。因此需要用DataOutputStream 方法将字节码解析成对应的数据类型。