Java的IO流是实现输入/输出流的基础,它可以方便的实现数据的输入/输出操作,在Java中把不同的输入输出源(键盘、文件、网络连接等)抽象描述为流,通过流的方式允许Java程序使用相同的方式来访问不同的输入/输出源。
一. 流的分类
- 输入流和输出流(根据内存判断)
- 字节流和字符流(8位字节和16位字符)
- 节点流和处理流(包装流)
InputStream/Reader (读取)
所有输入流的基类,前者是字节输入流,后者是字符输入流
OutputStream/Writer (写入)
所有输出流的基类,前者是字节输出流,后者是字符输出流
FileInputStream:从本地磁盘读取字节数据。
例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| //创建字节输入流,即要读取的文件 FileInputStream fis = new FileInputStream("D:\\学习\\Java\\b\\a.txt"); //创建一个长度为1024的“竹筒” byte[] bbuf = new byte[1024]; //用于保存实际读取的字节 int hasRead = 0; //循环读取数据 while((hasRead=fis.read(bbuf))>0){ //将字节数组转换为字符串输入 System.out.println(new String(bbuf,0,hasRead)); } //关闭文件输入流 fis.close();
|
FileOutputStream:将字节数据写到指定文件。如果文件存在则写到指定文件,如果目标文件不存在则自动创建该文件,如果目标文件所在目录也不存在,则报错
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| //用FileInputStream输入,FileOutputStream输出来实现文件复制 try (FileInputStream fis = new FileInputStream("D:\\学习\\Java\\b\\a.txt");//从读取的文件 FileOutputStream fos = new FileOutputStream("D:\\学习\\Java\\b\\c.txt");//创建要写入的文件 ){ byte[] buff = new byte[64]; int hasRead = 0; //循环读取数据 while((hasRead = fis.read(buff))>0){ //写入文件 fos.write(buff, 0, hasRead); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
|
三. FileReader和FileWriter(字符流)
FileReader:以字符为基本单读取文本文件
例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| //创建字符输入流,即要读取的文件 FileReader fr = new FileReader("D:\\学习\\Java\\b\\a.txt"); //创建一个长度为32的“竹筒” char[] cbuf = new char[32]; //用于保存实际读取的字符 int hasRead = 0; //循环读取数据 while((hasRead=fr.read(cbuf))>0){ //将字符数组转换为字符串输入 System.out.println(new String(cbuf,0,hasRead)); } //关闭文件输入流 fr.close();
|
FileWriter:将字符数据写入到文本文件
例:
1 2 3 4 5 6 7 8 9 10
| try (FileWriter fw = new FileWriter("D:\\学习\\Java\\b\\d.txt")) { fw.write("白日依山尽\r\n"); fw.write("黄河入海流\r\n"); fw.write("欲穷千里目\r\n"); fw.write("更上一层楼\r\n"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
|
四. PrintStream和PrintWriter(处理流)
PrintStream:打印类,只能封装OutputStream类型的字节输出流
例:
1 2 3 4 5 6 7 8
| try(FileOutputStream fos = new FileOutputStream("D:\\学习\\Java\\b\\e.txt"); //创建要写入的文件 PrintStream ps = new PrintStream(fos); //用PrintStream处理流来包装OutputStream ) { ps.print("成功"); //用PrintStream直接写入数据 } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }
|
PrintWriter:打印类,既可封装OutputStream类型的字节输出流,也能封装Writer类型的字符输出流
例:
1 2 3 4 5 6 7 8
| try (FileWriter fw = new FileWriter("D:\\学习\\Java\\b\\f.txt"); //创建写入的文件 PrintWriter pw = new PrintWriter(fw); //用PrintWriter处理流包装Writer ){ pw.print("f测试成功"); //直接写入数据 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
|
五. BufferedReader和BufferedWriter(字符流)
BufferedReader:是从字符输入流中读取文本,将多个字符存入缓存提供读取字符、数据或行的有效方法。
BufferedWriter:将字符输出流缓冲后写出。(缓冲区容量可以在构造方法中指定)
InputStreamReader:将字节输入流转换成字符输入流
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| try(//将system.in(字节)对象转换为reader(字符)对象 InputStreamReader reader = new InputStreamReader(System.in); //将普通的reader包装成BufferedReader BufferedReader br = new BufferedReader(reader); ) { String line = null; while((line = br.readLine())!=null){ if(line.equals("exit")){ System.exit(1); } System.out.println("输入的内容为:"+ line); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }
|
OutputStreamWriter:将字节输出流转换成字符输出流
七. 重定向标准输入/输出
在默认情况下,当程序通过System.in来获取输入时,实际上是从键盘读取输入,当程序通过System.out执行输出时,程序总是输出到屏幕
例:重定向到指定文件输出
1 2 3 4 5 6 7 8 9
| //设置输出文件路径 try (PrintStream ps = new PrintStream(new FileOutputStream("D:\\学习\\Java\\b\\g.txt"));){ //将标准输出重定向到ps输出流 System.setOut(ps); System.out.println("g测试成功"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
|
例:重定向到指定文件输入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| //设置要读取的文件路径 try(FileInputStream fis = new FileInputStream("D:\\学习\\Java\\b\\g.txt")) { //将标准输入重定向到fis输入流 System.setIn(fis); Scanner sc = new Scanner(System.in); sc.useDelimiter("\n"); //将回车符作为分隔符 while(sc.hasNext()){ System.out.println("内容:"+sc.next()); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }
|
八. RandomAccessFile
既可以读取文件内容,也可以向文件输出内容
任意访问,可以跳转到文件的任意地方读写数据
只能读写文件,不能读写其他IO节点
例:读取文件指定位置后的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| try { //设定要读取的文件 RandomAccessFile raf = new RandomAccessFile("D:\\学习\\Java\\b\\g.txt","r"); raf.seek(3); //从3字节处开始读取 byte[] buff = new byte[1024]; int hasRead = 0; //循环读取 while((hasRead = raf.read(buff))>0){ System.out.print(new String(buff,0,hasRead)); } raf.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
|
例:向指定文件后面追加内容
1 2 3 4 5 6 7 8 9
| //设定要写入的文件 try (RandomAccessFile raf = new RandomAccessFile("D:\\学习\\Java\\b\\g.txt","rw")){ //指针跳转到当前文件结尾处开始写入 raf.seek(raf.length()); raf.write("追加内容\r\n".getBytes()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
|
例:向文件指定位置插入插入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| //使用临时文件来保存插入点后的数据 File temp = File.createTempFile("temp", null); temp.deleteOnExit(); try (RandomAccessFile raf = new RandomAccessFile(fileName,"rw"); //设置项指定文件执行读写操作 FileOutputStream tempOut = new FileOutputStream(temp); //向指定文件执行写入操作 FileInputStream tempIn = new FileInputStream(temp); //向指定文件执行读取操作 ) { raf.seek(pos); //跳转到文件指定点 /*将指定点后的数据保存到临时文件中*/ byte[] buff = new byte[32]; int hasRead = 0; while((hasRead = raf.read(buff))>0){ tempOut.write(buff,0,hasRead); //从raf中读取数据到tempOut中去 } raf.seek(pos); //重新的跳转到文件指定点 raf.write(insertContent.getBytes()); //将数据写入到指定点后面 //从临时文件tempIn中读取数据写入到raf文件中 while((hasRead = tempIn.read(buff))>0){ raf.write(buff,0,hasRead); } } } public static void main(String[] args) throws IOException{ insert("D:\\学习\\Java\\b\\g.txt",3,"中间插入"); }
|
九. 对象序列化
对象序列化指将一个Java对象写入IO流中。对象的反序列化则指从IO流中恢复该Java对象。
序列化机制会把内存中的Java对象转换成与平台无关的二进制流,从而永久地保存在磁盘上或是通过网络传输到另一个网络节点。
为了使某个类是可序列化的,必须实现Serializable和Externalizable接口
序列化对象
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| //建立一个普通的Java类Person实现了Serializable接口 public class Person implements java.io.Serializable { private String name; private int age; public Person(String name,int age){ System.out.println("有参构造函数"); this.name = name; this.age = age; } 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 static void main(String[] args){ //创建一个ObjectOutputStream输出流 try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\学习\\Java\\b\\h.txt"));){ Person per = new Person("孙悟空",500); oos.writeObject(per); //将per对象写入输出流 } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
|
反序列化对象
例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static void main(String[] args) throws ClassNotFoundException{ //创建一个ObjectInputStream输入流 try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\学习\\Java\\b\\h.txt"));){ Person per = (Person)ois.readObject(); //从输入流读取一个java对象,并强制转换为Person类型 System.out.println("名字:"+per.getName()+"\n年龄:"+per.getAge()); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
|
序列化机制算法
1.所有保存到磁盘中的对象都有一个序列化编号。
2.当程序试图序列化一个对象时,程序先检查该对象是否已经被序列化过。如果从未被序列化过,系统就会将该对象转换成字节序列并输出。如果已经序列化过,将直接输出一个序列化编号。
序列化对象注意事项
1.对象的类名、属性都会被序列化;而方法、static属性(静态属性)、transient属性(即瞬态属性)都不会被序列化(这也就是第4条注意事项的原因)
2.虽然加static也能让某个属性不被序列化,但static不是这么用的
3.要序列化的对象的引用属性也必须是可序列化的,否则该对象不可序列化,除非以transient关键字修饰该属性使其不用序列化。
4.反序列化地象时必须有序列化对象生成的class文件(很多没有被序列化的数据需要从class文件获取)
5.当通过文件、网络来读取序列化后的对象时,必须按实际的写入顺序读取。