java基础--IO流之其它流对象

笔记摘要:

本篇文章主要是对于IO流中除了经常见到的读写流之外的一些其他流对象,

Properties打印流、合并流SequenceInputStream、对象的序列化,持久化的流对象、

管道流对象、操作基本数据类型的流对象、  随机访问文件流对象以及源和目的都是内存的IO流对象


一、Properties

Properties是hashtable的子类。

也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串。

是集合中和IO技术相结合的集合容器。


该对象的特点:

可以用于键值对形式的配置文件。

那么在加载数据时,需要数据有固定格式:键 = 值。

 

Properties练习:记录应用程序运行次数,如果使用次数已到,那么给出注册提示

示例说明:

该配置文件使用键值对的形式,这样便于阅读数据,并操作数据。

键值对数据是map集合,数据是以文件形式存储,使用io技术。

那么map+io-->properties.

配置文件可以实现应用程序数据的共享。

 

程序中发现的问题:

FileOutputStream的创建应该在count自加之后,这样每次创建FileOutputStream时,就可以用新值覆盖旧值。如果在之前创建,需要将boolean值设为true,这样count才不会被覆盖,否则,文件被覆盖,count获取到的值为null,那么count的值在+1后,第二次会重复这样的操作,所以永远是1,这样就会在文件后面写入,所以出现多条除了count其它都相同的信息,所以我们在count+1后再创建FileOutputStream,让它去覆盖


package cn.xushuai.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class RunCount {

	public static void main(String[] args) throws IOException {

		File file = new File("c:\\info.ini");
		
		if(!file.exists())
			try {
				file.createNewFile();
			} catch (IOException e) {
				throw new RuntimeException("创建文件失败");
			}
		
		int count =0;
		
		Properties prop = new Properties();
		FileInputStream fis = new FileInputStream(file);
		
		//在这里定义输出流,由于没有设为true,所以每次会覆盖源文件,
		//所以count只能在本次运行后+1,之后又被覆盖,所以总是1。
		//FileOutputStream fos = new FileOutputStream(file);
		
		try {
			prop.load(fis);
			String value = prop.getProperty("time");
			if(!(value == null))
				count = Integer.parseInt(value);
				if(count>5){
					System.out.println("试用期限已到,请到官网注册!");
					return ;
				}
			count++;
			
			//在count自增过后再创建,然后将count的新值覆盖旧值
			FileOutputStream fos = new FileOutputStream(file);

			prop.setProperty("time", count+"");
			
			prop.store(fos, "");
		
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	
}

二、  打印流

该流提供了打印方法,可以将各种数据类型的数据都原样打印                                                                                       

字节打印流:PrintStream

字符打印流:PrintWriter

 

字节打印流PrintStream构造函数可以接收的参数类型

1,file对象。File

2,字符串路径。String

3,字节输出流。OutputStream

 

字符打印流PrintWriter构造函数可以接收的参数类型:

1,file对象。File

2,字符串路径。String

3,字节输出流。OutputStream

4,字符输出流,Writer

PrintWriter可以同时传入输出流对象和boolean值,以便实现自动刷


import java.io.*;

class  PrintStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));

		//PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);
		
		PrintWriter out2 = new PrintWriter(System.out,true);//打印到控制台,并自动刷新
		String line = null;

		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			out2.println(line.toUpperCase());
			//out.flush();
		}

		out2.close();
		bufr.close();

	}	
}


三、 合并流SequenceInputStream

 

使用说明:

操作合并流的时候需要将操作的文件读取流封装成对象,存入集合中,然后进行写入,又因为SequenceInputStream接收一个Enumeration 类型的参数,而枚举类型只有在Vector中才有,所以只能使用Vector集合,通过elements方法获取枚举,传入SequenceInputStream。然后确定写入流,进行读写。

 

由于Vector效率低,所以用ArrayList替代,但要想得到枚举类型,可以利用迭代器,创建Enumeration对象,通过匿名内部类的方法实现Enumeration接口,并使用迭代器的方法覆盖其中的hasMoreElements和nextElement方法,然后确定写入流,进行读写。


文件的切割与合并示例代码:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;


public class SplitFileDemo {

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		//splitFile();
		merge();
	}
	
	public static void splitFile() throws IOException  {
		
		FileInputStream fis = new FileInputStream("c:\\1.mp3");
		FileOutputStream fos = null;
		
		byte[] buf = new byte[1024*1024];
		int len = 0;
		int count=1;
		
		while((len = fis.read(buf))!=-1){
			fos = new FileOutputStream("c:\\"+(count++)+".part");
			fos.write(buf,0,len);
			fos.close();
			}
			
		/*//应该让程序自动运行,而不是手动指定要切割多少次
		for(int i=1;i<13;i++){
			
			//FileOutputStream fos = new FileOutputStream("c:\\"+i+".part");
			
			while((len = fis.read(buf))!=-1){
				
				 fos = new FileOutputStream("c:\\"+(count++)+".part");//应该在外部定义,避免创建不需要的对象
				//FileOutputStream fos = new FileOutputStream("c:\\"+(count++)+".part");
				fos.write(buf,0,len);
				fos.close();
				System.out.println("......"+count);
			}
		}*/
		
		fis.close();
	}

	public static void merge() throws IOException{
		ArrayList<FileInputStream> al  = new ArrayList<FileInputStream>();
		
		for(int i=0;i<12;i++){
			
			al.add(new FileInputStream("c:\\"+i+".part"));
			
		}
		//通过Iterator来获取枚举方法
		final Iterator<FileInputStream> it = al.iterator();
		
		//通过内部类的形式来创建枚举
		Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {

			
			@Override
			public boolean hasMoreElements() {	//使用迭代器的方法来覆盖枚举的方法
				
				return it.hasNext();
			}

			@Override
			public FileInputStream nextElement() {
				
				return it.next();
			}
		};
		
		SequenceInputStream sis = new SequenceInputStream(en);
		FileOutputStream fos  = new FileOutputStream("c:\\merge.mp3");
		
		int len=0;
		byte[] buf = new byte[1024];
		while((len=sis.read(buf))!=-1){
			
			fos.write(buf,0,len);
		}
	
		sis.close();
		fos.close();
	}
}

四、对象的序列化,持久化对象

ObjectInputStream 

ObjectOutputStream

可以直接将对象持久的存入介质中,并读出,必须成对使用。


 使用注意事项:

1>进行序列化时需要继承Serializable,  以启用其序列化功能。(该序列化只能将堆内存中的数据序列化,序列号是根据类中的成员计算而得)

2>由于定义的类后期可能会进行修改,因此重新编译后会生成新的序列号,为了让原有类被序列化的数据可以继续使用,必须给类进行自定义标识,

该标识必须是静态,最终,长整型的 :static final long serialVersionUID = 42L;

3>静态不能够序列化,由于对象是在对内存中的。

4>对非静态成员也不想序列化,可以用transient修饰,保证其值在堆内存中存在而不在文本文件中存在。


示例代码:

import java.io.*;
class Person implements Serializable
{
	
	public static final long serialVersionUID = 42L;

	private String name;
	transient int age;
	static String country = "cn";
	Person(String name,int age,String country)
	{
		this.name = name;
		this.age = age;
		this.country = country;
	}
	public String toString()
	{
		return name+":"+age+":"+country;
	}
}

import java.io.*;

class ObjectStreamDemo 
{
	public static void main(String[] args) throws Exception
	{
		//writeObj();
		readObj();
	}
	public static void readObj()throws Exception
	{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));

		Person p = (Person)ois.readObject();

		System.out.println(p);
		ois.close();
	}

	public static void writeObj()throws IOException
	{
		ObjectOutputStream oos = 
			new ObjectOutputStream(new FileOutputStream("obj.txt"));

		oos.writeObject(new Person("lisi0",399,"kr"));

		oos.close();
	}
}

五、管道流

PipedInputStreamPipedReader

PipedOutputStream  PipedWriter

可以直接将输入流与输出流连接,将流中的数据直接写入到读取端,必须结合多线程使用,因为单线程可能导致死锁。

 

管道流使用示例代码:

import java.io.*;

class Read implements Runnable
{
	private PipedInputStream in;
	Read(PipedInputStream in)
	{
		this.in = in;
	}
	public void run()
	{
		try
		{
			byte[] buf = new byte[1024];

			System.out.println("读取前。。没有数据阻塞");
			int len = in.read(buf);
			System.out.println("读到数据。。阻塞结束");



			String s= new String(buf,0,len);

			System.out.println(s);

			in.close();

		}
		catch (IOException e)
		{
			throw new RuntimeException("管道读取流失败");
		}
	}
}

class Write implements Runnable
{
	private PipedOutputStream out;
	Write(PipedOutputStream out)
	{
		this.out = out;
	}
	public void run()
	{
		try
		{
			System.out.println("开始写入数据,等待6秒后。");
			Thread.sleep(6000);
			out.write("piped lai la".getBytes());
			out.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("管道输出流失败");
		}
	}
}

class  PipedStreamDemo
{
	public static void main(String[] args) throws IOException
	{

		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);

		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(r).start();
		new Thread(w).start();


	}
}

六、操作基本数据类型的流对象 

DataInputStream

DataOutputStream

 

直接对基本数据类型进行操作,由于存入数据的类型可能不一致,所以读取时应注意顺序的一致,否则出现乱码

 

示例代码

import java.io.*;
class DataStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		//writeData();
		//readData();

		//writeUTFDemo();

//		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
//
//		osw.write("你好");
//		osw.close();

//		readUTFDemo();

	}

	public static void writeData()throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));

		dos.writeInt(234);
		dos.writeBoolean(true);
		dos.writeDouble(9887.543);

		dos.close();

		ObjectOutputStream oos = null;
		oos.writeObject(new O());

		
	}

	public static void readData()throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));

		int num = dis.readInt();
		boolean b = dis.readBoolean();
		double d = dis.readDouble();

		System.out.println("num="+num);
		System.out.println("b="+b);
		System.out.println("d="+d);

		dis.close();
	}
	
	//使用修改版的UTF-8写入,必须用相应的方式读取,用转换流读不出来
	public static void writeUTFDemo()throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));

		dos.writeUTF("你好");

		dos.close();
	}

	public static void readUTFDemo()throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));

		String s = dis.readUTF();

		System.out.println(s);
		dis.close();
	}

}


七、RandomAccessFile

   随机访问文件,自身具备读写的功能,只能操作文件。


使用说明:

 1>通过skipBytes(intx) ,seek(int x)来达到随机访问


2>该类不是IO中的子类,而是直接继承自Object,但是它是IO包中的成员,因为它  具备读和写的功能,内部封装了一个数组,而且通过指针对数组的元素进行操作,其完成读写的原理就是内部封装了字节输入流和输出流、


3>该类只能操作文件,而且还有操作模式,

如果模式为只读 r,不会创建文件,会去读取一个已存在文件,如果该文件不存在,则会出现异常。

如果模式rw,操作的文件不存在,会自动创建,如果存则不会覆盖。


4>该类可以利用多线程实现分段写入,文件的下载就是此原理。


随机读取示例:

class RandomAccessFileDemo 
{
	public static void main(String[] args) throws IOException
	{
		//writeFile_2();
		//readFile();

		//System.out.println(Integer.toBinaryString(258));

	}
	//写入数据,同时移动指针
	public static void writeFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");

		raf.write("李四".getBytes());
		raf.writeInt(97);
		raf.write("王五".getBytes());
		raf.writeInt(99);

		raf.close();
	}
	//可以任意修改数据
	public static void writeFile_2()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
		raf.seek(8*0);
		raf.write("周期".getBytes());
		raf.writeInt(103);

		raf.close();
	}
	
	//可以任意读取数据
	public static void readFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
		
		//调整对象中指针。
		//raf.seek(8*1);

		//跳过指定的字节数
		raf.skipBytes(8);

		byte[] buf = new byte[4];

		raf.read(buf);

		String name = new String(buf);

		int age = raf.readInt();


		System.out.println("name="+name);
		System.out.println("age="+age);

		raf.close();


	}

}

八、源和目的都是内存的IO类

用流的读写思想来操作数据

 

   操作字节数组      ByteArrayInputStream   ByteArrayOutputStream   

字符数组                    CharArrayReader      CharArrayWrite

      字符串                        StingReader                         StringWriter       

 

ByteArrayInputStream :

在构造的时候,需要接收数据源,而且数据源是一个字节数组

ByteArrayOutputStream:

在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。


 注意:

因为这两个流对象都操作的数组,并没有使用系统资源, 所以,不用进行close关闭。


import java.io.*;
class ByteArrayStream 
{
	public static void main(String[] args) 
	{
		//数据源。
		ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());

		//数据目的
		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		int by = 0;

		while((by=bis.read())!=-1)
		{
			bos.write(by);
		}

		System.out.println(bos.size());
		System.out.println(bos.toString());

	//一次性地将数据写入文件
	//	bos.writeTo(new FileOutputStream("a.txt"));

	}
}


posted @ 2012-11-21 17:55  积小流,成江海  阅读(200)  评论(0编辑  收藏  举报