- GBK 编码中文占用 2 个字节,英文占用 1 个字节
- UTF-8 编码中文占用 3 个字节,英文占用 1 个字节
- java 是双字节编码,utf-16be 编码占用 2 个字节,英文占用 2 个字节
当字节序列是某种编码方式时,这时候想把字节序列编程字符串,也必须使用相同的编码方式,否则会出现乱码。
文本文件在计算机中存储的就是字节序列。可以是任意编码的字节序列。
如果我们在中文机器上直接创建文本文件,那么该文本文件只认识ANSI 编码。
联通
、联
是一种巧合,他们正好符合了 UTF-8 编码的规则。
java.io.File
用于表示文件或者目录。File
类只能用于表示文件(目录)的信息(名称、大小等),不能用于文件内容的访问。
File f = new File(String args0);
File f = new File(URI args0);
File f = new File(File args0, String args1);
File f = new File(String args0, String args1);
File f = new File("c:/demo");
System.out.println(f.exists());
if (!f.exists()) {
f.mkdir();
// 创建多级目录
f.mkdirs();
System.out.println("f created");
} else {
f.delete();
System.out.println("f deleted");
}
System.out.println(f.isDirectory());
System.out.println(f.isFile());
System.out.println(f.getPath());
System.out.println(f.getName());
System.out.println(f.getParent());
File f1 = new File("hello.txt");
if (!f1.exists()) {
f1.createNewFile();
System.out.println("f1 created");
} else {
f1.delete();
System.out.println("f1 deleted");
}
System.out.println(f1);
System.out.println(f1.getAbsolutePath());
System.out.println(f1.getPath());
System.out.println(f1.getName());
System.out.println(f1.getParent());
System.out.println(f1.getParentFile());
static void listDirectory(File dir) {
if (!dir.exists()) {
throw new IllegalArgumentException("目录" + dir + "不存在!");
}
if (!dir.isDirectory()) {
throw new IllegalArgumentException(dir + "不是目录!");
}
String[] filenames = dir.list();
if (filenames != null && filenames.length > 0) {
for (String string : filenames) {
System.out.println(string);
}
}
File[] files = dir.listFiles();
if (files != null && files.length > 0) {
for (File file : files) {
if (file.isDirectory()) {
System.out.println(file.getAbsolutePath());
listDirectory(file);
} else {
System.out.println(file.getAbsolutePath());
}
}
}
}
RandomAccessFile 类是 Java 提供的对文件内容的访问类。既可以读文件,也可以写文件。可以随机访问文件,可以访问文件的任意位置。
在硬盘上的文件是 Byte Byte Byte 存储的,是数据的集合。
r
rw
RandomAccessFile raf = new RandomAccessFile(file, "rw");
打开文件后,文件指针 int pointer = 0;
write()
方法只能写一个字节(后 8 位)。也就是说,如果要写一个int
,需要写 4 次,同时指针指向下一个位置,准备再次写入。
raf.write(int a);
read()
只读一个字节。
int b = raf.read();
文件读写完成后一定要关闭。
raf.close();
- 输入流
- 输出流
- 字节流
- 字符流
- InputStream:抽象了应用程序读取数据的方式
- OutputStream:抽象了应用程序写出数据的方式
EOF
-1
// 读取一个字节,无符号填充到 int 的低 8 位,-1 是 EOF
int b = in.read();
// 读取数据直接填充到字节数组 buf
in.read(byte[] buf);
in.read(byte[] buf, int start, int size);
// 写出 1 个 byte 到流,b 的低 8 位
out.write(int b);
// 将字节数组 buf 写入到流
out.write(byte[] buf);
out.write(byte[] buf, int start, int size);
- FileInputStream:具体实现了在文件上读取数据
- FileOutputStream:具体实现了向文件中写出 Byte 数据的方法
- DataInputStream:对流功能的扩展,可以更加方便地读取
int
/long
/ 字符型等类型数据 - DataOutputStream:对流功能的扩展,可以更加方便地写出
int
/long
/ 字符型等类型数据
实际上就是包装,底层实现还是写字节的方式。
writeInt()
writeDouble()
writeUTF()
writeChar()
writeChars()
readInt()
readDouble()
readUTF()
readChar()
- BufferedInputStream
- BufferedOutputStream
这两个流类为 I/O 提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了 I/O 性能(主要是输入输出性能)。
- 编码问题
- 文本:
char
是 16 为无符号整数,是字符的 Unicode 编码(双字节编码) - 文件:
Byte
Byte
Byte
的数据序列 - 文本文件:文本(char)按照某种编码方案(如
UTF-8
、UTF-16be
、GBK
)序列化为Byte
的存储结果
- Reader:输入流
- Writer:输出流
字符的处理,一次处理一个字节。字符的底层仍然是字节序列。
- InputStreamReader:完成
Byte
流解析为char
流,按照指定编码解析 - OutputStreamWriter:提供
char
流到Byte
流,按照指定编码处理
- FileReader
- FileWriter
- BufferedReader:
String line = br.readLine();
- BufferedWriter:
bw.write(line);
- PrintWriter + Buffer
- 对象序列化:将
Object
转换为Byte
序列 - 对象反序列化:将
Byte
序列转换为Object
- ObjectOutputStream:
writeObject()
- ObjectInputStream:
readObject()
对象必须实现序列化接口,才能进行序列化,否则出现异常。
Serializable
接口没有任何方法,只是一个标准。
transient
修饰后的元素,该元素不会进行 JVM 默认的序列化。但是也可以自己完成这个元素的序列化。
public class Student implements Serializable {
private String stuNo;
private transient String stuName;
private transient int stuAge;
// ...
private void writeObject(ObjectOutputStream s)
throws IOException {
// 把 JVM 默认能序列化的元素进行序列化
s.defaultWriteObject();
// 自己完成其他元素的序列化
s.writeObject(stuName);
s.writeInt(stuAge);
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
this.stuName = (String) s.readObject();
this.stuAge = s.readInt();
}
}
ArrayList
自定义元素的序列化,只让有效的元素进行序列化,调高序列化的效率。
一个类实现了 Serializable
接口,那么其子类都可以进行序列化。
序列化时,递归调用父类的构造函数。
反序列化时,如果其父类没有实现 Serializable
接口,那么父类的构造函数会被调用,如果其父类实现了 Serializable
接口,那么我们将看不到父类构造函数调用过程。