JAVA基础——IO输入/输出流

流的两种分类:

1:输入流----输入到控制台(读)和输出流----输出到目的地(写)。

2:因为处理的数据不同,分为字节流和字符流。

该体系一共有四个基类,而且都是抽象类。

字节流:InputStream(read)  OutputStream(write)

字符流:Reader  Writer


字符流:

既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件。
那么先以操作文件为主来演示。找到操作文件的字符流子类:FileWriter、FileReader。

写入:

例子(写):

/*
	创建流FileWriter对象并文件明确目的地
	将字符串写入流
	刷新流,关闭流
*/
import java.io.*;
class FileWriterDemo 
{
	public static void main(String[] args) throws IOException
	{
		//创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
		//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
		//其实该步就是在明确数据要存放的目的地。
		//因为可能会出现确定数据存放的目的地在计算机不存在,所以这句话会抛出异常。所以要对异常处理
		FileWriter fw=new FileWriter("haha.txt");


		//字符串并未直接写入到目的地中,而是写入到了流中,(其实是写入到内存缓冲区中)
		fw.write("cdefg");


		//有两种方法刷新流,让流的内容写入到目的地。
		//第一种:flush:刷新流对象中的缓冲中的数据,将数据刷到目的地中。
		//fw.flush();

		//第二种:close:关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据,将数据刷到目的地中。
		fw.close();

		//两者区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
	}
}


注:如果想续写已有文件,可创建对象时,多加一个参数true即可,如:FileWriter fw = new FileWriter("haha.txt",true);


BufferedWriter

作用:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

示例:

/*
缓冲区的出现是为了提高流的操作效率而出现的。
所以在创建缓冲区之前,必须要先有流对象。
该缓冲区中提供了一个跨平台的换行符。
newLine();

*/
import java.io.*;
class  BufferedWriterDemo
{
	public static void main(String[] args) throws IOException
	{
		//先有流对象
		FileWriter fw=new FileWriter("hehe.txt");

		//为了提高字符写入流效率。加入了缓冲技术。
		//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
		BufferedWriter bufw=new BufferedWriter(fw);
		for(int x=1; x<5; x++)
		{
			bufw.write("abcde"+x);
			bufw.newLine();
			bufw.flush();
		}

		//记住,只要用到缓冲区,就要记得刷新。
		//bufw.flush();

		//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
		bufw.close();
	}
}

读取:

例子1(读):低效

/*
	一个字符一个字符读取
	注意,read方法返回值是整数,大于零代表对应字符的码,故输出时需要强转成字符,-1代表末尾。
*/
import java.io.*;
class  FileReaderDemo
{
	public static void main(String[] args) throws IOException 
	{
		//创建一个文件读取流对象,和指定名称的文件相关联。
		//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
		FileReader fr=new FileReader("haha.txt");
		int ch=0;
		//read方法返回作为整数读取的字符,如果已到达流的末尾,则返回 -1 
		while ((ch=fr.read())!=-1)
		{
			System.out.print((char)ch);
		}

		fr.close();
	}
}

例子2:----自定义缓冲区读取法,高效

/*
	通过字符数组进行读取。
	注意:read(char[])返回是读到字符的个数。
	注意:new String对象是带参数的
*/
import java.io.*;

class FileReaderDemo2 
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("haha.txt");
		

		//定义一个字符数组。用于存储读到字符。
		//该read(char[])返回的是读到字符个数。
		char[] buf = new char[1024];

		int num = 0;
		while((num=fr.read(buf))!=-1)
		{
			System.out.println(new String(buf,0,num));
		}
		

		fr.close();
	}
}

练习:

/*
复制文件
原理:其实就是将C盘下的文件数据存储到D盘的一个文件中。
步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。
4,关闭资源。
*/
import java.io.*;
class CopyDemo 
{
	public static void main(String[] args) 
	{
		copy();
	}
	public static void copy()
	{
		FileWriter fw=null;
		FileReader fr=null;
		try
		{
			fr=new FileReader("haha.txt");
			fw=new FileWriter("D:\\haha.txt");
			char[] buf=new char[1024];
			int len=0;
			while(-1!=(len=fr.read(buf)))
			{
				fw.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
		}
		finally
		{
			try
			{
				if(fr!=null)
					fr.close();
			}
			catch (IOException e)
			{
			}
			try
			{
				if(fw!=null)
					fw.close();
			}
			catch (IOException e)
			{
			}
		}
	}
}

BufferedReader

示例:
/*
字符读取流缓冲区:
该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。
当返回null时,表示读到文件末尾。
readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。
*/
class BufferedReaderDemo 
{
	public static void main(String[] args) 
	{
		FileReader fr=new FileReader("hehe.txt");
		BufferedReader bufr=new BufferedReader(fr);
		String line=null;
		while(null!=(line=fr.readLine()))
		{
			System.out.println(line);
		}
		bufr.close();
	}
}

练习:
/*
利用缓存器copy文件
*/
import java.io.*;
class CopyByBuf 
{
	public static void main(String[] args) 
	{
		BufferedReader bufr=null;
		BufferedWriter bufw=null;
		try
		{
			bufr=new BufferedReader(new FileReader("hehe.txt"));
			bufw=new BufferedWriter(new FileWriter("D:\\hehe.txt"));
			String line=null;
			while(null!=(line=bufr.readLine()))
			{
				bufw.write(line);
				bufw.newLine();
				bufw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println("读写失败");
		}
		finally
		{
			try
			{
				if(bufr!=null)
					bufr.close();
			}
			catch (IOException e)
			{
				System.out.println("读取流关闭失败");
			}
			try
			{
				if(bufw!=null)
					bufw.close();
			}
			catch (IOException e)
			{
				System.out.println("写入流关闭失败");
			}
		}
	}
}


|----LineNumberReader

BufferedReader的子类,用法几乎相同。
作用:跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int)getLineNumber(),它们可分别用于设置和获取当前行号。

示例:

/*
此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
*/
import java.io.*;
class LineNumberReaderDemo 
{
	public static void main(String[] args) throws IOException
	{
		LineNumberReader lnr=new LineNumberReader(new FileReader("hehe.txt"));
		String line=null;
		lnr.setLineNumber(100);
		while(null!=(line=lnr.readLine()))
		{
			System.out.println(lnr.getLineNumber()+":"+line);
		}
		lnr.close();
	}
}

MyBufferedReader

用StringBuilder容器自写一个BufferedReader,要求具有ReadLine方法
/*
windows下换行是由:\r\n标示的。
明白了BufferedReader类中特有方法readLine的原理后,
可以自定义一个类中包含一个功能和readLine一致的方法。
来模拟一下BufferedReader
*/
import java.io.*;
class MyBufferedReader extends Reader 
{
	private Reader r;
	MyBufferedReader(Reader r)
	{
		this.r=r;
	}
	public String myReadLine() throws IOException	//使用了read需要抛出异常
	{
		//定义一个临时容器。原BufferReader封装的是字符数组。
		//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
		StringBuffer sb=new StringBuffer();
		int ch=0;
		while(-1!=(ch=r.read()))
		{
			if(ch=='\r')
				continue;
			if(ch=='\n')
				return sb.toString();
			else
				sb.append((char)ch);
		}
		//防止最后的结尾没有\n而导致损失数据,再做一次判断
		if(sb.length()!=0)
			return sb.toString();
		return null;
	}

	//覆盖Reader类下的抽象方法
	public void close() throws IOException
	{
		r.close();
	}
	public int read(char[] cbuf, int off, int len) throws IOException
	{
		return r.read(cbuf,off,len) ;
	}
}
class MyBufferedReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		MyBufferedReader mbufr=new MyBufferedReader(new FileReader("hehe.txt"));
		String line=null;
		while(null!=(line=mbufr.myReadLine()))
		{
			System.out.println(line);
		}
	}
}

装饰设计模式

从上面的MyBufferedReader可以看出,其实BufferedReader就是对原Reader类中的Read方法进行了增强而产生的ReadLine方法。
具体参见: 装设设计模式

IO异常处理:

IO流中异常处理是最为常见的,下面是IO异常处理的方式

例子:

/*
IO异常处理:注意关闭流资源需要放在finally中再单独try
*/
import java.io.*;
class FileWriterDemo2 
{
	public static void main(String[] args) 
	{
		FileWriter fw=null;
		try
		{
			fw=new FileWriter("haha.txt",true);
			fw.write("hjkloiuyp");
		}
		catch (IOException e)
		{
			System.out.println("catch:"+e.toString());
		}
		finally
		{
			try
			{
				if(null!=fw)
					fw.close();
			}
			catch (IOException e)
			{
				System.out.println("catch:"+e.toString());
			}
		}	
	}
}


字节流:

InputStream(读,抽象类)  

|——FileInPutStream

OutputStream(写,抽象类)

|——FileOutPutStream

具体用法类似字符流,有一点区别字符流使用字符数组,字节流使用字节数组。

//写
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("abcde".getBytes());
fos.close();

//读,低效
FileInputStream fis = new FileInputStream("fos.txt");
int ch = 0;
while((ch=fis.read())!=-1)
{
	System.out.println((char)ch);
}
fis.close();

//读,自定义缓冲区,高效
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1)
{
	System.out.println(new String(buf,0,len));
}
fis.close();

//读,利用available方法,但要避免文件过大,使得定义的缓冲区溢出
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用在循环了。
fis.read(buf);
System.out.println(new String(buf));
fis.close();

例子:复制图片

import java.io.*;
class CopyPicture 
{
	public static void main(String[] args) 
	{
		FileInputStream fis=null;
		FileOutputStream fos=null;
		try
		{
			fis=new FileInputStream("D:\\1.jpg");
			fos=new FileOutputStream("D:\\2.jpg");
			byte[] buf=new byte[1024];
			int len=0;
			while(-1!=(len=fis.read(buf)))
			{
				fos.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
			System.out.println("读写失败");
		}
		finally
		{
			try
			{
				if(fis!=null)
				fis.close();
			}
			catch (IOException e)
			{
				System.out.println("读取流关闭失败");
			}
			try
			{
				if(fos!=null)
				fos.close();
			}
			catch (IOException e)
			{
				System.out.println("写入流关闭失败");
			}
		}
	}
}

字符流与字节流转换

字节流转换成字符流

为什么会有字节流转换字符流的需求?

例子:

/*
读取键盘录入。
System.out:对应的是标准输出设备,控制台。
System.in:对应的标准输入设备:键盘。

需求:
通过键盘录入数据。
当录入一行数据后,就将该行数据进行打印。
如果录入的数据是over,那么停止录入。

*/
import java.io.*;
class ReadIn 
{
	public static void main(String[] args) throws IOException
	{
		InputStream in=System.in;
		StringBuffer sb=new StringBuffer();
		while(true)
		{
			int ch=in.read();
			if(ch=='\r')
				continue;
			if(ch=='\n')
			{
				String s=sb.toString();
				if("over".equals(s))
					break;
				System.out.println(s.toUpperCase());
				sb.delete(0,sb.length());
			}
			else
				sb.append((char)ch);
		}
	}
}

可以发现,如果使用字符流的readLine功能,能够轻松的完成上述需求,于是就产生字节流转换字符流的需求。

import java.io.*;
class TransStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		InputStreamReader isr=new InputStreamReader(System.in);
		BufferedReader buf=new BufferedReader(isr);
		String line=null;
		while(null!=(line=buf.readLine()))
		{
			System.out.println(line.toUpperCase());
			if("over".equals(line))
				break;
		}
	}
}

键盘的最常见写法:

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

分析:

以上是字节流转换为字符流。

源:键盘,字符----------------》选择Reader。

由于是键盘录入,对应对象是:System.in,而System.in是字节流,为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的,于是转成字符流,

利用InputStreamReader。


字符流转换成字节流

需求,比如,将文本文件输出到控制台上。

源:文本文件,字符

目的:控制台

由于输出到控制台,用System.out(字节流),于是需要字符转字节流,可以利用OutputSteamWriter.


当然字符流与字节流之间的转换还有其他作用,比如指定读或写的编码表就需要。

理清输入输出流思路


通过三个明确来完成。


1,明确源和目的。
源:输入流。InputStream  Reader
目的:输出流。OutputStream  Writer。

2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。

3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘。键盘
目的设备:内存,硬盘,控制台。



File类

File类常见方法:
1,创建。
boolean createNewFile(): 在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。

boolean mkdir():创建文件夹。
boolean mkdirs():创建多级文件夹。
2,删除。
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回false。如果此路径名表示一个目录,则该目录必须为空才能删。
void deleteOnExit();在程序退出时删除指定文件。.

.
3,判断。
boolean exists() :文件(文件夹)是否存在.
isFile():  测试此抽象路径名表示的文件是否是一个标准文件,不存在或不是文件返回false。
isDirectory();  测试此抽象路径名表示的文件是否是一个目录,不存在或不是目录返回false。
isHidden();     测试此抽象路径名指定的文件是否是一个隐藏文件。
isAbsolute();  测试此抽象路径名是否为绝对路径名,即使不存在,但只要是绝对路径就是true。


4,获取信息。
getName(): 返回由此抽象路径名表示的文件或目录的名称。

getPath():    将此抽象路径名转换为一个路径名字符串,返回一个字符串

getAbsolutePath() :返回此抽象路径名的绝对路径名字符串。

getParent():  返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回null

long lastModified() :返回此抽象路径名表示的文件最后一次被修改的时间。
long length() :返回由此抽象路径名表示的文件的长度。


其他:

static File[] listRoots() 列出可用的文件系统根。如:C.D.E.F.I

File[] listFiles()返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。

String[]  list()返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。

上面两个还有重载形式的函数,需要传递参数FilenameFilter filter------文件名称过滤器,可以通过建立此类对象,

并覆盖其类中方法public boolean accept(File dir,String name)来实现只列出如:.bmp文件类型的文件。

通过匿名内部类方便实现,例:

import java.io.*;
class FileDemo2 
{
	public static void main(String[] args) 
	{
		File dir=new File("i:\\a");
		String[] names=dir.list(new FilenameFilter()
		{
			//重写FilenameFilter类的accept函数
			public boolean accept(File dir,String name)
			{
				return name.endsWith(".bmp");
			}
		});
		for(String s:names)
			System.out.println(s);		
	}
}

其他的一些应用例子:

例:

/*
列出指定目录下文件或者文件夹,包含子目录中的内容。
也就是列出指定目录下所有内容。

因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。
在列出过程中出现的还是目录的话,还可以再次调用本功能。
也就是函数自身调用自身。
这种表现形式,或者编程手法,称为递归。

递归要注意:
1,限定条件。
2,要注意递归的次数。尽量避免内存溢出。
*/
import java.io.*;
class  FileDemo3
{
	public static void main(String[] args) 
	{
		File dir=new File("i:\\a");
		showDir(dir);
	}
	public static void showDir(File dir)
	{
		File[] files=dir.listFiles();
		for(int i=0;i<files.length;i++) //注意这里数组,用length,固有属性,不要用.size(),或.length();
		{
			if(files[i].isDirectory())
				showDir(files[i]);
			else
				System.out.println(files[i]);
		}
	}
}

例:
/*
删除一个带内容的目录。
删除原理:
在window中,删除目录从里面往外删除的。

既然是从里往外删除。就需要用到递归。

*/
import java.io.*;
class  FileDemo3
{
	public static void main(String[] args) 
	{
		File dir=new File("i:\\a");
		showDir(dir);
	}
	public static void showDir(File dir)
	{
		File[] files=dir.listFiles();
		for(int i=0;i<files.length;i++) //注意这里数组,用length,固有属性,不要用.size(),或.length();
		{
			if(files[i].isDirectory())
				showDir(files[i]);
			else
				System.out.println(files[i].delete());
		}
		System.out.println(dir.delete());
	}
}

打印流

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

也是一种装饰设计模式,类似Bufferedxxxxxx

字节打印流:PrintStream

构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream


字符打印流:PrintWriter

构造函数可以接收的参数类型:

1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,字符输出流,Writer。

打印流的特点在于,提供了print、println的方法,可以直接打印,

而且有一特殊构造函数,如PrintWriter(OutputStream out, boolean autoFlush)

可以自动刷新流。


例:

import java.io.*;
class PrintDemo 
{
	public static void main(String[] args) throws IOException 
	{
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
		PrintWriter pw=new PrintWriter(System.out,true);
		//PrintWriter pw=new PrintWriter(new FileWriter("a.txt"),true);
		//PrintWriter pw=new PrintWriter("a.txt"); 此处不能加true
		String line=null;
		while(null!=(line=bufr.readLine()))
		{
			if("over".equals(line))
				break;
			pw.println(line.toUpperCase());
			//pw.flush(); 构造时传递true即可
		}
		pw.close();
		bufr.close();
	}
}

Properties类

Properties是hashtable的子类。
特点:

Properties是hashtable的子类,也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。
该类是集合和IO技术相结合的集合容器。


该对象的主要作用:可以用于键值对形式的配置文件。在加载数据时,需要数据有固定格式:键=值。

	//设置和获取元素
	/*
	因为 Properties 继承于 Hashtable,所以可对 Properties 对象应用 put 和 putAll 方法。
	但不建议使用这两个方法,因为它们允许调用者插入其键或值不是 String 的项。
	相反,应该使用 setProperty 方法,其实就是在调用hashtable的put(string,string)方法。
	
	设置:setProperty(String ,String)
	获取:Set<String> stringPropertyNames()以及getProperty(String key)
	*/
	public static void setAndGet()
	{
		Properties prop=new Properties();
		prop.setProperty("xiaoming","22");
		prop.setProperty("lisi","25");
		//System.out.println(prop);
		
		Set<String> names =prop.stringPropertyNames();
		for(String name:names)
		{
			System.out.println(name+":::"+prop.getProperty(name));
		}	
	}


	//想要将已存在的配置文件info.txt中的键值数据存到集合中进行操作。
	/*
		1,用一个流和info.txt文件关联。
		2,读取一行数据,将该行数据用"="进行切割。
		3,等号左边作为键,右边作为值。存入到Properties集合中即可。

	*/
	public static void fileToProp() throws IOException
	{
		BufferedReader bufr=new BufferedReader(new FileReader("info.txt"));
		Properties prop=new Properties();
		String line=null;
		while(null!=(line=bufr.readLine()))
		{
			String[] arr=line.split("=");
			prop.setProperty(arr[0],arr[1]);
		}
		bufr.close();
		System.out.println(prop);
	}
}

上述方法没有用到Properties类的特点,其实可以:

/*
	Properties类的特有功能:
	void load(InputStream inStream) 
          从输入流中读取属性列表(键和元素对)。 
	void load(Reader reader) 
          按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。 
	*/
	public static void fileToProp_2() throws IOException
	{
		FileReader bufr=new FileReader("info.txt");
		Properties prop=new Properties();
		prop.load(bufr);
		bufr.close();
		System.out.println(prop);
	}

类似的还有:

void list(PrintStream out)  将属性列表输出到指定的输出流。 
void list(PrintWriter out)  将属性列表输出到指定的输出流。 

例:

	public static void fileToProp_3() throws IOException
	{
		FileReader bufr=new FileReader("info.txt");
		Properties prop=new Properties();
		prop.load(bufr);
		prop.list(System.out);
	}

如果要修改info文件的内容,需要用到store方法

void store(OutputStream out, String comments) 
void store(Writer writer, String comments) 

例:
	public static void fileToProp_3() throws IOException
	{
		FileReader bufr=new FileReader("info.txt");
		Properties prop=new Properties();
		prop.load(bufr);
		//修改数据,但并没有修改info.txt的内容
		prop.setProperty("youku","90");

		//下面是修改info.txt的内容。
		FileWriter fw=new FileWriter("info.txt");
		prop.store(fw,"haha");	
		
		bufr.close();
		fw.close();
	}

properties综合练习:

/*
简单的限制使用次数程序:
用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。

很容易想到的是:计数器。
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着该应用程序的退出,该计数器也在内存中消失了。

下一次在启动该程序,又重新开始从0计数。这样不是我们想要的。

程序即使结束,该计数器的值也存在。
下次程序启动在会先加载该计数器的值并加1后在重新存储起来。

所以要建立一个配置文件。用于记录该软件的使用次数。

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

键值对数据是map集合。
数据是以文件形式存储,使用io技术。
那么map+io -->properties.

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

import java.io.*;
import java.util.*;
class  RunCount
{
	public static void main(String[] args) throws IOException
	{
		//关联文件,如果没有创建文件
		File file=new File("d:\\info.txt");
		if(!file.exists())
			file.createNewFile();
		FileReader fr=new FileReader(file);
		//获取文件内容到集合中并操作
		Properties prop=new Properties();
		prop.load(fr);
		int count=0;
		String value=prop.getProperty("time");
		if(value!=null)
		{
			//将字符串value转成int型
			count=Integer.parseInt(value);
			if(count>=5)
			{
				System.out.println("使用次数已到");
				return;
			}
		}
		count++;
		prop.setProperty("time",count+"");
		//将集合内容保存至文件
		FileWriter fw=new FileWriter(file);
		prop.store(fw,"limit");
		System.out.println("程序正在运行!");
		fr.close();
		fw.close();
	}
}

SequenceInputStream类

他是InputStream的子类,特点在于输入流的逻辑串联。

它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,

依次类推,直到到达包含的最后一个输入流的文件末尾为止。

可以用了何必文件,如多个txt文本文件合成一个文本文件。

注意,其构造方法:

两个流时:SequenceInputStream(InputStream s1, InputStream s2)

多个流时:SequenceInputStream(Enumeration<? extends InputStream> e)

多个流的构造参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。

Enumeration是类似迭代器的类,具体参考Vector

例:

/*
利用SequenceInputStream类
将1.txt,2.txt,3.txt合并成4.txt
*/
import java.io.*;
import java.util.*;
class SequenceInputStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		//创建一个集合用于存放多个输入流
		Vector<InputStream> v=new Vector<InputStream>();
		//将流对象添加至集合
		v.add(new FileInputStream("1.txt"));
		v.add(new FileInputStream("2.txt"));
		v.add(new FileInputStream("3.txt"));
		//得到Enumeration,将其对象传入SequenceInputStream构造函数中
		Enumeration<InputStream> en=v.elements();
		SequenceInputStream sis=new SequenceInputStream(en); //组合成1个输入流sis
		
		FileOutputStream fos=new FileOutputStream("4.txt");
		byte[] buf=new byte[1024];
		int len=0;
		while(-1!=(len=sis.read(buf)))
		{
			fos.write(buf,0,len);
		}
		fos.close();
		sis.close();
	}
}


有了合并文件,自然有切割文件

例:

/*
	一兆一兆的切割文件
	*/
	public static void splitFile()throws IOException
	{
		FileInputStream fis =  new FileInputStream("c:\\1.bmp");
		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:\\splitfiles\\"+(count++)+".part");
			fos.write(buf,0,len);
			fos.close();
		}
		
		fis.close();


ObjectInputStream类与ObjectOutputStream类

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。通过在流中使用文件可以实现对象的持久存储。

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

简单的来说就是:

ObjectOutputStream可以用来将对象存储到文件上,ObjectInputStream可以用来读取文件上的对象。

例如有一个Person类创建一个对象,保存在文件中,然后再读取这个文件识别出对象。

需要注意的是

一:Person类对应一个UID(需要实现Serializable接口,以启用序列化功能------没有函数,不需要重写,称为标记接口),创建的对象也同样有一个相同的UID,

当Person类发生改变,Person类的UID就会改变,两者的UID不相同时,会无法读取。‘

二:有两种情况会使得Person类发生改变,UID不变

1、静态成员:静态成员内容发生改变 UID不变。

2、用transient修饰的成员发生改变UID不变。

三:可以给Person类固定一个UID,这样即使Person类发生改变,UID也不会变。

如:public static final long serialVersionUID = 42L;


例:

//Person类
import java.io.*;
class  Person implements Serializable
{
	//public static final long serialVersionUID = 42L; //固定UID的
	String name;
	int age;
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public String toString()
	{
		return name+":::"+age;
	}
}

//写入对象到文件并读取
import java.io.*;
class ObjectStreamDemo 
{
	public static void main(String[] args) throws IOException,ClassNotFoundException
	{
		//writeObj();
		readObj();
	}

	//写入对象函数
	public static void writeObj() throws IOException
	{
		//创建写入指定 OutputStream 的 ObjectOutputStream
		ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("person.txt"));
		//写入对象
		oos.writeObject(new Person("lisi",22));
		oos.close();
	}

	//读取对象函数
	public static void readObj() throws IOException,ClassNotFoundException
	{
		ObjectInputStream ois=new ObjectInputStream(new FileInputStream("person.txt"));
		Person p=(Person)ois.readObject();
		System.out.println(p);
		ois.close();
	}
}

注意,上述代码将对象写入person.txt文件中,一但Person类发生改变,即无法读取,除非改变的是静态或transient的成员。



管道流 

PipedInputStream、PipedOutputStream,可以将管道输出流连接到管道输入流来创建通信管道。
如果说Properties类是IO与集合的结合,那么管道流就是IO与多线程的结合。
因为:通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。
不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。
连接两个线程可用二种方法
构造方法:PipedOutputStream(PipedInputStream snk),反过来也行。
或者 connect方法。
两个流需要在两个线程中,也就是说要重写两个run方法。
例:
import java.io.*;
class Read implements Runnable
{
	//以创建对象就有读取流对象
	private PipedInputStream pis;
	Read(PipedInputStream pis)
	{
		this.pis=pis;
	}
	//覆盖run方法
	public void run()
	{
		try
		{
			byte[] buf=new byte[1024];
			int len=0;
			while(-1!=(len=pis.read(buf)))
			{
				System.out.println(new String(buf,0,len));
			}
			pis.close();
		}
		catch (IOException e)
		{
			throw new RuntimeException("管道读取流失败");
		}
	}
}
class Write implements Runnable
{
	private PipedOutputStream pos;
	Write(PipedOutputStream pos)
	{
		this.pos=pos;
	}
	public void run()
	{
		try
		{
			pos.write("Piped is running".getBytes());
			pos.close();
		}
		catch (IOException 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();
	}

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值