文章目录
IO流
一、java.io.File 类
File类是java.io包下代表与平台无关的文件和目录,也就是说如果希望在程序中操作文件和目录都可以通过File类来完成,File类能新建、删除、重命名文件和目录。
(1)递归打印多级目录
分析:多级目录的打印。遍历之前,无从知道到底有多少级目录,所以我们可以使用递归实现。
代码实现:
@Test
public void test3() {
File dir = new File("d:/atguigu");
listSubFiles(dir);
}
public void listSubFiles(File dir) {
if (dir != null && dir.isDirectory()) {
File[] listFiles = dir.listFiles();
if (listFiles != null) {
for (File sub : listFiles) {
listSubFiles(sub);//递归调用
}
}
}
System.out.println(dir);
}
(2)递归打印某目录下(包括子目录)中所有满足条件的文件
示例代码:列出"D:/atguigu"下所有".java"文件
@Test
public void test5() {
File dir = new File("D:/atguigu");
listByFileFilter(dir);
}
public void listByFileFilter(File file) {
if (file != null && file.isDirectory()) {
File[] listFiles = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".java") || new File(dir,name).isDirectory();
}
});
if (listFiles != null) {
for (File sub : listFiles) {
if(sub.isFile()){
System.out.println(sub);
}else
listByFileFilter(sub);
}
}
}
}
(3)递归求目录总大小
@Test
public void test4() {
File dir = new File("D:/atguigu");
long length = getLength(dir);
System.out.println("大小:" + length);
}
public long getLength(File dir){
if (dir != null && dir.isDirectory()) {
File[] listFiles = dir.listFiles();
if(listFiles!=null){
long sum = 0;
for (File sub : listFiles) {
sum += getLength(sub);
}
return sum;
}
}else if(dir != null && dir.isFile()){
return dir.length();
}
return 0;
}
(4)递归删除非空目录
如果目录非空,连同目录下的文件和文件夹一起删除
@Test
public void test6() {
File dir = new File("D:/atguigu/javase");
forceDeleteDir(dir);
}
public void forceDeleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
File[] listFiles = dir.listFiles();
if(listFiles!=null){
for (File sub : listFiles) {
forceDeleteDir(sub);
}
}
}
dir.delete();
}
二、IO流
表示对数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为
输入input
和输出output
,即流向内存是输入流,流出内存的输出流。
IO 流 的分类可以分为以下三种:
- 输入流和输出流
按照流的流向来分,可以分为输入流和输出流。输入,输出都是从程序运行所在内存的角度来划分的。- 输入流只能从中读取数据,而不能向其写入数据。InputStream 和 Reader 作为基类
- 输出流只能向其写入数据,不能从中读取数据。OutputStream 和 Writer 作为基类
- 字节流和字符流
字节流和字符流的用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不同。- 字节流操作的数据单元是8位字节,由 InputStream 和 OutputStream 作为基类。
- 字符流操作的数据单元是16位的字符, 由 Reader 和 Writer 作为基类
- 节点流和处理流
按照流的角色来分,可以分为节点流和处理流。- 节点流:可以人向一个特定的IO设备(如磁盘、网络) 读 / 写。也被称为低级流。
- 处理流:用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。也称为高级流
一、字节流
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
1.1 字节输出流【OutputStream】
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
- FileOutputStream类
java.io.FileOutputStream
类是文件输出流,用于将数据写出到文件。
1.2 字节输入流【InputStream】
- FileInputStream类
java.io.FileInputStream
类是文件输入流,从文件中读取字节。
二、字符流
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
文件字符输出流默认自带一个 8k 大小的数据缓冲区, 写出数据时先进入缓冲区,当缓冲区满后才会自动写出到文件。目的时提高 IO 性能flush 方法: 刷新缓冲区, 把数据直接写出去
close方法:先刷新缓冲区, 再关闭流
1.1 字符输入流【Reader】
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
- FileReader类
java.io.FileReader
类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
节缓冲区:
一个字节数组,用来临时存储字节数据。
1.2 字符输出流【Writer】
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
- FileWriter类
java.io.FileWriter
类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
三、缓冲流
缓冲流,也叫高效流,按照数据类型分类:
字节缓冲流:
BufferedInputStream
,BufferedOutputStream
字符缓冲流:BufferedReader
,BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小为8k的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
1.1 字节缓冲流
- BufferedInputStream
- BufferedOutputStream
1.2 字符缓冲流
- BufferedReader
- BufferedWriter
四、转换流
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
1.1 InputStreamReader类
转换流
java.io.InputStreamReader
,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
1.2 OutputStreamWriter类
转换流java.io.OutputStreamWriter
,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
五、数据流
前面学习的IO流,在程序代码中,要么将数据直接按照字节处理,要么按照字符处理。那么,如果要在程序中直接处理Java的基础数据类型,怎么办呢?
String name = “巫师”;
int age = 300;
char gender = ‘男’;
int energy = 5000;
double price = 75.5;
boolean relive = true;
完成这个需求,可以使用DataOutputStream进行写,随后用DataInputStream进行读取,而且顺序要一致。
示例代码:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestData {
public void save() throws IOException{
String name = "巫师";
int age = 300;
char gender = '男';
int energy = 5000;
double price = 75.5;
boolean relive = true;
DataOutputStream dos = new DataOutputStream(new FileOutputStream("game.dat"));
dos.writeUTF(name);
dos.writeInt(age);
dos.writeChar(gender);
dos.writeInt(energy);
dos.writeDouble(price);
dos.writeBoolean(relive);
dos.close();
}
public void reload()throws IOException{
DataInputStream dis = new DataInputStream(new FileInputStream("game.dat"));
String name = dis.readUTF();
int age = dis.readInt();
char gender = dis.readChar();
int energy = dis.readInt();
double price = dis.readDouble();
boolean relive = dis.readBoolean();
System.out.println(name+"," + age + "," + gender + "," + energy + "," + price + "," + relive);
dis.close();
}
}
六、对象流与序列化
Java 提供了一种对象序列化的机制。用字节序列可以表示一个对象,该字节序列包含该
对象的类型
和对象中存储的属性
等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据
、对象的类型
和对象中存储的数据
信息,都可以用来在内存中创建对象。
1. ObjectOutputStream类
java.io.ObjectOutputStream
类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
2. ObjectInputStream类
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
3. 序列化操作
-
该类必须实现
java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。 -
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。(比如网络中传输时,考虑安全因素银行卡字段可以使用transient不进行序列化) -
静态变量的值不会序列化(静态变量的值不属于某个对象的数据,而是属于类的数据)
-
另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个
InvalidClassException
异常。发生这个异常的原因如下:- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
-
Serializable
接口给需要序列化的类,提供了一个序列版本号。serialVersionUID
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
七、打印流 PrintStream 与 PrintWriter
打印流只有输出没有输入
八、标准输入 / 输出流
System类中有三个属性字段:
Modifier and Type | Field and Description |
---|---|
static PrintStream | err The “standard” error output stream. |
static InputStream | in The “standard” input stream. |
static PrintStream | out The “standard” output stream. |
System.in 标准输入流,本质是一个字节输入流,默认接受键盘录入的数据。
System.out 标准输出流,本质是一个字节输出流,默认输出数据到控制台。
九、JDK1.7之后引入新 try…catch
它没有finally,也不需要程序员去关闭资源对象,无论是否发生异常,都会关闭资源对象
try(需要关闭的资源对象的声明){
业务逻辑代码,可能发生异常的代码
}catch(异常类型 e){
处理异常代码
}catch(异常类型 e){
处理异常代码
}
....
十、IOUtils工具类
IOUtils是Apache出品的一个方便IO操作的工具类,简化了IO流的读、写、复制及关闭流等操作(使用前需要导包)
@Test
public void test2() throws IOException {
IOUtils.copy(new FileInputStream("ps.txt"), new FileOutputStream("pw.txt"));//复制文件
IOUtils.write("hello world",new FileOutputStream("ps.txt"),"UTF-8");//写数据到文件
}