🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Java从入门到大牛
🌠 首发时间:2023年8月31日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
🌟 一以贯之的努力 不得懈怠的人生
目录
- IO流-字符流
- 文件字符输入流-读字符数据进来
- 文件字符输出流-写字符数据出去
- IO流-缓冲流
- 字节缓冲流
- 字符缓冲流
- 原始流、缓冲流的性能分析 [重点]
- IO流-转换流
- 引出问题:不同编码读取时会乱码
- 字符输入转换流
- 字符输出转换流
- IO流-打印流
- IO流-数据流
- IO流-序列化流
- IO框架
IO流-字符流
文件字符输入流-读字符数据进来
FileReader(文件字符输入流)
作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去
具体应用
测试文本文件
代码实现
import java.io.FileReader;
import java.io.Reader;
/**
* 目标:掌握文件字符输入流
*/
public class FileReaderTest1 {
public static void main(String[] args) {
try (
// 1、创建一个文件字符输入流管道与源文件接通
Reader fr = new FileReader("io-app2/src/test01.txt");
){
// 2、读取文本文件的内容,每次读取一个字符,性能较差
// int c; // 记住每次读取的字符编号
// while ((c = fr.read()) != -1){
// System.out.print((char) c);
// }
// 3、每次读取多个字符,性能还行
char[] buffer = new char[3];
int len; // 记住每次读取了多少个字符
while ((len = fr.read(buffer)) != -1){
System.out.print(new String(buffer, 0, len));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果
文件字符输出流-写字符数据出去
FileWriter(文件字符输出流)
作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去
字符输出流使用时的注意事项
字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效,具体方法如下
字节流、字符流的使用场景小结:
- 字节流适合做一切文件数据的拷贝(音视频或文本);字节流不适合读取中文内容输出
- 字符流适合做文本文件的操作(读或写)
具体应用
import java.io.FileWriter;
import java.io.Writer;
/**
* 目标:掌握文件字符输出流:写字符数据出去
*/
public class FileWriterTest2 {
public static void main(String[] args) {
try (
// 创建一个文件字符输出流管道与目标文件接通
// 覆盖管道
// Writer fw = new FileWriter("io-app2/src/itheima02out.txt");
// 追加管道
Writer fw = new FileWriter("io-app2/src/test02out.txt", true);
){
// 1、public void write(int c):写一个字符出去
fw.write('a');
fw.write(97);
fw.write('好'); // 写一个字符出去
fw.write("\r\n"); // 换行
// 2、public void write(String c)写一个字符串出去
fw.write("我爱你中国abc");
fw.write("\r\n");
// 3、public void write(String c ,int pos ,int len):写字符串的一部分出去
fw.write("我爱你中国abc", 0, 5);
fw.write("\r\n");
// 4、public void write(char[] buffer):写一个字符数组出去
char[] buffer = {'你', '好', '1', '2', '3'};
fw.write(buffer);
fw.write("\r\n");
// 5、public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
fw.write(buffer, 0, 2);
fw.write("\r\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
IO流-缓冲流
对原始流进行包装,以提高原始流读写数据的性能
字节缓冲流
字节缓冲流的作用
提高字节流读写数据的性能
字节缓冲流的原理
字节缓冲输入流自带了 8KB 缓冲池;字节缓冲输出流也自带了 8KB 缓冲池
具体应用
import java.io.*;
/**
* 目标:掌握字节缓冲流的作用
*/
public class BufferedInputStreamTest1 {
public static void main(String[] args) {
try (
InputStream is = new FileInputStream("io-app2/src/test01.txt");
// 1、定义一个字节缓冲输入流包装原始的字节输入流
InputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream("io-app2/src/test01copy.txt");
// 2、定义一个字节缓冲输出流包装原始的字节输出流
OutputStream bos = new BufferedOutputStream(os);
){
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1){
bos.write(buffer, 0, len);
}
System.out.println("复制完成!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
字符缓冲流
BufferedReader(字符缓冲输入流)
作用:自带 8K(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能
字符缓冲输入流新增的功能:按照行读取字符
字符缓冲输入流具体应用
import java.io.*;
/**
* 目标:掌握字符缓冲输入流的用法
*/
public class BufferedReaderTest2 {
public static void main(String[] args) {
try (
Reader fr = new FileReader("io-app2\\src\\test02.txt");
// 创建一个字符缓冲输入流包装原始的字符输入流
BufferedReader br = new BufferedReader(fr);
){
String line; // 记住每次读取的一行数据
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
BufferedWriter(字符缓冲输出流)
作用:自带 8K(8192)的字符缓冲池,可以提高字符输出流写字符数据的性能
字符缓冲输出流新增的功能:换行
字符缓冲输出流具体应用
import java.io.*;
/**
* 目标:掌握字符缓冲输出流的用法
*/
public class BufferedWriterTest3 {
public static void main(String[] args) {
try (
Writer fw = new FileWriter("io-app2/src/test03out.txt", true);
// 创建一个字符缓冲输出流管道包装原始的字符输出流
BufferedWriter bw = new BufferedWriter(fw);
){
bw.write('a');
bw.write(97);
bw.write('好');
bw.newLine();
bw.write("我爱你中国abc");
bw.newLine();
} catch (Exception e) {
e.printStackTrace();
}
}
}
原始流、缓冲流的性能分析 [重点]
测试用例:
分别使用原始的字节流以及字节缓冲流复制一个很大的视频
测试步骤:
- 使用低级的字节流按照一个一个字节的形式复制文件
- 使用低级的字节流按照字节数组的形式复制文件
- 使用高级的缓冲字节流按照一个一个字节的形式复制文件
- 使用高级的缓冲字节流按照字节数组的形式复制文件
代码实现
import java.io.*;
/**
目标:观察原始流和缓冲流的性能
*/
public class TimeTest4 {
// 复制的视频路径
private static final String SRC_FILE = "D:\\resource\\test.mp4";
// 复制到哪个目的地
private static final String DEST_FILE = "D:\\";
public static void main(String[] args) {
copy01(); // 低级字节流一个一个字节的赋值,慢的简直让人无法忍受,直接淘汰!
copy02(); // 低级的字节流流按照一个一个字节数组的形式复制,速度较慢!
copy03(); // 缓冲流按照一个一个字节的形式复制,速度较慢,直接淘汰!
copy04(); // 缓冲流按照一个一个字节数组的形式复制,速度极快,推荐使用!
}
private static void copy01() {
long startTime = System.currentTimeMillis();
try (
InputStream is = new FileInputStream(SRC_FILE);
OutputStream os = new FileOutputStream(DEST_FILE + "1.mp4");
){
int b;
while ((b = is.read()) != -1){
os.write(b);
}
}catch (Exception e){
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("低级字节流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
}
private static void copy02() {
long startTime = System.currentTimeMillis();
try (
InputStream is = new FileInputStream(SRC_FILE);
OutputStream os = new FileOutputStream(DEST_FILE + "2.mp4");
){
byte[] buffer = new byte[1024*64];
int len;
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
}catch (Exception e){
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("低级字节流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
}
private static void copy03() {
long startTime = System.currentTimeMillis();
try (
InputStream is = new FileInputStream(SRC_FILE);
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream(DEST_FILE + "3.mp4");
BufferedOutputStream bos = new BufferedOutputStream(os);
){
int b;
while ((b = bis.read()) != -1){
bos.write(b);
}
}catch (Exception e){
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("缓冲流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
}
private static void copy04() {
long startTime = System.currentTimeMillis();
try (
InputStream is = new FileInputStream(SRC_FILE);
BufferedInputStream bis = new BufferedInputStream(is, 64 * 1024);
OutputStream os = new FileOutputStream(DEST_FILE + "4.mp4");
BufferedOutputStream bos = new BufferedOutputStream(os, 64 * 1024);
){
byte[] buffer = new byte[1024 * 64]; // 32KB
int len;
while ((len = bis.read(buffer)) != -1){
bos.write(buffer, 0, len);
}
}catch (Exception e){
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("缓冲流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
}
}
执行结果
结论
建议使用字节缓冲输入流和字节缓冲输出流,结合字节数组的方式,目前看来是性能最优的组合
IO流-转换流
引出问题:不同编码读取时会乱码
- 如果代码编码和被读取的文本文件的编码是一致的,使用字符流读取文本文件时不会出现乱码
- 如果代码编码和被读取的文本文件的编码是不一致的,使用字符流读取文本文件就会出现乱码
字符输入转换流
InputStreamReader(字符输入转换流)
- 解决不同编码时,字符流读取文本内容乱码的问题
- 解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不会乱码了
具体应用
import java.io.*;
/**
* 目标:掌握字符输入转换流的作用
*/
public class InputStreamReaderTest2 {
public static void main(String[] args) {
try (
// 1、得到文件的原始字节流(GBK的字节流形式)
InputStream is = new FileInputStream("io-app2/src/inputStreamReaderTest.txt");
// 2、把原始的字节输入流按照指定的字符集编码转换成字符输入流
Reader isr = new InputStreamReader(is, "GBK");
// 3、把字符输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(isr);
){
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
字符输出转换流
需要控制写出去的字符使用什么字符集编码,该咋整 ?
-
调用 String 提供的 getBytes 方法解决
String data = "我爱你中国abc"; byte[] bytes = data.getBytes("GBK");
-
使用 “字符输出转换流” 实现
OutputStreamWriter字符输出转换流
- 作用:可以控制写出去的字符使用什么字符集编码
- 解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了
具体应用
import java.io.*;
/**
* 目标:掌握字符输出转换流的使用
*/
public class OutputStreamWriterTest3 {
public static void main(String[] args) {
// 指定写出去的字符编
try (
// 1、创建一个文件字节输出流
OutputStream os = new FileOutputStream("io-app2/src/outputStreamWriterTest.txt");
// 2、把原始的字节输出流,按照指定的字符集编码转换成字符输出转换流
Writer osw = new OutputStreamWriter(os, "GBK");
// 3、把字符输出流包装成缓冲字符输出流
BufferedWriter bw = new BufferedWriter(osw);
){
bw.write("我是中国人abc");
bw.write("我爱你中国123");
} catch (Exception e) {
e.printStackTrace();
}
}
}
IO流-打印流
PrintStream/PrintWriter(打印流)
作用:打印流可以实现更方便、更高效地打印数据出去,能实现打印啥出去就是啥出去
PrintStream提供的打印数据的方案
PrintStream和PrintWriter的区别
- 打印数据的功能上是一模一样的,都是使用方便、性能高效的
- PrintStream 继承自字节输出流 OutputStream,因此支持写字节数据的方法
- PrintWriter 继承自字符输出流 Writer,因此支持写字符数据出去
具体应用
import java.io.*;
/**
* 目标:掌握打印流:PrintStream/PrintWriter的用法
*/
public class PrintTest1 {
public static void main(String[] args) {
try (
// 创建一个打印流管道
// PrintStream ps =
// new PrintStream("io-app2/src/testPrint.txt", Charset.forName("GBK"));
// PrintStream ps =
// new PrintStream("io-app2/src/testPrint.txt");
PrintWriter ps =
new PrintWriter(new FileOutputStream("io-app2/src/testPrint.txt", true));
){
ps.println(97);
ps.println('a');
ps.println("我爱你中国abc");
ps.println(true);
ps.println(99.5);
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印流的一种应用:输出语句的重定向
可以把输出语句的打印位置改到某个文件中去
PrintStream ps = new PrintStream("文件地址");
System.setOut(ps);
import java.io.PrintStream;
/**
* 目标:了解下输出语句的重定向
*/
public class PrintTest2 {
public static void main(String[] args) {
try ( PrintStream ps = new PrintStream("io-app2/src/testPrint.txt"); ){
// 把系统默认的打印流对象改成自己设置的打印流
System.setOut(ps);
System.out.println("烈士暮年");
System.out.println("壮心不已");
} catch (Exception e) {
e.printStackTrace();
}
}
}
IO流-数据流
DataOutputStream(数据输出流)
允许把数据和其类型一并写出去
import java.io.*;
/**
* 目标:数据输出流
*/
public class DataOutputStreamTest1 {
public static void main(String[] args) {
try (
// 1、创建一个数据输出流包装低级的字节输出流
DataOutputStream dos =
new DataOutputStream(new FileOutputStream("io-app2/src/testDataOutputStream.txt"));
){
dos.writeInt(97);
dos.writeDouble(99.5);
dos.writeBoolean(true);
dos.writeUTF("一二三456");
} catch (Exception e) {
e.printStackTrace();
}
}
}
DataInputStream(数据输入流)
用于读取数据输出流写出去的数据
import java.io.*;
/**
* 目标:使用数据输入流读取特定类型的数据
*/
public class DataInputStreamTest2 {
public static void main(String[] args) {
try (
DataInputStream dis =
new DataInputStream(new FileInputStream("io-app2/src/testDataOutputStream.txt"));
){
int i = dis.readInt();
System.out.println(i);
double d = dis.readDouble();
System.out.println(d);
boolean b = dis.readBoolean();
System.out.println(b);
String rs = dis.readUTF();
System.out.println(rs);
} catch (Exception e) {
e.printStackTrace();
}
}
}
IO流-序列化流
ObjectOutputStream(对象字节输出流)
可以把 Java 对象进行序列化:把 Java 对象存入到文件中去
注意:对象如果要参与序列化,必须实现序列化接口(java.io.Serializable
)
import java.io.Serializable;
// 注意:对象如果需要序列化,必须实现序列化接口
public class User implements Serializable {
private String loginName;
private String userName;
private int age;
// transient 表示这个成员变量将不参与序列化
private transient String passWord;
public User() {
}
public User(String loginName, String userName, int age, String passWord) {
this.loginName = loginName;
this.userName = userName;
this.age = age;
this.passWord = passWord;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
@Override
public String toString() {
return "User{" +
"loginName='" + loginName + '\'' +
", userName='" + userName + '\'' +
", age=" + age +
", passWord='" + passWord + '\'' +
'}';
}
}
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
/**
* 目标:掌握对象字节输出流的使用:序列化对象
*/
public class Test1ObjectOutputStream {
public static void main(String[] args) {
try (
// 2、创建一个对象字节输出流包装原始的字节 输出流
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("io-app2/src/testObjectOutput.txt"));
){
// 1、创建一个Java对象
User u = new User("admin", "张三", 32, "666888xyz");
// 3、序列化对象到文件中去
oos.writeObject(u);
System.out.println("序列化对象成功!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
ObjectInputStream(对象字节输入流)
可以把 Java 对象进行反序列化:把存储在文件中的 Java 对象读入到内存中来
如果要一次序列化多个对象,该怎么办呢 ?
用一个 ArrayList 集合存储多个对象,然后直接对集合进行序列化即可
注意:ArrayList 集合已经实现了序列化接口
import java.io.FileInputStream;
import java.io.ObjectInputStream;
/**
* 目标:掌握对象字节输入流的使用:反序列化对象
*/
public class Test2ObjectInputStream {
public static void main(String[] args) {
try (
// 创建一个对象字节输入流管道,包装低级的字节输入流与源文件接通
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("io-app2/src/testObjectOutput.txt"));
){
User u = (User) ois.readObject();
System.out.println(u);
} catch (Exception e) {
e.printStackTrace();
}
}
}
IO框架
什么是框架 ?
- 解决某类问题,编写的一套类、接口等,可以理解成一个半成品,大多框架都是第三方研发的
- 好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
- 框架的形式:一般是把类、接口等编译成 class 形式,再压缩成一个 .jar 结尾的文件发行出去
什么是IO框架 ?
封装了 Java 提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等
Commons-io
比较常用的有 Commons-io 框架,Commons-io 是 apache 开源基金组织提供的一组有关 IO 操作的小框架,目的是提高 IO 流的开发效率
Commons-io的下载
由于 Commons-io 框架是第三方开发的,所有我们需要进行下载才能使用
下载很简单,进入其官网下载即可,点击此处进入官网,下载图中这一个即可,然后解压缩
在解压后的文件夹中找到图中这一个,这个压缩包就是我们开发时会使用到的
下一步我们需要将这个压缩包导入到要使用的项目中去,步骤如下
- 在项目中创建一个文件夹 lib,用来放置压缩包
- 找到压缩包复制一下,然后粘贴到 lib 文件夹下
- 在 lib 文件夹下右键点击压缩包,选择 Add as Library,再点击 OK 即可
- 在类中导包使用
如下图这样即为成功
Commons-io的具体应用
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* 目标:使用CommonsIO框架进行IO相关的操作
*/
public class CommonsIOTest1 {
public static void main(String[] args) throws Exception {
FileUtils.copyFile(new File("io-app2\\src\\test01.txt"), new File("io-app2/src/a.txt"));
// FileUtils.copyDirectory(new File("D:\\resource\\test1"), new File("D:\\resource\\test2"));
// FileUtils.deleteDirectory(new File("D:\\resource\\test2"));
// Java官方提供的原生的一行代码搞定很多事情
// Files.copy(Path.of("io-app2\\src\\test01.txt"), Path.of("io-app2\\src\\b.txt"));
// System.out.println(Files.readString(Path.of("io-app2\\src\\test01.txt")));
}
}