1、File类
File提供了大量的文件操作:删除文件,修改文件,建立目录、列表文件等等。
如何递归读取目录及子目录下的文件
@Testpublic void test02(){File f = new File("E:\\");rec(f);}public void rec(File f){if(f.isFile()){System.out.println(f.getAbsolutePath()+File.separator+f.getName());}if(f.isDirectory()){File[] files = f.listFiles();if(files!=null){for (File file : files) {rec(file);}}}}
2、Java流概述
文件通常是由一连串的字节或字符构成,组成文件的字节序列称为字节流,组成文件的字符序列称为字符流。Java中根据流的方向可以分为输入流和输出流。输入流是将文件或其它输入设备的数据加载到内存的过程;输出流恰恰相反,是将内存中的数据保存到文件或其他输出设备,详见下图:
文件是由字符或字节构成,那么将文件加载到内存或再将文件输出到文件,需要有输入和输出流的支持,那么在Java语言中又把输入和输出流分为了两个,字节输入和输出流,字符输入和输出流,见下表:
2.1、InputStream(字节输入流)
InputStream是字节输入流,InputStream是一个抽象类,所有继承了InputStream的类都是字节输入流,主要了解以下子类即可:
主要方法介绍:
| void | close()  关闭此输入流并释放与该流关联的所有系统资源。  | 
|---|---|
| abstract int | read()  从输入流读取下一个数据字节。  | 
| int | read(byte[] b)  从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 中。  | 
| int | read(byte[] b, int off, int len)  将输入流中最多 len 个数据字节读入字节数组。  | 
2.2、OutputStream(字节输出流)
所有继承了OutputStream都是字节输出流
主要方法介绍
| void | close()  关闭此输出流并释放与此流有关的所有系统资源。  | 
|---|---|
| void | flush()  刷新此输出流并强制写出所有缓冲的输出字节。  | 
| void | write(byte[] b)  将 b.length 个字节从指定的字节数组写入此输出流。  | 
| void | write(byte[] b, int off, int len)  将指定字节数组中从偏移量 off 开始的 len 个字节写入此输出流。  | 
| abstract void | write(int b)  将指定的字节写入此输出流。  | 
2.3、Reader(字符输入流)
所有继承了Reader都是字符输如流
主要方法介绍
| abstract void | close()  关闭该流。  | 
|---|---|
| int | read()  读取单个字符。  | 
| int | read(char[] cbuf)  将字符读入数组。  | 
| abstract int | read(char[] cbuf, int off, int len)  将字符读入数组的某一部分。  | 
2.4、Writer(字符输出流)
所有继承了Writer都是字符输出流
主要方法介绍
| Writer | append(char c)  将指定字符追加到此 writer。  | 
|---|---|
| abstract void | close()  关闭此流,但要先刷新它。  | 
| abstract void | flush()  刷新此流。  | 
| 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)  写入字符串的某一部分。  | 
3、文件流
文件流主要分为:文件字节输入流、文件字节输出流、文件字符输入流、文件字符输出流
3.1、FileInputStream(文件字节输入流)
FileInputStream主要按照字节方式读取文件,例如我们准备读取一个文件,该文件的名称为
test.txt
【示例代码】
InputStream in = new FileInputStream("E:/a.txt");int c = 0;while((c = in.read()) != -1){System.out.println(c);}in.close();
文件可以正确的读取,但是我们的汉字乱码了,原因在于使用了字节输入流,它是一个字节一个字节读取的,而汉字是两个字节,所以读出一个字节就打印,那么汉字是不完整的,所以就乱码了
3.2、FileOutputStream(文件字节输出流)
FileOutputStream主要按照字节方式写文件,例如:我们做文件的复制,首先读取文件,读取后在将该文件另写一份保存到磁盘上,这就完成了备份

【示例代码】
InputStream in = new FileInputStream("E:/a.txt");OutputStream out = new FileOutputStream("E:/aa.txt");int c = 0;while((c = in.read()) != -1){out.write(c);}in.close();out.close();
3.3、FileReader(文件字符输入流)
FileReader是一字符为单位读取文件,也就是一次读取两个字节,如:
【示例代码】
Reader r = new FileReader("E:/a.txt");int c = 0;while((c = r.read()) != -1){char ch = (char)c;System.out.println(ch);}r.close();
因为采用了字符输入流读取文本文件,所以汉字就不乱吗了,因为一次读取两个字节(即一个字符)
3.4、FileWriter(文件字符输出流)
【代码示例】
Reader r = new FileReader("E:/a.txt");Writer w = new FileWriter("E:/aaa.txt",true);int c = 0;while((c = r.read()) != -1){char ch = (char)c;w.write(ch);}r.close();w.close();
【打印指定内容】
Writer w = new FileWriter("E:/aaa.txt",true);w.write("你好,这是个bug。。。。\n");w.write("你好,这是个bug。。。。\n");w.write("你好,这是个bug。。。。\n");w.write("你好,这是个bug。。。。\n");w.close();
4、缓冲流
缓冲流主要是为了提高效率而存在的,减少物理读取次数,缓冲流主要有:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter,并且BufferedReader提供了实用方法readLine(),可以直接读取一行,BufferWriter提供了newLine()可以写换行符。
4.1、采用字节缓冲流改造文件复制代码

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/aaa.txt"));int b = 0;while((b = bis.read()) != -1){System.out.println(b);}bis.close();
示例如下:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/a.txt"));BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream("E:/aaa.txt"));int b = 0;while((b = bis.read()) != -1){bout.write(b);}bout.flush();//手动刷新缓存bis.close();bout.close();
//可以显示的调用flush,flush的含义是刷新缓冲区,也就是将缓存区中的数据写到磁盘上,不再放到内存里了,在执行os.close()时,其实默认执行了os.flush(),我们在这里可以不用显示的调用
4.2、采用字符缓冲流改造文件复制代码
BufferedReader r = new BufferedReader(new FileReader("d:/abc/a.txt"));BufferedWriter w = new BufferedWriter(new FileWriter("d:/abc/b.txt"));String line = null;while( (line = r.readLine()) != null ){w.write(line+"\n");}r.close();w.close();
5、转换流
转换流主要有两个InputStreamReader和OutputStreamWriter
- InputStreamReader主要是将字节流输入流转换成字符输入流
 - OutputStreamWriter主要是将字节流输出流转换成字符输出流
 
5.1、InputStreamReader

【示例代码】
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("E:/a.txt")));String str = null;while((str = br.readLine()) != null){System.out.println(str);}br.close();
5.2、OutputStreamWriter
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("E:/a.txt")));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:/aaa.txt")));String str = null;while((str = br.readLine()) != null){bw.write(str+"\n");}br.close();bw.close();
6、打印流
打印流主要包含两个:PrintStream和PrintWriter,分别对应字节流和字符流
6.1、完成屏幕打印的重定向
System.out其实对应的就是PrintStream,默认输出到控制台,我们可以重定向它的输出,可以定向到文件,也就是执行System.out.println(“hello”)不输出到屏幕,而输出到文件
【示例代码】
OutputStream os = new FileOutputStream("E:/a.txt",true);PrintStream p = new PrintStream(os);p.println("哈哈哈哈哈");os.close();
OutputStream os = new FileOutputStream("E:/a.txt",true);PrintWriter pw = new PrintWriter(os);pw.println("嘿嘿嘿");pw.close();os.close();
OutputStream os = new FileOutputStream("E:/a.txt",true);System.setOut(new PrintStream(os));System.out.println("abcde哈哈哈哈");os.close();
6.2、接受屏幕输入
IDEA junit接收控制台输入需要配置
添加-Deditable.java.test.console=true,然后重启IDEA即可生效
【示例代码】
System.in可以接收屏幕输入
//IDEA junit控制台输入需要配置System.out.println("请输入:");BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String line = br.readLine();System.out.println("接收到的数据:"+line);br.close();
【接受循环输入】
//IDEA junit控制台输入需要配置System.out.println("请输入:");BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String line = null;while(! (line = br.readLine()).equals("") ){System.out.println("接收到的数据:"+line);}br.close();
7、对象流
对象流可以将Java对象转换成二进制写入磁盘,这个过程通常叫做序列化,并且还可以从磁盘读出完整的Java对象,而这个过程叫做反序列化。
对象流主要包括:ObjectInputStream和ObjectOutputStream
7.1、如何实现序列化和反序列化
如果实现序列化该类必须实现序列化接口java.io.Serializable,该接口没有任何方法,该接口只是一种标记接口,标记这个类是可以序列化的
序列化
import java.util.Date;public class People {private Long id;private String name;private Date birthday;//get/set...}
People p = new People(1L,"张三",new Date());ObjectOutput oos = new ObjectOutputStream(new FileOutputStream("E:/p.txt"));oos.writeObject(p);oos.close();
不能序列化,对序列化的类是有要求的,这个序列化的类必须实现一个接口Serializable,这个接口没有任何方法声明,它是一个标识接口,如:java中的克隆接口Cloneable,也是起到了一种标识性的作用
序列化
import java.io.Serializable;import java.util.Date;public class People implements Serializable {private Long id;private String name;private Date birthday;}
以上可以完成序列化
反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/p.txt"));//反序列化People person = (People)ois.readObject();System.out.println(person);ois.close();
7.2、关于transient关键字
import java.io.Serializable;import java.util.Date;public class People implements Serializable {private Long id;private transient String name;//序列号过程中会忽略该字段private Date birthday;}
7.3、关于serialVersionUID属性
【示例代码】,在person中加入一个成员属性sex,然后在读取person.dat文件
public class People implements Serializable {private Long id;private transient String name;private Date birthday;private String sex;}
java.io.InvalidClassException: com.test.People; local class incompatible: stream classdesc serialVersionUID = -7100224129210986212, local class serialVersionUID = -6142766582074026610
错误的原因:在序列化存储Person时,他会为该类生成一个serialVersionUID= -6120276268074674235,而我们在该类中加入了一个sex属性后,那么在使用的时候他就会为该类生成一个新的serialVersionUID= 1923863382018150382,这个两个UID(-6120276268074674235和1923863382018150382)不同,所以Java认为是不兼容的两个类。如果解决呢?
通常在实现序列化的类中增加如下定义:
static final long serialVersionUID = -111111111111111111L;
如果在序列化类中定义了成员域serialVersionUID,系统会把当前serialVersionUID成员域的值作为类的序列号(类的版本号),这样不管你的类如何升级,那么他的序列号(版本号)都是一样的,就不会产生类的兼容问题。
【代码示例】,解决序列化版本冲突的问题
public class People implements Serializable {//加入版本号,防止序列化兼容问题private static final long serialVersionUID = 1L;private Long id;private transient String name;private Date birthday;private String sex;}
以上不再出现序列化的版本问题,因为他们有统一的版本号:-111111111111111111L
进一步理解一下serialVersionUID
【代码示例】,将Person的版本号修改为-111111111111111222L, 该客户端与服务器通信会出现序列化版本兼容问题
java.io.InvalidClassException: com.test.People; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689)at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2023)at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1873)at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2180)at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1690)at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:499)at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:457)at com.test.FileTest.test21(FileTest.java:264)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.base/java.lang.reflect.Method.invoke(Method.java:566)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)at org.junit.runners.ParentRunner.run(ParentRunner.java:300)at org.junit.runner.JUnitCore.run(JUnitCore.java:157)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
.
