一、IO字节流
1.1 简介
- 以内存为自我角色:对硬盘写、输出–>Ouput和writer,读取硬盘、读、输入–>Input和reader
- IO流的分类:流向的划分–>输入流和输出流、类型的划分–>字节流和字符流
- 万物皆字节,电脑上的任意文件都可用字节来表示
- 字符流底层也是用字节流进行操作的,只是通过编码表将字节转换为字符
- 注意点:A.所有的流资源都是Java.io下的 B.使用流资源可能会出现异常 C.所有的流资源使用后都要进行关闭,输出和写需要进行流的刷新
1.2 OutputStream抽象类及子类FileOutputStream
- OutputStrea此类是所有的字节输出流的超类
- FileOutputStream 用于将图像视频之类的原始字节的流操作
1.2.1 FileOutputStream写入文件代码实现
package qf22020305_IOStream.Demo02;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
public class Test01 {
public static void main(String[] args) {
FileOutputStream fos = null;
Scanner sc = new Scanner(System.in);
System.out.println("输入要写的内容:");
String next = sc.nextLine();
byte[] b = next.getBytes();
try {
fos = new FileOutputStream("D:\\IODemo\\test.txt", true);
fos.write(b);
fos.flush();
System.out.println("写入成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.3 InputStream抽象类及子类FileInputStream
- InputStream抽象类
- FileInputStream 是所有字节输入流的超类,用于读取诸如图像数据之类的原始字节流 用于操作字节
1.3.1 FileInputStream读取文件代码实现
package qf22020305_IOStream.Demo01_File;
import java.io.FileInputStream;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("D:\\IODemo\\test.txt");
byte[] b = new byte[fis.available()];
int temp;
while ((temp = fis.read(b)) != -1) {
System.out.println(new String(b, 0, temp));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.4 上诉两个流进行文件复制
package qf22020305_IOStream.Demo03;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test03 {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("D:\\IODemo\\3.jpg");
fos = new FileOutputStream("C:\\3.jpg", true);
byte[] b = new byte[1024 * 1024];
int temp;
while ((temp = fis.read(b)) != -1) {
fos.write(b, 0, temp);
}
fos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.5 写入的原理

1.6 写入内容说明
- 问题:写入的字节是 97 98 99 使用记事本打开就是 a b c
- 原因:记事本打开的文件的时候,如果字节的范围是 0-127使用ASCII码来表示,操作其范围则用记事本默认的码来表示
1.7 Buffered Out/In putStream高效流
- BufferedOutputStream和BufferedInputStream
- 内部创建一个缓冲数组,内置默认长度都为8192
- 缓冲流效率高的原因:底层封装一个缓冲的数组,会一次读取8192个字节数组,存入缓冲数组中,避免对次交互,提高效率
- 高效率本身没有对文件内容操作的功能,只提供一个缓冲数组,对Out/In putStream的进行了功能的封装
- 流的关闭都是从下往上进行关闭(最后的使用先关闭)
1.7.1 高效流对文件的复制
package qf22020305_IOStream.Demo04_Buffer;
import java.io.*;
public class Demo01 {
public static void main(String[] args) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
int temp;
try {
bis = new BufferedInputStream(new FileInputStream("D:\\IODemo\\3.jpg"));
bos = new BufferedOutputStream(new FileOutputStream("D:\\3.jpg"));
while ((temp = bis.read()) != -1) {
bos.write(temp);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bos != null) {
bos.close();
}
if (bis != null) {
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
二、字符流Reader 和 Writer 抽象类
2.1 简介
- 用于字符的读取,纯文本文件用的比较多
- 使用字符流的原因:使用字节流读取字符,需要将读取的内容来进行转换,转换错误可能会出现乱码 ,使用字节操作字符比较麻烦
- 字符流=字节流+编码表
- 任何的编码表下,中文的第一个字节都是负数,根据这个规律来进行转换
2.1.1 Writer写入代码实现
package qf22020305_write_read;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Write01 {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("writer.txt");
writer.write(new char[]{'1','2'});
writer.write("你好啊");
writer.write('b');
writer.flush();
writer.close();
}
}
2.1.2 Reader读取代码实现
package qf22020305_write_read;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Read01 {
public static void main(String[] args) throws FileNotFoundException {
Reader reader = new FileReader("writer.txt");
char[] ch = new char[1024];
try {
int temp;
while ((temp = reader.read(ch)) != -1) {
new String(ch, 0, temp);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 编码表

2.3 字符流不能复制音频文件的原因
- 原因:因为使用字符流读取文件的时候,需要依赖于编码表来进行转换,然而音频文件不能使用常规编码表来进行转换
- 图:

2.4 字符高效流
- 前面字节高效流语法基本一致,略…
三、转换流
3.1 InputStreamReader 字节流转字符流
- 是字节流通向字符流的桥梁,因为字节进行存储,然后读取出来用字符进行表述
- 它使用指定的charset 读取字节并将其解码为字符,可以设置其编码格式
- 为达到最高效率,可考虑BufferedReader
3.1.1 InputStreamReader读取文件
package qf22020307_InputStreamReader_outputStreamWriter;
import java.io.*;
public class Demo01 {
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\IODemo\\test.txt"), "UTF-8"));
int temp;
while ((temp = br.read()) != -1) {
System.out.println((char) temp);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.2 OutputStreamWriter 字符流转字节流
- OutputStreamWriter 是字符流通向字节流的桥梁,因为使用字符写入,转为字节进行存储
- 使用指定的 charset 将要写入流中的字符编码成字节 可以设置其编码表(编码格式)
- 为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中
3.2.1 利用两个转换流复制文件
package qf22020307_InputStreamReader_outputStreamWriter;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class Demo02 {
public static void main(String[] args) {
BufferedWriter bw = null;
BufferedReader br = null;
List<String> list = new ArrayList<>();
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\IODemo\\test.txt"), "UTF-8"));
String temp;
while ((temp = br.readLine()) != null) {
list.add(temp);
}
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:\\test.txt"), "UTF-8"));
for (int i = list.size() - 1; i >= 0; i--) {
bw.write(list.get(i));
bw.newLine();
}
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null) {
bw.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.2 close()与flush()的比较
- flush() 将缓冲区的数据刷新到硬盘中
- close() 关闭流资源,close() 方法的底层会调用flush()方法
- 区别:调用 close() 方法之后,流资源就不能使用,调用 flush() 方法之后 流资源是可以使用
四、对象流
4.1 序列化概念
- 使用场景:在读写存储文件的时候或在网络通信传输对象时
- 序列化机制:使用一个序列化的字节(唯一标识)来表示文件中的对象(属性值、方法)
- 什么是序列化:将对象写入到文件中的过程就是序列化,将对象进行流化,加快对象写入到文件的速度
- 什么反序列化:将文件中序列化的对象,读取的过程就是序列化,加快读取的速度
- 只支持将实现了Serializable 接口的对象写流中
4.2 ObjectOutputStream和ObjectIntputStream
- 将实现了序列化接口的多个对象分别写入文件和读取出来
- 定义一个实体类Person
package qf22020307_objectInputStream_objectOutputStream.Demo02;
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private Integer age;
public Person() {
}
;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 测试类
package qf22020307_objectInputStream_objectOutputStream.Demo02;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class Demo01 {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("小明", 20));
list.add(new Person("小李", 21));
list.add(new Person("小张", 22));
addObject(list);
getObject();
}
public static void addObject(List<Person> list) {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("D:\\test.txt"));
oos.writeObject(list);
oos.writeObject(null);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void getObject() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("D:\\test.txt"));
Object o;
while ((o = ois.readObject()) != null) {
System.out.println(o);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
五、关于流的总结
5.1 字符流、字节流、缓冲流、对象流总结
- 图

六、方法的多参数
6.1 简介
- 使用场景:在定义方法不确定其参数的个数的时候,就可以使用多参数
- 多参数的本质就是一个数组,所以可以直接传入一个数组
- 多参数的类型都必须一致
- 多参数可以有其他的参数,但多参数必须放在最后面
6.2 案例
package qf22020307_objectInputStream_objectOutputStream;
public class Demo03 {
public static void main(String[] args) {
add("加法", 10, 10, 10, 10, 10);
}
public static Integer add(String s, Integer... n) {
int sum = 0;
for (int i = 0; i < n.length; i++) {
sum += n[i];
}
System.out.println(s);
return sum;
}
}
七、Properties
7.1 简介
- 具有属性集将数值以字符串键值对的方式存储、具有持久化保持到文件中
- 继承Hashtable
7.2 案例综合应用
- 问题:利用properties方法、反射机制、双重锁设计模式设计属性读取工具类,读取到properties的对象属性,来通过反射创建对象
- 创建属性配置文件

- 创建一个Student实体类
package qf22020310_properties;
public class Student {
private Student() {}
public void add(String name,Integer age){
System.out.println("姓名:"+name+"\t年龄:"+age);
}
public void get(){
System.out.println("无参数get方法");
}
}
- 封装一个工具类
package qf22020310_properties;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class PropertiesUtils {
private static PropertiesUtils pu;
Properties p;
private PropertiesUtils() {
try {
p = new Properties();
InputStream ras = PropertiesUtils.class.getResourceAsStream("properties.properties");
p.load(ras);
} catch (IOException e) {
e.printStackTrace();
}
}
public static synchronized PropertiesUtils getInstance() {
if (pu == null) {
synchronized (PropertiesUtils.class) {
if (pu == null) {
pu = new PropertiesUtils();
}
}
}
return pu;
}
public String getValue(String name) {
return p.getProperty(name);
}
}
- 通过反射利用类属性值构造对象
package qf22020310_properties;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
public class ReflectInstance {
public void createInstance() throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
PropertiesUtils instance = PropertiesUtils.getInstance();
String classname = instance.getValue("classname");
String methodname = instance.getValue("methodname");
String par = instance.getValue("par");
Class<?> aClass = Class.forName(classname);
Constructor<?> constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true);
Object o = constructor.newInstance();
Class[] classes = classes(par);
if (classes == null || classes.length <= 0) {
aClass.getDeclaredMethod(methodname).invoke(o);
} else {
aClass.getMethod(methodname, classes).invoke(o, "小明", 22);
}
}
public static Class[] classes(String par) {
if (par == null || "".equals(par)) {
return null;
}
String[] split = par.split(",");
List<Class> list = new ArrayList<>();
for (String s : split) {
if (s.equals("String")) {
list.add(String.class);
} else if (s.equals("Integer")) {
list.add(Integer.class);
} else {
list.add(Object.class);
}
}
return list.toArray(new Class[list.size()]);
}
}
- 测试类
package qf22020310_properties;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
public class Test01 {
@Test
public void test() throws ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException, IllegalAccessException {
ReflectInstance ri = new ReflectInstance();
ri.createInstance();
}
}