Java IO 流全介绍
java 流概述
- 流根据方向可以分为:输入流和输出流
- 注意:输入和输出是相对于内存而言的
- 从内存中出来就是输出,到内存中就是输入
- 输入流 又叫做 InputStream
- 输出流又叫做 OutputStream
- 输入 还叫作"读",Read
- 输出还叫作“写”, Write
- 流根据读取数据的方式可以分为:字节流和字符流
- 字节流是按照字节的方式读取
- 字符流是按照字符的方式读取,一次读
2
个字节java
语言中一个字符 占2
个字节
- 字节流适合读取:视频、声音、图片等二进制文件
- 字符流适合读取:纯文本文件
- Java语言中所有的字节流都以
Stream
结尾 - 所有的字符流都含有
Reader
或者Writer
-
需要重点掌握的
16
个流java.io.*; FileInputStream FileOutputStream FileReader FileWriter BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter DataInputStream DateOutputStream ObjectInputStream ObjectOutputStream 转换流(字节流转换成字符流) InputStreamReader OutputStreamWriter PrintWriter PrintStream // 标准的输出流(默认输出到控制台)
-
Java语言中的流分为四大家族:
InputStream
OutputStream
Reader
Writer
-
InputStream
和OutputStream
继承结构图
-
Reader
和Writer
继承结构图
文件流
FileInputStream
-
继承结构
java.io.InputStream; --- java.io.FileInputStream; // 文件字节输入流
按照字节方式读取文件
-
举例1-使用
read()
方法 一次读取一个字节
该方法返回的int
类型的值表示读取的字节。import java.io.*; public class Test01 { public static void main(String[] args){ FileInputStream fis = null; // 1. 要读取文件,先与这个文件创建一个输入流 // 文件路径 String filePath = "D:\\CodeFiles\\CodeTrain\\code_train\\src\\com\\test\\inputoutput\\temp01"; try { fis = new FileInputStream(filePath); // 2. 开始读 // int read() // --> Reads a byte of data from this input stream. int i1 = fis.read();// 以字节的方式读取 int i2 = fis.read();// 以字节的方式读取 int i3 = fis.read();// 以字节的方式读取 int i4 = fis.read();// 以字节的方式读取 int i5 = fis.read();// 以字节的方式读取 int i6 = fis.read();// 以字节的方式读取 int i7 = fis.read();// 以字节的方式读取 int i8 = fis.read();// 以字节的方式读取 System.out.println(i1);// a -> 97 System.out.println(i2);// b -> 98 System.out.println(i3);// c -> 99 System.out.println(i4);// d -> 100 System.out.println(i5);// e -> 101 System.out.println(i6);// f -> 102 System.out.println(i7);// 结束 -> -1 System.out.println(i8);// 结束 -> -1 }catch(FileNotFoundException e) { e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }finally { try { fis.close(); }catch(IOException e) { e.printStackTrace(); } } } } /*输出 97 98 99 100 101 102 -1 -1 */
-
测试
import java.io.*; public class Test02 { public static void main(String[] args) throws Exception{ // 1. 创建文件流 // 文件路径 String filePath = "D:\\CodeFiles\\CodeTrain\\code_train\\src\\com\\test\\inputoutput\\temp01"; FileInputStream fis = new FileInputStream(filePath); // 2. 读 while(true) { int temp = fis.read(); if(-1 == temp) { break; } System.out.println(temp); } // 3. 关闭流 fis.close(); } } /*输出 97 98 99 100 101 102 */
-
测试
import java.io.*; public class Test03 { public static void main(String[] args) throws Exception{ // 1. 创建文件流 // 文件路径 String filePath = "D:\\CodeFiles\\CodeTrain\\code_train\\src\\com\\test\\inputoutput\\temp01"; FileInputStream fis = new FileInputStream(filePath); // 2. 读 int temp = 0; while((temp = fis.read()) != -1) { System.out.println(temp); } // 3. 关闭流 fis.close(); } } /*输出 97 98 99 100 101 102 */
-
使用
read()
方法读取文件数据的缺点- 效率较低,一次只能读取1字节
- 频繁访问磁盘,对磁盘伤害较大
-
-
举例2- 使用
read(byte[] b)
读取文件,每次都读取固定长度的字节存储到字节数组中
int read(byte[] b);
读取之前在内存中准备一个 byte 类型的数组,每次读取多个字节存储在 byte 数组中。
一次读取多个字节,不是单字节读取了,效率较高
该方法返回的int
类型的值代表的是:该次读取了多少字节
- 如果没有读取到,即已经到达了文件的末尾,则返回
-1
import java.io.*;
public class Test04 {
public static void main(String[] args) throws Exception{
// 1. 创建输入流
String filePath = "D:\\CodeFiles\\CodeTrain\\code_train\\src\\com\\test\\inputoutput\\temp01";
FileInputStream fis = new FileInputStream(filePath);
// 2. 读
byte[] bytes = new byte[3];// 每一次最多读取3个字节
int i1 = fis.read(bytes); // 3
// byte数组转换成字符串
System.out.println(new String(bytes));//abc
int i2 = fis.read(bytes); // 3
System.out.println(new String(bytes));// def
int i3 = fis.read(bytes); // 1
System.out.println(new String(bytes, 0, i3));// g
int i4 = fis.read(bytes); // -1 已经到达文件末尾, 返回 -1
// 3. 关闭
fis.close();
}
}
-
测试-使用循环读取
import java.io.*; public class Test05 { public static void main(String[] args) throws Exception{ String filePath ="D:\\CodeFiles\\CodeTrain\\code_train\\src\\com\\test\\inputoutput\\temp01"; FileInputStream fis = new FileInputStream(filePath); byte[] bytes = new byte[1024];// 每次最读取1024字节 while(true) { int temp= fis.read(bytes); if(-1 == temp) { break; } // 讲byte中有效的数据转换成字符串 System.out.println(new String(bytes, 0, temp)); } fis.close(); } } // 输出 // abcdefg
-
测试-使用循环读取
import java.io.*; public class test06 { public static void main(String[] args) throws Exception{ String filePath = "D:\\CodeFiles\\CodeTrain\\code_train\\src\\com\\test\\inputoutput\\temp01"; FileInputStream fis = new FileInputStream(filePath); int temp =0; byte[] bytes = new byte[1024]; while((temp = fis.read(bytes)) != -1) { System.out.println(new String(bytes, 0, temp)); } fis.close(); } }
-
举例3-使用
available()
方法 返回流中剩余的估计字节数,使用skip(long n)
方法跳过n
个字节import java.io.*; public class Test07 { public static void main(String[] args) throws Exception{ String filePath = "D:\\CodeFiles\\CodeTrain\\code_train\\src\\com\\test\\inputoutput\\temp01"; FileInputStream fis = new FileInputStream(filePath); int i1 = fis.available(); System.out.println(i1); // 7 int i2 = fis.read();// 读取一个字节:a-97 int i3 = fis.available();// 剩余的估计字节数 System.out.println(i3);// 6 long l1 = fis.skip(2); // 返回实际跳过的字节数 System.out.println(l1);//2 int i4 = fis.available();//4 System.out.println(i4); int i5 = fis.read(); System.out.println(i5);// d-100 fis.close(); } }
FileOutputStream
-
继承结构
java.io.OutputStream; ---- java.io.FileOutputStream; // 文件字节输出流
将计算机内存中的数据写入硬盘文件中。
-
构造方法
-
举例1-使用
FileOutputStream(String name)
方法覆盖原始文件内容import java.io.*; public class Test08 { public static void main(String[] args) { String filePath = "temp02"; FileOutputStream fos = null; try { // 创建文件字节输出流 fos = new FileOutputStream(filePath);// 文件不存在则自动创建 // 开始写数据 String msg = "HelloWorld"; // 字符串转换为byte数组 byte[] bytes = msg.getBytes(); fos.write(bytes); // 推荐最后的时候为了保证数据完全写入硬盘,执行刷新函数 fos.flush();// 强制写入 }catch(Exception e) { e.printStackTrace(); }finally { if(fos != null) { try { fos.close(); }catch(IOException e) { e.printStackTrace(); } } } } }
-
举例2-使用使用
FileOutputStream(String name, boolean append)
方法在原来的文件后面追加数据import java.io.*; public class Test09 { public static void main(String[] args) throws Exception{ String filePath = "temp02"; // 创建文件字节输出流 // 在文件末尾追加数据 FileOutputStream fos = new FileOutputStream(filePath, true); String msg = "helloWorld!!"; // 字符串转换为 bytep[] byte[] bytes = msg.getBytes(); // 写 fos.write(bytes); // 写一部分数据 void write(byte[] b, int off, int len) fos.write(bytes, 2, 3); fos.flush(); fos.close(); } }
-
举例3-实现文件的复制粘贴
import java.io.*; public class Test10 { public static void main(String[] args) throws Exception{ // 创建输入流 FileInputStream fis = new FileInputStream("D:\\personalData\\music\\music163\\Aki阿杰 - 一世妆.mp3"); // 创建输出流 FileOutputStream fos = new FileOutputStream("test.mp3"); // 一边读,一边写 byte[] bytes = new byte[1024]; int temp = 0; while((temp=fis.read(bytes)) != -1) { fos.write(bytes, 0, temp); } // 刷新 fos.flush(); // 关闭流 fis.close(); fos.close(); } }
FileReader
-
继承结构
java.io.Reader; java.io.InputStreamReader; // 转换流(字节输入流--> 字符输入流) java.io.FileReader; // 文件字符输入流
-
举例1-读取纯文本文件
import java.io.*; public class Test11 { public static void main(String[] args) { // 创建文件字符输入流 FileReader fr = null; try { fr = new FileReader("temp02"); char[] ch = new char[512]; int temp = 0; while((temp = fr.read(ch)) != -1) { System.out.println(new String(ch, 0, temp)); } }catch(Exception e) { e.printStackTrace(); }finally { if(fr != null) { try { fr.close(); }catch(Exception e) { e.printStackTrace(); } } } } }
FileWriter
-
继承结构图
java.io.Write; java.io.OutputStreamWriter;//转换流(字节输出流转换成字符输出流) java.io.FileWriter;// 文件字符输出流
-
举例1-写文件
import java.io.*; public class Test12 { public static void main(String[] args) throws Exception{ // FileWriter fw = new FileWriter("temo03"); FileWriter fw = new FileWriter("temo03", true); //写 String str = "巴拉巴西"; fw.write(str); char[] nchr = {'我','是','中'}; fw.write(nchr, 0, 2); // 刷新 fw.flush(); // 关闭 fw.close(); } }
-
举例2-使用
FileReader
和FileWriter
实现文件的复制粘贴import java.io.*; public class Test13 { public static void main(String[] args) throws Exception{ // 创建文件字符输入流 FileReader fr = new FileReader("temp02"); // 创建字符输出流 FileWriter fw = new FileWriter("temp03"); int temp = 0; char[] ncha = new char[512]; while((temp = fr.read(ncha))!=-1) { fw.write(ncha, 0, temp); } // 刷新 fw.flush(); // 关闭 fr.close(); fw.close(); } }
缓冲流
BufferedInputStream
不说了,参照
BufferedReader
BufferedOutputStream
不说了,参照
BufferedWriter
BufferedReader
-
继承结构
java.io.Reader; java.io.BufferedReader;
-
构造方法
Reader
是一个抽象类,抽象类不能实例化,所以可以使用FileReader
文件字符输入流
-
举例1-使用带缓冲区的字符输入流读文件
import java.io.*; public class Test14 { public static void main(String[] args) throws Exception{ FileReader fr = new FileReader("temp02");//创建一个文件字符输入流 BufferedReader br = new BufferedReader(fr);// 将文件字符输入流包装成带有缓冲区的字符输入流 // 开始读 String temp = null; while((temp=br.readLine())!=null) {// br.readline();方法读取一行,但是行尾不带换行符 System.out.println(temp);// 输出一行 } // 关闭 // 注意:关闭只需要关闭最外层的包装流(这里有一个装饰者模式) br.close(); } }
- 根据流出现的位置,可以分为:包装流或者处理流 和 节点流
FileReader fr
是一个节点流BufferedReader br
是一个包装流或者处理流readline()
方法读取一行时,不会读取换行符
-
举例2-使用
InputStreamReader
转换流将字节输入流转换成字符输入流传入到BufferedReader()
中import java.io.*; public class Test15 { public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("temp02");// 文件字节输入流 // 转换流,将字节输入流转换成字符输入流 InputStreamReader isr = new InputStreamReader(fis);// isr是字符流 BufferedReader br = new BufferedReader(isr); //br = new BufferedReader(new InputStreamReader(new FileInputStream("temp02"))); // 开始读 String temp = null; while((temp = br.readLine())!=null) { System.out.println(temp); } // 关闭 // 只需要关闭最外层包装流(装饰者模式) br.close(); } }
-
举例3-接收用户键盘输入
import java.io.*; public class Test16 { public static void main(String[] args) throws Exception{ // System.in 是一个标准字节输入流,默认接收键盘的输入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = br.readLine(); System.out.println(str); } }
BufferedWritter
-
继承结构
java.io.Writer; java.io.BufferedWriter;
-
举例1-使用带缓冲区的字符输出流实现文件写操作
import java.io.*; public class Test17 { public static void main(String[] args) throws Exception{ BufferedWriter bw = new BufferedWriter(new FileWriter("temp04", true)); bw.write("hello world"); bw.newLine();// 写入换行符 bw.write("嘻嘻哈哈和"); bw.flush(); bw.close(); } }
-
举例2- 使用
BufferedReader
和BufferedWriter
实现文件的拷贝import java.io.*; public class Test18 { public static void main(String[] args) throws Exception{ BufferedReader br = new BufferedReader(new FileReader("temp04")); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("temp05"))); String temp = null; while((temp=br.readLine())!=null) { bw.write(temp); bw.newLine(); } bw.flush(); br.close(); bw.close(); } }
装饰着模式(Decorator)
-
如何实现对类中某一个方法的扩展(不改变原来的类)?
-
继承(泛化关系,耦合度较高)
public class FileReader{ void close(){ System.out.println("fileReader closed!"); } }
public class BufferedReader extends FileReader{ // 扩展 FileReader 里面的close() 方法 void close(){ System.out.println("BufferedReader closed"); super.close(); System.out.println("BufferedReader closed"); } }
public class Test{ public static void main(String[] args){ BufferedReader br = new BufferedReader(); br.close(); } }
-
装饰者模式(关联关系,耦合度低)
- 装饰者模式中要求:装饰者中含有被装饰者的引用
- 装饰者模式中要求:装饰者和被装饰者应该实现同一个类型
public abstract class Reader { public abstract void close(); }
public class FileReader extends Reader{ @Override public void close() { System.out.println("FileReader closed!"); } }
public class BufferedReader extends Reader{ private Reader in;//被装饰者的引用 public BufferedReader(Reader in) {// 多态 this.in = in; } @Override public void close() { System.out.println("BufferedReader closed!"); in.close(); System.out.println("BufferedReader closed!"); } }
-
数据字节流
DataOutpurStream 和 DataInputStream
数据字节输出流和数据字节输入流是比较专用的流。主要用于存储变量等(用的比较少)
-
DataOutputStream
java.io.OutputStream; java.io.FilterOutputStream; java.io.DataOutputStream;
数据字节输出流可以将 内存中的 “int i=10;” 写入到硬盘文件中。写进去的不是字符串格式,而是二进制串,带有数据类型。
import java.io.*; public class Test19 { public static void main(String[] args) throws Exception{ // 创建流 DataOutputStream dos = new DataOutputStream(new FileOutputStream("./resource/temp06")); // 准备数据 byte b = 10; short s = 13; int i = 24; long l = 123L; float f = 0.23f; double d = 1.32; boolean flag = true; char c = 'a'; // 写 dos.writeByte(b); dos.writeShort(s); dos.writeInt(i); dos.writeLong(l); dos.writeFloat(f); dos.writeDouble(d); dos.writeBoolean(flag); dos.writeChar(c); // 刷新 dos.flush(); // 关闭 dos.close(); } }
-
DataInputStream
java.io.InputStream; java.io.FilterInputStream; java.io.DataInputStream;
数据字节输入流:用于读取DataOutputStream写的文件,且必须提前知道该文件中的数据存储格式和存储顺序,读取的顺序必须和写入的顺序相同。
package com.test.inputoutput; import java.io.*; public class Test20 { public static void main(String[] args) throws Exception{ DataInputStream dis = new DataInputStream(new FileInputStream("./resource/temp06")); byte b = dis.readByte(); short s = dis.readShort(); int i = dis.readInt(); long l = dis.readLong(); float f = dis.readFloat(); double d = dis.readDouble(); boolean flag = dis.readBoolean(); char c = dis.readChar(); System.out.println(b); System.out.println(s); System.out.println(i); System.out.println(l); System.out.println(f); System.out.println(d); System.out.println(flag); System.out.println(c); dis.close(); } }
//输出 10 13 24 123 0.23 1.32 true a
打印流
java.io.PrintStream; // 标准输出流,默认打印到控制台,以字节方式
java.io.PrintWriter; // 标准输出流,默认打印到控制台,以字符方式
- 举例1-使用
printStream
import java.io.*;
import java.text.*;
import java.util.*;
public class Test21 {
public static void main(String[] args) throws Exception{
// 默认输出到控制台
System.out.println("hello world");
PrintStream ps = System.out;
ps.println("hello world");
// 可以改变输出方向
System.setOut(new PrintStream(new FileOutputStream("./resource/logs", true)));// log 文件
// 输出
System.out.println("HAHA");
// 通常使用上面方式记录日志
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println("m1 方法开始执行"+sdf.format(new Date()));
m1();
System.out.println("m1 方法执行结束"+sdf.format(new Date()));
}
static void m1() {
System.out.println("hello");
}
}
对象流
对象序列化&反序列化
序列化:将堆内存中的对象信息转换为可以存储或者传输的形式的过程(
Serial
)反序列化:将硬盘中存储的对象信息加载到
JVM
内存中(DeSerial
)
java.io.ObjectOutputStream;
序列化java对象到硬盘
java.io.ObjectInputStream;
将硬盘中的数据“反序列化”到JVM
内存
-
举例1-对象序列化
import java.io.Serializable; public class User implements Serializable{ String name; public User(String name){ this.name = name; } @Override public String toString() { return "User[name:"+this.name+"]"; } }
import java.io.*; public class Test{ public static void main(String[] args) throws Exception{ User u1 = new User("张三"); User u2 = new User("李四"); User u3 = new User("王五"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("temp07")); oos.writeObject(u1); oos.writeObject(u2); oos.writeObject(u3); // flush oos.flush(); // close oos.close(); } }
- 序列化的对象必须实现
Serializable
接口Serializable
接口内没有定义任何方法,将这类接口称为:标识接口- 没有任何方法的接口称为标识接口
- 类似的接口还有
java.lang.Clonable
- 标识接口的作用:
- 起到标识的作用
- JVM如果看到了某个对象实现了某个标识接口,会对它**“特殊待遇”**
- 序列化的对象必须实现
-
举例2-反序列化
import java.io.*; public class Test02{ public static void main(String[] args) throws Exception{ // 创建反序列化流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("temp07")); // 读 Object o = ois.readObject(); System.out.println(o);//User[name:张三] o = ois.readObject(); System.out.println(o);//User[name:李四] o = ois.readObject(); System.out.println(o);//User[name:王五] // 关闭 ois.close(); } }
序列化版本号:SerialVersion_UID
考虑这样一种情形:
-
有一个User类
public class User implements Serializable{ }
将该类对应的对象序列化
import java.io.*; public class Test{ public static void main(String[] args) throws Exception{ User u1 = new User(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("temp07")); oos.writeObject(u1); // flush oos.flush(); // close oos.close(); } }
编译运行后,对该对象反序列化
-
但是原先生成 User.class 的类删掉了,原先的 User.java 也重写了,重新编译生成 User.class
public class User implements Serializable{ String name; }
-
反序列化出现错误:
Exception in thread "main" java.io.InvalidClassException: User; local class incompatible: stream classdesc serialVersionUID = -7020619477594468968, local class serialVersionUID = 5211548487022640024
产生的原因:因为 User 类 实现了 Serializable 接口,JVM 会特殊待遇:会给该类添加一个属性:
static final long serialVersionUID=-7020619477594468968
在反序列化的时候,JVM会比较硬盘中存储的对象文件和User.class 中的 SerialVersionUID 是否一样,如果不一样,则会报错。
之前的
User.class
的SerialVersionUID
是-7020619477594468968;
序列化后存储到硬盘中的SerialVersionUID
也是-7020619477594468968;User.java
重写后,jvm
给它添加的属性SerialVersionUID
的值为:5211548487022640024
,所以在反序列化的时候出现错误。如何实现在更改原先的类之后,仍可以反序列化对象呢?
-
不让
JVM
自动生成,自己写一个序列化版本号import java.io.Serializable; public class User implements Serializable{ // 手动创建序列化版本号 static final long serialVersionUID = 123123123L; // 下面可以对代码升级 String name; public User(String name){ this.name = name; } }
-
-
如果不想让类中的某一个属性序列化,可以在属性前面添加
transient
关键字修饰import java.io.Serializable; public class User implements Serializable{ // 序列化版本号 static final long serialVersionUID = 123123123L; // 下面可以对代码升级 transient String name; // transient 短暂的,暂时的 public User(String name){ this.name = name; } @Override public String toString() { return "User[name="+this.name+"]"; } }
反序列化得到结果:
User[name=null]
File 类
-
继承结构
java.lang.Object; java.io.File;
-
File和流没有关系,通过File不能完成文件的读和写
-
File 是 文件和目录路径名的抽象表现形式
- 代表的硬盘上的文件夹(Directory)和文件(File)
-
-
举例1-File中的常用方法
import java.io.File; import java.util.*; import java.text.*; public class Test01 { public static void main(String[] args) throws Exception{ String parentPath = "D:\\CodeFiles\\CodeTrain\\code_train\\resource"; File f1 = new File(parentPath); // 1. 判断是否存在 System.out.println(f1.exists());// true // 2. 创建新文件 File newFile = new File("newFile.java"); System.out.println(newFile.createNewFile());// true // 3.获取新文件对应的绝对路径 File newFile2 = newFile.getAbsoluteFile(); System.out.println(newFile2.getAbsolutePath()); // 4.获得文件名 System.out.println(newFile.getName()); // 5. 获得父目录 System.out.println(newFile2.getAbsoluteFile().getParent()); // 6. 判断文件路径是否是绝对路径 System.out.println(newFile.getAbsoluteFile().isAbsolute());// true // 7.判断是否是目录 System.out.println(newFile.isDirectory());// false // 8.判断是否是文件 System.out.println(newFile.isFile());// true // 9.判断文件是否是隐藏文件 System.out.println(newFile.isHidden()); // false // 10.获取文件最后一次更改的时间 long time = newFile.lastModified(); Date date = new Date(time); System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(date));//2020-03-17 17:50:25 084 // 11.获取文件对应的长度(大小/字节) System.out.println(newFile.length());// 0 System.out.println(newFile.getAbsoluteFile().getParent().length());//33 // 12. 获取该目录下的所有文件/目录 String[] names = new File(newFile.getAbsoluteFile().getParent()).list(); for(String str:names) { System.out.println(str); } File[] files = new File(newFile.getAbsoluteFile().getParent()).listFiles(); for(File file:files) { System.out.println(file.getAbsolutePath()); } // 13.在指定目录下创建目录 File newD = new File(newFile.getParentFile(), "test_dir"); if(! newD.exists()) { newD.mkdir(); } System.out.println(newD.getAbsolutePath()); // 14. 删除对应目录 System.out.println(newD.delete()); // 15.创建多层目录 File multi_dir = new File(newFile.getParentFile(), "a/b/v/c/d/f"); if(!multi_dir.exists()) { multi_dir.mkdirs(); } System.out.println(multi_dir.getAbsolutePath()); } }
-
举例2-递归遍历目录下所有文件
import java.io.*; public class Test02 { public static void main(String[] args) { String parentPath = "D:\\CodeFiles"; File f = new File(parentPath); getFilesR(f); } private static void getFilesR(File f) { if(f.isFile()) { return; } File[] files = f.listFiles(); for(File file:files) { System.out.println(file); getFilesR(file); } } }
以上