码迷,mamicode.com
首页 > 其他好文 > 详细

IO流(四):其他流

时间:2015-05-27 22:51:26      阅读:356      评论:0      收藏:0      [点我收藏+]

标签:properties   objectinputstream   sequenceinputstream   printwriter   datainputstream   

一、操作基本数据类型的流
(一)构造方法
1、数据输入流:DataInputStream(InputStream in) 
2、数据输出流:DataOutputStream(OutputStream out) 

(二)方法
1、DataOutputStream:
  • writeByte(10);
  • writeShort(100);
  • writeInt(1000);
  • writeLong(10000);
  • writeFloat(12.34F);
  • writeDouble(12.56);
  • writeChar(‘a‘);
  • writeBoolean(true);
2、DataInputStream:
  • readByte();
  • readShort();
  • readInt();
  • readLong();
  • readFloat();
  • readDouble();
  • readChar();
  • readBoolean();
<span style="font-family:Arial;font-size:18px;">private static void read() throws IOException {
		// DataInputStream(InputStream in)
		// 创建数据输入流对象
		DataInputStream dis = new DataInputStream(
				new FileInputStream("dos.txt"));

		// 读数据
		byte b = dis.readByte();
		short s = dis.readShort();
		int i = dis.readInt();
		long l = dis.readLong();
		float f = dis.readFloat();
		double d = dis.readDouble();
		char c = dis.readChar();
		boolean bb = dis.readBoolean();

		// 释放资源
		dis.close();

		System.out.println(b);
		System.out.println(s);
		System.out.println(i);
		System.out.println(l);
		System.out.println(f);
		System.out.println(d);
		System.out.println(c);
		System.out.println(bb);
	}

	private static void write() throws IOException {
		// DataOutputStream(OutputStream out)
		// 创建数据输出流对象
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("dos.txt"));

		// 写数据了
		dos.writeByte(10);
		dos.writeShort(100);
		dos.writeInt(1000);
		dos.writeLong(10000);
		dos.writeFloat(12.34F);
		dos.writeDouble(12.56);
		dos.writeChar('a');
		dos.writeBoolean(true);

		// 释放资源
		dos.close();
	}</span>

二、内存操作流
(一)概述
1、用于处理临时存储信息的,程序结束,数据就从内存中消失。
2、流对象:
1)字节数组:
  • ByteArrayInputStream 
  • ByteArrayOutputStream 
2)字符数组: 
  • CharArrayReader 
  • CharArrayWriter 
3)字符串:
  • StringReader 
  • StringWriter 
(二)功能:
1、特点:
1)可以操作字节、字符和字符串。
2)存放在内存,所以流不需要close() 关闭。

2、字节数组
1)ByteArrayOutputStream 
  • 此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。
  • 可使用 toByteArray() 和 toString() 获取数据。
  • writeTo(OutputStream out)
    • 将此 byte 数组输出流的全部内容写入到指定的输出流参数中,
    • 这与使用 out.write(buf, 0, count) 调用该输出流的 write 方法效果一样。
2)ByteArrayInputStream 

<span style="font-family:Arial;font-size:18px;">// 写数据
		// ByteArrayOutputStream()
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		// 写数据
		for (int x = 0; x < 10; x++) {
			baos.write(("hello" + x).getBytes());
		}

		// 释放资源
		// 通过查看源码我们知道这里什么都没做,所以根本需要close()
		// baos.close();

		// public byte[] toByteArray()
		// 获取数据
		byte[] bys = baos.toByteArray();

		// 读数据
		// ByteArrayInputStream(byte[] buf)
		ByteArrayInputStream bais = new ByteArrayInputStream(bys);

		int by = 0;
		while ((by = bais.read()) != -1) {
			System.out.print((char) by);
		}

		// bais.close();</span>

三、打印流
(一)概述
1、分类:
1)字节流打印流: PrintStream
2)字符打印流: PrintWriter

2、特点
1)只有写数据的,没有读取数据。只能操作目的地,不能操作数据源。
2)可以操作任意类型的数据。
3)如果启动了自动刷新,能够自动刷新。
4)该流是可以直接操作文本文件的。

3、哪些流对象是可以直接操作文本文件的呢?
  • FileInputStream 
  • FileOutputStream 
  • FileReader 
  • FileWriter 
  • PrintStream 
  • PrintWriter 
1)看API,查流对象的构造方法,如果同时有File类型和String类型的参数,一般来说就是可以直接操作文件的。
2)流的分类: 
  • 基本流:就是能够直接读写文件的 
  • 高级流:在基本流基础上提供了一些其他的功能 
(二)功能
1、可以操作任意类型的数据。
  • print() 
  • println() 
2、启动自动刷新
  • PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true); 
    • 还是应该调用println()的方法才可以 
    • 这个时候不仅仅自动刷新了,还实现了数据的换行。
    • println() 其实等价于于三个方法连用: 
      • bw.write(); 
      • bw.newLine();
      • bw.flush(); 
<span style="font-family:Arial;font-size:18px;">	/*
	 * 1:可以操作任意类型的数据。
	 * 		print()
	 * 		println()
	 * 2:启动自动刷新
	 * 		PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
	 * 		还是应该调用println()的方法才可以
	 * 		这个时候不仅仅自动刷新了,还实现了数据的换行。
	 * 
	 * 		println()
	 *		其实等价于于:
	 *		bw.write();
	 *		bw.newLine();		
	 *		bw.flush();
	 */
	public void test2() throws IOException{
		// 创建打印流对象
		// PrintWriter pw = new PrintWriter("pw2.txt");
//		PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
		PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("c:\\2") , "utf-8"), true);

		// write()是搞不定的,怎么办呢?
		// 我们就应该看看它的新方法
		// pw.print(true);
		// pw.print(100);
		// pw.print("hello");

		pw.println("hello");
		pw.println(true);
		pw.println(100);

		pw.close();
	}</span>


四、标准输入输出流
(一)概述
1、流对象
1)System.out:输出流
  • 本质是 PrintStream对象
2)System.in:输入流,是从键盘获取数据。
  • 本质是 InputStream 

2、键盘录入数据的方式:
1)main方法的args接收参数。
  • java HelloWorld hello world java 
2)Scanner(JDK5以后的)
  • Scanner sc = new Scanner(System.in); 
  • String s = sc.nextLine(); 
  • int x = sc.nextInt() 
3)通过字符缓冲流包装标准输入流实现
  • BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
(二)实例

<span style="font-family:Arial;font-size:18px;">		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		System.out.println("请输入一个字符串:");
		String line = br.readLine();
		System.out.println("你输入的字符串是:" + line);

		System.out.println("请输入一个整数:");
		// int i = Integer.parseInt(br.readLine());
		line = br.readLine();
		int i = Integer.parseInt(line);
		System.out.println("你输入的整数是:" + i);</span>


五、随机访问流
1、RandomAccessFile 类不属于流,是Object类的子类。
  • 但它融合了InputStream和OutputStream的功能。 
  • 支持对文件的随机访问读取和写入。 
2、构造方法:
  • public RandomAccessFile(String name,String mode)
    • 第一个参数是文件路径,
    • 第二个参数是操作文件的模式。
      • 模式有四种,我们最常用的一种叫"rw",这种方式表示既可以写数据,也可以读取数据  
3、随机访问流,有一个文件指针,读到哪,指针指到哪。
  • 该文件指针可以通过 getFilePointer方法读取
  • 通过 seek 方法设置,即可以跳跃阅读
4、方法
1)写:
  • writeInt(100);
  • writeChar(‘a‘);
  • writeUTF("中国");
2)读:
  • readInt()
  • readChar()
  • readUTF()
<span style="font-family:Arial;font-size:18px;">/*
 * 随机访问流:
 * 		RandomAccessFile类不属于流,是Object类的子类。
 * 		但它融合了InputStream和OutputStream的功能。
 * 		支持对文件的随机访问读取和写入。
 * 
 * public RandomAccessFile(String name,String mode):第一个参数是文件路径,第二个参数是操作文件的模式。
 * 		模式有四种,我们最常用的一种叫"rw",这种方式表示我既可以写数据,也可以读取数据 
 */
public class RandomAccessFileDemo {
	public static void main(String[] args) throws IOException {
		// write();
		read();
	}

	private static void read() throws IOException {
		// 创建随机访问流对象
		RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");

		int i = raf.readInt();
		System.out.println(i);
		// 读到哪,指针指到哪。
		// 该文件指针可以通过 getFilePointer方法读取,并通过 seek 方法设置。
		System.out.println("当前文件的指针位置是:" + raf.getFilePointer());

		char ch = raf.readChar();
		System.out.println(ch);
		System.out.println("当前文件的指针位置是:" + raf.getFilePointer());

		String s = raf.readUTF();
		System.out.println(s);
		System.out.println("当前文件的指针位置是:" + raf.getFilePointer());

		// 我不想重头开始了,我就要读取a,怎么办呢?
		raf.seek(4);
		ch = raf.readChar();
		System.out.println(ch);
	}

	private static void write() throws IOException {
		// 创建随机访问流对象
		RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");

		// 怎么玩呢?
		raf.writeInt(100);
		raf.writeChar('a');
		raf.writeUTF("中国");

		raf.close();
	}
}</span>


六、合并流
1、SequenceInputStream:包含多个输入流
1)SequenceInputStream(InputStream s1, InputStream s2)
2)SequenceInputStream(Enumeration e)

2、实例
<span style="font-family:Arial;font-size:18px;">	/*
	 * 以前的操作:
	 * a.txt -- b.txt
	 * c.txt -- d.txt
	 * 
	 * 现在想要:
	 * a.txt+b.txt -- c.txt
	 */
	public void test1() throws IOException {

		// SequenceInputStream(InputStream s1, InputStream s2)
		// 需求:把ByteArrayStreamDemo.java和DataStreamDemo.java的内容复制到Copy.java中
		InputStream s1 = new FileInputStream("ByteArrayStreamDemo.java");
		InputStream s2 = new FileInputStream("DataStreamDemo.java");
		SequenceInputStream sis = new SequenceInputStream(s1, s2);
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream("Copy.java"));

		// 如何写读写呢,其实很简单,你就按照以前怎么读写,现在还是怎么读写
		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = sis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}

		bos.close();
		sis.close();
	
	}
	
	/*
	 * 以前的操作:
	 * a.txt -- b.txt
	 * c.txt -- d.txt
	 * e.txt -- f.txt
	 * 
	 * 现在想要:
	 * a.txt+b.txt+c.txt -- d.txt
	 */
	public void test2() throws IOException{
		// 需求:把下面的三个文件的内容复制到Copy.java中
		// ByteArrayStreamDemo.java,CopyFileDemo.java,DataStreamDemo.java

		// SequenceInputStream(Enumeration e)
		// 通过简单的回顾我们知道了Enumeration是Vector中的一个方法的返回值类型。
		// Enumeration<E> elements()
		Vector<InputStream> v = new Vector<InputStream>();
		InputStream s1 = new FileInputStream("ByteArrayStreamDemo.java");
		InputStream s2 = new FileInputStream("CopyFileDemo.java");
		InputStream s3 = new FileInputStream("DataStreamDemo.java");
		v.add(s1);
		v.add(s2);
		v.add(s3);
		Enumeration<InputStream> en = v.elements();
		SequenceInputStream sis = new SequenceInputStream(en);
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream("Copy.java"));

		// 如何写读写呢,其实很简单,你就按照以前怎么读写,现在还是怎么读写
		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = sis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}

		bos.close();
		sis.close();
		
	}</span>


七、序列化流
(一)概述
1、概念:序列化数据其实就是把对象写到文本文件。
1)ObjectOutputStream:序列化流,把对象按照流一样的方式存入文本文件或者在网络中传输。对象 --> 流数据
2)ObjectInputStream:反序列化流,把文本文件中的流对象数据或者网络中的流对象数据还原成对象。流数据 --> 对象

2、序列化接口
1)类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。 该接口居然没有任何方法,类似于这种没有方法的接口被称为标记接口。
2)NotSerializableException:未序列化异常

3、构造方法:只能套接字节流
  • ObjectOutputStream(OutputStream out);
  • ObjectInputStream(InputStream in) 
4、反序列化的异常 InvalidClassException :
1)序列化后,然后修改类的成员变量(把私有变量编程默认),然后反序列化会报这个异常,显示 stream classdesc serialVersionUID = -2071565876962058344, 而local class serialVersionUID = -8345153069362641443
2)原因:
  • 实现序列化接口的类,会对应有一个标记值(序列化值),这个标记值与类的成员变量有关,保存在class文件中。序列化成功,这个值同样保存在文件或网络流中。
  • 修改类的成员变量后,class文件中的序列化值改变了,二保存在文件或网络流中的却没变。
  • 在反序列化的过程中,两个序列化值比对不上,即报InvalidClassException ,指出两个serialVersionUID 不匹配。
3)实际解决方案:
  • 规定这个类的序列化值,即规定序列化值在java文件中是一个固定的值。
  • private static final long serialVersionUID = -2071565876962058344L;
  • 产生这个值以后,我们对类进行任何改动,它读取以前的数据是没有问题的。
5、不想序列化的成员变量:使用 transient 关键字声明不需要序列化的成员变量。

(二)实例
<span style="font-family:Arial;font-size:18px;">package io.other;

import java.io.Serializable;

/*
 * NotSerializableException:未序列化异常
 * 
 * 类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
 * 该接口居然没有任何方法,类似于这种没有方法的接口被称为标记接口。
 * 
 * java.io.InvalidClassException: 
 * cn.itcast_07.Person; local class incompatible: 
 * stream classdesc serialVersionUID = -2071565876962058344, 
 * local class serialVersionUID = -8345153069362641443
 * 
 * 为什么会有问题呢?
 * 		Person类实现了序列化接口,那么它本身也应该有一个标记值。
 * 		这个标记值假设是100。
 * 		开始的时候:
 * 		Person.class -- id=100
 * 		wirte数据: oos.txt -- id=100
 * 		read数据: oos.txt -- id=100	
 * 
 * 		现在:
 * 		Person.class -- id=200
 * 		wirte数据: oos.txt -- id=100
 * 		read数据: oos.txt -- id=100
 * 我们在实际开发中,可能还需要使用以前写过的数据,不能重新写入。怎么办呢?
 * 回想一下原因是因为它们的id值不匹配。
 * 每次修改java文件的内容的时候,class文件的id值都会发生改变。
 * 而读取文件的时候,会和class文件中的id值进行匹配。所以,就会出问题。
 * 但是呢,如果我有办法,让这个id值在java文件中是一个固定的值,这样,你修改文件的时候,这个id值还会发生改变吗?
 * 不会。现在的关键是我如何能够知道这个id值如何表示的呢?
 * 不用担心,你不用记住,也没关系,点击鼠标即可。
 * 你难道没有看到黄色警告线吗?
 * 
 * 我们要知道的是:
 * 		看到类实现了序列化接口的时候,要想解决黄色警告线问题,就可以自动产生一个序列化id值。
 * 		而且产生这个值以后,我们对类进行任何改动,它读取以前的数据是没有问题的。
 * 
 * 注意:
 * 		我一个类中可能有很多的成员变量,有些我不想进行序列化。请问该怎么办呢?
 * 		使用transient关键字声明不需要序列化的成员变量
 */
public class Person implements Serializable {
	private static final long serialVersionUID = -2071565876962058344L;

	private String name;

	// private int age;

	private transient int age;

	// int age;

	public Person() {
		super();
	}

	public Person(String name, int age) {
		super();
		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;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}
</span>


<span style="font-family:Arial;font-size:18px;">package io.other;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/*
 * 序列化流:把对象按照流一样的方式存入文本文件或者在网络中传输。对象 -- 流数据(ObjectOutputStream)
 * 反序列化流:把文本文件中的流对象数据或者网络中的流对象数据还原成对象。流数据 -- 对象(ObjectInputStream)
 */
public class ObjectStreamDemo {
	public static void main(String[] args) throws IOException,
			ClassNotFoundException {
		// 由于我们要对对象进行序列化,所以我们先自定义一个类
		// 序列化数据其实就是把对象写到文本文件
		// write();

		read();
	}

	private static void read() throws IOException, ClassNotFoundException {
		// 创建反序列化对象
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
				"oos.txt"));

		// 还原对象
		Object obj = ois.readObject();

		// 释放资源
		ois.close();

		// 输出对象
		System.out.println(obj);
	}

	private static void write() throws IOException {
		// 创建序列化流对象
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));

		// 创建对象
		Person p = new Person("林青霞", 27);

		// public final void writeObject(Object obj)
		oos.writeObject(p);

		// 释放资源
		oos.close();
	}
}
</span>


八、Properties
(一)概述
1、属性集合类,Properties,读取properties文件
  • 是一个可以和IO流相结合使用的集合类。 
  • 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
  • 是Hashtable的子类,说明是一个Map集合。 
2、构造方法:
  • Properties prop = new Properties();
3、注意:
  • 该类不是一个泛型类
(二)功能
1、基本功能
1)Map的方法:
2)特有功能
  • setProperty(String key, String value) :存放键值
  • stringPropertyNames():获取所有的键的集合
  • getProperty(key):通过key 获取值
  • list(PrintStream out):调试使用

2、与Io流结合的读写功能
  • load(Reader reader):把文件中的数据读取到集合中 ,数据必须是键值对形式
  • store(Writer writer,String comments):把集合中的数据存储到文件
    • prop.store(w, null),可以设置为空。
<span style="font-family:Arial;font-size:18px;">	/*
	 * 这里的集合必须是Properties集合:
	 * public void load(Reader reader):把文件中的数据读取到集合中
	 * public void store(Writer writer,String comments):把集合中的数据存储到文件
	 * 
	 * 单机版游戏:
	 * 		进度保存和加载。
	 * 		三国群英传,三国志,仙剑奇侠传...
	 * 
	 * 		吕布=1
	 * 		方天画戟=1
	 */
	public void test3() throws IOException{
		// myLoad();

		myStore();
	}
	
	private static void myStore() throws IOException {
		// 创建集合对象
		Properties prop = new Properties();

		prop.setProperty("林青霞", "27");
		prop.setProperty("武鑫", "30");
		prop.setProperty("刘晓曲", "18");
		
		//public void store(Writer writer,String comments):把集合中的数据存储到文件
		Writer w = new FileWriter("name.txt");
		prop.store(w, "helloworld");
		w.close();
	}

	private static void myLoad() throws IOException {
		Properties prop = new Properties();

		// public void load(Reader reader):把文件中的数据读取到集合中
		// 注意:这个文件的数据必须是键值对形式
		Reader r = new FileReader("prop.txt");
		prop.load(r);
		r.close();

		System.out.println("prop:" + prop);
	}</span>


3、实例1
<span style="font-family:Arial;font-size:18px;">	/*
	 * 我有一个文本文件(user.txt),我知道数据是键值对形式的,但是不知道内容是什么。
	 * 请写一个程序判断是否有“lisi”这样的键存在,如果有就改变其实为”100”
	 * 
	 * 分析:
	 * 		A:把文件中的数据加载到集合中
	 * 		B:遍历集合,获取得到每一个键
	 * 		C:判断键是否有为"lisi"的,如果有就修改其值为"100"
	 * 		D:把集合中的数据重新存储到文件中
	 */
	public void test4() throws IOException{
		// 把文件中的数据加载到集合中
		Properties prop = new Properties();
		Reader r = new FileReader("f:\\user.txt");
		prop.load(r);
		r.close();

		// 遍历集合,获取得到每一个键
		Set<String> set = prop.stringPropertyNames();
		for (String key : set) {
			// 判断键是否有为"lisi"的,如果有就修改其值为"100"
			if ("lisi".equals(key)) {
				prop.setProperty(key, "100");
				break;
			}
		}
		
		// 把集合中的数据重新存储到文件中
		FileWriter w = new FileWriter("f:\\user.txt");
		
		prop.list(new PrintWriter(w));
		prop.store(w, null);
		w.close();
	}</span>

4、实例2
<span style="font-family:Arial;font-size:18px;">/*
	 * 我有一个猜数字小游戏的程序,请写一个程序实现在测试类中只能用5次,超过5次提示:游戏试玩已结束,请付费。
	 */
	public void test5() throws IOException{
		// 读取某个地方的数据,如果次数不大于5,可以继续玩。否则就提示"游戏试玩已结束,请付费。"
		// 创建一个文件
		// File file = new File("count.txt");
		// if (!file.exists()) {
		// file.createNewFile();
		// }

		// 把数据加载到集合中
		Properties prop = new Properties();
		Reader r = new FileReader("count.txt");
		prop.load(r);
		r.close();

		// 我自己的程序,我当然知道里面的键是谁
		String value = prop.getProperty("count");
		int number = Integer.parseInt(value);

		if (number > 5) {
			System.out.println("游戏试玩已结束,请付费。");
			System.exit(0);
		} else {
			number++;
			prop.setProperty("count", String.valueOf(number));
			Writer w = new FileWriter("count.txt");
			prop.store(w, null);
			w.close();

			GuessNumber.start();
		}
	}</span>


九、NIO
1、新IO使用了不同的方式来处理输入输出,采用内存映射文件的方式,将文件或者文件的一段区域映射到内存中,就可以像访问内存一样的来访问文件了,这种方式效率比旧IO要高很多
2、Buffer(缓冲)、Channer(通道)

3、Java 7以后的IO:
1)Path:路径
2)Paths:有一个静态方法返回一个路径
  • public static Path get(URI uri) 
3)Files:提供了静态方法供我们使用
  • public static long copy(Path source,OutputStream out):复制文件 
  • public static Path write(Path path,Iterable<? extends CharSequence> lines,Charset cs,OpenOption... options)


  • writeTo(OutputStream out)
    • 将此 byte 数组输出流的全部内容写入到指定的输出流参数中,
    • 这与使用 out.write(buf, 0, count) 调用该输出流的 write 方法效果一样。
2)ByteArrayInputStream 

IO流(四):其他流

标签:properties   objectinputstream   sequenceinputstream   printwriter   datainputstream   

原文地址:http://blog.csdn.net/u012228718/article/details/46053305

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!