一.Java中流的概念细分
1.按流的方向分(容易混淆)
输入流: 数据流向是数据源到程序,以InputStream,Reader结尾的流
输出流: 数据流向是程序到目的地,以OutputStream,Writer结尾的流
2.按处理的数据单元分类
字节流:通常以Stream结尾,以字节为单位获取数据
字符流:通常以Reader/Writer结尾,以字符为单位获取数据
3.按处理对象不同分类
节点流:直接进行文件的读写,系统输入输出(System.in与System.out)都为字节流。
包装流:通过对其他的流进行处理来提高性能
二.Java中的IO流体系
三.常用的流详解
1.文件字节流
FileOutputStream和FileInputStream用来读取文件,适合于所有类型.
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import org.junit.Test; /** * 利用文件流,实现文件的复制 * @author MOTI * */ public class Demo_3 { @Test public void demo3() { copyFile("demo1.txt", "demo3.txt"); } public static void copyFile(String src,String dec) { FileInputStream is = null; FileOutputStream os = null; //为了提高效率,设置一个缓冲数组,读取的文件会暂时放在缓冲数组中 byte[] bytes = new byte[1024]; int len = 0;//每次读取的真实长度,等于-1代表读取完成 try { is = new FileInputStream(src); os = new FileOutputStream(dec); try { //边读边写 while((len = is.read(bytes)) != -1) { //将缓冲区的数据写入到文件,注意这里写入的是真实长度,如果使用os.write(bytes)方法,那么写入的就是整个缓冲区的长度为1024 os.write(bytes, 0, len); } System.out.println("文件复制成功!"); } catch (IOException e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); } } }
注意:
1.为了减少对硬盘的读写次数,提高效率,通常设置缓存数组.相应的读取时使用read(byte[] b),写入的时候使用write(byte[] b,int off, int length)
2.程序中如果出现多个流,每个流都需要单独关闭,并且关闭顺序要与创建流的顺序相反
2.文件字符流
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import org.junit.Test; /** * 使用FileReader和FileWriter进行文本的复制 * @author MOTI */ public class Demo_4 { @Test public void demo4(){ copyFile("demo1.txt", "demo4.txt"); } public static void copyFile(String src,String dec){ FileReader fr = null; FileWriter fw = null; int len = 0; //为了提高效率,缓冲区用的也是字符数组 char[] chars = new char[1024]; try { fr = new FileReader(new File(src)); fw = new FileWriter(new File(dec)); while((len = fr.read(chars)) != -1){ fw.write(chars, 0, len); } System.out.println("文本复制成功!"); } catch (Exception e) { e.printStackTrace(); }finally{ if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } if(fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
3.缓冲字节流
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import org.junit.Test; /** * 使用缓冲字节流实现文件的高效复制 * @author MOTI * */ public class Demo_5 { @Test public void demo5(){ copyFile("demo1.txt", "demo5.txt"); } public static void copyFile(String src,String dec){ FileInputStream is = null; FileOutputStream os = null; BufferedInputStream bis = null; BufferedOutputStream bos = null; int len = 0; try { is = new FileInputStream(src); os = new FileOutputStream(dec); /* 使用缓冲字节流包装文件字节流,增加缓冲功能,提高效率 缓冲区的大小,默认8192,也可以自定义 */ bis = new BufferedInputStream(is); bos = new BufferedOutputStream(os); while((len = bis.read()) != -1){ bos.write(len); } System.out.println("文件复制成功!"); } catch (Exception e) { e.printStackTrace(); }finally{ if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } if(os != null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
注意:关流的顺序"后开启的先关闭"
4.缓冲字符流
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import org.junit.Test; /** * 使用缓冲字符流,实现文本的复制 * @author MOTI * BufferedReader类提供了更为方便的readline(),直接按行读取 */ public class Demo_6 { @Test public void demo6(){ copyFile("demo1.txt", "demo6.txt"); } public static void copyFile(String src, String dec){ FileReader fr = null; FileWriter fw = null; BufferedReader br = null; BufferedWriter bw = null; String tempString = ""; try { fr = new FileReader(src); fw = new FileWriter(dec); /* 使用缓冲字符流包装 */ br = new BufferedReader(fr); bw = new BufferedWriter(fw); while((tempString = br.readLine()) != null){ //将读到的一行字符串写入文件 bw.write(tempString); //下一次写入之前先换行,不然就在第一行一直追加 bw.newLine(); } System.out.println("文本复制成功!"); } catch (Exception e) { e.printStackTrace(); }finally{ if(bw != null){ try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(br != null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } if(fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
注意:
1.readline()方法是BufferedReader特有的方法
2.写入一行后要记得使用newLine()方法换行
5.数据流
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import org.junit.Test; /** * * @author MOTI * DataInputStream * DataOutputStream */ public class Demo_8 { @Test public void demo8(){ DataInputStream dis = null; DataOutputStream dos = null; FileInputStream fis = null; FileOutputStream fos = null; try { fos = new FileOutputStream("demo8.txt"); fis = new FileInputStream("demo8.txt"); //使用数据流对缓冲流进行包装,增加缓冲功能 dis = new DataInputStream(new BufferedInputStream(fis)); dos = new DataOutputStream(new BufferedOutputStream(fos)); //将下列数据写入到文件 dos.writeInt(520); dos.writeUTF("哈哈哈哈,我是莫提!"); dos.writeChar('H'); //手动刷新缓冲区 dos.flush(); //直接读取数据,读取的顺序要与写入的顺序一致,不然会读取到错误数据 System.out.println(dis.readInt()); System.out.println(dis.readUTF()); System.out.println(dis.readChar()); } catch (Exception e) { e.printStackTrace(); }finally{ if(dos != null){ try { dos.close(); } catch (IOException e) { e.printStackTrace(); } } if(dis != null){ try { dis.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
注意:使用数据流时,读取顺序一定要与写入顺序一致,否则不能正确的读取数据.
6.对象流
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.Date; import org.junit.Test; /** * 对象流:数据源是一个对象,要求该对象的必须要进行序列化和反序列化操作 * 对象流对象也可以对基本数据类型进行读写操作 * @author MOTI * */ public class Dmeo_10 { @Test public void demo10() { write(); read(); } /** * 使用对象输出流将数据写入文件 */ public static void write() { //创建一个文件输出流,并包装缓冲流,增加缓冲功能 OutputStream os = null; BufferedOutputStream bos = null; ObjectOutputStream oos = null; try { os = new FileOutputStream(new File("demo10.txt")); bos = new BufferedOutputStream(os); oos = new ObjectOutputStream(bos); //使用Object输出流 oos.writeInt(520); oos.writeDouble(2.50); oos.writeUTF("我是莫提!"); //对象流对象可以对对象类型进行读写操作,但是要求必须实现序列化的接口(这里以Date对象为例) oos.writeObject(new Date()); System.out.println("写入成功!"); } catch (Exception e) { e.printStackTrace(); }finally { if(oos != null) { try { oos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(bos != null) { try { bos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(os != null) { try { os.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * 使用对象输入流将数据读入程序 */ public static void read() { //创建一个文件输入流,并包装缓冲流,增加缓冲功能 InputStream is = null; BufferedInputStream bis = null; ObjectInputStream ois = null; try { is = new FileInputStream(new File("demo10.txt")); bis = new BufferedInputStream(is); ois = new ObjectInputStream(bis); //使用Object输入流按照顺序读取 System.out.println(ois.readInt()); System.out.println(ois.readDouble()); System.out.println(ois.readUTF()); System.out.println(ois.readObject().toString()); } catch (Exception e) { e.printStackTrace(); }finally { if(ois != null) { try { ois.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(bis != null) { try { bis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(is != null) { try { is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
注意:
1.对象流不尽可以读写对象,还可以读写基本数据类型
2.使用对象流时,该对象必须经过序列化和反序列化
3.系统提供的类(如:Date)已经实现了序列化接口,自定义类必须手动实现
7.转换流
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import org.junit.Test; /** * 转换流:将字节流转换为字符流 * @author MOTI * */ public class Demo_9 { public static void main(String[] args) { //创建字符输入和输出流:使用转换流将字节流转换为字符流 BufferedReader br = null; BufferedWriter bw = null; br = new BufferedReader(new InputStreamReader(System.in)); bw = new BufferedWriter(new OutputStreamWriter(System.out)); try { String str = br.readLine(); while(!"exit".equals(str)){ bw.write(str); bw.newLine(); bw.flush(); str = br.readLine(); } } catch (IOException e) { e.printStackTrace(); }finally{ if(bw != null){ try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(br != null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
8.随意访问文件流
import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Iterator; /** * 随意访问文件流:实现对一个文件的读和写,可以访问文件的任意位置 * @author MOTI * */ public class Demo_11 { public static void main(String[] args) { RandomAccessFile raf = null; int[] data = {10,20,30,40,50,60,70,80,90,100}; try { raf = new RandomAccessFile("demo11.txt", "rw"); for (int i : data) { raf.writeInt(i); } //直接读取文件,位置从第36个字节开始 raf.seek(4); System.out.println(raf.readInt());//读取4个字节(int为4个字节) //直接读取数据,隔一个读取一个 for (int i = 0; i < data.length; i += 2) { raf.seek(i * 4); System.out.print(raf.readInt()+"\t"); } System.out.println(); //在第8字节处,插入新数据,替换原来的30 raf.seek(8); raf.writeInt(520); //遍历查看结果 for (int i = 0; i < data.length; i ++) { raf.seek(i * 4); System.out.print(raf.readInt()+"\t"); } } catch (Exception e) { e.printStackTrace(); } } }
注意学习这个流需要掌握三个核心方法:
1.RandomAccessFile(String name, String mode);
name 用于确定文件,mode去r(读),或rw(读写),以此来确定对流的访问权限
2.seek(long a);
用来定位流对象读写文件的位置,a确定读写位置距离文件开头的字节数.
3.getFilePointer();
用来获得流当前的读写位置
四.Java对象的序列化和反序列化
1.序列化和反序列化的概念
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
2.JDK类库中的序列化API
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
对象序列化包括如下步骤:
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2) 通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下:
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2) 通过对象输入流的readObject()方法读取对象
准备一个Student类,并让该类实现Serializable接口
import java.io.Serializable; public class Student implements Serializable { // 添加序列化ID,他决定了是否能够反序列化成功 private static final long serialVersionUID = 1L; private String name; private int age; boolean isMan; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean isMan() { return isMan; } public void setMan(boolean isMan) { this.isMan = isMan; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", isMan=" + isMan + "]"; } public Student() { super(); // TODO Auto-generated constructor stub } public Student(String name, int age, boolean isMan) { super(); this.name = name; this.age = age; this.isMan = isMan; } }
开始将Student类的对象序列化和反序列化
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * java 对象的序列化和反序列化 * @author MOTI * */ public class SerializableTest { public static void main(String[] args) { FileInputStream fis = null; FileOutputStream fos = null; ObjectInputStream ois = null; ObjectOutputStream oos = null; Student student = new Student("莫提", 20, true); Student student1 = new Student("Moti", 20, true); //通过ObjectOutputStream将Student对象写到文件中,即为序列化 try { fos = new FileOutputStream("serializabletest.txt"); oos = new ObjectOutputStream(fos); oos.writeObject(student); oos.writeObject(student1); System.out.println("序列化成功!"); //************************************************** //通过ObjectInputStream将文件中的数据变回原来的对象 fis = new FileInputStream("serializabletest.txt"); ois = new ObjectInputStream(fis); Student s = (Student) ois.readObject(); Student s1 = (Student) ois.readObject(); System.out.println(s); System.out.println(s1); } catch (Exception e) { e.printStackTrace(); }finally { if(oos != null) { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } if(ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }