Java笔记目录可以点这里:Java 强化笔记(适合有基础的童鞋,不适合小白)
I/O流 全称是 Input/Output Stream,译为“输入/输出流”。
不管什么数据,本质上都是二进制而已,所谓 I/O流,就是将这些 01 二进制进行传输。
I/O流的常用类型都在 java.io
包中:
类型 | 输入流 | 输出流 |
---|---|---|
字节流(Byte Streams) | InputStream |
OutputStream |
字符流(Character Streams) | Reader |
Writer |
缓冲流(Buffered Streams) | BufferedInputStream 、BufferedReader |
BufferedOutputStream 、BufferedWriter |
数据流(Data Streams) | DataInputStream |
DataOutputStream |
对象流(Object Streams) | ObjectInputStream |
ObjectOutputStream |
一个 File 对象就代表一个文件或目录(文件夹)
// file1、file2都能访问test.txt文件
File file1 = new File("F:\\Files\\Texts\\test.text");
File file2 = new File("F:/Files/Texts/test.text");
名字分隔符(name separator):File.separator
/
\
路径分隔符(path separator):File.pathSeparator
:
;
是否区分大小写
String getName()
String getParent()
File getParentFile()
String getPath()
String getAbsolutePath()
File getAbsoluteFile()
long lastModified()
long length()
boolean isAbsolute()
boolean exists()
boolean isDirectory()
boolean isFile()
boolean isHidden()
boolean canRead()
boolean canwrite()
String[] list()
String[] list(FilenameFilter filter)
File[] listFiles()
File[] listFiles(FilenameFilter filter)
File[] listFiles(FileFilter filter)
boolean createNewFile()
boolean delete()
boolean mkdir()
boolean mkdirs()
boolean renameTo(File dest)
boolean setLastModified(long time)
boolean setReadOnly()
boolean setWritable(boolean writable, boolean ownerOnly)
boolean setWritable(boolean writable)
boolean setReadable(boolean readable, boolean ownerOnly)
boolean setReadable(boolean readable)
package com.mj;
import java.io.File;
import java.util.function.Consumer;
public class Files {
/**
* 搜索目录下的所有文件
* @param dir 目录
* @param operation 执行的操作
*/
public static void search(File dir, Consumer<File> operation){
if(dir == null || operation == null) return;
// 目录不存在 或者 传入的是文件, 不执行操作
if(!dir.exists() || dir.isFile()) return;
File[] subfiles = dir.listFiles();
for (File sf : subfiles) {
operation.accept(sf);
if(sf.isFile()) continue; // 如果是文件,跳过,进行后面的搜索
search(sf, operation); // 如果是文件夹, 递归搜索该文件夹
}
}
/**
* 删除文件夹
* @param file
*/
public static void delete(File file){
// 传入null 或者 文件夹不存在, 不执行操作
if(file == null || !file.exists()) return;
clean(file);
file.delete();
}
/**
* 清空文件夹中文件
* @param dir
*/
public static void clean(File dir){
// 如果文件夹不存在 或者 传入的是文件, 不执行操作
if(dir == null || !dir.exists() || dir.isFile()) return;
File[] subfiles = dir.listFiles();
for (File sf : subfiles) {
delete(sf);
}
}
/**
* 剪切文件或文件夹到目标路径
*/
public static void move(File src, File dest){
if(src == null || dest == null) return;
// 如果源File不存在 或者 目标File 已经存在, 不执行操作
if(!src.exists() || dest.exists()) return;
mkparents(dest);
src.renameTo(dest);
}
/**
* 创建父路径
*/
private static void mkparents(File file){
File parent = file.getParentFile();
if(parent == null) return;
parent.mkdirs();
}
}
import java.io.File;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) throws Exception {
// file1、file2都能访问test.txt文件
// File file1 = new File("F:\\");
File file = new File("F:/");
Files.search(file, (sub) -> {
if (sub.isFile()) { // 是文件
System.out.println("\t文件:" + sub.getName());
} else {
System.out.println("目录:" + sub.getName());
}
});
}
}
在计算机里面
字符集(简称 Charset):由字符组成的集合字符集(Character Set)
常见的字符集有:
ASCII
:128个字符(包括了英文字母大小写、阿拉伯数字等)ISO-8859-1
:支持欧洲的部分语言文字,在有些环境也叫 Latin-1GB2312
:支持中文(包括了 6763 个汉字)BIG5
:支持繁体中文(包括了 13053 个汉字)GBK
:是对 GB2312、BIG5 的扩充(包括了 21003 个汉字),支持中日韩GB18030
:是对 GBK 的扩充(包括了 27484 个汉字)ISO-8859-1
、GB2312
、BIG5
、GBK
、GB18030
、Unicode
中都已经包括了 ASCII
中的所有字符
每个字符集都有对应的字符编码,它决定了每个字符如何转成二进制存储在计算机中。
ASCII
:单字节编码,编码范围是 0x00 ~ 0x7F (0 ~ 127)
ISO-8859-1
:单字节编码,编码范围是 0x00 ~ 0xFF
ASCII
一致,0x80 ~ 0x9F 是控制字符,0xA0 ~ 0xFF 是文字符号GB2312
、BIG5
、GBK
:采用双字节表示一个汉字
GB18030
:采用单字节、双字节、四字节表示一个字符
Unicode
:有 Unicode
、UTF-8
、UTF-16
、UTF-32
等编码,最常用的是 UTF-8
编码
UTF-8
采用单字节、双字节、三字节、四字节表示一个字符
如果 String.getBytes
方法没有传参,就使用 JVM 的默认字符编码,一般跟随 main 方法所在文件的字符编码。
可以通过 Charset.defaultCharset
方法获取 JVM 的默认字符编码
Charset
类的全名是 java.nio.charset.Charset
计算机中真正的代码其实是二进制,所以转到它是编码,它转别的就是解码。
编码、解码时使用的字符编码必须要保持一致,否则会造成乱码。
String str1 = "Java不难";
// UTF-8编码为二进制: [74, 97, 118, 97, -28, -72, -115, -23, -102, -66]
byte[] bytes = str1.getBytes("UTF-8");
// GB18030解码: Java涓嶉毦
String str2 = new String(bytes, "GB18030");
// UTF-8解码: Java不难
String str3 = new String(bytes, "UTF-8");
从Java7开始推出的try-with-resources
语句(可以没有catch
、finally
)
try(资源1; 资源2; ...) {
} catch (Exception e) {
} finally {
}
可以在try
后面的小括号中声明一个或多个资源(resource)
java.lang.AutoCloseable
接口的实例,都可以称之为是资源不管try
中的语句是正常还是意外结束
close
方法(close
方法的调用顺序与资源的声明顺序相反)close
方法后,再执行 finally
中的语句字节流的特点:
InputSteam
、OutputStream
常用的是字节流有 FileInputStream
、FileOutputStream
// 字节流
// true表示追加内容,并非覆盖原来内容
FileOutputStream fos = new FileOutputStream("F:/1.txt", true);
fos.write("MJ码哥".getBytes("GBK"));
// 可以写直接字节
fos.write(74); // M
fos.write(77); // J
fos.close();
MJ码哥MJ
FileInputStream is = new FileInputStream("F:/1.txt");
// 读取第1个字节
int byte1 = is.read();
// 读取第2个字节
int byte2 = is.read();
is.close();
InputStream is = new FileInputStream("F:/2.txt");
byte[] bytes = new byte[1024];
// read返回实际读取的字节数
int len = is.read(bytes);
is.close();
/**
* 将内存中的数据写入文件
*/
public static void write(byte[] bytes, File file){
if(bytes == null || file ==null) return;
if(file.exists()) return; // 如果文件已经存在,直接结束,不覆盖
mkparents(file);
try(FileOutputStream fos = new FileOutputStream(file)){
fos.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 将数据从文件读入到内存中
*/
public static byte[] read(File file){
if(file == null || !file.exists()) return null;
if(file.isDirectory()) return null; // 不读取文件夹
try(FileInputStream fis = new FileInputStream(file)){
byte[] bytes = new byte[(int)file.length()];
fis.read(bytes);
return bytes;
} catch (IOException e) {
e.printStackTrace();
return null;
}
};
/**
* 复制(只限于文件)
*/
public static void copy(File src, File dest){
if(src == null || dest == null) return;
if(!src.exists() || dest.exists()) return;
if(src.isDirectory()) return; // 不拷贝文件夹
mkparents(dest);
try(
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
){
byte[] bytes = new byte[8192];
int len;
while((len = fis.read(bytes)) != -1){
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
字符流的特点:
Reader
、Writer
常用的是字符流有 FileReader
、FileWriter
.txt
、.java
等这类文件Writer writer = new FileWriter("F:/2.txt");
writer.write('M');
writer.write('J');
writer.write('码');
writer.write('哥');
writer.close();
Writer writer = new FileWriter("F:/2.txt");
writer.write("MJ");
writer.write("码哥".toCharArray());
writer.close();
Reader reader = new FileReader("F:/2.txt");
// 读取第1个字符
int c1 = reader.read();
// 读取第2个字符
int c2 = reader.read();
reader.close();
Reader reader = new FileReader("F:/2.txt");
char[] chars = new char[1024];
// read方法返回实际读取的字符数
int len = reader.read(chars);
reader.close();
这个需求不太适合使用字节流
/**
* 练习 – 将文本文件的内容逐个字符打印出来
*/
public static void writeChar(File file){
if(file == null || file.isDirectory()) return;
try(FileReader reader = new FileReader(file)) {
int c;
while((c = reader.read()) != -1){
System.out.print((char)c);
Thread.sleep(100);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
前面的字节流、字符流,都是无缓冲的 I/O 流,每个读写操作均由底层操作系统直接处理
为了减少读写操作带来的开销,Java 实现了缓冲的 I/O 流
上述 4 个缓冲流的默认缓冲区大小是 8192 字节(8KB),可以通过构造方法传参设置缓冲区大小
close
、flush
缓冲流的常见使用方式:将无缓冲流传递给缓冲流的构造方法(将无缓冲流包装成缓冲流)
File file = new File("F:/1.txt");
InputStream is = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(is, 16384);
bis.close();
File file = new File("F:/1.txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write("111");
writer.newLine();
writer.write("222");
writer.close();
只需要执行缓冲流的 close
方法,不需要执行缓冲流内部包装的无缓冲流的 close
方法
调用缓冲输出流的 flush
方法,会强制调用本地的输出 API,将缓冲区的数据真正写入到文件中
close
方法内部会调用一次 flush
方法System.in
System.out
InputStreamReader
可以实现 【字节输入流】转【字符输入流】OutputStreamWriter
可以实现 【字节输出流】转【字符输出流】// InputStream -> InputStreamReader -> BufferedReader
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
while((line = reader.readLine()) != null){
line = line.replace("你", "朕");
line = line.replace("吗", "");
line = line.replace("么", "");
line = line.replace("?", "!");
line = line.replace("?", "!");
System.out.println("\t" + line);
}
reader.close();
/**
* 练习 - 缓冲流逐行打印字符串
*/
public static void writeLine(File file){
try(BufferedReader reader = new BufferedReader(new FileReader(file))){
String line;
while((line = reader.readLine()) != null){
System.out.println(line);
Thread.sleep(100);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
各种流的操作很灵活,方法特别多。
方法1:
File file = new File("F:/gb2312.txt");
File file2 = new File("F:/utf-8.txt");
try(
BufferedReader reader = new BufferedReader(
// 利用 InputStreamReader 将字节流转字符流
new InputStreamReader(new FileInputStream(file), "GB2312")
);
BufferedWriter writer = new BufferedWriter(
// 利用 InputStreamReader 将字节流转字符流
new OutputStreamWriter(new FileOutputStream(file2), "UTF-8")
);
){
String line;
while((line = reader.readLine()) != null){
writer.write(line);
writer.newLine();
}
}
方法2:
File file = new File("F:/gb2312.txt");
File file2 = new File("F:/utf-8.txt");
try(
BufferedReader reader = new BufferedReader(
new InputStreamReader(// 字节流转字符流
new FileInputStream(file), "GB2312"));
BufferedOutputStream writer = new BufferedOutputStream(
new FileOutputStream(file2));
){
String line;
while((line = reader.readLine()) != null){
writer.write((line + "\n").getBytes("utf-8"));
}
}
方法3:
File file = new File("F:/gb2312.txt");
File file2 = new File("F:/utf-8.txt");
try(
BufferedInputStream reader = new BufferedInputStream(
new FileInputStream(file));
BufferedOutputStream writer = new BufferedOutputStream(
new FileOutputStream(file2));
){
int len;
String string;
byte[] bytes = new byte[4196];
while((len = reader.read(bytes)) != -1){
string = new String(bytes, "gb2312");
writer.write(string.getBytes("utf-8"), 0, len);
System.out.println(string);
}
}
方法4:
File file = new File("F:/gb2312.txt");
File file2 = new File("F:/utf-8.txt");
try(
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(file), "GB2312")
);
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(file2), "UTF-8")
);
){
char[] chars = new char[1024];
int len;
while((len = reader.read(chars)) != -1){
writer.write(chars, 0, len);
}
}
java.util.Scanner
是一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器
Scanner sc = new Scanner("jack rose kate");
while(sc.hasNext()){
System.out.println(sc.next());
}
sc.close();
jack
rose
kate
Scanner sc = new Scanner("jack 666 888 ak47");
System.out.println(sc.next()); // jack
System.out.println(sc.nextInt()); // 666
System.out.println(sc.nextInt()); // 888
// 利用正则表达式分隔文本
System.out.println(sc.next("[a-z]{2}\\d{2}")); // ak47
sc.close();
jack
666
888
ak47
Scanner.useDelimiter
方法可以自定义分隔符Scannersc = new Scanner("aa 1 bb 22 cc33dd");
sc.useDelimiter("\\s*\\d+\\s*");
while(sc.hasNext()){
System.out.println(sc.next());
}
// aa bb cc dd
sc.close();
aa
bb
cc
dd
Scanner sc = new Scanner("aa11bb22cc");
sc.useDelimiter(""); // 任意字符都被分隔
while(sc.hasNext()) {
System.out.println(sc.next());
} // a a 1 1 b b 2 2 c c
sc.close();
a
a
1
1
b
b
2
2
c
c
Scanner
- 标准输入流Scannersc = new Scanner(System.in);
System.out.print("请输入第1个整数:");
int n1 = sc.nextInt();
System.out.print("请输入第2个整数:");
int n2 = sc.nextInt();
System.out.format("%d + %d = %d%n", n1, n2, n1 + n2);
sc.close();
请输入第1个整数:6
请输入第2个整数:8
6 + 8 = 14
Scanner
– “价值几百万”的 AI 代码
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()){
String str = sc.nextLine();
str = str.replace("你", "朕");
str = str.replace("吗", "");
str = str.replace("么", "");
str = str.replace("?", "!");
str = str.replace("?", "!");
System.out.println("\t" + str);
}
sc.close();
有 2 个类可以实现格式化输出
PrintStream
PrintWriter
它们有 3 个常用方法:print
、println
、format
print
、write
的区别
write(97)
写入的是字符 'a'
print(97)
写入的是字符串 "97"
PrintStream
System.out
、System.err
是 PrintStream
类型的实例
PrintStream
是字节流,但它内部利用字符流对象来模拟字符流的许多功能
PrintWriter
平时若要创建格式化的输出流,一般使用 PrintWriter
,它是字符流
String name = "Jack";
int age = 20;
PrintWriter writer = new PrintWriter("F:/1.txt");
writer.format("My name is %s,age is %d", name, age);
writer.close();
可以通过构造方法设置 PrintWriter.autoflush
为 true
println
、printf
、format
方法内部就会自动调用 flush
方法PrintWriter writer = new PrintWriter(
new FileOutputStream(new File("F:/1.txt")), true);
有 2 个数据流:DataInputStream
、DataOutputStream
,支持基本类型、字符串类型的 I / O 操作
DataOutputStream
将数据写到文件当中int age = 20;
int money = 3000;
doubleheight = 1.75;
String name = "Jack";
DataOutputStream dos = new DataOutputStream(
new FileOutputStream("F:/66.txt"));
dos.writeInt(age);
dos.writeInt(money);
dos.writeDouble(height);
dos.writeUTF(name);
dos.close();
DataInputStream
将数据从文件当中读取数据DataInputStream dis = new DataInputStream(
new FileInputStream("F:/66.txt"));
System.out.println(dis.readInt());
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readUTF());
dis.close();
有 2 个对象流:ObjectInputStream
、ObjectOutputStream
,支持引用类型的 I / O 操作
只有实现了 java.io.Serializable
接口的类才能使用对象流进行 I / O 操作
java.io.NotSerializableException
异常Serializable
是一个标记接口(Maker Interface),不要求实现任何方法
序列化(Serialization)
ObjectOutputStream
可以实现对象的序列化反序列化(Deserialization )
ObjectInputStream
可以实现对象的反序列化若将对象比作是一座冰雕
每一个可序列化类都有一个 serialVersionUID
,相当于类的版本号
serialVersionUID
的值,根据编译器实现的不同可能千差万别serialVersionUID
的值就会发生变化如果序列化、反序列时的 serialVersionUID
不一致
java.io.InvalidClassException
异常强烈建议每一个可序列化类都自定义 serialVersionUID
,不要使用它的默认值
static final long
private
serialVersionUID
,编译器会发出 “serial” 警告被 transient
修饰的实例变量不会被序列化
public class Dog implements Serializable {
private transient int age; // age不会被序列化
private String name;
public Dog(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Dog [age=" + age + ", name=" + name + "]";
}
}
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:/d.txt"));
oos.writeObject(new Dog(5, "Larry"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:/d.txt"));
System.out.println(ois.readObject()); // Dog [age=0, name=Larry],age没有被序列化
ois.close();
Dog [age=0, name=Larry]
import java.io.Serializable;
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
private double price;
private String band;
public Car(double price, String band) {
this.price = price;
this.band = band;
}
@Override
public String toString() {
return "Car [price=" + price + ", band=" + band + "]";
}
}
import java.io.Serializable;
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
private double price;
private String name;
public Book(double price, String name) {
super();
this.price = price;
this.name = name;
}
@Override
public String toString() {
return "Book [price=" + price + ", name=" + name + "]";
}
}
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private int age;
private String name;
private Car car;
private List<Book> books = new ArrayList<>();
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public List<Book> getBooks(){ return books; }
public void setCar(Car car) { this.car = car; }
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name +
", car=" + car + ", books=" + books + "]";
}
}
ObjectOutputStream
– 序列化ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("F:/p.txt"));
Person p = new Person(20, "Jack");
p.setCar(new Car(305.6, "Bently"));
p.getBooks().add(new Book(19.9, "Java"));
p.getBooks().add(new Book(38.8, "C++"));
oos.writeObject(p);
Car c = new Car(107.8, "BMW");
oos.writeObject(c);
oos.close();
ObjectInputStream
– 反序列化ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("F:/p.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
Car c = (Car)ois.readObject();
System.out.println(c);
ois.close();
Person [
age=20, name=Jack, car=null,
books=[Book [price=19.9, name=Java], Book [price=38.8, name=C++]]
]
Car [price=107.8, band=BMW]
package com.mj;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.util.function.Consumer;
public class Files {
/**
* 搜索目录下的所有文件
* @param dir 目录
* @param operation 执行的操作
*/
public static void search(File dir, Consumer<File> operation){
if(dir == null || operation == null) return;
// 目录不存在 或者 传入的是文件, 不执行操作
if(!dir.exists() || dir.isFile()) return;
File[] subfiles = dir.listFiles();
for (File sf : subfiles) {
operation.accept(sf);
if(sf.isFile()) continue; // 如果是文件,跳过,进行后面的搜索
search(sf, operation); // 如果是文件夹, 递归搜索该文件夹
}
}
/**
* 删除文件夹
* @param file
*/
public static void delete(File file){
// 传入null 或者 文件夹不存在, 不执行操作
if(file == null || !file.exists()) return;
clean(file);
file.delete();
}
/**
* 清空文件夹中文件
* @param dir
*/
public static void clean(File dir){
// 如果文件夹不存在 或者 传入的是文件, 不执行操作
if(dir == null || !dir.exists() || dir.isFile()) return;
File[] subfiles = dir.listFiles();
for (File sf : subfiles) {
delete(sf);
}
}
/**
* 剪切文件或文件夹到目标路径
*/
public static void move(File src, File dest){
if(src == null || dest == null) return;
// 如果源File不存在 或者 目标File 已经存在, 不执行操作
if(!src.exists() || dest.exists()) return;
mkparents(dest);
src.renameTo(dest);
}
/**
* 将内存中的数据写入文件
*/
public static void write(byte[] bytes, File file){
if(bytes == null || file ==null) return;
if(file.exists()) return; // 如果文件已经存在,直接结束,不覆盖
mkparents(file);
try(FileOutputStream fos = new FileOutputStream(file)){
fos.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 将数据从文件读入到内存中
*/
public static byte[] read(File file){
if(file == null || !file.exists()) return null;
if(file.isDirectory()) return null; // 不读取文件夹
try(FileInputStream fis = new FileInputStream(file)){
byte[] bytes = new byte[(int)file.length()];
fis.read(bytes);
return bytes;
} catch (IOException e) {
e.printStackTrace();
return null;
}
};
/**
* 复制(只限于文件)
*/
public static void copy(File src, File dest){
if(src == null || dest == null) return;
if(!src.exists() || dest.exists()) return;
if(src.isDirectory()) return; // 不拷贝文件夹
mkparents(dest);
try(
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
){
byte[] bytes = new byte[8192];
int len;
while((len = fis.read(bytes)) != -1){
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 练习 – 将文本文件的内容逐个字符打印出来
*/
public static void writeChar(File file){
if(file == null || file.isDirectory()) return;
try(FileReader reader = new FileReader(file)) {
int c;
while((c = reader.read()) != -1){
System.out.print((char)c);
Thread.sleep(100);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 练习 - 缓冲流逐行打印字符串
*/
public static void writeLine(File file){
try(BufferedReader reader = new BufferedReader(new FileReader(file))){
String line;
while((line = reader.readLine()) != null){
System.out.println(line);
Thread.sleep(100);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 创建父路径
*/
private static void mkparents(File file){
File parent = file.getParentFile();
if(parent == null) return;
parent.mkdirs();
}
}