07. IO流
01. 文件
1. 什么是文件
文件对我们并不陌生,文件是保存数据的地方,比如大家经常使用的word文档,txt文件,excel文件…都是文件。它既可以保存一张图片,可以保存视频,声音
2.文件流
文件在程序中是以流的形式来操作的
- 流:数据在数据源(文件)和程序(内存)之间的经历的路径
2. 输入流:数据从数据源(文件)到程序(内存)的路径
3. 输出流:数据从程序(内存)到数据源(文件)的路径
02. 常用的文件操作
1. 创建文件对象相关构造器和方法
1. 相关方法:
- new File(String pathname)//根据路径构建一个File对象
- new File(File parent, String child)//根据父目录文件+子路径构建
- new File(String parent, String child)//根据父目录+子路径构建
- createNewFile 创建新文件
2. 应用案例
要求:在E盘下,创建文件 news1.txt news2.txt news3.txt
import jdk.jfr.StackTrace;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
public class FileCreate {
public static void main(String[] args) {
}
@Test
//1. 方式一:new File(String pathname)
public void create01() {
String filePath = "e:\\news1.txt";
File file = new File(filePath);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
//2. 方式二:new File(File parent, String child)//根据父目录文件+子路径构建
public void creat02() {
File file = new File("e:\\");
String fileName = "news2.txt";
File file1 = new File(file, fileName);
try {
file1.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
//3. 方式三:new File(String parent, String child)//根据父目录+子路径构建
public void creat03() {
String parentPath = "e:\\";
String childPath = "news3.txt";
File file = new File(parentPath, childPath);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 获取文件的相关信息
1. 方法
getName、getAbsolutePath、getParent、Length、exists、isFile、isDirectory
2. 应用案例
import java.io.File;
public class FileInformation {
public static void main(String[] args) {
}
@Test
//1. 获取文件的信息
public void inf() {
//1.1 先创建文件对象
File file = new File("e:\\news1.txt");
//1.2 调用相应的方法,得到对应的信息
//1.3getName、getAbsolutePath、getParent、Length、exists、isFile、isDirectory
System.out.println("文件的名字=" + file.getName());
System.out.println("文件的绝对路径=" + file.getAbsolutePath());
System.out.println("文件的父级目录=" + file.getParent());
System.out.println("文件的大小(字节)=" + file.length());
System.out.println("文件是否存在" + file.exists());
System.out.println("是不是一个文件" + file.isFile());
System.out.println("是不是一个目录" + file.isDirectory());
}
}
3. 目录的操作和文件删除
1. 方法
mkdir创建一级目录,mkdirs创建多级目录,delete删除空目录或文件
2. 应用案例
package com.yzjedu.file;
import org.junit.jupiter.api.Test;
import java.io.File;
/**
* 03. 目录的操作和文件删除
**/
public class Directory_ {
public static void main(String[] args) {
}
@Test
//1.判断 e:\\news1.txt 是否存在,如果存在就删除
public void m1() {
String filePath = "e:\\news1.txt";
File file = new File(filePath);
if (file.exists()) {
if (file.delete()) {
System.out.println("删除成功");
} else {
System.out.println("删除失败");
}
}else {
System.out.println("文件不存在");
}
}
@Test
//2. 判断e:\\demo02 是否存在,存在就删除,否则提示不存在
//体会,在java编程中,目录也被当做一种文件存在
public void m2() {
String filePath = "e:\\demo02";
File file = new File(filePath);
if (file.exists()) {
if (file.delete()) {
System.out.println("删除成功");
} else {
System.out.println("删除失败");
}
}else {
System.out.println("该目录不存在");
}
}
@Test
//3. 判断e:\\demo\\a\\b\\c 目录是否存在,如果存在就提示已经存在,否则就创建
public void m3() {
String directoryPath = "e:\\demo\\a\\b\\c";
File file = new File(directoryPath);
if (file.exists()) {
System.out.println(directoryPath + "存在");
}else {
if (file.mkdirs()) {
System.out.println(directoryPath + "创建成功");
} else {
System.out.println("创建失败");
}
}
}
}
03. Java IO流原理及流的分类
1. Java IO流原理
- I/O是Input/Output的缩写,I/O技术是非常实用的技术, 用于处理数据传输。如读/写文件,网络通讯等。
- Java程序中,对于数据的输入/输出操作以”流(stream)"的方式进行。
- java.io包下提供了各种“流"类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
- 输入input: 读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
- 输出output: 将程序(内存)数据输出到磁盘、光盘等存储设备中
2. 流的分类
- 按操作数据单位不同分为: 字节流(8 bit) 二进制文件, 字符流(按字符) 文本文件
- 按数据流的流向不同分为: 输入流,输出流
- 按流的角色的不同分为:节点流,处理流/包装流
(抽象基类) | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
- Java的I0流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的。
- 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
3. 流和文件的区别
- 流是数据的抽象和传输方式,而文件是数据的存储形式。
- 流是实时的、动态的,而文件是静态的、持久的。
- 流通常用于数据的临时传输和处理,而文件用于长期存储和持久化。
04. IO体系-常用的类
1. InputStream(字节输入流)
InputStream抽象类是所有类字节输入流的超类
2. InputStream常用的子类
- FileInputStream: 文件输入流
- BufferedInputStream:缓冲字节输入流
- ObjectInputStream:对象字节输入流
3. FileInputStream应用实例
**要求:**使用FileInputStream 在 a.txt文件,写入“hello,world”,如果文件不存在,会创建文件(注意:前提是目录已经存在)
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStream_ {
public static void main(String[] args) {
}
/**
* 演示读取文件
*/
@Test
public void readFile01() {
String filePath = "d:\\hello.txt";
FileInputStream fileInputStream = null;
//2. 字节数组
byte[] buf = new byte[8];//2.1 一次读取8个字节
int readLen = 0;
try {
//1. 创建 FileInputStream 对象,用于读取文件
fileInputStream = new FileInputStream(filePath);
//1.1 从改输入流读取一个字节的数据。如果没有输入可用,此方法将阻止
//1.2 如何返回-1,表示读取完毕
//2.2 如果读取正常,返回实际读取的字节数
while ((readLen = fileInputStream.read(buf)) != -1) {
System.out.print(new String(buf, 0, readLen));
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
//1.3 关闭文件,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4. FileOutputStream
1. 介绍
该类的主要作用是创建一个输出流,使程序能够将数据写入文件。通过 FileOutputStream 类,可以逐字节地将数据写入文件,也可以一次性写入字节数组。
2. FileOutputStream应用实例1
要求: 请使用 FileOutputStream 在 a.txt 文件,中写入 “hello,world”, 如果文件不存在,会创建 文件(注意:前提是目录已经存在.)
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStream01 {
public static void main(String[] args) {
}
/**
* 演示使用FileOutputStream 将数据写到文件中
* 如果文件不存在,则创建该文件
*/
@Test
public void writeFile() {
//1. 创建 FileOutputStream 对象
String filePath = "d:\\a.txt";
FileOutputStream fileOutputStream = null;
try {
//1.1 得到FileOutputStream对象
//2. new FileOutputStream(filePath) 创建方式,当写入内容时,会覆盖原来的内容
//2.1 new FileOutputStream(filePath, true) 创建方式,当写入入内容时,会增加到文件的末尾
fileOutputStream = new FileOutputStream(filePath, true);
//1.2 写入一个字节
// fileOutputStream.write('H');
//1.3 写入字符串
String str = "hello,world!";
//getBytes() 这个方法的作用可以把 字符串 -> 字节数组
// fileOutputStream.write(str.getBytes());
//1.4 write(byte[] b, int off, int len) 方法二
fileOutputStream.write(str.getBytes(), 0, /*str.length()将所有的字符串内容写入*/ 3);
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. FileOutputStream应用实例2
要求:编程完成图片/音乐的拷贝
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopy {
public static void main(String[] args) {
//1. 完成文件拷贝,将d:\\pg.png 拷贝 c:\\
//2. 思路分析
//2.1 创建文件的输入流,将文件读入到程序
//2.2 创建文件的如初六,将读取到的文件数据,写入到指定的文件
String srcFilePath = "d:\\bg.png";
String destFilePath = "d:\\bg2.png";
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream(srcFilePath);
fileOutputStream = new FileOutputStream(destFilePath);
//3. 定义一个字节数组,提高读取效果
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = fileInputStream.read(buf)) != -1) {
//3.1 读取到后,就写入到文件,通过 fileOutputStream
//3.2 即 边读边写
fileOutputStream.write(buf, 0, readLen);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5. FileReader和FileWriter
1. 介绍
FileReader
作用: FileReader
用于读取字符文件中的数据。
特点: 它是从文件中读取字符数据的便捷类,通常与 BufferedReader
结合使用,以提高读取效率。
构造方法:
FileReader(File file)
:根据指定的文件创建一个新的 FileReader。
FileReader(String fileName)
:根据指定的文件名创建一个新的 FileReader。
FileWriter
作用: FileWriter
用于向文件中写入字符数据。
特点: 它是从文件中写入字符数据的便捷类,通常与 BufferedWriter
结合使用,以提高写入效率。
构造方法:
FileWriter(File file)
:根据指定的文件创建一个新的 FileWriter。
FileWriter(String fileName)
:根据指定的文件名创建一个新的 FileWriter。
2. FileReader常用方法
- new FileReader(File/String)
- read: 每次读取单个字符,返回该字符,如果到文件未尾返回-1
- read(char[]): 批量读取多个字符到数组,返回读取到的字符数,如果到文件未尾返回-1
相关API: - new String(char[]): 将char[]转换成String
- new String(char[],off,len): 将char[]的指定部分转换成String
3. FileWriter常用方法
- new FileWriter(File/String):覆盖模式,相当于流的指针在首端
- new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
- write(int):写入单个字符
- write(char[]):写入指定数组
- write(char[],off,len):写入指定数组的指定部分
- write (string) :写入整个字符串
- write(string,off,len):写入字符串的指定部分
相关API:String类: toCharArray:将String转换成char[]
注意:FileWriter使用后,必须要关闭(close)或刷新(flush), 否则写入不到指定的文件!
4. FileReader和FileWriter应用案例
要求:
- 使用FileReader从story.txt 读取内容,并显示
import org.junit.jupiter.api.Test;
import java.io.FileReader;
import java.io.IOException;
public class FileReader_ {
public static void main(String[] args) {
}
/**
* 单个字符读取文件
*/
@Test
public void readFile01() {
String filePath = "d:\\story.txt";
FileReader fileReader = null;
int data = 0;
//1. 创建FileReader对象
try {
fileReader = new FileReader(filePath);
//2. 循环读取 使用read, 单个字符读取
while ((data = fileReader.read()) != -1) {
System.out.print((char)data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
*多个字符读取文件
*/
@Test
public void readFile02() {
String filePath = "d:\\story.txt";
FileReader fileReader = null;
int readLen = 0;
char[] buf = new char[8];
//1. 创建FileReader对象
try {
fileReader = new FileReader(filePath);
//2. 循环读取 使用read, 8个字符读取
while ((readLen = fileReader.read(buf)) != -1) {
System.out.print(new String(buf, 0, readLen));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 使用 FileWriter 将 “风雨之后,定见彩虹” 写入到 note.txt 文件中, 注意细节
import java.io.FileWriter;
import java.io.IOException;
public class FileWriter_ {
public static void main(String[] args) {
String filePath = "d:\\note.txt";
FileWriter fileWriter = null;
char[] chars = {'a', 'b', 'c'};
try {
fileWriter = new FileWriter(filePath);
// 3. write(int):写入单个字符
fileWriter.write('h');
// 4. write(char[]):写入指定数组
fileWriter.write(chars);
// 5. write(char[],off,len):写入指定数组的指定部分
fileWriter.write("加油加油", 0, 3);
// 6. write (string) :写入整个字符串
fileWriter.write(" 你好,我来了");
fileWriter.write("风雨之后,定见彩虹");//完成要求
// 7. write(string,off,len):写入字符串的指定部分
fileWriter.write("真的是好像吃饭", 0, 3);
} catch (IOException e) {
e.printStackTrace();
} finally {
//8. 对于FileWriter, 一定要关闭流,或者flush才能真正把数据写入到文件
try {
fileWriter.flush();
//fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("程序结束");
}
}
05. 节点流和处理流
1. 基本介绍
1. 节点流:
节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter
想象一下你需要从一个水源中直接取水。节点流就像是直接与数据源连接的水龙头,它们直接读取或写入数据到数据源。
在Java中,节点流直接与文件、网络连接或内存数据相连,可以实现基本的读写操作。
2. 处理流
处理流(也叫包装流)是"连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader/BufferedWriter
现在想象一下你在取水时使用了一个过滤器或者水桶来处理水流。处理流就像是对节点流的增强,它们提供了更高级别的功能和操作。
处理流不直接与数据源相连,而是对节点流进行包装,并提供了缓冲、转换、过滤等功能。
2. 节点流和处理流的区别和联系
- 节点流是底层流/低级流,直接跟数据源相接。
- 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。[源码理解]
- 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连[模拟修饰器设计模式]
3. 处理流的功能主要体现在以下两个方面
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
- 操作的便捷:处理流可能提供了一系列便捷的方法来一 次输入输出大批量的数据,使用更加灵活方便
4. 处理流-BufferedReader和BufferedWriter
1. BufferedReader应用案例
要求:使用BufferedReader读取文本文件,并显示在控制台
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class BufferedReader_ {
public static void main(String[] args) throws Exception {
String filePath = "d:\\a.txt";
//1. 创建BufferedReader
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
//2. 读取
String line;//2.1 按行读取
//说明:
//1. bufferedReader.readLine();按行读取文件
//2. 当返回null时,表示文件读取完毕
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
//关闭流,这里注意,只需要关闭 BufferedReader,因为底层会自动去关闭 节点流
bufferedReader.close();
}
}
2. BufferedWriter应用案例
要求使用BufferedWriter将“你好,我可以进来吗”,写入到文件中
import java.io.*;
public class BufferWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "d:\\a.txt";
//创建BufferedWriter对象
//说明:
//1. new FileWriter(filePath, true) 表示以追加的方式写入
//2. new FileWriter(filePath) 表示已覆盖的方式写入
BufferedWriter bufferedWriter = null;
bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));
bufferedWriter.write("你好,我可以进来吗");
bufferedWriter.newLine();
bufferedWriter.write("你好,我可以进来吗");
bufferedWriter.newLine();
bufferedWriter.write("你好,我可以进来吗");
//插入一个换行
//说明关闭外层流即可,传入的new FileWriter(filePath),会在底层关闭
bufferedWriter.close();
}
}
3. 综合案例
要求:综合使用BufferedReader和BufferedWriter完成文本文件的拷贝,注意文件编码
import java.io.*;
public class BufferedCopy_{
public static void main(String[] args) {
String srcFilePath = "d:\\a.txt";
String destFilePath = "d:\\a2.txt";
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
//说明:
//1. BufferedReader和BufferedWriter是安装字符操作
//2. 不要操作二进制文件,可能造成文件损坏
try {
bufferedReader = new BufferedReader(new FileReader(srcFilePath));
bufferedWriter = new BufferedWriter(new FileWriter(destFilePath));
String Line;
//1. 读取文件内容
while ((Line = bufferedReader.readLine()) != null) {
//2. 每读取一行,就写入
bufferedWriter.write(Line);
//2.1 插入一个换行
bufferedWriter.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5. 处理流-BufferedInputStream 和 BufferedOutputStream
1. 介绍BufferedInputStream
BufferedInputStream是字节流,在创建BufferedInputStream时,会创建-个内部缓冲区数组.
2. 介绍bufferedOutputStream
BufferedOutputStream是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层
3. 应用案例
要求:编程完成图片/音乐的拷贝(要求使用Buffered…流)
package com.yzjedu.outputstream_;
import java.io.*;
/**
* @author Icaru
* @version 1.0
* @date 2024/3/3 20:58
* 13.
*/
public class BufferedCopy02 {
public static void main(String[] args) {
String srcFilePath = "d:\\win.jpg";
String destFilePath = "d:\\win2.jpg";
//1. 创建BufferedInputStream对象和BufferedOutputStream对象
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//因为 FileInputStream 是 InputStream的子类
bis = new BufferedInputStream(new FileInputStream(srcFilePath));
bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
//2. 循环读取文件,并写入到 destFilePath
byte[] buff = new byte[1024];
int readLen = 0;
while ((readLen = bis.read(buff)) != -1) {
bos.write(buff, 0, readLen);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3. 关闭外层流即可,底层会自动关闭节点流
try {
if (bis != null) {
bis.close();
}
if (bos != null) {
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6. 对象流-ObjectInputStream 和 ObjectOutputStream
1. 看一个需求
- 将int num = 100这个int数据保存到文件中,注意不是100数字,而是int 100,井且,能够从文件中直接恢复int 100
- 将Dog dog = new Dog(”小黄”,3)这个dog对象保存到文件中,并且能够从文件恢复.
- 上面的要求, 就是能够将基本数据类型或者对象进行序列化和反序列化操作
2. 序列化和反序列化
- 序列化就是在保存数据时, 保存数据的值和数据类型
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
Serializable // 这是一个标记接口,没有方法
Externalizable //该接口有方法需要实现,因此我们一般实现上面的Serializable接口
3. 对象流介绍
功能:提供了对基本类型的序列化和反序列化的方法
ObjectOutputStream 提供序列化功能
ObjectInputStream 提供反序列化功能
4. 应用案例
- 要求:使用ObjectOutputStream序列化基本数据类型和一个Dog对象(name, age), 并保存到data.dat文件中
import java.io.*;
public class ObjectOutputStream_ {
public static void main(String[] args) throws IOException {
//1. 序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
String filePath = "d:\\data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
//2. 序列化数据到 e:\data
oos.write(100);//int -> Integer(实现了 Serializable)
oos.writeBoolean(true);//boolean -> Boolean(实现了 Serializable)
oos.writeChar('a');//char ->character(实现了 Serializable)
oos.writeDouble(9.5);//double -> Double(实现了 Serializable)
oos.writeUTF("你好,我的宝");//String
//保存一个对象
oos.writeObject(new Dog("旺财", 10));
oos.close();
System.out.println("数据保存完成(序列化形式)");
}
}
class Dog implements Serializable {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
}
- 使用ObjectInputStream读取data.dat并反序列化恢复数据
import com.yzjedu.outputstream_.Dog;
import java.io.*;
public class ObjectInputStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1. 制定反序列化的文件
String filePath = "d:\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
//2. 读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
//2.1 否则会出现异常
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
//3.2 dog 的编译类型是Object,dog运行类型是Dog
Object dog = ois.readObject();
System.out.println("运行类型" + dog.getClass());
System.out.println("dog信息 = " + dog);
//3. 这里是特别重要的细节:
//3.1 如果我们希望调用Dog方法,需要向下转型
//3.2 需要我们将Dog类的定义,拷贝到可以引用的位置
Dog dog2 =(Dog)dog;
System.out.println(((Dog) dog).getName());
//关闭流, 只关闭外层流即可
ois.close();
}
}
**注意:**假如我们要反序列化,记得将上一个ObjectOutputStream_类中的Dog类移出,设置为public类,这样反序列化,就不会出现毛病
5. 注意事项和细节说明
- 读写顺序要一致
- 要求序列化或反序列化对象,需要实现Serializable
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
//1.还记得我们的上个案例中的Dog类吗
//1.1 在它的属性中添加
private static final long serialVersionUID = 1L;
//1.2 添加后就可以进行序列化和反序列化时,在Dog类中添加其他的属性不会影响其序列化(反序列化),提高了版本兼容性
- 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 这句话的意思是,当你要将一个对象序列化成字节流保存到文件或者通过网络传输时,这个对象的所有属性都需要是可序列化的。也就是说,对象的属性类型必须要实现Serializable接口,否则在序列化过程中会抛出NotSerializableException异常
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化
7. 标准输入输出流
1. 介绍
类型 | 默认设备 | |
---|---|---|
System.in 标准输入 | InputStream | 键盘 |
System.out 标准输出 | PrintStream | 显示器 |
2. 应用案例
- 传统方法System.out.println(“”); 是使用out对象将数据输出到显示器
- 传统方法,Scanner是从标准输入 键盘接受数据
import java.util.Scanner;
public class InputAndOutput {
public static void main(String[] args) {
//1. System 类 的 public final static InputStream in = null;
//1.1 System.in 编译类型 InputStream
//1.2 System.in 运行类型 BufferedInputStream
//1.3 表示是标准输入 键盘
System.out.println(System.in.getClass());
//2. public final static PrintStream out = null;
//2.1 编译类型 PrintStream
//2.2 运行类型 PrintStream
//2.3 表示标准输出 显示器
System.out.println(System.out.getClass());
System.out.println("hello,world!");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入内容:");
String next = scanner.next();
System.out.println(next);
}
}
8. 转换流-InputStreamReader 和 OutputStreamWriter
1. 引出问题
在前面的内容中我们经常见到创建的文件有乱码问题,那么我们该如何解决乱码问题,这就要引出转换流。让我们先写一段代码,看看乱码的问题
import java.io.*;
public class CodeQuestion {
public static void main(String[] args) throws IOException {
/*
看一个乱码问题
*/
//1. 创建字符输入流 BufferedReader [处理流]
//2. 使用 BufferedReader 对象读取 a.txt
//3. 默认情况下,读取文件是按照 utf-8 编码
//4. 如果你将文件修改为其他编码例如国标码,你会发现文件出现乱码
String filePath = "d:\\a.txt";
BufferedReader br = new BufferedReader(new FileReader(filePath));
String s = br.readLine();
System.out.println("读取到的内容是:" + s);
br.close();
}
}
2. 介绍
- InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成(转换)Reader(字符流)
- OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)
- 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
- 可以在使用时指定编码格式(比如utf-8, gbk,gb2312, IS08859-1等)
3. 应用案例
- 编程将字节流FilelnputStream 包装成(转换成)字符流InputStreamReader,对文件进行读取(按照utf-8/gbk格式),进而在包装成BufferedReader
import java.io.*;
public class InputStreamReader_ {
public static void main(String[] args) throws IOException {
String filePath = "d:\\a.txt";
//解读
//1. 把FileInputStream 转成 InputStreamReader
//2. 指定编码 gbk
//InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
//3. 把InputStreamReader 传入 BufferedReader
//BufferedReader br = new BufferedReader(isr);
//5. 一般情况下我们可以将2 和 3 结合起来
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(filePath),"gbk"));
//4. 读取
String s = br.readLine();
System.out.println("读取内容= " + s);
br.close();
}
}
- 编程将字节流FileOutputStream 包装成(转换成) 字符流OutputStreamWriter,对文件进行写入(按照gbk格式,可以指定其他,比如utf-8)
import com.yzjedu.writer_.BufferWriter_;
import java.io.*;
/**
* 演示 OutputStreamWriter 使用
* 把FileOutputStream 字节流,转成 OutputStreamWriter
* 指定处理的编码 gbk/utf-8/utf8
*/
public class OutputStreamWriter {
public static void main(String[] args) throws IOException {
String filePath = "d:\\a.txt";
String charSet = "utf-8";
BufferedWriter osw = new BufferedWriter(new java.io.OutputStreamWriter(
new FileOutputStream(filePath), charSet));
osw.newLine();
osw.write("你好,hello!");
osw.close();
System.out.println("按照 " + charSet + " 保存文件成功");
}
}
9. 打印流-PrintStream和PrintWriter
1. 介绍
打印流(PrintStream和PrintWriter)是Java中的输出流类,它们使得将数据输出到文件、控制台或网络等目标变得更加简单。PrintStream用于处理字节流数据,而PrintWriter则处理字符流数据。
**注意:**打印流只有输出流,没有输入流
2. 应用实例
- PrintStream案例
import java.io.IOException;
import java.io.PrintStream;
public class PrintStream_ {
public static void main(String[] args) throws IOException {
PrintStream out = System.out;
//1. 在默认情况下,PrintStream输出数据的位置是 标准输出,即显示器
/*
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
*/
out.print("java,i like");
//1.1 因为print的底层使用的是Write,所以我们可以直接调用write进行打印/输出
out.write("lmf,你好".getBytes());
//2. 我们可以去修改打印流输出的位置/设备
//2.1 输出修改成了"d:\\f1.txt"
//2.2 "hello, lmf" 会打印到f1.txt的位置
System.setOut(new PrintStream("d:\\f1.txt"));
System.out.println("hello, lmf");
}
}
- PrintWriter案例
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class PrintWrite_ {
public static void main(String[] args) throws IOException {
// PrintWriter printWriter = new PrintWriter(System.out);
PrintWriter printWriter = new PrintWriter(new FileWriter("d:\\f2.txt"));
printWriter.print("hi, 天津你好");
printWriter.close();//注意要记得关闭,如果没有关闭,不会输出
}
}
10. Properties类
1. 看一个需求
如下一个配置文件 mysql.properties
ip: 192.168.0.13
ueser=root
pwd=12345
请问编程读取ip、user和pwd的值是多少
分析
- 传统方法
import javax.sound.sampled.Line;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Properties01 {
public static void main(String[] args) throws IOException {
//1. 读取mysql.properties 文件,并得到ipk, user 和 pwd
BufferedReader bufferedReader = new BufferedReader(
new FileReader("src\\mysql.properties"));
String line = "";
while ((line = bufferedReader.readLine()) != null) {//循环读取
String[] parts = line.split("=");
System.out.println(parts[0] + "值是:" + parts[1]);
}
}
}
如果我们只取ip,我们还要手动去写"ip".equals(split[0]) 判断,比较麻烦
2. 基本介绍
- 专门用于读写配置文件的集合类
配置文件的格式:
键=值
键=值 - 注意:键值对不需要有空格,值不需要用引号括起来。默认类型是String
- Properties的常用方法
- load:加载配置文件的键值对到Properties对象
- list:将数据显示到指定设备/流对象
- getProperty(key):根据键获取值
- setProperty(key,value):设置键值对到Properties对象
- store:将Properties中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会存储为unicode码
3. 应用案例
- 使用Properties类完成对mysql.properties的读取
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
/**
* 演示Properties类 来读取mysql.properties
*/
public class Properties02 {
public static void main(String[] args) throws IOException {
//1. 创建Properties 对象
Properties properties = new Properties();
//2. 加载到指定的文件
properties.load(new FileReader("src\\mysql.properties"));
//3. 把k-v显示控制台
properties.list(System.out);
//4. 根据key 获取对应的值
String user = properties.getProperty("user");
String pwd = properties.getProperty("pwd");
System.out.println("user的值是" + user);
System.out.println("pwd的值是" + pwd);
}
}
- 使用Properties类添加key-val 到新文件mysql2.properties 中
- 使用Properties类完成对mysql2.properties的读取,并修改某个key-val
/**
* 演示Properties 类来配置文件,修改配置文件的内容
*/
public class Properties03 {
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
//创建
//1. 如果该文件没有key 就是创建
//2. 如果该文件有key 就是修改
//3. Properties 父类是 Hashtable底层就是Hashtable的核心方法
properties.setProperty("charset", "utf-8");
properties.setProperty("user","江南");//注意保存时,中文是unicode码值
properties.setProperty("pwd","bdhs");
//将k-v存储到一个文件
properties.store(new FileOutputStream("src\\mysql2.properties"),"hello"/*这是一个注释,可以为空*/);
System.out.println("保存配置文件成功");
}
}