一、介绍
IO -- I 表示 inputStream,O 表示 OutputStream,称为输入输出流;专门用来进行各类文件的读取和写出操作;根据读写文件的类型,IO 流分为字节流和字符流,根据输入输出的流向可分为输入和输出流; 其中还有缓冲流(辅助工具,提高节点流的工作效率,本身不具备读写文件的功能)、序列化流(用来实现对象的序列化与反序列化操作)、转换流(实现字节流到字符流的转换);
二、结构体系
三、字节输入输出流
1、字节输入流 - InputStream
介绍
主要方法
int available() -- 返回该输入流中可以读取的字节数
void close() -- 关闭流
abstract int read() -- 从输入流读取数据的下一个字节,没有读取到返回 -1,读取到就返回读到的字节数
int read(byte[] b) -- 从输入流读取一些字节数,并将它们存储到缓冲区 b
int read(byte[] b, int off, int len) -- 从输入流读取最多 len 字节的数据到一个字节数组
long skip(long n) -- 跳过字节数
FileInputStream
介绍
FileInputStream 是最主要的字节输入流子类,可用于任意文件的读取操作;
构造方法
主要方法
测试 -- 读取文件内容
public void test_file() throws IOException {
FileInputStream f1 = new FileInputStream(new File("d://a/a.txt"));
byte[] buf = new byte[1024]; // 使用数组可以提升读取效率
int read;
while((read = f1.read(buf)) != -1){
System.out.println(Arrays.toString(buf));
}
}
2、字节输出流 - OutputStream
介绍
OutputStream 字节输出流最顶层的父类,是一个抽象类,不能使用构造方法直接创建对象,需使用其子类 进行对象的创建,最常用的子类为 FileOutputStream,可用于一切数据(txt/mp3/mp4/png/doc/md) 的输出;
主要方法
FileOutputStream
介绍
构造方法
核心方法
案例
@Test
public void test_file() throws IOException {
File file = new File("d://test");
if(!file.exists()){
file.mkdirs();
}
String string = "百度百科是百度公司推出的一部内容开放、自由的网络百科全书.";
OutputStream fos = new FileOutputStream("d://test/test.txt");
byte[] bytes = string.getBytes();
fos.write(bytes);
fos.close();
}
四、字符流输入输出流
介绍
① 字符流是以读写字符的方式进行文件操作,不管是中文、数字、还是字符,单个的存在都是一个字符,不会 像字节流一样进行汉字的拆分(字节流操作纯文本内容乱码的原因);
② 字符流内部自带缓冲空间,可以更快速的进行内容读写操作;但是读写的内容相对局限,只能操作纯文本内容;
1、字符输入流 - Reader
介绍
Reader 是字符输入流最顶层抽象类,内部定义了读取字符的核心方法,其最常用的子类为 FileReader
主要方法
FileReader
介绍
构造方法
主要方法
都继承于父类,自己没有定义新的方法;
案例
@Test
public void test_reader() throws IOException {
FileReader fr = new FileReader("d://1.txt");
char[] chars = new char[8];
int read;
while((read = fr.read(chars)) != -1){
System.out.println(new String(chars,0,read));
}
}
2、字符输出流 - Writer
介绍
字符输出流是以字符(字符串)的形式进行内容的输出,自带缓存,每一次的输出都需要进行流的冲刷,否则数据在缓存中进行保存,不会直接写到文件中;
冲刷的方式可以使用 flush 方法,也可以使用 close 方法;
主要方法
FileWriter
介绍
构造方法
案例
五、转换流
介绍
转换流用于字节流到字符流的转换;没有字符流到字节流的转换;在转换的同时可以实现编码格式的设置;因 此转换流用来进行目标文件和编码环境编码格式不统一时进行编码格式的同意设置;
转换流的创建实际就是字符流的创建,比普通的字符流创建多了一个设置编码格式的选择;
1、输入转换流 -- InputStreamReader
介绍
构造方法
主要方法
案例
2、输出转换流 -- OutputStreamWriter
介绍
构造方法
主要方法
案例
六、缓冲流
介绍
缓冲流不能直接操作文件,需要配合辅助节点流(字节流或字符流)进行文件操作;
缓冲流字节流内部创建了 8192 字节或字符的空间,用来进行读写数据的临时储存,类似于使用字节流使用数 组的方式实现读写数据,这样可以大大的提升读写的效率;
因此,缓冲流的作用就是提升字节流或字符流读写的能力;
1、缓冲字节流
输入流 -- BufferedInputStream
构造方法
案例
@Test
public void test_buffered() throws IOException {
// 构建缓冲流
BufferedInputStream bis = new BufferedInputStream(newFileInputStream("d://1.txt"));
int read;
while((read = bis.read()) != -1){}
bis.close();
}
注意:缓冲流在使用时,比普通字节流和字符流的效率要高,但是不如使用小数组的字节流或字符流;
在缓冲流中也能自定义数组来进行数据的缓存,但是不建议如此操作,使用缓冲流尽量使用其自带的缓冲区;
输出流 -- BufferedOutputStream
构造方法
案例
public void test_buffered() throws IOException {
// 构建缓冲流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d://1.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d://2.txt"));
int read;
while((read = bis.read()) != -1){
bos.write(read);
}
bos.close();
bis.close();
}
注意:BufferedOutputStream 也自带了 8192 字节的缓冲空间;能够提升写出的效率;
2、缓冲字符流
输入流 -- BufferedReader
构造方法
案例
@Test
public void test_buffered2() throws IOException{
BufferedReader br = new BufferedReader(new FileReader("d://1.txt"));
String read;
while((read = br.readLine()) != null){}
br.close();
}
注意:readLine 是字符输入流特有的方法,可以一次读取一行的字符串,返回值为读取到的字符串;可以进
一步提升读取数据的效率;
输出流 -- BufferedWriter
构造方法
案例
public void test_buffered3() throws IOException{
BufferedWriter br = new BufferedWriter(new FileWriter("d://2.txt"));
br.write("静夜诗");
br.newLine();
br.write("床前明月光,");
br.newLine();
br.write("疑是地上霜。");
br.newLine();
br.write("举头望明月,");
br.newLine();
br.write("低头思故乡。");
br.close();
}
注意:newLine()方法可以实现在任意操作系统上的换行功能;比起使用字符换行的操作更加通用,因为不同
操作系统的换行符是不一样的;
七、序列化流
介绍
序列化表示将 java 对象通过字节流写出到指定的文件中的过程,以便于 Java 对象进行持久化保存和传 输;由于 java 语言的特点,对象只能在虚拟机运行期间存在,一旦虚拟机关闭,对象就消失;对象中储存的 数据也会消失,想要在虚拟机关闭后仍然保存对象信息,序列化对象是当前比较常用的一种手段;
跟序列化对应的操作为反序列化,反序列化是在序列化对象文件在传输过程中,读取文件中对象信息的一种手段;
序列化和反序列化都是以字节的形式进行数据的读写,因此归根到底即是字节输入流和输出流的使用;
对应的字节输出流 -- 序列化流(ObjectOutputStream)
对应的字节输入流 -- 反序列化流(ObjectInputStream)
一个类的对象想能被序列化,那么这个类需要实现序列化接口 -- Serializable
1、序列化 - ObjectOutputStream
介绍
构造方法
主要方法
案例
@Test
public void test_serializable() throws IOException {
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("d://user.txt"));
oos.writeObject(new User("小红",18));
oos.close();
}
注意:序列化是使用 ObjectOutputStream 的 writeObject 方法将对象以字节的方式输出到指定文件中;文件中保存的内容是该对象的属性和值,以及序列化的版本号(如果类中没有手动定义,系统会自动生成一个);类的静态内容不能被序列化;
2、反序列化 - ObjectInputStream
介绍
构造方法
核心方法
案例
@Test
public void test_serializable2() throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("d://user.txt");
ObjectInputStream ois = new ObjectInputStream(fileInputStream);
Object o = ois.readObject();
System.out.println(o);
}
注意:反序列时,jvm 虚拟机中必须要存在反序列化对象类型,而且类必须序列化之前具有相同的 serialVersionUID 否则反序列化会失败,并且抛出 InvalidClassException 异常; InvalidClassException 异常出现的原因:序列化时用的类的 serialVersionUID 和反序列化时用的 类 serialVersionUID 值不一致;
InvalidClassException 异常处理:
① 不要修改类的任何内容,serialVersionUID 是类中的内容进行生成的。
② 手动进行 serialVersionUID 声明,且声明为静态常量;
transient 关键字
transient 模糊序列化属性,被 transient 修饰的属性在进行序列化时,值不会序列化到文件中;
八、Properties 类
介绍
Properties 是 java 中用于配置文件读取的类,在 java 编程中,很多地方需要有配置文件的支持,配 置文件有多种类型,例如,以 .properties、.xml、.txt 等结尾的文件;想要获取配置文件配置的信息, 就需要进行文件的读取,Properties 是可以进行多种不同类配置文件读取的类;
Properties 内部有大量读/写配置文件的方法,都是根据 IO 流来进行的文件读/写取操作; Properties 还是 Hahtable 的子类,属于双列集合的范畴,通过键值对的方式进行数据的储存,并且要求 健和值都是String 类型;除了有 map 集合通用的添加数据的方式以外,自己还定义专属的数据添加方法;
构造方法
主要方法
操作内部元素的方法
读取配置文件的方法
写出数据到文件中
案例
@Test
public void test_properties1(){
Properties properties = new Properties();
properties.setProperty("1","2");
properties.getProperty("1","3");
Set<String> strings = properties.stringPropertyNames();
}
@Test
public void test_properties2() throws IOException {
Properties properties = new Properties();
properties.loadFromXML(new FileInputStream("d://jdbc.xml"));
System.out.println(properties.getProperty("user"));
}
@Test
public void test_store() throws IOException{
Properties properties = new Properties();
properties.setProperty("driverClass","com.mysql.cj.jdbc.Driver");
properties.setProperty("jdbcUrl","jdbc:mysql:///test1");
properties.setProperty("user","root");
properties.setProperty("password","root");
properties.store(new FileOutputStream("d://jdbc.properties"),"mysql数据库的连接信息");
}
@Test
public void test_list(){
Properties properties = new Properties();
properties.setProperty("driverClass","com.mysql.cj.jdbc.Driver");
properties.setProperty("jdbcUrl","jdbc:mysql:///test1");
properties.setProperty("user","root");
properties.setProperty("password","root");
properties.list(System.out);
}
最后补充
IO 和 NIO 的区别:
这个NIO是 JDK1.7 以后有的,它们俩的主要区别是:io 是面向流,是阻塞 io,nio是面向缓冲,非阻塞的io; io 的话每次流中取一 或多个字节 ,直到读取完所有的字节 ,没有缓存到任何地方。nio 读取的是数据是有缓存,就是说他读取的数据是在缓冲里读的。另外的话,java中的各种 io 是阻塞的。就是说一个线程调用 read 或者 write() 时,这个线程就已经被阻塞了,直到读取到一些数据为止,或者是完全写入。在这个过程中不能干其他的事情。nio 的非阻塞模式,当发送一个读取数据的请求的时候,如果没有读取到可用的数据,就什么也不会获取,且不会让线程阻塞,写也是这样。非阻塞的 IO 的空闲时间可用来做其他的操作所以,一个单独的非阻塞线程可以管理多个输 入和输 出通道,另 外 NIO 还有一个 selector(选择器 ),它是可以管理多个输入输出的通道。