2022-08-09 第4组 蒋萍 IO流的简单使用
“ 把今天过好 ”
最近是怎么了??
我总是感到莫名其妙的累,效率也不高,脑子也不想动
IO流
File类不能操作文件内容
1、分类
1、按流向分
-
输入流(读):从物理内存读取数据到运行内存
-
输出流(写):从内存写出数据到硬盘
2、按操作单元分
-
字节流:一个字节一个字节的操作(二进制操作),可以操作任意类型的文件
-
字符流:一个字符一个字符的操作,一个字符是两个字节,主要用于处理文本文件,只能是纯文字(.txt、.java、.xml、.py、.properties……)
3、按角色分
-
节点流:直接操作一个特定的IO设备(麻烦),是二进制操作!!!
-
处理流:在节点流的基础上做进一步处理 (写法简单,性能高);缓冲流
2、在Java中IO流常用流:
字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reder | Writer |
访问文件(节点流) | FileInputStream | FileOutputStream | FileReder | FileWriter |
缓冲流(处理流) | BufferedInputStream | BufferedOutputStream | BufferedReder | BufferedWriter |
操作对象 | ObjectInputStream | ObjectOutputStream |
3、流的使用
1.字节输入流
往内存读数据
1、创建字节输入流
2、开始读的操作,read,返回值是int,返回值是-1时,说明文件读取到了末尾
字节流:一个一个字节的读所以用到循环读取
用啥循环??
while(){}
并且现在打印出来的是ASCII码,怎么转成我们看得懂的??
用一个构造器:new String(buf) 但不好控制,得控制字节数组的大小
字节流读的操作,目的也不是让你看懂,哈哈哈哈哈
@Test
public void test01() {
InputStream inputStream = null;
try {
// 创建字节流对象
inputStream = new FileInputStream("f:/aaa.txt");
// 做个标记 ----- 操作的是二进制,所以用 int
int read;
// 缓冲 ???
byte[] buf = new byte[1024];
// 开始读取 ----- read() 要处理异常
while((read = inputStream.read(buf)) != -1){
System.out.println(Arrays.toString(buf) + " ");
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
// 关闭流 ---- close() 也要处理异常
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
总结:
字节输入流读数据步骤:
1、创建字节输入流对象
2、定义标记控制输入流的读取
3、循环读取,若读到了-1,说明读到了末尾,循环结束
4、关闭字节流(只能手动关闭)
注意异常处理 !!!(最好处理掉,不要抛出)
一些注意点
感觉写起来有点臃肿 ? ? ?
可以封装一个工具类~~
- 能不能再读一次??---------->不可以
当一个流读完之后,会默认调用mark
和reset
来记录和重置,这个流就已经重置到了上次读完的位置,所以就无法再次读取,并不是读完一次后就关闭了流。
2. 字节输出流
写入文件,原文件里有东西 ??那就追加or覆盖
FileOutputStream构造器中
boolean append参数:true:追加;false或无参:覆盖
使用和输入流类似
@Test
public void test02() {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream("e:/aaa.txt",true); // 追加 !
// 一个字节一个字节的写
outputStream.write("\r\n".getBytes()); // 换行
outputStream.write("八月正午的阳光都没你耀眼".getBytes());
System.out.println("数据写出成功...");
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
IOUtil.closeIO(null,outputStream);
/* try {
if(Objects.nonNull(outputStream)){
outputStream.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
*/
}
注意点
1、一个字节一个字节地写 write()
(费劲)括号里传的不是字符串
如果文件不存在还没报错???----> 会自动给新建一个然后写入
2、write()
如果传入的是字符串,就成字节型数组" xxx ".getBytes()
换行就是"\r\n"
3、关闭流的操作注意判断下,这么麻烦,整个工具类!!
*关闭流的工具类
/**
* 关闭流
* 注意保证所有的流都关闭!! 抓取异常的最大父类 Exception
*/
public class closeIOUtil {
public static void closeIO(Reader reader, Writer writer){
if(Objects.nonNull(reader)) {
try {
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(Objects.nonNull(writer)){
try {
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void closeIO(InputStream inputStream, OutputStream outputStream){
// 避免空指针异常
if (Objects.nonNull(inputStream)) {
try {
inputStream.close();
} catch (Exception e) {
// 保证出了异常也能让所有的流关闭
throw new RuntimeException(e);
}
}
if (Objects.nonNull(outputStream)) {
try {
outputStream.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
了解即可:
在JDK1.7之后,很多资源类实现了AutoCloseable接口
包括常见的流:FileInputStream,FilOutputStream……
可以直接在try中定义资源,它会主动释放资源,不用手动关闭
*案例:文件复制
/**
* 两个流:
* FileInputStream:把对应文件的内容读出来
* FileOutputStream:把读到的内容写出去
*
* 复制粘贴
* 复制:从哪复制,读 read(byte[])
* 粘贴:粘贴到哪,写 write(byte[])
*
* 多个流同时使用时,流的关闭顺序??从后往前关 (先用的流后关,)
*
*/
public class StreamCopy {
@Test
public void copy(){
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = new FileInputStream("f:/aaa.txt");
outputStream= new FileOutputStream("f:/bbb.txt");
int len;
byte[] buf = new byte[3]; //一次读三个字节
// 循环读进来
while ((len = inputStream.read(buf)) != -1){
// 写出去
outputStream.write(buf,0,len);
// outputStream.write(buf,0,len); 读几个写几个
}
System.out.println("文件复制完毕。。。");
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
CloseIOUtil.closeIO(inputStream,outputStream);
}
}
}
3、字符流操作
很少用FileReader这个节点流,简单了解即可~
读 Reader reader = new FileReader("e:/aaa.txt")
一个中文就当一个字符;
读出来是汉字就汉字,读到的是字符型数组,输出可以转成字符串new String(buf ,0,len)
字符处理流(用的最多)
只能处理纯文本文件,最好用缓冲流
缓冲字符输入流
// BufferedRead buffer = new BufferedRead(这里传节点流)
缓冲字符输出流
@Test
public void test02() {
Writer writer = null;
try {
writer = new FileWriter("e:/bbb.txt");
writer.write("阿里巴巴规约手册...");
System.out.println("数据写出成功...");
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
IOUtil.closeIO(null,writer);
}
}
@Test
public void test01() {
Reader reader = null;
try {
reader = new FileReader("e:/aaa.txt");
int len;
char [] buf = new char[10];
while((len = reader.read(buf)) != -1){
System.out.println(new String(buf,0,len));
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
IOUtil.closeIO(reader,null);
}
}
*案例:缓冲字符流做文件复制
public static void main(String[] args) {
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
try {
bufferedReader = new BufferedReader(new FileReader("e:/aaa.txt"));
bufferedWriter = new BufferedWriter(new FileWriter("e:/bbb.txt"));
String str;
while((str = bufferedReader.readLine()) != null){
bufferedWriter.write(str);
bufferedWriter.newLine(); // 换行
}
System.out.println("文件复制成功!!!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtil.closeIO(bufferedReader,bufferedWriter);
}
}
缓冲流是处理流
序列化与反序列化(可能面试)
操作的是对象
序列化:将对象写入IO流中,是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。 核心作用是对象状态的保存与重建
反序列化:从IO流中恢复对象,客户端从文件中或网络上获得序列化后的对象字节流,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。
使用??
所有可在网络传输的对象都必须可序列化,所有存在硬盘上的对象也必须可序列化,否则报错
如何实现序列化??
实现接口:
Serializable
;将对象写入文件
1、先锁定一个文件(用到IO流)(output
)
2、创建对象流
3、造对象
4、写出writeObject(造的对象)
5、关流
@Test
public void test01() {
ObjectOutputStream objectOutputStream = null;
// 先锁定一个文件
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream("e:/user.txt"));
User user = new User("张三",25,1);
objectOutputStream.writeObject(user);
System.out.println("对象写出成功...");
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
IOUtil.closeIO(null,objectOutputStream);
}
}
实现反序列化:
对象读入文件
1、先锁定一个文件(用到IO流)input
2、创建对象流
3、写出readObject()
注意强转
4、关流
@Test
public void test02() {
ObjectInputStream objectInputStream = null;
// 先锁定一个文件
try {
objectInputStream = new ObjectInputStream(new FileInputStream("e:/user.txt"));
User user = (User) objectInputStream.readObject();
System.out.println("对象读取成功:" + user);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
IOUtil.closeIO(objectInputStream,null);
}
}
关于序列化版本号错误:
反序列化必须拥有class文件,但随着项目的升级,class文件也会升级
所以序列化保证升级前后的兼容性
- 所以在类里给个版本号就行,这个版本号可以自由指定,但不能不指定,不然JVM会自己计算一个,就不能匹配,就报错
如果不指定版本号:不利于JVM移植,而且不同JVM计算规则不同,导致无法反序列化
如果只修改了方法,反序列化不受影响,如果修改的是静态变量static或瞬态变量transient反序列化也不受影响,无序修改序列化
其他
总结:
1、所有需要网络传输的对象都要实现序列化接口
2、对象的类名、实例变量都会被序列化,但方法、类变量、瞬态变量不会被序列化
3、如果想让某个变量不被序列化,可以用transient修饰
4、序列化对象的引用类型的成员变量必须是可序列化的,否则报错
5、反序列化时,必须有序列化对象的class文件
6、同一个对象被序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化的版本号
7、建议所有可序列化的类
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY