IO流
> 什么是 IO?
--> I:Input
--> O:Output
1、IO流的分类
> 按照流的方向分为:输入流 and 输出流 (以内存作为参照物)
-->输入(Input)流:往内存中去,也可以称为“读(Read)”。
-->输出(Output)流:从内存中出来,也可以称为“写(Write)”。
> 按照流的读取方式分类:
--> 字节流:按照一个字节读入或写出 (类名以 “Stream” 结尾)
--> 字符流:按照一个字符读入或写出 (类名以 “Reader/Writer” 结尾)
字节流可读入或写出任意格式的文件,而字符流只能处理纯文本文件。
2、常用IO流(十六种)
> 文件流
--> java.io.FileInputStream(掌握)
--> java.io.FileOutputStream(掌握)
--> java.io.FileReader
--> java.io.FileWriter
> 转换流 (将字节流转换为字符流)
--> java.io.InputStreamReader
--> java.io.OutputStreamWriter
> 缓冲流
--> java.io.BufferedInputStream
--> java.io.BufferedOutputStream
--> java.io.BufferedReader
--> java.io.BufferedWtriter
> 数据流
--> java.io.DataInputStream
--> java.io.DataOutputStream
> 标准输出流
--> java.io.PrintStream(掌握)
--> java.io.PrintWriter
> 对象流
--> java.io.ObjectInputStream(掌握)
--> java.io.ObjectOutputStream(掌握)
3、IO流的使用
虽然 IO 流的种类不少,但对于常用 IO 流的基本使用,其代码相差无几。
3.1 文件流
以下不妨借用 FileInputStream 和 FileOutprintStream 两种文件流进行简单的操作说明:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 四个步骤:
* 1、创建源
* 2、选择流
* 3、操作流
* 4、关闭流
*/
public class FileInputStreamTest{
public static void main (String[] args){
// 1、创建源
FileInputStream fis = null;
try{ /*
该代码块中写流的操作,例如读取数据并打印...
下面展示使用文件字节输入流读取文件中的数据并打印到控制台上。
*/
// 2、选择流
fis = new FileInputStream("文件路径");
// 3、操作流
// 下面创建一个缓冲区,用于一次性读取多个字节,减少内存和文件
// 系统的交互次数
int readLen = -1; // read()读取到的字节大小
byte[] flush = new byte[1024*1024]; // 1M的缓冲区
// 若read()读取到文件末尾,则返回 -1
while ( (readLen = fis.read(flush) ) != -1 ){
// 下面语句输出文件中读取到的数据
System.out.println(new String(flush, 0, readLen));
}
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} finally { /*
因为流的使用比较占资源,所以用完之后一定要记得关闭!
因为不管程序是否发生异常,finally 语句总会执行,
所以,可将关闭流的操作放到 finally 语句的作用域中。
*/
// 4、关闭流
if (fis != null){
try{
fis.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
}
以上就是 FIleInputStream 的简单使用。而 FileOutputStream 的使用只需把上面的代码做一点小改动就能实现:
// 1、将需要用到的类导入:
import java.io.FileOutputStream;
// 2、将创建源的代码改为如下代码:
FileOutputStream fos = null;
// 3、将选择流的代码改为如下代码:
// 该处的文件路径随意,文件不存在时,会自动创建文件。
fos = new FileOutputStream("文件路径");
// 该构造器还有一个参数可传:boolean append。
// fos = new FileOutputStream("文件路径", true); 追加式写入模式
// 4、将操作流的代码改为如下代码:
String str = "我是中国人!";
byte[] src = str.getBytes();
fos.write(src); // 将数据写入文件
fos.flush(); // 刷新输出流
// 5、将关闭流的代码中的 fis 改为 fos 即可
综合以上所知,我们可以简单写一个文件复制的代码实现:
package IOSteam;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyTest {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream("文件路径");
fos = new FileOutputStream("文件路径");
int len = -1;
byte[] flush = new byte[1024*1024];
while ((len = fis.read(flush)) != -1){
fos.write(flush);
fos.flush(); //记得刷新,养成好习惯
}
} catch(FileNotFoundException e){
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
} finally{
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null){
try{
fis.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
}
}
对于上述代码,只有一个点需要注意:关闭流的次序。即为先启用的流后关闭。
3.2 缓冲流(Buffered开头的流)
新增有两个概念:节点流 and 包装流/处理流。如下代码所示:
/*
当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。
外部负责包装的这个流叫做:包装流/处理流。
*/
FileReader reader = new FileReader("文件路径");
BufferedReader br = new BufferedReader(reader);
// 对于这个程序来说,FileReader就是一个节点流,BufferedReader就是一个包装流。
缓冲流的使用与文件流相差无几,只是不用创建一个byte数组来作数据缓冲区,因为内部已经内嵌缓冲区。而且,关闭流的时候只需关闭包装流即可。
缓冲流因其内部有一个封装流的过程,所以效率应该要比文件流低一点。
3.3 数据流
DataOutputStream:这个流可以将数据及其数据类型一并写入文件中。(该文件记事本打不开)
DataInputStream:这个流可以将DataOutputStream 产生的文件数据及其数据类型读取到内存中,要求读的顺序需要和写的顺序一致!
应用:数据加密存储
3.4 标准输出流
PrintStream:标准的字节输出流,默认输出到控制台。
标准输出流不需要手动close()关闭。
标准输出流的输出方向可以改变!
// 标准输出流的输出方向不再指向控制台,而是指向"log"文件。
PrintStream printStream = new PrintStream(new FileOutputStream("log"));
// 修改输出方向,将输出方向修改到"log"文件。
System.setOut(printStream);
应用:日志工具
3.5 对象流
ObjectInputStream:反序列化(DeSerialize)–>(将硬盘文件中的数据读入到内存当中,恢复成Java对象)
ObjectOutputStream:序列化(Serialize)–>(将Java对象从内存中写出到硬盘文件中,将Java对象的状态保存下来)
参与序列化和反序列化的对象,必须实现Serializable接口!
Serializale接口只是一个标志接口:这个接口中没有任何代码,仅仅起到标识的作用。这个标志接口是给JVM参考的,JVM看到这个接口之后,会自动为该类生成一个序列化版本号。
–>(序列化版本号的作用:当类名相同时,序列化版本号可以用来区分类,推荐自行设置序列化版本号!)
public interface Serializable{
}
如何序列化多个对象:将对象放入集合中,序列化集合即可。
transient 关键字的使用:如果待序列化对象中的某些属性不需要序列化,可以使用该关键字进行修饰。
4、IO流与Properties的联合应用
应用:对于经常改变的数据,可以单独写到一个文件中,使用程序动态读取。以后修改数据的时候只需要修改这个文件的内容,不需要改动Java代码,也不需要重新编译,也不需要重启服务器,就可以拿到动态的信息。
类似于以上机制的文件被称为配置文件。
并且,当配置文件中的内容格式为:
key=value
的时候,我们把这种配置文件称为属性配置文件。
Java的属性配置文件推荐以 propertie 为后缀名。