Updated on 2016-11-11
https://docs.oracle.com/javase/10/docs/api/java/nio/file/Path.html
https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html
File
用于代表文件(夹),是文件(夹)的抽象化形式,不能用于对文件内容的访问。
File file = new File("D:\\123"); \ 是转义字符
File file = new File("D:/123");
File file = new File("D:/", "123");
File file = new File("D:" + File.separator + "123");
file.createNewFile(); 创建为文件
file.mkdir(); 创建为文件夹
file.mkdirs(); 递归创建文件夹
file.delete(); 删除文件(夹)
file.deleteOnExit(); 退出虚拟机时删除文件(夹)
file.exists(); 判断文件(夹)是否存在
file.isFile(); 判断是否为文件(不存在返回 false)
file.isDirectory(); 判断是否为文件夹(不存在返回 false)
file.length(); 获得文件大小,以字节为单位
file.list(); 获得文件夹下的文件(夹)名称,返回 String 数组(不是文件夹返回 null)
file.listFiles(); 获得文件夹下的文件(夹)抽象,返回 File 数组(不是文件夹返回 null)
file.getName(); 获得文件(夹)名称,123
file.getParent(); 获得父路径,D:\
file.getAbsolutePath(); 获得文件(夹)的绝对路径,D:\123
file.getCanonicalPath(); 获得文件(夹)的绝对路径(规范后)
1 B = 8 bit
1 KB = 1024 B
1 MB = 1024 KB
1 GB = 1024 MB
RandomAccessFile
用于对文件内容的访问,可读可写,可以访问文件的任意位置(任意字节)。
public class Test {
public static void main(String[] args) throws IOException {
File file = new File("D:/", "123.dat");
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); (文件,模式)rw:读写;r:只读
打开文件时指针在开头 Pointer = 0
开始写入
byte[] bytes = "好A1".getBytes("utf8"); 使用指定字符集 编码 为字节数组
randomAccessFile.write(bytes); 将字节数组写入
randomAccessFile.seek(8); 移动指针
randomAccessFile.write(bytes); 将字节数组写入
开始读取
randomAccessFile.seek(0); 移动指针
byte[] bytes2 = new byte[(int) randomAccessFile.length()];
randomAccessFile.read(bytes2); 将文件内容一次性读取至字节数组中
System.out.println(new String(bytes2, "utf8")); 使用指定字符集 解码 为字符串
for (byte b : bytes2) {
System.out.print(Integer.toHexString(b & 0xff) + "\t");
}
System.out.println("\n" + randomAccessFile.getFilePointer() + "," + randomAccessFile.length() + "," + bytes2.length); 指针位置,文件大小,数组长度
randomAccessFile.write('A'); 写一个字节,指针移至下一位置(Char 为 2 字节,所以只会写入 Char 的低 8 位)
int read = randomAccessFile.read(); 读一个字节,指针移至下一位置(填充至 Int 的低 8 位)
randomAccessFile.close(); 关闭流
}
}
----
输出:
好A1 好A1
e5 a5 bd 41 31 0 0 0 e5 a5 bd 41 31
13,13,13
对象的序列化和反序列化
- 一序列化:Object ➜ Byte 序列—–ObjectOutputStream.writeObject
- 反序列化:Byte 序列 ➜ Object—–ObjectInputStream.readObject
- 对象所属的类必须实现序列化接口(Serializable),才能够被序列化。
- 如果所属的类的父类已实现序列化接口,则子类便不需要再实现序列化接口。
- 如果所属的类的父类没有没有实现序列化接口,其父类的构造函数会被调用。
Code
public class Test {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "好A1";
encode(str, "gbk"); 汉字 2 字节,字母和数字 1 字节
encode(str, "utf-8"); 汉字 3 字节,字母和数字 1 字节
encode(str, "utf-16be"); 汉字 2 字节,字母和数字 2 字节
byte[] bytes = "你好".getBytes("gbk"); 使用指定字符集 编码 为字节数组
String string = new String(bytes, "gbk"); 使用指定字符集 解码 为字符串
}
private static void encode(String str, String encode) throws UnsupportedEncodingException {
byte[] bytes = str.getBytes(encode); 使用指定字符集 编码 为字节数组
System.out.println(bytes.length); 数组长度
for (byte b : bytes) {
System.out.print(Integer.toHexString(b & 0xff) + "\t"); byte ➜ int ➜ 将前 24 个 1 清零 ➜ 以 16 进制显示
}
System.out.println();
}
}
Tips:文件的实质就是字节序列。
----
输出:
4
ba c3 41 31
5
e5 a5 bd 41 31
6
59 7d 0 41 0 31
-------------------------------------------------------
public class Test {
public static void main(String[] args) {
listDirectory(new File("D:\\Download\\Java"));
}
public static void listDirectory(File dir) {
if (!dir.exists()) {
throw new IllegalArgumentException("文件夹不存在");
}
if (!dir.isDirectory()) {
throw new IllegalArgumentException("不是文件夹");
}
String[] name = dir.list(); 只能显示一级子目录
if (name != null) {
for (String s : name) {
System.out.println(dir + "\\" + s); 打印文件路径
}
}
File[] files = dir.listFiles(); 显示文件夹下的所有文件
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
listDirectory(f); 递归
} else {
System.out.println(f); 打印文件路径
}
}
}
}
}
-------------------------------------------------------
public class Test {
public static void main(String[] args) throws IOException {
}
private static void read(String readName) throws IOException {
FileInputStream fileInputStream = new FileInputStream(readName);
for (int read, m = 1; (read = fileInputStream.read()) != -1; m++) { 读取一个字节填充至 int 的低 8 位,返回读到的内容
if (read <= 0xf) {
System.out.print("0"); 前面补0
}
System.out.print(Integer.toHexString(read) + "\t");
if (m % 10 == 0) {
System.out.println(); 换行
}
}
fileInputStream.close();
----
FileInputStream fileInputStream = new FileInputStream(readName);
byte[] bytes = new byte[32 * 1024]; 32KB (1KB=1024B) (1B=8b)
for (int read, m = 1; (read = fileInputStream.read(bytes)) != -1; ) { 读取字节填充至数组中,返回读到的个数
for (int i = 0; i < read; i++, m++) {
if ((bytes[i] & 0xff) <= 0xf) {
System.out.print("0"); 前面补0
}
System.out.print(Integer.toHexString(bytes[i] & 0xff) + "\t");
if (m % 10 == 0) {
System.out.println(); 换行
}
}
}
fileInputStream.close();
}
private static void write(String writeName) throws IOException {
byte[] bytes = "ABC123一二三".getBytes("utf8");
FileOutputStream fileOutputStream = new FileOutputStream(writeName, false);
fileOutputStream.write(bytes); 将字节数组写入
fileOutputStream.write(1); 将一个字节写入 (1B=8b)
fileOutputStream.close();
----
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(writeName, false));
DataInputStream dataInputStream = new DataInputStream(new FileInputStream(writeName));
dataOutputStream.writeUTF("ABC123一二三"); 使用 utf-8 编码写入
System.out.println(dataInputStream.readUTF()); 读取
dataOutputStream.writeChars("ABC123一二三"); 使用 utf-16be 编码写入
byte[] bytes = new byte[((int) new File(writeName).length())];
dataInputStream.readFully(bytes); 读取
System.out.println(new String(bytes, "utf-16be"));
dataInputStream.close();
dataOutputStream.close();
}
private static void copy(String srcName, String destName) throws IOException { 复制文件
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(srcName), 32 * 1024);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destName, false), 32 * 1024);
byte[] bytes = new byte[32 * 1024]; 32KB
for (int read; (read = bufferedInputStream.read(bytes)) != -1; ) {
bufferedOutputStream.write(bytes, 0, read);
}
bufferedOutputStream.close();
bufferedInputStream.close();
}
private static String string2Unicode(String str) { 字符串转为 Unicode
StringBuilder unicode = new StringBuilder();
char[] chars = str.toCharArray();
for (char c : chars) {
String hex = Integer.toHexString(c);
unicode.append("\\u").append(hex);
}
return unicode.toString();
}
private static String unicode2String(String unicode) { Unicode 转为字符串
StringBuilder str = new StringBuilder(); 第一种:纯 Unicode
String[] split = unicode.split("\\\\u");
for (String s : split) {
if (s.equals("")) {
continue;
}
int data = Integer.parseInt(s, 16);
str.append(((char) data));
}
return str.toString();
----
StringBuilder str = new StringBuilder(); 第二种:只转换了中文的 Unicode
for (int j = 0, i = 0; i != unicode.length(); ) { j=指针缓存,i=指针
i = unicode.indexOf("\\u", i);
if (i == -1) {
return str.append(unicode.substring(j)).toString();
}
str.append(unicode.substring(j, i));
String hex = unicode.substring(i + 2, i + 6);
int data = Integer.parseInt(hex, 16);
str.append(((char) data));
j = i += 6;
}
return str.toString();
}
}
-------------------------------------------------------
public class Student implements Serializable { 实现序列化接口
private int id;
private transient int age; 用 transient 修饰的属性不会自动进行序列化,但可以手动进行序列化
private String name;
public Student(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { 序列化方法
s.defaultWriteObject(); 自动序列化属性
s.writeInt(age); 手动序列化 transient 修饰的属性
}
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { 反序列化方法
s.defaultReadObject(); 自动反序列化属性
this.age = s.readInt(); 手动反序列化 transient 修饰的属性
}
}
----
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
save(); 保存(序列化)
load(); 读取(反序列化)
}
private static void save() throws IOException {
Student student = new Student(1, 21, "张三");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:/student.dat"));
objectOutputStream.writeObject(student); 保存(序列化)
objectOutputStream.close();
}
private static void load() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:/student.dat"));
Student student = (Student) objectInputStream.readObject(); 读取(反序列化)
objectInputStream.close();
System.out.println(student);
}
}
----
输出:
Student{id=1, age=21, name='张三'}
NIO
用于替代原有的 IO 体系。
Path
用于代表文件(夹),是文件(夹)的抽象化形式,不能用于对文件内容的访问。
Path path = Paths.get("D:/123/123"); D:\123\123
Path name = path.getFileName(); 123
Path parent = path.getParent(); D:\123
Path root = path.getRoot(); D:\
int nameCount = path.getNameCount(); 2
Path name1 = path.getName(0); 123
Path name2 = path.getName(1); 123
Path path1 = path.resolve("1.txt"); D:\123\123\1.txt
Path path2 = path.resolveSibling("1.txt"); D:\123\1.txt
path.toFile().toPath(); 相互转换
Files
包含一系列静态方法,用于对文件(夹)的具体操作。(注意 IO Stream 要关闭)
递归创建目录
----
Path path = Paths.get("D:/123/123");
Files.createDirectories(path);
创建文件
----
Path path = Paths.get("D:/123.txt");
Files.createFile(path);
直接获取文件的输入流、输出流
----
Path path = Paths.get("D:/123.txt");
BufferedReader bufferedReader = Files.newBufferedReader(path, Charset.forName("utf-8"));
BufferedWriter bufferedWriter = Files.newBufferedWriter(path, Charset.forName("utf-8"));
直接将集合内容写入
----
Path path = Paths.get("D:/123.txt");
List<String> list = Arrays.asList("AAA", "BBB");
Files.write(path, list, Charset.forName("utf-8"), StandardOpenOption.CREATE, StandardOpenOption.APPEND); 不存在便创建,存在便追加
合并文件
----
Vector<InputStream> inputStreams = new Vector<>(Arrays.asList(Files.newInputStream(Paths.get("D:/1.txt")), Files.newInputStream(Paths.get("D:/2.txt")), Files.newInputStream(Paths.get("D:/3.txt"))));
Enumeration<InputStream> elements = inputStreams.elements(); 获得 Vector 中所有元素的枚举
try (SequenceInputStream sequenceInputStream = new SequenceInputStream(elements)) { 传入枚举合并为一个输入流
Files.copy(sequenceInputStream, Paths.get("D:/4.txt"), StandardCopyOption.REPLACE_EXISTING); 复制:InputStream ➜ Path
} 自动关闭资源
监视目录的增删改
----
WatchService watchService = FileSystems.getDefault().newWatchService(); 监视服务
Path dir = Paths.get("D:/1"); 监视目录
WatchKey watchKey = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); 注册监视服务,返回监视事件
while (true) { 无限轮询监视事件
watchKey.pollEvents().forEach(o -> { 轮询
WatchEvent.Kind<?> kind = o.kind(); 事件类型
Path path = (Path) o.context(); 事件路径
System.out.printf("%s__%s\n", kind, path);
});
if (!watchKey.isValid()) {
System.out.println("失效");
break;
}
}
遍历当前目录
----
Path path = Paths.get("D:/");
Files.list(path).forEach(System.out::println); 只接收一个参数,不够强大
Files.newDirectoryStream(path, "*.txt").forEach(System.out::println); 只显示 txt 文件(接收匹配字符串)
Files.newDirectoryStream(path, Files::isRegularFile).forEach(System.out::println); 只显示文件(接收 Filter 接口)
递归遍历目录并访问文件属性
----
Path path = Paths.get("D:/123");
Files.walk(path, 3, FileVisitOption.FOLLOW_LINKS).filter(Files::isRegularFile).forEach(o -> { (递归目录,递归深度,跟随软连接),只显示文件
try {
Map<String, Object> map = Files.readAttributes(o, "size,lastModifiedTime,lastAccessTime"); 访问文件属性
System.out.println(map + "__" + o);
} catch (IOException e) {
e.printStackTrace();
}
});