编码表
ASCII
码表: 包含大小写英文字母、标点符号、控制字符,一个字符占用 1 个字节GBK
: 中文简体操作系统默认的字符集码表,兼容ASCII
字符集,包含2万多个汉字,以及一些日韩文字,一个字母占 1 个字节、一个汉字占 2 个字节Unicode
: 由国际组织ISO
指定,是统一的万国码,包含常用的3种不同的编码方案(UTF-8、UTF-16、UTF-32
),所以实际上是不存在UTF-8
表的,被市场认可的是UTF-8
,1个字母占用 1 个字节,1个汉字占用 3 个字节
编码解码
getBytes()
: 使用平台默认的字符集将该字符串转换为字符集,也可以指定编码方案String(byte[] bytes)
: 使用平台默认的字符集解码指定的字符集为字符串,也可以指定编码集
例:
String str = "你好";
System.out.println(Arrays.toString(str.getBytes()));
System.out.println(Arrays.toString(str.getBytes("GBK")));
byte[] bytes1 = {-28, -67, -96, -27, -91, -67};
System.out.println(new String(bytes1));
byte[] bytes2 = {-60, -29, -70, -61};
System.out.println(new String(bytes2, "GBK"));
File
概述
- 文件的目录可以通过
File
封装成对象 File
封装的对象仅仅是一个路径名,它可以是存在的,也可以是不存在的
路径
- 相对路径: 从项目的根路径开始
- 相对路径: 从系统的盘符开始
getAbsoluteFile()
获取绝对路径
File的基本方法
creatNewFile
- 如果文件不存在,返回
true
,创建成功 - 如果文件存在,返回
false
,创建失败 - 只能创建文件,无论有没有后缀
- 如果文件不存在,返回
mkdir
- 只能创建单级文件夹,不能创建多级文件夹
- 不管有没有后缀,只能创建单击文件夹
mkdirs
- 可以创建单级文件夹,也能创建多级文件夹
- 不管有没有后缀,只能创建文件夹
delete
- 如果删除的是文件,可以直接删除
- 如果删除的是文件夹,只能删除空文件夹,如果文件夹中有东西,必须进入文件夹中删除里面内容之后,才能删除该文件夹
常用判断和获取功能
isDirectory
: 是否是目录isFile
: 是否是文件exists
: 是否存在getName
: 获取文件名listFiles
: 进入文件夹,并获取这个文件夹下所有的文件和文件夹的File
对象,并把这些File
对象保存在一个List
对象中- 当调用者不存在时: 返回
null
- 当调用者为一个文件时: 返回
null
- 当调用者是一个空文件夹时: 返回一个长度为 0 的数组
- 当调用者是一个有内容的文件夹时: 返回里面所有的文件和文件夹的路径放在
File
数组中返回 - 当调用者是一个无权进入的文件夹时: 返回
null
- 当调用者是一个有隐形文件的文件夹时: 将里面的所有文件和文件夹的路径放在
File
数组中返回,包含隐藏内容
- 当调用者不存在时: 返回
IO流
IO流是用来读写数据的(可以是文件中的数据,也可以是网络中的数据)
IO流的分类
- 按照数据的流向分类:
- 输入流: 把数据从硬盘或网络读出
- 输出流: 把数据从内存写入硬盘或网络
- 按照读写的文件类型分类:
- 字节流: 它可以读写任何类型的文件
- 字符流: 它只能读写文本文件
- 将上述的两种划分方式进行结合划分:
- 字节输入流
- 字节输出流
- 字符输入流
- 字符输出流
字节流
字节输出流 FileOutputStream
专门往文件中写字节数据
步骤
- 创建FileOutputStream对象,关联一个文件路径(如果文件不存在,会自动创建;如果存在,会覆盖掉,第二个参数为续写开关)
- 调用write方法写数据,只能写字节,不能写字符串(String.getBytes可以将字符串转换为字节数组)
- 调用close关闭流
- 如果不关闭流,则该文件一直被占用,其余程序不可使用该文件
- JDK7之后,Java会自己释放资源,格式是:
try (创建资源对象) {
} catch (异常类) {
} // 特点: 不需要手动释放资源
例:
try (FileOutputStream fos = new FileOutputStream("a.txt")) {
for (int i = 0; i < 26; i++) {
fos.write(97 + i);
}
fos.write("\r\n".getBytes());
} catch (IOException e) {
e.getMessage();
}
注:
- win换行符为: \r\n 先回车 再换行
- unix换行符为: \n
- mac换行符为: \r
字节流读入数据FileInputStream
步骤
- 创建字节读入流对象,如果文件存在,那么就不会报错,如果文件不存在,就会报错
- 调用read方法读数据 (一次读一个字节,读到末尾返回 -1,不能读中文,会乱码)
- 调用close方法关闭流
例:
try (FileInputStream fis = new FileInputStream("a.txt")) {
int ch = fis.read();
while (ch != -1) {
System.out.print((char)ch);
ch = fis.read();
}
} catch (IOException e) {
e.getMessage();
}
文件复制
方法1(不推荐)
try (FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("test\\a.txt")) {
int ch = fis.read();
while (ch != -1) {
System.out.print((char)ch);
fos.write(ch);
ch = fis.read();
}
} catch(IOException e) {
e.printStackTrace();
}
方法2
try (FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("test\\a.txt")) {
byte[] bytes = new byte[1024]; // 一次可以读取的最大字符数
int len = fis.read(bytes); // len是该次读取到的字符数
while (len != -1) {
System.out.println("此次读取到" + len + "个字符");
fos.write(bytes, 0, len);
len = fis.read(bytes);
}
} catch(IOException e) {
e.printStackTrace();
}
字节缓冲流
字节缓冲流仅提供缓冲区,真正读写的是基本的字节流,所以参数是基本字节流
例
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test/a2.txt"));
byte[] bytes = new byte[1024];
len = bis.read(bytes);
while (len != -1) {
System.out.println("本次读出: " + len);
bos.write(bytes, 0, len);
len = bis.read(bytes);
}
注
- 字节缓冲流关闭,原始流也关闭
- 创建缓冲流之后生成一个长度为8192的缓冲数组
字符流
概述
- 不同的编码方案对于同一个中文汉字底层用的字节数是不一样的,所以如果读取的时候读取到的字节,查询编码表,查不到对应的字符就会有乱码
- 为了解决这个问题,所以
Java
提供了字符流,字符流的底层其实是字节流+编码表,字符流在读取数据时,会按照中文字符和英文字符底层编码的特征自动识别,那些字节是中文,哪些字节是英文 - 所有的英文字符它底层的编码特征都是正数,所有的中文字符编码特征都是负数,字符流读取字母时,它就一个字节一个字节地读;字符流在读取汉字时(假设是
UTF-8
编码方案),它就3个字节 3 个字节的读
字符流写数据
步骤
- 创建一个字符输出流
FileWriter
对象 - 写入数据:
write
- 释放资源:
close
flush、close
flush
: 刷新流,之后还可以继续写数据close
: 关闭流,之后不可继续写数据
注意事项
- 如果
FileWriter
关联的文件不存在,会自动创建(父路径需存在) - 如果
FileWriter
关联的文件的父路径不存在,会报错 - 如果
FileWriter
关联的文件存在,此时会将已有的文件覆盖掉 - 调用
write
方法写数据,其实数据并没有直接到文件中去,而是在内存中有缓存,此时可以调用flush
方法,将数据刷新到文件中 - 当调用
close
方法时,即使前面没有调用flush
方法,也会自动刷新到文件中,再关闭流
例:
try (FileWriter fw = new FileWriter("a.txt", true)) {
char[] chars = {'你', '好', '世', '界'};
fw.write(chars);
fw.write("\r\n");
fw.write(chars, 0,1);
fw.write("\r\n");
fw.write(chars, 0, 2);
} catch (IOException e) {
e.printStackTrace();
}
字符流读数据
在
Java
中提供了一个类叫Reader
类,这是所有字符输入流的父类,我们如果想要从文件中读取字符数据
步骤
- 创建
FileReader
输入流对象,关联一个文件路径(文件必须存在) - 调用
read
方法读取文件中的字符 - 调用
close
关闭流
例:
try (FileReader fr = new FileReader("a.txt")) {
int len;
char[] chars = new char[1024];
while ((len = fr.read(chars)) != -1) {
System.out.println("本次读到: " + len + "个字符");
System.out.println(new String(chars, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
字符缓冲流
类似于字节缓冲流
特有方法
newLine()
: 写一个行分隔符,分隔符字符由系统决定readLine()
: 读一整行数据
例:
try (BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("test/a.txt"))) {
char[] chars = new char[1024];
int len = 0;
while ((len = br.read(chars)) != -1) {
System.out.println("本次复制: " + len + "个字符");
bw.write(chars, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
// 对源文件数据进行排序,再写回
// 3 2 1 5 4 2 1 10 9 8 7 1 11 10
int[] nums = null;
try (BufferedReader br = new BufferedReader(new FileReader("a.txt"))) {
String s = br.readLine();
String[] numsStr = s.split(" ");
nums = new int[numsStr.length];
for (int i = 0; i < numsStr.length; i++) {
nums[i] = Integer.parseInt(numsStr[i]);
}
Arrays.sort(nums);
} catch (IOException e) {
e.printStackTrace();
}
try (BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"))) {
for (int num : nums) {
bw.write(num + " ");
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
其他流
转换流 InputStreamReader/OutputStreamWriter
JDK11之前可以使用该方法指定字符集 JDK11之后使用FileReader/FileWriter的新构造方法
截图
- 使用转换流可以针对不同的字符集进行读取
- 当拿到一个流是
InputStream
或者OutputStream
时,可以利用转换流转换成字符流
例:
try (InputStreamReader isr = new InputStreamReader(new FileInputStream("窗里窗外.txt"), "GBK")) {
char[] chars = new char[1024];
int len = 0;
while ((len = isr.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
扩展
- 不用转换流,也能指定编码读写数据
// JDK8不支持
FileReader fr = new FileReader("", Charset.forName("UTF-8"));
FileWriter fw = new FileWriter("", Charset.forName("UTF-8"));
- 既想指定字符集又想用
BufferedReader/Writer
进行操作
InputStreamReader isr = new InputStreamReader(new FileInputStream(), "GBK");
BufferedReader br = new BufferedReader(isr);
对象操作流
可以把对象以字节的形式写入文件中,将对象从文件中读入到内存中,该对象需实现
Serializable
对象操作输入流(对象序列化流)ObjectInputStream
方法
对象.readObject();
对象操作输出流(对象反序列化流)ObjectOutputStream
方法
对象.writeObect();
例:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
Student student = new Student("test1", 1);
oos.writeObject(student);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
Student student = (Student) ois.readObject();
System.out.println(student);
注
- 被序列化或者反序列化的类,需实现
Serializable
接口 - 序列化和反序列化,它们的序列号必须保持一致
- 如果我们自己没有定义,那么虚拟机会根据类中的信息自动计算出来一个序列号
- 如果我们修改了类中的信息,那么虚拟机会再次计算出一个序列号
- 两次对比序列号不一致,就会抛出异常
- 所以我们可以手动给出一个序列号:
private static final long serialVersionUID = 42L; // 数值可以随意写
- 如果对象的一个成员变量不想被序列化,可以加上
transent
关键字修饰,该关键字标记的成员变量不参与序列化过程 - 如果想要往文件中写多个对象,建议把多个对象先存入一个集合中,然后把集合当作一个整体存入文件中去
Properties
概述
- 是一个
Map
体系的集合类 Properties
中有跟IO
有关的方法- 只存字符串
方法
put
: 增,没有泛型,所以可以存储任意类型,不过只存储字符串remove
: 删,根据键删除put
: 改get
: 查
特有方法
setProperty
: 类似于put
getProperty
: 类似于get
stringPropertyName
: 从该属性列表返回一个键值集合
与IO集合相结合
load
: 从本地文件的键值对数据读取store
: 将集合中的数据以键值对形式保存在本地
注意事项
- 本质上是
HashTable
的一个子类,和HashMap
基本使用相同 Properties
中的put
和get
方法的参数、返回类型都是obj
Properties
类一般只会存储字符串