1、File类

File提供了大量的文件操作:删除文件,修改文件,建立目录、列表文件等等。
如何递归读取目录及子目录下的文件

  1. @Test
  2. public void test02(){
  3. File f = new File("E:\\");
  4. rec(f);
  5. }
  6. public void rec(File f){
  7. if(f.isFile()){
  8. System.out.println(f.getAbsolutePath()+File.separator+f.getName());
  9. }
  10. if(f.isDirectory()){
  11. File[] files = f.listFiles();
  12. if(files!=null){
  13. for (File file : files) {
  14. rec(file);
  15. }
  16. }
  17. }
  18. }

2、Java流概述

文件通常是由一连串的字节或字符构成,组成文件的字节序列称为字节流,组成文件的字符序列称为字符流。Java中根据流的方向可以分为输入流和输出流。输入流是将文件或其它输入设备的数据加载到内存的过程;输出流恰恰相反,是将内存中的数据保存到文件或其他输出设备,详见下图:
1648290969(1).png
文件是由字符或字节构成,那么将文件加载到内存或再将文件输出到文件,需要有输入和输出流的支持,那么在Java语言中又把输入和输出流分为了两个,字节输入和输出流,字符输入和输出流,见下表:
1648291002(1).png

2.1、InputStream(字节输入流)

InputStream是字节输入流,InputStream是一个抽象类,所有继承了InputStream的类都是字节输入流,主要了解以下子类即可:
1648291105(1).png
主要方法介绍:

void close()
关闭此输入流并释放与该流关联的所有系统资源。
abstract int read()
从输入流读取下一个数据字节。
int read(byte[] b)
从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len)
将输入流中最多 len 个数据字节读入字节数组。

2.2、OutputStream(字节输出流)

所有继承了OutputStream都是字节输出流
1648291222(1).png

主要方法介绍

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都是字符输如流
1648291279(1).png

主要方法介绍

abstract void close()
关闭该流。
int read()
读取单个字符。
int read(char[] cbuf)
将字符读入数组。
abstract int read(char[] cbuf, int off, int len)
将字符读入数组的某一部分。

2.4、Writer(字符输出流)

所有继承了Writer都是字符输出流
1648291330(1).png

主要方法介绍

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
1648291407(1).png

【示例代码】

  1. InputStream in = new FileInputStream("E:/a.txt");
  2. int c = 0;
  3. while((c = in.read()) != -1){
  4. System.out.println(c);
  5. }
  6. in.close();

文件可以正确的读取,但是我们的汉字乱码了,原因在于使用了字节输入流,它是一个字节一个字节读取的,而汉字是两个字节,所以读出一个字节就打印,那么汉字是不完整的,所以就乱码了

3.2、FileOutputStream(文件字节输出流)

FileOutputStream主要按照字节方式写文件,例如:我们做文件的复制,首先读取文件,读取后在将该文件另写一份保存到磁盘上,这就完成了备份

1648291519(1).png
【示例代码】

  1. InputStream in = new FileInputStream("E:/a.txt");
  2. OutputStream out = new FileOutputStream("E:/aa.txt");
  3. int c = 0;
  4. while((c = in.read()) != -1){
  5. out.write(c);
  6. }
  7. in.close();
  8. out.close();

3.3、FileReader(文件字符输入流)

FileReader是一字符为单位读取文件,也就是一次读取两个字节,如:
1648291567(1).png
【示例代码】

  1. Reader r = new FileReader("E:/a.txt");
  2. int c = 0;
  3. while((c = r.read()) != -1){
  4. char ch = (char)c;
  5. System.out.println(ch);
  6. }
  7. r.close();

因为采用了字符输入流读取文本文件,所以汉字就不乱吗了,因为一次读取两个字节(即一个字符)

3.4、FileWriter(文件字符输出流)

【代码示例】

  1. Reader r = new FileReader("E:/a.txt");
  2. Writer w = new FileWriter("E:/aaa.txt",true);
  3. int c = 0;
  4. while((c = r.read()) != -1){
  5. char ch = (char)c;
  6. w.write(ch);
  7. }
  8. r.close();
  9. w.close();

【打印指定内容】

  1. Writer w = new FileWriter("E:/aaa.txt",true);
  2. w.write("你好,这是个bug。。。。\n");
  3. w.write("你好,这是个bug。。。。\n");
  4. w.write("你好,这是个bug。。。。\n");
  5. w.write("你好,这是个bug。。。。\n");
  6. w.close();

4、缓冲流

缓冲流主要是为了提高效率而存在的,减少物理读取次数,缓冲流主要有:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter,并且BufferedReader提供了实用方法readLine(),可以直接读取一行,BufferWriter提供了newLine()可以写换行符。

4.1、采用字节缓冲流改造文件复制代码

1648291690(1).png

  1. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/aaa.txt"));
  2. int b = 0;
  3. while((b = bis.read()) != -1){
  4. System.out.println(b);
  5. }
  6. bis.close();

示例如下:

  1. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/a.txt"));
  2. BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream("E:/aaa.txt"));
  3. int b = 0;
  4. while((b = bis.read()) != -1){
  5. bout.write(b);
  6. }
  7. bout.flush();//手动刷新缓存
  8. bis.close();
  9. bout.close();

//可以显示的调用flush,flush的含义是刷新缓冲区,也就是将缓存区中的数据写到磁盘上,不再放到内存里了,在执行os.close()时,其实默认执行了os.flush(),我们在这里可以不用显示的调用

4.2、采用字符缓冲流改造文件复制代码

  1. BufferedReader r = new BufferedReader(new FileReader("d:/abc/a.txt"));
  2. BufferedWriter w = new BufferedWriter(new FileWriter("d:/abc/b.txt"));
  3. String line = null;
  4. while( (line = r.readLine()) != null ){
  5. w.write(line+"\n");
  6. }
  7. r.close();
  8. w.close();

5、转换流

转换流主要有两个InputStreamReader和OutputStreamWriter

  1. InputStreamReader主要是将字节流输入流转换成字符输入流
  2. OutputStreamWriter主要是将字节流输出流转换成字符输出流

5.1、InputStreamReader

1648292172(1).png
【示例代码】

  1. BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("E:/a.txt")));
  2. String str = null;
  3. while((str = br.readLine()) != null){
  4. System.out.println(str);
  5. }
  6. br.close();

5.2、OutputStreamWriter

  1. BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("E:/a.txt")));
  2. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:/aaa.txt")));
  3. String str = null;
  4. while((str = br.readLine()) != null){
  5. bw.write(str+"\n");
  6. }
  7. br.close();
  8. bw.close();

6、打印流

打印流主要包含两个:PrintStream和PrintWriter,分别对应字节流和字符流

6.1、完成屏幕打印的重定向

System.out其实对应的就是PrintStream,默认输出到控制台,我们可以重定向它的输出,可以定向到文件,也就是执行System.out.println(“hello”)不输出到屏幕,而输出到文件
【示例代码】

  1. OutputStream os = new FileOutputStream("E:/a.txt",true);
  2. PrintStream p = new PrintStream(os);
  3. p.println("哈哈哈哈哈");
  4. os.close();
  1. OutputStream os = new FileOutputStream("E:/a.txt",true);
  2. PrintWriter pw = new PrintWriter(os);
  3. pw.println("嘿嘿嘿");
  4. pw.close();
  5. os.close();
  1. OutputStream os = new FileOutputStream("E:/a.txt",true);
  2. System.setOut(new PrintStream(os));
  3. System.out.println("abcde哈哈哈哈");
  4. os.close();

6.2、接受屏幕输入

IDEA junit接收控制台输入需要配置
image.png
添加-Deditable.java.test.console=true,然后重启IDEA即可生效
image.png

【示例代码】
System.in可以接收屏幕输入

  1. //IDEA junit控制台输入需要配置
  2. System.out.println("请输入:");
  3. BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  4. String line = br.readLine();
  5. System.out.println("接收到的数据:"+line);
  6. br.close();

【接受循环输入】

  1. //IDEA junit控制台输入需要配置
  2. System.out.println("请输入:");
  3. BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  4. String line = null;
  5. while(! (line = br.readLine()).equals("") ){
  6. System.out.println("接收到的数据:"+line);
  7. }
  8. br.close();

7、对象流

对象流可以将Java对象转换成二进制写入磁盘,这个过程通常叫做序列化,并且还可以从磁盘读出完整的Java对象,而这个过程叫做反序列化。
对象流主要包括:ObjectInputStream和ObjectOutputStream

7.1、如何实现序列化和反序列化

如果实现序列化该类必须实现序列化接口java.io.Serializable,该接口没有任何方法,该接口只是一种标记接口,标记这个类是可以序列化的

序列化

  1. import java.util.Date;
  2. public class People {
  3. private Long id;
  4. private String name;
  5. private Date birthday;
  6. //get/set...
  7. }
  1. People p = new People(1L,"张三",new Date());
  2. ObjectOutput oos = new ObjectOutputStream(new FileOutputStream("E:/p.txt"));
  3. oos.writeObject(p);
  4. oos.close();

不能序列化,对序列化的类是有要求的,这个序列化的类必须实现一个接口Serializable,这个接口没有任何方法声明,它是一个标识接口,如:java中的克隆接口Cloneable,也是起到了一种标识性的作用

序列化

  1. import java.io.Serializable;
  2. import java.util.Date;
  3. public class People implements Serializable {
  4. private Long id;
  5. private String name;
  6. private Date birthday;
  7. }

以上可以完成序列化

反序列化

  1. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/p.txt"));
  2. //反序列化
  3. People person = (People)ois.readObject();
  4. System.out.println(person);
  5. ois.close();

7.2、关于transient关键字

  1. import java.io.Serializable;
  2. import java.util.Date;
  3. public class People implements Serializable {
  4. private Long id;
  5. private transient String name;//序列号过程中会忽略该字段
  6. private Date birthday;
  7. }

7.3、关于serialVersionUID属性

【示例代码】,在person中加入一个成员属性sex,然后在读取person.dat文件

  1. public class People implements Serializable {
  2. private Long id;
  3. private transient String name;
  4. private Date birthday;
  5. private String sex;
  6. }

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成员域的值作为类的序列号(类的版本号),这样不管你的类如何升级,那么他的序列号(版本号)都是一样的,就不会产生类的兼容问题。

【代码示例】,解决序列化版本冲突的问题

  1. public class People implements Serializable {
  2. //加入版本号,防止序列化兼容问题
  3. private static final long serialVersionUID = 1L;
  4. private Long id;
  5. private transient String name;
  6. private Date birthday;
  7. private String sex;
  8. }

以上不再出现序列化的版本问题,因为他们有统一的版本号:-111111111111111111L
进一步理解一下serialVersionUID
1648292405(1).png
【代码示例】,将Person的版本号修改为-111111111111111222L, 该客户端与服务器通信会出现序列化版本兼容问题

  1. java.io.InvalidClassException: com.test.People; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
  2. at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689)
  3. at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2023)
  4. at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1873)
  5. at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2180)
  6. at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1690)
  7. at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:499)
  8. at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:457)
  9. at com.test.FileTest.test21(FileTest.java:264)
  10. at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  11. at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  12. at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  13. at java.base/java.lang.reflect.Method.invoke(Method.java:566)
  14. at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
  15. at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
  16. at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
  17. at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
  18. at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
  19. at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
  20. at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
  21. at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
  22. at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
  23. at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
  24. at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
  25. at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
  26. at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
  27. at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
  28. at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
  29. at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
  30. at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
  31. at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
  32. at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
  33. at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

serialVersionUID就和序列化有关

.