IO
IO即时输入输出流,流既是流水,从一端流向另一端。而java中的输入输出流,即以java程序为中心,往外部(数据库,磁盘,网络……)既是输出流,外部(数据库,磁盘,网络……)往内部传输既是输入流。
IO流的分类数据分类
按照处理的数据可分为,字节流和字符流。所有的数据都可以是字节流(音频,视频,doc,文本……),字符流仅仅能处理字符集合(文本)他们处理方式几乎完全相同。他们所操作的数据单元不同,字节流操作单个字节(8)位,字符流操作字符(16)位(内存中字符都为2个字节存储。getBytes()方法是将内码按编码转换为指定的外码)。
功能分类
节点流:直接从外部读写数据的流。
处理流:对于已存在的流进行封装(包装)对其性能和功能加强,装饰节点流的流
所有io操作都是基于节点流的基础上。
步骤
- 建立联系
- 选择流
- 操作:写出 读取
- 释放资源(程序中打开的文件 IO 资源不属于内存中的资源,垃圾回收无法回收,需要显示关闭。)
输入流
抽象类:InputStream 和 Reader
InputStream和Reader是所有输入流的基类,它们是两个抽象类,是所有输入流的模版,其中定义的方法在所有输入流中都可以使用。
InputStream 中包含以下3个方法(读完返回-1)
-
read(); 从输入流中读取数据的下一个字节 (一个字节一个字节读)
读取时候,java中一个字节是int (0-255)存储的。所以转成byte字节有时候是负数。但其 实存储的。**返回的是字节的int值**。需要转成byte字节。值是一样的。
-
read(byte []);
从输入流中读取容器大小的字节,并将他们存放在byte数组中。传入的参数为存放的数组容器。
-
read(byte[] b,int 起始位置,int 长度)
输入流中最多len个数据读入byte数组中。存放的byte数组的起始位置(包含),和读取的 长度,可以又选择的去读
Reader 中包含以下3个方法(读完返回-1)
-
read();
读取单个字符。返回字符对应的int值。转成char进行查看。读完了则返回-1;
-
read(char [] ch)
读取容器长度的字符到char数组中。存放到char数组中。多次读会覆盖之前内容。
-
read(char[] ch,int 起始位置,int 长度)
(包含起始位置)将指定长度的字符读取到char数组中
注意
返回结果为-1 时表明到了输入 流的结束点。
InputStream 和 Reade 都是抽象的,不能直接创建它们的实例,可以使用它们的子类。
文件节点类: FileInputStream 和 FileReader
FileInputStream 和 FileReader,它们都是节点流,直接和指定文件关联。 操作方式基本一致。
FileInputStream
FileInputStream可以用来读取任何数据(图片,视频,音乐,文字)都可以
读取文件的代码顺序
一次读b数组长度的字节数。
File file = new File("test\\bb.txt");// 建立和文件的联系
InputStream is=null; //创建流。先赋值为null。因为finally中要把流关闭,会有作用域问题
try {
is = new FileInputStream(file); //建立和文件之间的流通管道
int len =0; //定义变量
b= new byte[1024];
while(-1!=(len=is.read(b))){ //将读取的字节放入byte数组中。如果返回-1说明到了末尾
System.out.println(new String(b,0,b.length))//将字节转为String来输出。
}catch (IOException e) {//异常
e.printStackTrace();
}finally{
try {
is.close(); //关闭流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
FileReader
FileReader只能用来读取字符内容。可以一个字符一个字符读,也可以读取char数组容量的字符
一个个读取字符
File file = new File("test\\bb.txt"); //建立文件联系
Reader read=null; //提升作用域。
int t=0; //字符的长度
try {
read =new FileReader(file);//文件和程序之间建立管道
while(-1!=(t=read.read())){ //一个个进行读取,返回的是int类型的数字。
System.out.println((char)t); //打印字符。
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
输出流
抽象类:OutputStream 和 Writer
写入时文件不存在则会创建,但是文件路径必须完全存在。
方法与输入流的特别相似。
OutputStream中常用方法:
-
flush(); ——刷新输出流,并将内容强制写出。
-
write(byte [] b);——将byte里的数据写出到外部设备。此byte也是数据的容器,只是存储写出内容。
-
write(byte[] b,int 起始位置,int 长度)——将byte数组中,起始位置开始,指定长度的数据写出。
-
write(int b) ——写入一个字节,此字节为int型。
3个字符组成一个中文(utf中)如果想转出的字节byte 变 int 则需要 把 (n个)256+byte值此时才是真正的int值。大于0个byte值则不变。如“中字”utf解码后 byte值为“-28, -72, -83” 对应的int值就为“228,184,173”;
Writer类中常用方法:
-
append(char c) ——将指定字符添加到此writer的写出流中
可以是一个字符,也可以追加一个字符串。或者指定范围的字符串
-
public Writer append(CharSequence csq, int start, int end)
指定的字符序列,参2:开始位置,参3:结束位置,左闭右开区间。
-
close() ——关闭流(默认有个刷新操作)
-
flush()——刷新流。将数据刷新出去。
-
write(char [] ch)——写出字符数组。
-
write(int c)——写出单个字符
-
write(String str)——写出指定字符串
-
write(String str,int 起始位置,int 长度)——同上。
文件节点流:FileOutputStream和FileWriter
FileOutputStream 和 FileWriter,它们都是节点流,直接和指定文件关联。
FileOutputStream 以字节形式输出。8位一次输出。
File file = new File("bb.txt"); //找到指定文件,没有则创建。但是如果父路径不完整,则报错
OutputStream os = null;
byte [] bytes = "aaaaaa".getBytes();
try {
os = new FileOutputStream(file,true);//建立流联系
os.write(228); //写入单个字节(int形式)
os.write(184); //写入单个字节(int形式)
os.write(173); //写入单个字节(int形式)
os.write(bytes); //一次写出所有数据;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
os.close(); //关闭流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
FileWriter
以字符形式输出到指定位置。16位一次输出。
File file = new File("bb.txt"); //找到指定文件,没有则创建。但是如果父路径不完整,则报错
Writer write =null; //创建对象
try {
write= new FileWriter(file);
write.write("你好啊");//写出到流中
write.append("ninini",0,1);//追加字符到流对象中
write.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
write.close(); //关闭流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
注意
关闭流 先打开的后关闭
缓冲处理类
第一类:缓冲类。用于提高效率
BufferedInputStream和 BufferedReader 对于已存在的流进行封装(包装),加强其性能,效率。
字节流直接套上即可。
字符流要是要到新增的方法所以不能使用多态
BufferedInputStream
在读取操作的时候,其他模块并没有改变只是在字节流外套了层BufferedStream();需要传入一个
字节流作为参数。
InputStream is =new BufferedInputStream(new FileInputStream(src));
读取操作同上。
BufferedReader
因为BufferedReader中新增了一个readLine()方法,可以让我们读取一行。
因此使用时候不能用到多态
BufferedReader read= new BufferedReader(new FileReader(file));
read.readLine() //返回一行的字符串。
其他同上,参考FileReader;
BufferedOutputStream
同BufferedInputSream.只是嵌套在字节流外。写出方式都相同
BufferedWriter
同BufferedReader,有个新增方法,newLine();默认打一个换行符。\n;
第二类:处理类用于处理字节流转字符流乱码问题
InputStreamReader
普通处理的时候,如果读取的字符是gbk,而程序用的是utf时候,就必须指定字符集。并且需要最后在
new String的地方指定字符长度和字符集。特别麻烦。
这时候使用处理流。InputStreamReader就非常方便——存储的是哪种字符集就必须用那种字符集解析,不然出错。
char[] cs = new char[1024];
Reader isr = new InputStreamReader(new FileInputStream(file),"gbk"); //指定读取的字符集。
int len = isr.read(cs); //读取内容到char数组中
System.out.println(new String(cs,0,len));
OutputWriter也类似。可以指定字符集写出去。
数据处理流
可以处理基本类型+String,保留数据的类型。前提是读取顺序与写出顺序一致,否则读取数据不正确。(必须保证顺序完全一致,因为写出的是基本类型,所以我们直接打开看不懂,因为直接打开是以字符串形式显示。而我们除了程序之外写入的方式,都是以字符串形式写入的)。
数据流一样要以字节流为媒介,在字节流上包装成基本数据类型或String类型。这里没有字符流,因为字符流只能处理字符串。
DataOutputStream
DataOutputStream dos = null;
try{
dos = new DataOutputStream( //外层是数据流
new BufferedOutputStream( //套一层处理流,更加效率
new FileOutputStream) //底层还是File字节流,最基本。并且都要用到。
);
dos.writeInt(-1); //写int类型的数据出去。其他类型 类似。
dos.flush();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
dos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
DataInputStream
readUTF() 方法用来读取字符串
DataInputStream dis = null;
try{
dos = new DataInputStream ( //外层是数据流
new BufferedInputStream( //套一层处理流,更加效率
new FileInputStream) //底层还是File字节流,最基本。并且都要用到。
);
int i = dis.readInt(); //写int类型的数据出去。其他类型 类似。
System.out.println(i+1); //用来证明读到的是基本类型。
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
dis.close(); //关闭流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
必须按照类型。writeInt 就必须用readInt 来读。
对象流
作为java中最最常见的类型,有时候我们也想存储到外部设备。这个时候就必须用到了对象流
序列化:是一个用于将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序
反序列化:从字节流创建对象的相反的过程称为反序列化
对象处理流(反序列化):ObjectInputStream
ObjectInputStream能够让你从输入流中读取Java对象,而不需要每次读取一个字节。你可以把InputStream包装到ObjectInputStream中,然后就可以从中读取对象了。
条件
- 放置到io中的对象必须实现Serializable 接口。此接口为序列化接口(标记接口)没有任何实现方法
File src=new File(srcPath);
ObjectInputStream dis=new ObjectInputStream(
new BufferedInputStream(
new FileInputStream(src)
)
Object obj=dis.readObject();//读取的顺序与写出的顺序一致 必须存在才能读取
//获取到object对象,如果知道其类型,可以强转。然后使用方法。
序列化ObjectOutputStream
要先序列化后反序列化
File file=new File(destPath); //选择流
OjbectOutputStream dos=new ObjectOutputStream(
new BufferedOutputStream( new FileOutputStream(file)
) );
Employee obj=new Employee (“xiaoming”,11);
dos.writeObject(obj);
dos.flush();
dos.close();
//写入对象的顺序必须一致。不然出错。
我们也可以使用一些工具类来帮助我们进行读写操作
如commons-io
以下是一些commons-io中一些常用的方法
readFileToString(File file):读取文件内容并返回一个String
writeStringToFile(File file,String content):将内容content写入到file中 copyDirectoryToDirectory(File srcDir,File destDir):文件夹复制
copyFile(File srcFile,File destFile):文件复制