1、File类
File提供了大量的文件操作:删除文件,修改文件,建立目录、列表文件等等。
如何递归读取目录及子目录下的文件
@Test
public 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 = 2
at 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)
.