java从零开始系列(六)IO处理流

luoyjx · 2014-10-07 10:40 · 1083次阅读

1 处理流

java中的处理流是相对于java中的节点流来说的。

打个简单的比方,一个放水的例子:

一个水龙头就是一个数据节点,水就是数据,而这个数据在从水龙头里放出之后,可能有两个去处,

    1.直接被放出来放到了某个需要水的地方

    2.先用水桶接上一桶之后再拿去需要水的地方

而在第二点里面的这个水桶就相当于处理流的角色,他可能接收了数据,但是不会马上写出去,而是先在这里分配了一段内存,先缓存一部分数据,等数据量到了一定的时候,再拿来写出去。

处理流类型:包在别的流上的流

缓冲流

缓冲流“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法。
JDK提供了四种缓冲流,其常用的构造方法为:

  • BufferedInputStream(InputStream in) 
  • BufferedInputStream(InputStream in, int size) 
  • BufferedOutputStream(OutputStream out) 
  • BufferedOutputStream(OutputStream out, int size) 
  • BufferedReader(Reader in) 
  • BufferedReader(Reader in, int sz) // sz 为自定义缓冲区的大小
  • BufferedWriter(Writer out) 
  • BufferedWriter(Writer out, int sz) 

缓冲输入流支持其父类的mark() 和reset() 方法。
BufferedReader 提供了 readLine() 方法用于读取一行字符串。
BufferedWriter 提供了 newLine() 用于写入一个行分隔符。
对于输出的缓冲流,写出的数据会先在内存中缓存,使用 flush() 方法将会使内存中的数据立刻写入磁盘。

BufferedInputStream

import java.io.*;
public class BufferStream {
    public static void main(String[] args) {
        try {
	     FileInputStream in = new FileInputStream("D:\\workspaceUML\\FileInput.java");
	     BufferedInputStream bis = new BufferedInputStream(in);
	     System.out.println(bis.read());
	     System.out.println(bis.read());
    	     bis.mark(100);
	     int b = 0;
	     for (int i = 0; i <= 10 && (b = bis.read()) != -1; i++) {
		System.out.print(b + " ");
	     }
	     System.out.println();
	     bis.reset();
	     for (int i = 0; i <= 10 && (b = bis.read()) != -1; i++) {
		System.out.print(b + " ");
	     }
	     bis.close();
	     in.close();
  	} catch (FileNotFoundException ex) {
	} catch (IllegalArgumentException ex) {
	} catch (IOException ex) {
	}
    }
}

BufferedReader     BufferedWriter 

import java.io.*;
public class BufferedReaderWriter {
	public static void main(String[] args) {
	      try {
		BufferedReader in = new BufferedReader(
			          new FileReader("D:\\workspaceUML\\FileInput.java"));
		BufferedWriter out = new BufferedWriter(
			          new FileWriter("D:\\workspaceUML\\FileInfor.java"));	
		String v = null;
		while ((v = in.readLine()) != null) {
			out.write(v);
			out.newLine();
		}
		in.close();
		out.flush();
		out.close();			
	       } catch (IOException ex) {
	       }
	}
}

转换流(字节与字符相互转换)

InputStreamReader 和 OutputStreamWriter 用于将字节数据转换为字符数据
InputStreamReader 需要和 InputStream “套接”
OutputStreamWriter 需要和 OutputStream “套接”
转换流在构造时可以指定其编码,例如:
InputStream ist = new InputStreamReader(System.in, “ISO-8859-1”);

InputStreamReader

import java.io.*;
class TestKI {
	public static void main(String[] args)  {
		//进行字符串的包装,就可以读取一行字符串
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		System.out.println("按 ctrl+c 键或者输入exit 退出程序!");
		try {
			String s = br.readLine( );
			while( s != null && !s.equals("exit") ) {
				System.out.println("Read: " + s);
				s = br.readLine();
			}
			br.close();
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
} 

OutputStreamWriter

import java.io.*;
public class TransFormIn {
	public static void main(String[] args) {
	     try {
		OutputStreamWriter osw = new OutputStreamWriter(
			new FileOutputStream(
			"D:\\workspaceUML\\FileInfor.txt"));
		osw.write("地球人都知道");
		System.out.println(osw.getEncoding());
		osw.close();

		osw = new OutputStreamWriter(
			new FileOutputStream(
			"D:\\workspaceUML\\FileInfor.txt", true),
			"ISO-8859-1");
		osw.write("地球人都知道");
		System.out.println(osw.getEncoding());
		osw.close();
	     } catch (Exception ex) {
	     }
	}
}

数据操作流

解决问题:把一个long 类型的数写入文件里?
1、转成字符串来写,浪费空间、性能低下。
2、用数据流来写
DataInputStream和DataOutputStream属于处理流,需要分别“套接”在InputStream 和 OutputStream 类型的节点流上。
DataInputStream 和 DataOutputStream 提供了可以存取与机器无关的Java基本类型数据(如:int, double, float 等)的方法。
构造函数:

  • DataInputStream(InputStream in) 
  • DataOutputStream(OutputStream out) 

打印流- Print

在整个IO包中,打印流是输出信息最方便的类,主要包括字节打印流(PrintStream)和字符打印就(PrintWriter)。打印流提供了非常方便的打印功能,可以打印任何的数据类型。如:小数、整数、字符串等。

  • PrintStream 和 PrintWriter都属于输出流,分别针对写字节和字符
  • PrintStream 和 PrintWriter提供了重载的print()、println() 方法用于多种数据类型的输出。
  • PrintStream 和 PrintWriter的输出操作不会抛出异常,用户通过检测错误状态获取错误信息。
  • PrintStream 和 PrintWriter有自动flush 功能。

常用构造函数:

  • PrintStream(OutputStream out) 
  • PrintStream(OutputStream out, boolean autoFlush) PrintWriter(OutputStream out) 
  • PrintWriter(OutputStream out, boolean autoFlush) 
  • PrintWriter(Writer out) 
  • PrintWriter(Writer out, boolean autoFlush) 

输入、输出重定向 

import java.io.*;
public class PrintTest {
	public static void main(String[] args) {
	      try {
		PrintStream ps = new PrintStream(
			new FileOutputStream("D:\\workspaceUML\\FileInfor.java"));
		if (ps != null) {
		     System.setOut(ps);
	            }

		for (int i = 0; i <= 600; i++) {
		     System.out.print(i + " ");
		     if (i % 20 == 0) {
		         System.out.println();
		     }
		}
		ps.close();
  	       } catch (IOException ex) {
	       }
	}
}

对象序列化 

什么叫对象序列化
一个对象产生之后实际上是在内存中为其开辟了一个存储空间,方便存储信息。 
对象序列化就是把一个对象变为二进制的数据流的一种方法,通过对象序列化可以方便的实现对象的传输或存储。 

如果一个类的对象想被序列化,则对象所在的类必须实现 java.io.Serializable 接口。此接口定义:public interface Serializable{}。此接口中没有任何一个方法,此接口属于一个标识接口,标识具备了某种能力。 
通过ObjectOutputStream把对象序列化。变为二进制 byte 流。

对象的序列化和反序列化: 
要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象输入流
(ObjectInputStream) 
使用对象输出流输出序列化对象的步骤,有时也称为序列化。而使用对象输入流读入对象的过程,有时也称为反序列化 

serialVersionUID 常量 
在对象进行序列化或反序列化操作的时候,要考虑 JDK 版本的问题。如果序列化的 JDK 版本和反序列化的 JDK 版本不统一,则可能造成异常。因此在序列化操作中引入了一个serialVersionUID 的常量来验证版本的一致性。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体(类)的serialVersionUID 进行比较。如果相同就认为是一致的,可以进行反序列化,否则就会出现反序列化版本不一致的异常。 

序列化:ObjectOutputStream

所有的对象拥有各自的属性值,但是所有的方法都是公共的,所以序列化对象的时候实际上序列化的就是属性,静态属性不能序列化。  

transient 关键字
当使用Serializable 接口实现序列化操作时,如果一个对象中的某个属性不希望被序列化的话,则可以使用 transient 关键字进行声明。  

反序列化:ObjectInputStream
使用ObjectInputStream 可以直接把被序列化的对象反序列化回来。 

Externalizable接口 
使用Serilizable 接口可以方便的序列化一个对象,但是在序列化操作中也提供了另外一种序列化机制——Externalizable 接口。 被Serializable 接口声明的类其对象的内容都将被序列化。如果用户希望可以自己指定序列化的内容,则可以让一个类实现 Externalizable接口。此接口定义如下: 

public interface Externalizable extends Serializable{ 
  void writeExternal(ObjectOutput out) throws IOException 
  void readExternal(ObjectInput in) throws IOException,ClassNotFoundException 
} 

注意:在使用Externalizable 接口的时候需要在被序列化的类中定义一个无参构造。因为此接口在进行反序列化的时候,会先使用类中的无参构造方法为其进行实例化,之后再将内容分别设置到属性之中。
另外需要注意的是,当反序列化的时候,读取的顺序必须按序列化顺序完全相同。

import java.io.Externalizable ; 
import java.io.* ; 
public class Person implements Externalizable{ 
  private static final long serialVersionUID = 1L; 
  private String name ;  //  声明name 属性,但是此属性不被序列化 
  private int age ;    //  声明age属性 
 public Person(){} 

  public Person(String name,int age){  //  通过构造设置内容 
    this.name = name ; 
    this.age = age ; 
  } 
  public String toString(){  //  覆写toString()方法 
    return "姓名:" + this.name + ";年龄:" + this.age ; 
  } 
  public void writeExternal(ObjectOutput out) throws IOException {   
    out.writeObject(this.name) ;    //保存姓名属性 
    out.writeInt(this.age) ;        //保存年龄属性 
  } 
  public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {   
    this.name=(String)in.readObject() ;      //读取姓名属性 
    this.age=in.readInt() ;        //读取年龄属性 
 } 
}

Externaliable、Serializable

Externaliable继承Serializable接口,所以使用Externalialbe也可以达到间接使用Serializable的目的。

但是Serializable和Externaliable还是有所区别的,具体体现在:

  • Externalizable具有两个自己的扩展的方法:writeExternal()和readExteranl()。这两个方法的作用是对象序列化和反序化。
  • 当类是用Externalizable接口的时候,当调用ObjectOutputStream类的writeObject()方法的时候,会用writeExternal()方法。同理,ObjectInputStream类的readObject()会调用readExternal()方法。
  • 否则,如果类实现serializable接口,则上面的ObjectOutputStream和ObjectInputeStream调用writeObject和readObject的时候会调用默认方法。
  • Externalizable的好处。正因为Externalizable接口的writeExternal()方法和readExternal()方法的存在,使得在序列化的时候又更多的灵活性,通过对这两个方法的重写获得对象有选择的序列化。

对象的类名、属性被序列化,但是静态属性、方法、 transient属性不被序列化
当通过文件、网络读取序列化后的对象,必须按照实际写入的先后顺序读取
Serializable  所有对象都保持、性能低,Externaliable由开发人员决定保存那个对象,性能高

序列化多个对象 

  1. 多次使用ObjectOutputStream 序列化对象,ObjectInputStream 反序列化对象里的方法。
  2. 采用数组、集合实现。
收藏

暂无评论

登录后可以进行评论。没有账号?马上注册