JavaSE学习之IO流
一. IO流
IO流的作用: 用来完成数据的传输
IO流的分类:
按照流向分类: 输入流,输出流
按照操作数据的类型: 字节流,字符流
IO流学习的前提:
1.所有文件都是以字节的形式存在的,它们的最小存储单元都是字节
2.输入和输出: 我们站在内存的角度去考虑输入和输出
二.字节流
1.字节输入流
FileInputStream
构造方法:
FileInputStream(File file) FileInputStream(String name)
如果关联的文件不存在的话,抛出异常(FileNotFoundException)
成员方法:
public int read(int b): 读取一个字节,读到文件末尾,值返回-1;
2.字节输出流
FileOutputStream
构造方法:
FileOutputStream(File file) FileOutputStream(String name)
输出流关联的文件如果不存在的话,会自动创建
输出流关联的文件如果已存在,则会将其内容清空(有不会清空的方法)
成员方法:
public void write(int b): 写出一个字节
3.一个字节一个字节的拷贝
// 创建输入流对象
FileInputStream fis = new FileInputStream("E:/Projects/study/src/resource/aaa.text");
// 创建输出流对象
FileOutputStream fos = new FileOutputStream("E:/Projects/study/src/resource/Copyaaa.text");
// 使用输入流读取
// 创建变量, 用来记录每一次读取到的字节
int b;
while ((b = fis.read()) != -1) {
// 使用输出流写出
fos.write(b);
}
// 关闭输出流
fos.close();
// 关闭输入流
fis.close();
4.数组拷贝
使用到的成员方法:
FileOutputStream -----write
public void read( byte[] b, //数组
int off, //开始索引
int len) //长度,字节个数
写出b数组中从off开始的len个字节
FileInputStream ----read
public int read(byte[] b) : 将文件中的字节, 读取到b数组中
read方法的返回, 返回的是读取到有效的字节个数
如果读取到文件的末尾, 返回-1
拷贝
// 创建输入流对象
FileInputStream fis = new FileInputStream("Day09_io\\aaa.txt");
// 创建输出流对象
FileOutputStream fos = new FileOutputStream("Day09_io\\copyAaa.txt");
// 创建字节数组
byte[] arr = new byte[1024 * 8];
// 创建变量, 用来记录读取到有效的字节个数
int len;
// 使用输入流读取
while ((len = fis.read(arr)) != -1) {
// 使用输出流写出
fos.write(arr, 0, len);
}
// 关闭输出流
fos.close();
// 关闭输入流
fis.close();
三.字符流
只能操作我们能看懂的纯文本文件,不过一般也不用
1.字符输出流
FileWriter
public void write(int c) : 写出单个字符
public void write(String str) : 写出字符串
2.字符输入流
FileReader
public int read():读取单个字符
从网络下载
public class DemoIOStream {
public static void main(String[] args) throws IOException {
URL url = new URL("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2655406508,1191616699&fm=26&gp=0.jpg");
// 打开输入流
InputStream is = url.openStream();
// 创建输出流
FileOutputStream fos = new FileOutputStream("E:/Projects/study/src/resource/aaa.text");
// 创建字节数组
byte[] arr = new byte[1024 * 8];
// 创建变量, 用来记录读取到有效的字节个数
int len;
while ((len = is.read(arr)) != -1) {
fos.write(arr, 0, len);
}
fos.close();
is.close();
}
}
四.异常处理
finally: 释放资源
1.jdk1.6之前
public class DemoIOStream {
public static void main(String[] args) throws IOException {
FileInputStream fis = null;
try {
fis = new FileInputStream("E:/Projects/study/src/resource/aaa.text");
fis.read();
}finally {
// 非空判断 inn
if (fis != null) {
// 流尽量能够关掉, 将关流的代码放在finally中
fis.close(); // 1. 作用域 2.局部变量初始化
}
}
}
}
2.jdk1.7
1.7版本出现了AutoCloseable
try (FileInputStream fis = new FileInputStream("E:/Projects/study/src/resource/aaa.text");) {
fis.read();
}
3.jdk1.9
FileInputStream fis = new FileInputStream("E:/Projects/study/src/resource/aaa.text");
try (fis) {
fis.read();
}
五.Properties类
public void load(InputStream inStream): 读取输入流关联配置文件中的, 配置信息(键值对)
// 通过Properties, 获取配置文件中的信息
Properties p = new Properties();
// 创建和配置文件关联的输入流
// FileInputStream fis = new FileInputStream("E:/Projects/study/src/resource/config.properties");
InputStream fis = DemoIOStream.class.getClassLoader().getResourceAsStream("config.properties");
p.load(fis);
// 关流
fis.close();
// getProperty()
String user = p.getProperty("user");
String password = p.getProperty("password");
System.out.println(user + "---" + password);
六.缓冲流
1.字节流
带缓冲区的字节输入流(高效的字节输入流)
BufferedInputStream
构造方法
public void BufferedInputStream(InputStream in)
带缓冲区的字节输出流(高效的字节输出流)
BufferedOutputStream
public void BufferedOutputStream(OutputStream out)
拷贝
// 创建输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Buffer\\actor.mp3"));
// 创建输出流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("Buffer\\copyActor.mp3"));
// 使用输入流读取
int b;
while ((b = bis.read()) != -1) {
// 使用输出流写出
bos.write(b);
}
// 关闭输出流
bos.close();
// 关闭输入流
bis.close();
2.字符流
带缓冲区的字符流
BufferedReader
构造方法
public BufferedReader(Reader in)
特有方法
public String readLine(): 读取一个文本行,遇到换行符就结束,读取到文件末尾会返回null
换行符
windows默认的换行符: “\r\n”
\t: 制表符 tab
\" : 单个双引号, 没有任何其他含义, 只是一个字符
带缓冲区的字符输出流
BufferedWriter
构造方法
public BufferedWriter(Writer out)
特有方法
public void newLine(): 写出跨平台换行符
面试题
- close()和flush()方法的区别?
- 如果使用的输出流带有缓冲区(数组), 写出的内容会先写到缓冲区中, 然后在从缓冲区中刷出到文件上.
- 缓冲区中内容刷出到文件有两种方式:
- 缓冲区满了, 自动刷出.
- 手动刷出.
- close() : 在关闭输出流之前, 刷出了一次缓冲区.
- flush(): 刷出缓冲区中的内容到文件
七.转换流
- 指定字符集进行读写
- 转换流是字符流
输入流
InputStreamReader
public InputStreamReader(InputStream in, String charsetName)
是字节流通向字符流的桥梁
可以通过指定的字符集将字节转换成字符
输出流
OutputStreamWriter
public OutputStreamWriter(OutputStream out, String charsetName)
是字符流通向字节流的桥梁
可以通过指定的字符集将字符转换成字节
转换流拷贝
public class Demo02 {
public static void main(String[] args) throws IOException {
// 创建转换流的输入流
InputStreamReader isr = new InputStreamReader(new FileInputStream("d:\\gbk.txt"), "gbk");
// 创建转换流的输出流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("Demo01\\utf-8.txt"), "utf-8");
int c;
while ((c = isr.read()) != -1) {
osw.write(c);
}
osw.close();
isr.close();
}
}
需求
-
文件上是中文, 读取这个文件, 只能获取到字节流, 想要读取一行.
-
能够得到FileInputStream(字节流)
转换↓
-
需要读取一行: BufferedReader(字符流)
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Demo01\\aaa.txt")));
String s = br.readLine();
System.out.println(s);
br.close();
八.序列化流
序列化流是字节流
1.流对象解析
输出流
- 序列化流, 对象输出流 —
ObjectOutputStream
- 构造方法
public ObjectOutputStream(OutputStream out)
- 成员方法
public final void writeObject(Object obj)
输入流
- 反序列化流, 对象输入流 —
ObjectInputStream
- 构造方法
public ObjectInputStream(InputStream in)
- 成员方法
public final Object readObject()
2.序列化字符串
- 序列化
public class Demo01_序列化 {
public static void main(String[] args) throws IOException {
// 创建序列化流对象, 对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Demo01\\object.txt"));
// 写出字符串对象
oos.writeObject("你好");
// 关流
oos.close();
}
}
- 反序列化
public class Demo01_反序列化 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 反序列化流, 对象输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Demo01\\object.txt"));
// 读取对象
String o = (String) ois.readObject();
System.out.println(o);
// 关流
ois.close();
}
}
3.序列化自定义对象
- 自定义对象
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
// 瞬时, 瞬态: 序列化时跳过此变量
private transient int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 序列化
public class Demo02_序列化自定义对象 {
public static void main(String[] args) throws IOException {
// 创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Demo01\\object.txt"));
// 创建Person对象
Person p = new Person("张三", 23);
// 写出对象
oos.writeObject(p);
// 关流
oos.close();
}
}
- 反序列化
public class Demo02_反序列化自定义对象 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建对象输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Demo01\\object.txt"));
// 读取对象
Person p = (Person) ois.readObject(); // 找不到.class 字节码文件
System.out.println(p);
// 关流
ois.close();
}
}
- 为什么要用序列化?
- 当两个进程在进行远程通信时,对彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
说的再直接点,序列化的目的就是为了跨进程传递格式化数据