Java基础--IO流技术详解

IO流
IO流:用来处理设备之间的数据传输.java对数据的操作是通过流的方式,操作流的对象都在io包中,输入流和输出流.
字节流的抽象基类:
InputStream,OutputStream
字符流的抽象基类:
Reader,Writer

注:由这四个类派生出来的子类名称都是由这个父类名作为子类名的后缀
如:InputStream的子类:FileInputStream
如:Reader的子类:FileReader

既然IO是用来操作数据的,那么数据的最常见的体现形式就是文件.
那么先以操作文件来演示:
需求:
在硬盘上创建一个文件,并写入一些文字数据.
使用FileWriter

BufferedReader 一次读一行

装饰设计模式:
当想要对已有的对象进行功能增强时,可以自定义类,将已有的对象传入,基于已有的功能并提供加强的功能.
那么自定义的该类称为装饰类.
装饰类通常通过构造函数接受被装饰的对象,并基于被装饰的对象的功能进行增强

MyReader //专门读取数据的类,下面是继承关系 MyTextReader继承MyReader
MyBufferedTextReader继承MyTextReader MyMediaReader
MyBufferedMediaReader MyDataReader MyBufferedDataReader

这样 每次出现新的需求都要继承或者写新的类.MyBufferedReader.

可以定义class MyBufferedReader,谁需要缓冲区,就把谁传进来.

		class MyBufferedReader extends MyReader 
		{
			private MyReader mr;
			MyBufferedReader(MyReader mr){}
			MyReader这时候可以接收
				MyTextReader,MyMediaReader,MyDataReader
		}

MyReader 专门用于读取数据的类 下列都是MyReader 的子类 MyTextReader MyMediaReader
MyDataReader MyBufferedReader //作为装饰类

装饰模式比继承要灵活,避免了继承体系的臃肿,而且降低了类之间的关系.
装饰类增强已有对象,具备的功能和已有对象是相同的.只不过提供了更强的功能,所以
装饰类和被装饰类通常是属于同一个体系中.

想要操作图片数据就要用到字节流.
InputStream Read
OutputStream Write

源:键盘录入
目的:控制台.
想要把键盘录入的数据存储到一个文件中,那么还可以使用new BufferedReader(new InputStreamReader(System.in))
new BufferedWriter(new OutputStreamWriter(new FileOutputStream(“1.txt”))

源:文件
目的:控制台
//两个明确:
/*
* 1.明确源和目的
* 源:输入流 InputStream Reader
* 目的:输出流 OutputStream Writer
* 2.确定操作的数据是不是纯文本.
* 是:则用Reader 或Writer
* 不是:字节流.
* 3.体系明确后,再明确要使用哪个具体的对象
* 通过设备来进行区分.
* 源设备:内存,硬盘,键盘.
* 目的设备:内存,硬盘,控制台.
*/

System setIn setOut改变标准输入输出设备.

File类的出现弥补了流对文件操作的不足.想要直接操作文件的数据,需要使用此类.
1.创建 boolean createNewFile() 输出流则会直接覆盖已经存在的文件.
2.删除 boolean delete() void deleteOnExit()
3.判断 boolean exists(); boolean isFile(); boolean isDirectory(); boolean isHidden(); boolean isAbsolute();是否为绝对路径
4.获取 String getName(); String getPath(); String getAbsolutePath(); String getParent();

Properties 是HashTable的子类,也就是说具备了Map集合的特点,存储的是字符串.是集合中io技术相结合的集合容器.
该对象的特点: 可以用于键值形式的配置文件.

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

字节打印流:
PrintStream构造函数可以接受的参数类型:
1.file对象
2.字符串路径
3.OutputStream字节输出流

PrintWriter
构造函数可以接收
1.file对象
2.字符串路径
3.OutputStream字节输出流
4.Writer

ObjectInputStream ObjectOutputStream
被操作的对象需要实现Serializable,将堆内存中的对象存储到硬盘上,或将硬盘上的对象读取到内存中.

没有方法的接口通常称为标记接口.

源设备:
键盘 System.in 硬盘FileStream 内存 ArrayStream
目的设备:
控制台 System.out 硬盘FileStream 内存 ArrayStream

编码:字符串变成字节数组
解码:字节数组变成字符串

String–>byte[]: str.getbytes(charsetName)
byte[] -->String: new String(byte[],csn).

字节流和字符流:
字符流的由来:计算机中,如果想让它识别我们生活中的一些事物,就得定义一些01010的序列对应一些字符,也就是编码表.ASCII UTF-8(之后的Unicode) unicode无论什么字符都要用2个8位来表示,前者则看情况,是对后者的优化. 中国汉字的码表GBK.

字节流的抽象基类:InputStream OutputStream
字符流的抽象基类:Reader Writer
这四个类派生出来的子类名称都是以其父类名作为子类名的后缀
字符流的特点:Writer构造方法,权限为protected.需要子类去做不同的实现,最重要的方法write(),写入字符或者字符串或者字符串字符数组中的某一部分.既然io流用于操作数据,数据最常见的体现形式是文件,那么 先以操作文件为主来演示.

详解
Writer有一个子类专门操作文件的类叫做FileWriter,后缀名是父类名,前缀名是功能.该类没有无参构造函数,因此一旦初始化该类的实例就必须有被操作的文件.

1. 创建一个对象这时候要使用就必须抛出一个IOException.这一步就是要明确文件的路径.
2. 调用对象的write("abc");  //其实这时候是把abc写到内存中的流对象中去了.
3. 调用对象的flush(),刷新流的缓冲,使流对象中缓冲的数据送达目的地.
4. 调用close()也会刷新流的缓冲,之后关闭该流(删除该流在内存中的位置).

其实java调用的方法也是调用了系统内部的api完成了数据的建立,这些操作都在使用windows的内部资源,因此操作完以后要close();一下.

package testtest;

import java.io.FileWriter;

public class Demo1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		FileWriter fw = null;//这样可以避免局部变量造成的问题.在代码块外定义对象,代码块内new对象.
		try {
			fw = new FileWriter("demo.txt");
			fw.write("abcde");
		} catch (Exception e) {
			// TODO: handle exception
		}finally {try {
			fw.close();
		} catch (Exception e2) {
			// TODO: handle exception
		}
		}
	}
//
}

这时候两个异常,一个异常是io异常,另一个空指针异常由于对象未能创建成功所以产生.在close()方法前加一个判断关闭流是否为空的if语句即可!

对已有文件的续写:

1. 建立对象 new …("a.txt",true)
2. Fw.write("哈哈")
3. Fw.close().  //windows下换行是\r\n

Reader类:
流对象几乎都是对应的,有读的就有写的.FileReader,要读一个文件,先有这个文件,因此构造的时候必须指定文件.然后调用对象的读取方法read()方法.

package iodemo;

import java.io.FileReader;
import java.io.IOException;

public class FileReaderDemo1 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		FileReader fr = null;
		int ch = 0;
		try
		{
			fr = new FileReader("K:IOdemo");
			//FileWriter(File f,boolean b);b是末尾添加的标记
			while((ch=fr.read())!=-1)
			{
				System.out.print((char)ch);
			}
		}catch(IOException e){
			System.out.println("文件读取错误!!");
	}
		finally{
			try{
				if(fr!=null)
					fr.close();
			}catch(IOException e2)
			{
				System.out.println(e2.toString());
			}
		}

	}
}

复制文件的原理:
其实就是将一个路径的文件数据存储到另一个路径一个文件中.步骤:
在后者中建立一个文件,用于存储前者文件中的数据,然后定义一个读取流跟前者关联,通过不断的读写完成数据的存储,关闭资源.

字符流的缓冲区:
缓冲区的出现提高了对数据的读写效率.
对应类:

* BufferWriter
* BufferReader

缓冲区要结合流才可以使用.在流的基础上对流的功能进行了增强.缓冲区是为了提高流的操作效率出现的,所以在创建缓冲区之前,必须要先有流对象.

//创建一个字符写入流对象
FileWriter fw = new FileWriter(“buf.java”);
//为了提高字符写入流效率,加入了缓冲技术.只要将需要提高效率的流对象作为参数传递给缓冲区的构造函数即可.
BufferWriter bufw = new BufferWriter(fw);
Bufw.write(“abcde”);
Bufw.flush();
Bufw.close();
//fw还用关闭吗? Fw.close();
缓冲区为了提高效率存在,真正和文件相关的操作其实是FileWriter,因此真正关闭的就是fw对象.
//其实关闭缓冲区就是在关闭缓冲区中的流对象.
newLine()是换写入一个行分隔符,就是一个换行符.

BufferReader //实现对字符,数组和行的高效存取.
在进行对象建立的时候也要有个被缓冲的对象.
//创建一个读取流的对象和文件相关联.
FileReader fr = new FileReader(“buf.txt”);
//为了提高效率加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象传递给缓冲对象的构造函数.
BufferReader bufr = new BufferReader();
String s1 = bufr.readLine();
Sop(s1);
//String line = null;
While((line=bufr.readLine())!=null)
Sop(line);
//该缓冲区提供了一个一次读一行的方法,方便于对文本数据的获取readLine();返回的一行不包含换行符,如果已到达流的末尾那么返回null.因此输出后可以加一个 newLine();其实readLine()就是在增强read()方法.
这个类增强了FileReader(),其实是一种装饰设计模式.
装饰设计模式:
当想要对已有的对象进行功能增强的时候,可以定义类,将一有对象传入,基于已有的功能提供增强功能,那么自定义的该类成为装饰类.

package iodemo;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedTxtCopyDemo1 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		BufferedReader br = null;
		BufferedWriter bw = null;
		try{
		br = new BufferedReader(new FileReader("col"));
		bw = new BufferedWriter(new FileWriter("colnew",true));
		String line = null;
		while((line = br.readLine())!=null)
		{
				System.out.println(line);
				bw.write(line);
				bw.newLine();
				bw.flush();
		}
		
		}catch(IOException e1){
			System.out.println(e1.toString()+"复制失败");
		}finally{
			try{
				if(br!=null)
				br.close();
			}catch(IOException e2){
				System.out.println(e2.toString()+"读取关闭失败");
			}try{
				if(bw!=null)
				bw.close();
			}catch(IOException e3){
				System.out.println(e3.toString()+"写入关闭失败");
			}
		}
	}

}

字节流:
跟字符流一样,不过操作的是字节.
需求:想要操作图片数据,这时就要用到字节流.

InputStream 读
OutputStream写
字符流使用的是字符数组
,字节流使用的是字节数组.
FileWriter
FileOutputStream.

package iodemo;

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

public class CopyFileDemo2 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根

		FileInputStream fis = null;
		FileOutputStream fos = null;
		byte[] b = new byte[1024];
		try {
			fis = new FileInputStream("1.jpg");
			fos = new FileOutputStream("D:\\1.jpg");
			int num = 0;
			while ((num = fis.read(b)) != -1)
				fos.write(b, 0, num);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			throw new RuntimeException("复制失败");
		} finally {
			try {
				if (fis != null)
					fis.close();
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				throw new RuntimeException("读取关闭失败");
			}
			try {
				if (fos != null)
					fos.close();
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				throw new RuntimeException("写入关闭失败");
			}
		}

	}
}

读取键盘录入:需要用到System类.
System.out:对应的是标准输出设备,控制台.
System.in:对应的标准输入设备:键盘.
InputStream:标准字节输入流.
InputStream in = System.in;
Int by = in.read();
System.out.print(by);
Read()是一个阻塞式方法,运行时会等待输入,输入完回车会读取输入的数据.
需求:
通过键盘录取数据,当录入一行数据后,就将该行数据进行打印,如果录入的数据是over那么停止录入.

package testtest;

import java.io.IOException;
import java.io.InputStream;

public class Demo1 {

	public static void main(String[] args) throws IOException {
		InputStream in = System.in;
		StringBuilder sb = new StringBuilder();
		
		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(ch);
		}
	}
}

1.明确源和目的
源:输入流.InputStream Reader
目的:输出流.OutputStream Writer
2.明确操作的数据是否是纯文本.
如果操作的数据是纯文本的用字符流,不是的话就用字节流.
现在我们就知道用哪个体系了.当体系明确后,再明确具体使用哪个类的对象.例如输入流,字符,就用Writer.输入流,字节,则用InputStream.
通过设备来区分:内存,硬盘,键盘
目的设备:内存,硬盘,控制台

需求1:
将一个文本文件中的数据存储到另一个文件中,就是复制文件的操作.
源:因为是源,所以使用读取流.文件 InputStream,Reader
是不是操作文本文件?是,因此选择Reader(字符流)
这样体系就明确了.
接下来明确要使用该体系中的哪个对象:
明确设备:硬盘.上一个文件
Reader体系中可以操作文件的对象是FileReader

FileReader fr = new FileReader(“a.txt”);

目的:OutputStream Writer
是否为纯文本呢,是,选择Writer.
设备:硬盘,一个文件
Writer体系中可以操作文件的对象FileWriter.
FileWriter fw = new FileWriter(“b.txt”)

再问自己一句的是:是否需要提高效率?
如果仅仅演示,那么不需要.但是当文件过大时,要加入Reader体系中的缓冲区:BufferedReader
BufferedReader bufr = new BufferedReader(fr);
BuferedWriter bufw = new BufferedWriter(fw);

需求2:
将键盘录入的数据保存到一个文件中,这个需求中有源和目的都存在.那么分别分析:
源:InputStream Reader
是不是纯文本?是,因此选择Reader.
设备:键盘.对应的对象是System.in.
不是选择Reader吗?System.in对应的应该是字节流吗?为了操作键盘的文本数据方便,可以转成字符串操作是最方便的.
所以既然明确了Reader那么久将System.in转成Reader,用到了Reader体系中的转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);

目的:OutputStream Writer
是否保存为文本? 是,Writer
设备:硬盘.一个文件,因此选择FileWriter对象.
FileWriter fw = new FileWriter(“c.txt”);
需要提高效率,用BufferedWriter bufw = new BufferedWriter(fw);
稍微扩展一下:想要把这个录入的数据按照指定的编码表将数据存到文件中,那么要使用转换流:
OutputStreamWriter(outstream,charset)

目的同样要用Writer,
设备:硬盘,文件.使用FileWriter用默认编码表.但在存储的时候要指定编码表,所以要使用的对象应该是OutputStreamWriter.而最终该转换流对象要接收一个字节输出流,而且还可以操作文件的字节输出流,因此要使用FileOutputStream作为对象操作.
OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream(),“utf-8”);
需要高效吗?需要!

BufferedWriter bufw = new BufferedWriter(osw);
所以记住转换流什么时候使用:设计到字符编码转换时,需要用到转换流.
练习:将一个文本数据打印到控制台上:按照以上格式完成三个明确.
改变标准输入输出数据.

File概述:
File类:用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作.File对象可以作为参数传递给流的构造函数.
创建对象:
File f = new File(“a.txt”); //将a.txt封装成File对象.因为该文件不一定存在,因此它可以将已有的或者未出现的文件或者文件夹封装成对象.
File类常见方法:

1. 创建

boolean createNewFile();第一次创建时返回true,如果存在返回false.和输出流不一样,输出流对象一建立就会创建文件,如果文件已经存在会覆盖文件.
创建目录:File dir = new File(); dir.mkdir();只能创建一级目录,mkdirs()可以创建多级目录.

2. 删除

Boolean delete();
deleteOnExit();

3. 判断

Boolean exists();
Is Directory(); Is file();不存在或不是时返回false.在判断文件对象是否是文件或者目录的时候,必须先判断该文件对象封装的内容是否存在.通过exists()判断.因为创建的时候文件夹的名称也可以为a.txt文件夹.
IsHidden();判断抽象路径名表示的文件是否是一个隐藏文件.
IsAbsolute();判断是否是绝对路径.即使文件不存在也会返回true.

4. 获取信息

String getName();
String getPath();
String getParent();//该方法返回的是绝对路径中的文件父目录.如果获取的是相对路径则返回null.如果相对路径中有上一层目录那么该目录就是返回结果.
Long lastModified();
Long length();
Boolean renameto();
f1.renameTo(f2); f1的文件移动到f2并重命名文件.

实例

//列出所给路径下的所有文件
package iodemo;

import java.io.File;

public class ListFilesDemo1 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		File f = new File("D:\\Movies");
		list(f);
	}

	public static void list(File f) {
		File[] files = f.listFiles();
		for (File file : files) {
			if (!file.isHidden()) {
				if (file.isDirectory()) {
					System.out.println(file);
					list(file);
				} else
					System.out.println(file);
			}
		}
	}

}


//将以上代码改进以便于文件信息存储到文件中去
package iodemo;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class JavaFileListDemo {
	public static void main(String[] args) {
		File dir = new File("D:\\Study\\Codes\\Java\\test\\src");
		List<File> list = new ArrayList<>();
		fileToList(dir, list);
		/*
		 * for (File f : list) { System.out.println(f); }
		 */
		writeToFile_2(list, new File("a.txt"));
	}

	public static void fileToList(File dir, List<File> list) {
		File[] files = dir.listFiles();

		for (File f : files) {
			if (f.isDirectory())
				fileToList(f, list);
			else {
				if (f.getName().endsWith(".java"))
					list.add(f);
			}
		}
	}

	public static void writeToFile(List<File> list, File javaListFile) {
		BufferedWriter bw = null;
		try {
			bw = new BufferedWriter(new FileWriter(javaListFile));
			for (File f : list) {
				String path = f.getAbsolutePath();
				bw.write(path);
				bw.newLine();
				bw.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (bw != null)
				try {
					bw.close();
				} catch (IOException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
		}
	}
	public static void writeToFile_2(List<File> list,File javaFileList){
		BufferedWriter bw = null;
		try{
			bw = new BufferedWriter(new FileWriter(javaFileList));
			for(File f:list){
				String path = f.getAbsolutePath();
				bw.write(path);
				bw.newLine();
				bw.flush();
			}
		}catch(IOException e){
			e.printStackTrace();
		}finally{
			if(bw!=null)
			{
				try{
					bw.close();
				}catch(IOException e){
					e.printStackTrace();
				}
			}
		}
	}
}

Properties对象是HashTable的子类.,也就是说他具备了Map集合的特点,里面存储的键值对都是字符串.
Properties是集合中和IO技术相结合的集合容器,该对象的特点:
可以用于键值对形式的配置文件.
比如ini文件,里面有属性,有属性值.
设置和获取元素:

setAndGet()
{
	Properties prop = new Properties();
	Properties.setProperty("张三","20");
	Stringvalue = Prop.getProperty("张三");
	Sop.sop(prop);
	Sop.sop(value);
	Set<String> names = prop.stringPropertyNames();
	For(String s :names){
	Sop.sop(s+prop.getProperty(s));
	}
}

//里面有个stringPropertyNames()
如何将流中的数据存储到集合中?
想要把info.txt中键值数据存到集合中进行操作.

1. 用一个流和info.txt关联
2. 读取一行数据,将该行数据用"="进行切割
3. 等号左边作为键,右边作为值存入到Properties集合中即可.

纯文本,读取,需要缓冲,需要只读一行,FileReader.BufferedReader

BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));
String line =null;
Properties prop = new Properties();
While(line=bufr.readLine())!=null){
	String[]arr=line.split("=");
	Prop.setProperty(arr[0],arr[1]);
	}
Bufr.close();
Sop.sop(prop);
Properties类里面有一个方法叫做load
将流中的数据加载进集合那么就是
Properties prop = new Properties();
FileInpurStream fis = new FileInputStream("info.txt";)
prop.load(fis);
Prop.list(System.out);

我吧数据加载完以后发现文档里面年龄错了,这时候应该怎么办?
集合已经有了,直接操作集合里面的元素就可以修改了.
Prop.setProperty("","");
除了加载流中的数据,还可以将集合中的数据存储到制定的文件当中.
setProperty()改变的是内存,但是store()是存储到文件中.

Properties prop = new Properties();
FileInpurStream fis = new FileInputStream("info.txt";)
FileOutputStream fos = new FileOutputStream(info.txt);
Prop.setProperty("","");
prop.load(fis);//将流中的数据加载进集合
Prop.store(fos,"test");
Fis.close();
Fos.close();
Prop.list(System.out);

Properties的特点:可用于键值对形式的配置文件,那么在加载的时候,需要数据有固定的格式:“键=值”.

用于记录应用程序运行次数:
如使用次数已到,那么给出注册提示.
很容易想到的是计数器.可是该计数器随着程序的运行而在内存中存在,并进行了自增,可是随着该应用程序的退出,计数器在内存中消失.下一次再启动该程序,又重新开始从0记数,这样不是我们想要的.
我们想要的是:
即使结束,该计数器的值也存在,下次程序启动后会先加载该计数器并加1后重新存储.所以需要建立一个配置文件,用于记录该软件的使用次数.
配置文件使用键值对的形式,这样便于读取数据并操作数据.键值对数据是Map集合,数据是以文件形式存储,使用io技术.那么Map+io = Properties.
因此配置文件可以实现应用程序数据共享.

Void main()
{
Properties prop=new Properties();
File file=new File("count.ini");
//为了不让报异常,先把文件封装成对象.
If(!file.exists())
File.createNewFile();

FileInputStream fis = new FileInputStream(file);
Prop.load(fis);
String value = Prop.getProperty("time");
Int Count=0;
If(value!=null)
{
Count = Integer.parseInt(value);
If (count>=5)
{
Sop.sop("您好,使用次数已到.请缴费继续使用.");
}
}
Count++;
Prop.setProperty("time",count+"");
FileoutputStream fos = new FileOutputStream(file);
Prop.store(fos,"");
Fos.close();
Fis.close();
}

配置文件有两种:一种Properties,另一种是xml.

<persons>
<person id=001>
<name>zhangsan</name>
<age>30</age>
<address>北京</address>
</person>
<person>
</person>
</persons>

IO流:
PrintWriter:字符打印流.构造函数可以直接操作文件.

1. File对象 File
2. 字符串路径 String
3. 字符输出流 OutputStream
4. 字符输出流:Writer

PrintStream继承于OutputStream.该流提供了打印方法,将各种数据类型的数据都原样打印.是字节打印流.构造函数可以直接操作文件.

1. File对象 File
2. 字符串路径 String
3. 字符输出流 OutputStream
package iodemo;

import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

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

		PrintWriter pw = new PrintWriter(new FileWriter("a.txt"),true);

		String line = null;
		while((line=br.readLine())!=null)
		{
			if("over".equals(line))
				break;
			pw.println(line.toUpperCase());
//			pw.flush();
		}
		br.close();
		pw.close();
	}
}

序列流:
SequenceInputStream

这个流表示的是其他输入流的逻辑串联.从输入流的有序集合开始,从第一个输入流开始读取到文件末尾然后接着从第二个输入流读取直到最后一个输入流的文件末尾为止.

package iodemo;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;

public class SequenceInputStreamDemo {

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
		FileInputStream is2 = new FileInputStream("io");
		FileInputStream is1 = new FileInputStream("times.ini");
		Vector<FileInputStream> v = new Vector<>();
		v.addElement(is1);
		v.addElement(is2);
		Enumeration<FileInputStream> en = v.elements();

		SequenceInputStream sis = new SequenceInputStream(en);

		OutputStream osw = System.out;
		int i = 0;
		byte[] b = new byte[1024];
		while ((i = sis.read(b)) != -1) {
			osw.write(b, 0, i);
		}
		method();
	}
	public static void method() throws IOException{
		FileInputStream fis1 = new FileInputStream("io");
		FileInputStream fis2 = new FileInputStream("times.ini");
		Vector<FileInputStream> v = new Vector<>();
		
		Enumeration<FileInputStream> en = v.elements();
		
		v.addElement(fis1);
		v.addElement(fis2);
		
		SequenceInputStream sis = new SequenceInputStream(en);
		
		OutputStream os = System.out;
		
		int num = 0;
		byte[] b = new byte[1024];
		while((num=sis.read(b))!=-1)
		{
			os.write(b, 0, num);
		}
		fis1.close();
		fis2.close();
		os.close();
		sis.close();
	}

}

切割与合并文件示例:

package iodemo;

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

public class FileSplitDemo {

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
		fileSplit();
		fileMerge();
	}

	public static void fileSplit() throws IOException {
		FileInputStream fis = new FileInputStream("1.jpg");

		FileOutputStream fos = null;

		byte[] buf = new byte[1024 * 1024];
		int len = 0;
		int count = 0;
		while ((len = fis.read(buf)) != -1) {
			fos = new FileOutputStream("D:\\splitFiles\\" + count++ + ".part");
			fos.write(buf, 0, len);
			fos.close();
		}
		if (fos != null)
			fos.close();
		fis.close();
	}

	public static void fileMerge() throws IOException{
/*		Vector<FileInputStream> v = new Vector<>();
		int count = 0;
		FileInputStream f1 = new FileInputStream("D:\\splitFiles\\"+count+++".part");
		FileInputStream f2 = new FileInputStream("D:\\splitFiles\\"+count+++".part");
		FileInputStream f3 = new FileInputStream("D:\\splitFiles\\"+count+++".part");
		FileInputStream f4 = new FileInputStream("D:\\splitFiles\\"+count+++".part");
		
		Enumeration<FileInputStream> en = v.elements();
		*/
		ArrayList<FileInputStream> al = new ArrayList<>();
		for(int x = 0;x<=4;x++){
			al.add(new FileInputStream("D:\\splitFiles\\"+x+".part"));
		}
		
		Iterator<FileInputStream> it = al.iterator();
		
		Enumeration<FileInputStream> en = new Enumeration<FileInputStream>(){
			public boolean hasMoreElements(){
				return it.hasNext();
			}
			public FileInputStream nextElement(){
				return it.next();
			}
		};
		
		
		SequenceInputStream sis = new SequenceInputStream(en);
		FileOutputStream fos = new FileOutputStream("D:\\splitFiles\\1.jpg");
		
		byte[] buf = new byte[1024];
		int len = 0;
		while((len=sis.read(buf))!=-1){
			fos.write(buf,0,len);
		}
		fos.close();
		sis.close();
		
	}
}

对象流:ObjectInputStream与ObjectOutputStream
被操作的对象需要实现Serializable(标记接口).因此这个接口没有方法,只是一个标记接口.

package iodemo;

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

public class ObjectStreamDemo {

	public static void main(String[] args) throws Exception {
		// TODO 自动生成的方法存根
		writeObj();
		readObj();
	}
	public static void writeObj() throws Exception{
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("objectStream"));
		oos.writeObject(new Person("lisi",4));
		oos.writeObject(new Person("阿大声道",33));
		oos.writeObject(new Person("zhangsan",4));
		oos.close();
	}
	public static void readObj() throws  IOException, ClassNotFoundException{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("objectStream"));
		Object obj = new Object();
		while((obj=ois.readObject())!=null)
			System.out.println(((Person)obj).toString());
		ois.close();
	}

}
class Person implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1756896262500096205L;
	private String name;
	transient int age;
	Person(String n,int a){
		this.name = n;
		this.age = a;
	}
	public String toString(){
		return name + age;
	}
	/*静态不能被序列化.不想被序列化也可以加上transient关键字.保证值在堆内存中不被写入到文件.读取的时候,依赖于Person.class文件.如果person类改变了,那么加一句public static final long serialVersionUID=42L;就可以.但是静态成员是不能被序列化的.因为只序列化栈内存.
如果不想被序列化也可以加个transient关键字.
*/
}

管道流:PipedInputStream和PipedOutputStream
输入和输出可以直接进行连接,结合线程使用.输入流提供要写入管道输出流的所有数据字节.
设计到多线程的io流就是管道流.
怎么连接这两个对象呢?
创建对象的时候,要么直接传入对方,要么使用connect()方法.

package iodemo;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class PipedStreamDemo {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		try {
			out.connect(in);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(r).start();
		new Thread(w).start();
	}
}

class Read implements Runnable {
	private PipedInputStream in;

	Read(PipedInputStream in) {
		this.in = in;
	}

	public void run() {
		try {
			byte[] buf = new byte[1024];
			int len = in.read(buf);
			System.out.println("读取前..");
			Thread.sleep(1000);
			System.out.println(new String(buf, 0, len));
			in.close();
		} catch (Exception e) {
			throw new RuntimeException("read fail");
		}
	}

}

class Write implements Runnable {
	private PipedOutputStream out;

	Write(PipedOutputStream out) {
		this.out = out;
	}

	public void run() {
		try {
			System.out.println("wait for 2s");
			Thread.sleep(2000);
			out.write("管道流来啦!".getBytes());
			out.close();
		} catch (Exception e) {
			// TODO: handle exception
			throw new RuntimeException("write fail");
		}
	}
}

RandomAccessFile
直接继承Object,并不是IO体系中的子类,但是存在于io包中,因为它具备读和写的功能,内部封装了一个数组,而且通过指针对数组的元素进行操作.可以通过getFilePointer()获取指针位置,seek改变指针的位置.,支持对随机访问文件的读取和写入.
其实完成读写的原理就是内部封装了字节输入输出流InputOutputStream.为什么不是字符流呢?因为访问的是一个byte[].
构造方法:
通过构造方法可以看出该类只能操作文件.而且操作文件还有模式:
r w rws rwd
四种模式.

package iodemo;

import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo {

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
		writeFile_2();
		readFile();
	}
	public static void writeFile() throws IOException{
		RandomAccessFile raf = new RandomAccessFile("a.txt", "rw");
		raf.write("李四".getBytes());
		raf.writeInt(258);
		raf.write("王五".getBytes());
		raf.writeInt(28);
		
		raf.close();
	}
	public static void readFile() throws IOException{
		RandomAccessFile raf = new RandomAccessFile("a.txt","r");
		
		byte[] buf = new byte[4];
//		raf.seek(8);
		raf.skipBytes(8);
		raf.read(buf);
		
		System.out.println(new String(buf));
		System.out.println(raf.readInt());
		raf.close();
	}
	public static void writeFile_2() throws IOException{
		RandomAccessFile raf = new RandomAccessFile("a.txt","rw");
		
		raf.seek(2*8);
		
		raf.write("周七".getBytes());
		raf.writeInt(103);
		raf.close();
	}

}
/*
该类不算io体系中的子类而是直接继承自Object.
但是是IO中的成员.因为具备读写功能,内部封装了一个数组,而且通过指针对数组
中的元素进行操作,可以通过getFilePointer()获取指针位置,通过seek()改变指针的位置.

其实完成读写的原理就是内部封装的字节io流.
通过构造函数可以看出该类只能操作文件.而且操作文件还有模式需要遵从.
如果要操作的文件不存在会自动创建.模式只读则不会创建文件并报出异常.如果存在不会覆盖.

这个类有什么用呢?
可以实现随机读写,可以用多线程同时操作一个文件,来完成分段写入,提高效率.
*/

文件的存储是一个数组.想要直接读取"王五",那就需要用到seek()方法.
调整数组的指针(游标).
Raf.seek(8);就可以直接指定指针的位置从8开始.这就要求你的数据是有规律的可以算出来的.
还有一个方法叫做skipBytes(),跳过指定的字节数,skipBytes(8),效果与seek(8)同样,只能往大的角标跳,seek()方法则比较灵活!

还可以随机地往数组里面写入数据.自己指定位置写入数据.那么如果设置位置为0,那么会覆盖原有数据.
如果模式为只读则不会创建文件,会去读取一个已存在的文件,如果该文件不存在则出现FileNotFoundException异常.
因为随机写入读取的特性,因此这个类可以实现分段写入数据.
先把数据分成段,然后用多个线程负责多段.
下载文件的时候就可以使用这个原理!多线程会更快.其他的流不可以使用这个方法.

DataInputStream DataOutputStream

在进行构造的时候,将基本数据类型和字节流结合.

package iodemo;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class DataStreamDemo {

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
//		writeData();
		writeUTF();
		readData();
		readUTf();
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"), "utf-8");
		osw.write("你好");
		osw.close();
	}
	public static void writeUTF() throws IOException{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utf.txt"));
		dos.writeUTF("李四");
		dos.close();
	}
	public static void readUTf() throws IOException{
		DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
		String s = dis.readUTF();
		System.out.println(s);
		dis.close();
	}
	public static void writeData() throws IOException{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utf.txt"));
		dos.writeInt(234);
		dos.writeBoolean(true);
		dos.writeDouble(1.2345);
		dos.close();
	}
	public static void readData() throws IOException{
		DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
		
		System.out.println(dis.readInt());
		System.out.println(dis.readBoolean());
		System.out.println(dis.readDouble());
		dis.close();
		
	}
}

ByteArrayInputStream
这个文件调用过底层资源吗?
这个流是不用关闭的,这个方法在关闭后还可以被使用,这个流没有涉及到系统底层资源.缓冲区会随着数据的不断写入而自动增长!可使用toByteArray() toString()方法获取数据.
在构造的时候需要接受数据源而且数据源是一个字节数组.ByteArrayOutputStream:在构造的时候不用定义数据目的,因为该对象中内部已经封装了一个可变长度的字节数组,也就是数据目的地.因为这两个流对象操作的都是数组没有使用系统资源,所以不用关闭.

ByteArrayInputStream bis = new ByteInputStream("ABCDEFG".getBytes());

ByteArrayOutputStream bos =new ByteOutputStream();
System.out.println(bos.size());
//没有数据因此返回值为0

Int by = 0;
While((by=bis.read())!=0)
{
bos.write(by);
}
System.out.print(bos.toString());
package iodemo;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteArrayStreamDemo {

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
		ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		
		int b = 0;
		while((b = bis.read())!=-1) {
			bos.write(b);
		}
		
		System.out.println(bos.size());
		System.out.println(bos.toString());
		bos.writeTo(new FileOutputStream("a.txt"));
	}
}
/*
 * ByteArrayInputStrem 构造时需要接收数据源,而且数据源是一个字节数组.
 * 
 * ByteArrayOutputStrem 构造时不用定义数据目的,因为内部已经封装了可变长度的字节数组.
 * 因为这俩对象都操作数组,没有使用系统资源,因此不用关闭.
 */

操作字符数组的类:CharArrayWriter CharArrayReader

操作字符串的类:StringReader StringWriter

字符流的出现就是方便操作字符数据,里面添加了一个编码表.要通过两个对象来完成:
InputStreamReader
OutputStreamWriter
PrintStreamWriter
PrintStreamReader

为了让计算机可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这张表就叫做编码表.

常见编码表:utf-8,GBK.
涉及到编码表的流就是转换流了.
InputStreamReader
OutputStreamWriter
编码:字符串变字节数组.
解码:字节数组变字符串

String -->byte[]:str.getBytes(charsetName);
Byte[]–>String:new String(byte[],charsetName);

**练习:**有五个学生,每个学生有3门课的成绩,从键盘输入以上数据,姓名,三门课成绩.输入格式为 zhangsan,30,40,50计算出总成绩.
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.TXT"中.

1. 描述学生对象
2. 定义一个可以操作学生对象的工具类

思想:

1. 通过键盘录入数据,并将该行数据中的信息取出封装成学生对象
2. 因为学生对象有很多,那么久需要存储到集合,因为要对学生的总分排序,因此可以使用TreeSet.
3. 将集合中的信息写入到一个文件中
package iodemo;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class IOTest {

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
		Comparator<Student> cmp = Collections.reverseOrder();
		Set<Student> s = StudentInfoTool.getStudents(cmp);
		StudentInfoTool.write2File(s);
	}

}

class StudentInfoTool {
	public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		Set<Student> ts = new TreeSet<>(cmp);
		String line = null;
		System.out.println("Input Info");
		if (cmp == null)
			ts = new TreeSet<Student>();
		else
			ts = new TreeSet<Student>(cmp);
		while ((line = br.readLine()) != null) {
			if (line.equals("over"))
				break;
			String[] s = line.split(",");
			if (s.length == 4) {
				ts.add(new Student(s[0], Integer.valueOf(s[1]), Integer.valueOf(s[2]), Integer.valueOf(s[3])));
			} else {
				System.out.println("输入有误!请重新输入!");
				System.out.println();
			}
		}
		br.close();
		return ts;
	}

	public static Set<Student> getStudents() throws IOException {
		return getStudents(null);
	}

	public static void write2File(Set<Student> ts) throws IOException {
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("stud.txt")));

		for (Student s : ts) {
			bw.write(s.toString() + "\t");
			bw.write(s.getSum() + "");
			bw.newLine();
			bw.flush();
		}
		bw.close();
	}
}

class Student implements Comparable<Student> {
	private String name;
	private int math;
	private int chn;
	private int eng;
	private int sum;

	Student(String name, int math, int chn, int eng) {
		this.name = name;
		this.math = math;
		this.chn = chn;
		this.eng = eng;
		sum = math + chn + eng;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getMath() {
		return math;
	}

	public void setMath(int math) {
		this.math = math;
	}

	public int getChn() {
		return chn;
	}

	public void setChn(int chn) {
		this.chn = chn;
	}

	public int getEng() {
		return eng;
	}

	public void setEng(int eng) {
		this.eng = eng;
	}

	public int getSum() {
		return sum;
	}

	public void setSum(int sum) {
		this.sum = sum;
	}

	public int compareTo(Student s) {
		int num = new Integer(this.sum).compareTo(new Integer(s.sum));
		if (num == 0)
			return this.name.compareTo(s.name);
		return num;
	}

	public int hashCode() {
		return name.hashCode() + sum * 37;
	}

	public boolean equals(Object obj) {
		if (!(obj instanceof Student))
			throw new ClassCastException("ClassCastException");
		Student s = (Student) obj;
		return this.name.equals(s.name) && this.sum == s.sum;
	}

	public String toString() {
		return "Student[" + name + "," + math + "," + chn + "," + eng + "]";
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值