24Java基础之IO
字符集
标准的ASCII字符集
- ASCII(American Standard Code for Information Interchange):美国信息交换标准代码,包括了英文,符号等。
- 标准ASCII使用1个字节存储一个字符,首位是0,总共可表示128个字符,对美国佬来说完全够用。
![image]()
GBK(汉字内码拓展规范,国际)
- 汉字编码字符集,包含了2万多个汉字等字符,GBK中一个中文字符编码成两个字节的形式存储。
- 注意:GBK兼容了ASCII字符集。
- GBK规定汉字的第一个字节的第一位必须是1.
Unicode字符集(统一码,也叫万国码)
- UTF-32,每四个字节表示一个字符。
UTF-8
- 是Unicode字符集的一种编码方案,采取可变长编码方案,共分4个长度区:1个字节,2个字节,3个字节,4个字节。
- 英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节。
![image]()
注意: - 技术人员在开发时都应该使用UTF-8编码。
- 字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。
- 英文,数字一般不会乱码,因为很多字符集都兼容了ASCII编码。
字符集的编码、解码操作
- 编码:把字符按照指定字符集编码成字节。
- 解码:把字节按照指定字符集解码成字符。
Java代码完成对字符的编码

Java代码完成对字符的解码

//目标:掌握字符集的编码和解码
public class CharSetDemo1 {
public static void main(String[] args) throws Exception {
String info = "我在公司当牛马";
//1. 编码成字节
byte[] datas = info.getBytes();//默认用的平台编码,utf-8
System.out.println(Arrays.toString(datas));
byte[] datas1 = info.getBytes("GBK");//默认用的平台编码,utf-8
System.out.println(Arrays.toString(datas1));
//2. 解码成字符串
String rs = new String(datas); //默认用的平台解码,utf-8
System.out.println(rs);
String rs1 = new String(datas1, "GBK"); //指定解码方式,GBK
System.out.println(rs1);
}
}
IO流概述
I指的是input,称为输入流:负责把数据读到内存中去。
O指的是output,称为输出流:负责写数据出去。
IO流的应用场景

怎么学IO流?
- 先搞清楚IO流的分类、体系。
- 再挨个学习每个IO流的作用、用法。
IO流的分类


IO流总体来看就是有四大流
- 字节输入流、字节输出流、字符输入流、字符输出流
总结流的四大类:
- 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流。
- 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流
- 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流。
IO流的体系

FileInputStream(文件字节输入流)
- 作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。
![image]()
//目标:掌握字节流输入每次读取一个字节的形式
public class FileInputStreamDemo01 {
public static void main(String[] args) throws Exception {
//1. 创建文件字节输入流管道与目标文件接通
InputStream in = new FileInputStream("day09-io-code\\src\\test.txt");
//2. 读取一个字节,如果读取不到字节,返回-1.
int b = in.read();
System.out.println((char)b);
int b1 = in.read();
System.out.println((char)b1);
int b2 = in.read();
System.out.println(b2);
//3. 使用循环改进
int c;
while((c = in.read()) != -1){
System.out.print((char)c);
}
//拓展:1. 性能差,每次一个一个字节的读取,性能差。
// 2. 无法避免读取汉字输出乱码的问题。
}
}
注意事项:使用FileInputStream每次读取一个字节,读取性能差,而且无法避免读取汉字输出乱码的问题。
一次读取多个字节
//目标:掌握文件输入流每次读取多个字节。
public class FileInputStreamDemo02 {
public static void main(String[] args) throws Exception {
//1. 创建一个文件直接输入流管道与源文件对接
InputStream in = new FileInputStream("day09-io-code\\src\\test.txt");
/* //2. 定义一个字节数组,用于存储读取到的字节。
byte[] buf = new byte[3];
//3. 调用输入流对象的read方法,读取字节。
int len = in.read(buf);
System.out.println("字节长度为:" +len + "读取内容为:" + (new String(buf)));
//读取多少就倒出多少
int len2 = in.read(buf);
System.out.println(new String(buf));*/
//4. 使用循环改进
byte[] buf = new byte[3];
int len;
while((len = in.read(buf)) != -1){
String str = new String(buf, 0, len);
System.out.print(str);
}
//拓展:1. 性能是比较好的!缺点:无法避免读取汉字输出乱码的问题。
}
}
注意事项:使用FileInputstream每次读取多个字节,读取性能得到提升,但依然无法避免读取汉字输出乱码问题。
使用字节流读取中文,如何保证输出不乱码问题呢?
- 定义一个与文件一样大的字节数组,一次性读完文件的全部内容。
![image]()
//目标:学会如何使用文件字节流一次性读取完文本的全部字节。
public class FileInputStreamDemo03 {
public static void main(String[] args) throws Exception {
//1. 创建一个文件字节输入流管道与源文件连通
InputStream in = new FileInputStream("day09-io-code\\src\\test.txt");
/* //2. 定义一个字节数组
File file = new File("day09-io-code\\src\\test.txt");
long size = file.length();
System.out.println("文件的大小是:" + size);
byte[] buf = new byte[(int) size];
//3. 调用流对象的读取方法,把数据一次性读取到字节数组中
int len = in.read(buf);
System.out.println(new String(buf));
System.out.println(len);*/
byte[] buf = in.readAllBytes();
System.out.println(new String(buf));
}
}
- 如果文件过大,创建的字节数组也会过大,可能引起内存溢出。
- 读写文本内容更适合用:字符流。
- 字节流:更适合做数据的转移,如文件复制等。
FileOutputStream(文件字节输出流)
- 作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去。
![image]()
案例
//目标:文件字节输出流的使用
public class FileOutputStreamDemo04 {
public static void main(String[] args) throws Exception {
//1. 创建一个文件字节输出流管道与目标文件连通
// OutputStream out = new FileOutputStream("day09-io-code\\src\\test1.txt"); // 这是一个覆盖管道
OutputStream out = new FileOutputStream("day09-io-code\\src\\test1.txt", true); // 这是一个追加管道
//2. 开始写字节数据出去
// public void write(int a):每次写出去一个字节
out.write('a');
out.write(98);
out.write('郭'); //会乱码,因为一个中文是三个字节,它只写一个
out.write("\r\n".getBytes());
// public void wirte(bute[] buffer):每次写一个字节数组的数据出去
// 参数1:字节数组
// 参数2:数组的起始索引
// 参数3:写出去的字节个数
byte[] buf = "我是中国人,我爱我的祖国!".getBytes();
out.write(buf, 0, 15);
out.write("\r\n".getBytes());
//3. 关闭流
// out.flush();//刷新缓存中的数据到磁盘文件中去。
out.close(); //关闭管道,包含刷新
//io流管道属于系统资源,会占用内存和相应的IO资源。
// 用完之后要关闭管道,释放占用的系统资源
}
}
案例:复制文件
//目标:复制文件
public class CopyTest05 {
public static void main(String[] args) throws Exception {
try{
//1. 创建字节流管道与源文件接通
InputStream in = new FileInputStream("D:\\java_project\\resource\\test.jpg");
//2.创建一个字节输出流管道与目标文件接通
OutputStream out = new FileOutputStream("D:\\java_project\\resource\\test_bak.jpg");
//3. 创建一个字节数组
byte[] buf = new byte[1024];
//4. 转移数据
int len;
while((len = in.read(buf))!= -1){
out.write(buf, 0, len);
}
//5.释放资源
out.close();
in.close();
System.out.println("复制完成!");
}
catch (Exception e){
e.printStackTrace();
}
}
}
- 任何文件的底层都是字节,字节流做复制,是一个字不漏的转移完全部字节,只要复制后的文件格式一致就没问题。
释放资源的方式
- try-catch-finally
![image]()
- finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止。
- 作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)
案例
//目标:认识finally的作用。try后或者catch后,最终一定要跑一次,除非JVM崩溃。
public class FinallyDemo01 {
public static void main(String[] args) {
try{
System.out.println(10/0);
}
catch (Exception e){
e.printStackTrace();
}
finally{
System.out.println("finally");
}
}
public static int divide(){
try{
return 10/2;
}
catch (Exception e){
e.printStackTrace();
return -1;
}
finally{
System.out.println("finally");
return 100; //如果finally中也有return,会覆盖try或catch中的return。
}
}
}
改良复制文件案例
//目标:复制文件
public class FinallyDemo02 {
public static void main(String[] args){
InputStream in = null;
OutputStream out = null;
try{
//1. 创建字节流管道与源文件接通
in = new FileInputStream("D:\\java_project\\resource\\test.jpg");
//2.创建一个字节输出流管道与目标文件接通
out = new FileOutputStream("D:\\java_project\\resource\\test_bak.jpg");
//3. 创建一个字节数组
byte[] buf = new byte[1024];
//4. 转移数据
int len;
while((len = in.read(buf))!= -1){
out.write(buf, 0, len);
}
}
catch (Exception e){
e.printStackTrace();
}
finally{
//5.释放资源
try{
if(out != null) out.close();
}catch (Exception e){
e.printStackTrace();
}
try{
if(in!= null) in.close();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("复制完成!");
}
}
}
JDK7开始提供了更简单的资源释放方案:try-with-resource

- 该资源使用完毕后,会自动调用其close()方法,完成对资源的释放。
- ()中只能放置资源,否则报错。
- 什么是资源呢?
- 资源一般指的是最终实现了AutoCloseable接口。
![image]()
案例
//目标:JDK7开始的资源释放的新方式:try-with-resource
public class FinallyDemo03 {
public static void main(String[] args){
try(//这里只能防止资源对象,用完后会自动调用资源的close()方法,释放资源
//1. 创建字节流管道与源文件接通
InputStream in = new FileInputStream("D:\\java_project\\resource\\test.jpg");
//2.创建一个字节输出流管道与目标文件接通
OutputStream out = new FileOutputStream("D:\\java_project\\resource\\test_bak.jpg");
MyConn conn = new MyConn();
){
//3. 创建一个字节数组
byte[] buf = new byte[1024];
//4. 转移数据
int len;
while((len = in.read(buf))!= -1){
out.write(buf, 0, len);
}
}
catch (Exception e){
e.printStackTrace();
}
}
static class MyConn implements AutoCloseable{
@Override
public void close() throws Exception {
System.out.println("我的资源被自动关闭了");
}
}
}








浙公网安备 33010602011771号