黑马程序员——IO流——IO体系、装饰设计模式、流对象的选择

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

IO流:

    用来处理设备之间的数据传输,Java对数据的操作时通过流的方式。Java用于操作流的对象都是在IO包中。

    输出流和输入流相对与内存设备(可以理解为流对象本身,数据进入流为输入流,数据传出流为输出流)。

   Java提供了很多的流对象,对应不同的流操作,本意是好的,老程序员都能很好的调用合适的流对象为自己搭建代码,但是对于初学者来说往往是痛苦的,

FileInputStream/FileOutputStream/FileReader/FileWriter就够让人觉得奔溃了。为什么仅仅是对文件的操作就要分这么细致呢?何况这对于IO流体系只是冰山一角。

JavaIO体系看似庞大复杂,其实还是有规律的,我们来了解一下结构:

1. 其对称性质:InputStream OutputStream, Reader Writer,他们分别是一套字节输入-输出,字符输入-输出体系

2. 原始处理器(适配器)与链接流处理器(装饰器)

字节流体系:


字符流体系:


我们仔细查看上述的两张图片,就能看出其实还是基本上能一一对应的。我们可以根据流对象的前缀来判断流的功能,我们先简单使用一下,看看是不是这样:

需求:将一些文字存储到硬盘一个文件中。

记住:如果要操作文字数据,建议优先考虑字符流。

而且要将数据从内存写到硬盘上,要使用字符流中的输出流:Writer。

硬盘的数据基本体现是文件,希望找到一个可以操作文件的Writer:FileWriter。

示例:

package com.leaf.iotest;

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterDemo {

	public static void main(String[] args) throws IOException {
		String path = "d:\\dome.txt";
		//创建一个FileWriter对象,传入一个路径
		FileWriter fw = new FileWriter(path);
		//使用FileWriter时,假如路径下没有文件会自动创建,如何依据有该文件,则会覆盖
		
		//使用write来吧数据写入到流中
		fw.write("haha,wo lai le");
		//把流中的数据刷新出去,注:字符流才需要
		fw.flush();
		//关闭流
		fw.close();
	}
}
result:


注:1.因为流对象调用的系统的资源,在使用完毕后一定要关闭。

         2.在FileWriter的构造函数中,添加一个参数true可以在创建文件的时候进行续写,而不是覆盖。即:FileWriter fw = new FileWriter(path,true);

IO的异常处理方式(所有涉及IO的方法写法都是大同小异的):

示例:

package com.leaf.iotest;

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterDemo {

	public static void main(String[] args) {
		String path = "d:\\dome.txt";
		
		FileWriter fw = null;
		try {
			fw = new FileWriter(path);
			fw.write("haha,wo lai le");
			fw.flush();
		} catch (IOException e) {
			System.out.println("我在处理异常");
		}finally{
			if(fw!= null)
			try {
				fw.close();
			} catch (IOException e) {
				System.out.println("关闭流异常");
			}finally{
				fw = null;
			}
		}
	}
}

需求:读取一个文件,将读取的字符打印到控制台;

分析:来源:文件

            目的:控制台

示例:

package com.leaf.iotest;

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

public class FileReaderDemo {

	public static void main(String[] args) {
		String path = "d:\\dome.txt";
		FileReader fr = null;
		try {
			fr = new FileReader(path);
			int ch = 0;
			while((ch = fr.read())!=-1){
				System.out.println((char)ch);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if(fr!= null)
			try {
				fr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}finally{
				fr = null;
			}
		}
		
	}
}
result:


查看结果,read方法是一个个字节在读取。

那有更高效的方法吗?查看api  int read(char[]);

示例:

package com.leaf.iotest;

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

public class FileReaderDemo {

	public static void main(String[] args) {
		String path = "d:\\dome.txt";
		FileReader fr = null;
		try {
			fr = new FileReader(path);
			char[] chs = new char[1024];
			int len = 0;
			while((len = fr.read(chs))!=-1){
				System.out.println(new String(chs,0,len));
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if(fr!= null)
			try {
				fr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}finally{
				fr = null;
			}
		}
		
	}

}
result:

经过上面的俩个类的使用,我们把上述两个类进行结合。

需求:将d盘下的demo.txt复制到e盘下,名字叫做demo_copy.txt

分析:

操作:字符

         来源:文件 FileReader

         目的:文件     FileWriter

         示例:

package com.leaf.iotest;

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

public class CopyFileDemo {

	public static void main(String[] args) throws IOException {
		String path_src = "d:\\dome.txt";
		String path_goal = "e:\\File\\demo_copy.txt";
		//创建文件的读取和输出流
		FileReader fr = new FileReader(path_src);
		FileWriter fw = new FileWriter(path_goal);
		int len = 0;
		char[] chs = new char[1024];
		while ((len = fr.read(chs))!= -1) {
			fw.write(chs,0,len);
		}
		//记得关闭流0 
		fw.close();
		fr.close();
	}
}
result:

运行前

运行后:


字符流缓冲区

缓冲区的出现提高了对数据的读写效率

对应类:

BufferedWriter         BufferedReader

作用:在流的基础上对流的功能的、进行了增强

示例:能提高写入的效率

package com.leaf.iotest;

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

public class BufferedDemo {

	public static void main(String[] args) throws IOException {
		String path_src = "d:\\dome.txt";
		String path_goal = "e:\\File\\demo_copy.txt";
		//创建文件的读取和输出流
		//为了提高读取文件的效率,使用读取文件的字符流缓冲区
		FileReader fr = new FileReader(path_src);
		BufferedReader bufr = new BufferedReader(fr);
		//为了提高写入文件的效率,使用写入文件的字符流缓冲区
		FileWriter fw = new FileWriter(path_goal);
		BufferedWriter bufw = new BufferedWriter(fw);
		String buf = null;
		while((buf = bufr.readLine())!= null){
			bufw.write(buf);
		}
		bufr.close();
		bufw.close();
	}
}
result:

运行前:

运行后:





装饰设计模式:

对原有的类进行了功能的改变,增强。

示例;

package com.leaf.iotest;

public class PersonDemo {

	public static void main(String[] args) {
		Person p = new Person();
		NewPerson np = new NewPerson(p);
		np.chifan();
		System.out.println("-----------");
		NewPerson2 np2 = new NewPerson2();
		np2.chifan();
	}
}
/**
 * 采用装饰类的方式对类的功能进行增强
 * @author Administrator
 *
 */
class NewPerson{
	private Person p;
	public NewPerson(Person p) {
		this.p = p;
	}
	public void chifan(){
		System.out.println("开胃酒");
		p.chifan();
		System.out.println("来一根");
	}
}
/**
 * 采用继承的方式对类的功能进行增强
 * @author Administrator
 *
 */
class NewPerson2 extends Person{
	public void chifan(){
		System.out.println("开胃酒");
		super.chifan();
		System.out.println("来一根");
	}
}
class Person{
	public void chifan() {
		System.out.println("吃饭!");
	}
	
}
result:

继承和装饰都能对类的功能进行拓展,但是他们还是很大的区别的。

继承体系:会因为对功能的拓展会越来越臃肿,今天添加要添加一个功能,明天要添加另一个不相关的功能,其中一个子类有了这个功能,那其他相似的子类呢? 样一级级往下加,继承体系就会十分臃肿,而且实现起来很难。而装饰类,只要将需要进行装饰的类传进装饰类中,这样就不必一级级的对每个子类进行实现增强的子类。

好处:装饰类比继承灵活

         一般情况下:装饰类和被装饰类都必须所属同一个接口或者父类。

IO常用字节流

字符流只能操作字符,如果操作了图片信息,很可能就让图片不能进行显示。而字节流就能操作所以文件。

示例:

package com.leaf.iotest;

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

public class ByteStreamDemo {

	public static void main(String[] args) throws IOException {
		String path_src = "d:\\1.png";
		String path_goal = "e:\\File\\1_copy.png";
		FileInputStream fis = new FileInputStream(path_src);
		FileOutputStream fos = new FileOutputStream(path_goal);
		byte[] buf = new byte[1024];
		int len = 0;
		while((len = fis.read(buf))!= -1){
			fos.write(buf, 0, len);
		}
		fis.close();
		fos.close();
	}
}
result:

运行前

运行后:



在字节流中可以不用flush,字符流要进行刷新是因为字符流在加载数据到内存中时,数据会进行编码,所以数据会在内存中,必须刷新出去,而字节流两边操作的都是字节,不行进行编码,直接传输就好。

字节流也要缓冲区,和字符流的用法一样。这里不进行演示。

 

需求:读取一个键盘录入的数据,并打印在控制台上。

分析:键盘本身就是一个标准的输入设备,对于java来说,这种输入设备都有对应的对象。

package com.leaf.iotest;

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

public class ReaderFromKey {

	public static void main(String[] args) throws IOException {
		readFromKey();
	}
	public static void readFromKey() throws IOException{
		InputStream in = System.in;
		int ch = in.read();//阻塞方法
		System.out.println(ch);
		ch = in.read();//阻塞方法
		System.out.println(ch);
		ch = in.read();//阻塞方法
		System.out.println(ch);
		in.close();
	}
}

result:


ps.

1.      获取键盘录入数据,然后将数据流向显示器,那么显示器就是目的地。

通过System类的setIn和setOut方法可以对默认设备进行改变。

System.setIn(newFileInputStream(“1.txt”));//将源改成文件1.txt;

System.setOut(newPrintStream(“2.txt”));//将目的改成文件2.txt;

因为是字节流处理的是文本数据,可以转换成字符流,操作更方便。

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

BufferedWriterbufw = new BufferedWriter(new OutputStreamWriter(System.out));

 

2.      默认的输入和输出系统不需要关,他会随着系统的结束而消失。

示例:

获取用户录入的数据并将数据变成大写显示在控制台上,如果用户输入的是over,结束键盘录入。

思路:

1.      因为键盘录入只读取一个字节,要判断是否是over,需要将读取的字节拼成字符串。

2.      需要一个容器:StringBuilder

3.      在用户回城之前将录入的数据变成字符串判断即可。

package com.leaf.iotest;

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

public class ReaderFromKey {

	public static void main(String[] args) throws IOException {
		readFromKey();
	}
	public static void readFromKey() throws IOException{
		StringBuilder sb = new StringBuilder();
		
		InputStream in = System.in;
		int ch =0;
		while((ch = in.read())!=-1){
			//判断标记
			if(ch == '\r')
				continue;
			if(ch == '\n'){
				String temp = sb.toString();
				if("over".equals(temp))
					break;
				System.out.println(temp.toUpperCase());
				sb.delete(0, sb.length());
			}else{
				//将读取的数据存入StringBuilder
				sb.append((char)ch);
			}
		}
	}
}

result:


重点:转换流

由来:字符流和字节流之间的桥梁,方便字符流与字节流之间的操作。

应用:字节流的数据都是字符时,转换成字节流操作更高效。

转换流:

InputStreamReader:字节到字符的桥梁,解码。

         OutputStreamWriter:字符到字节的桥梁,编码。

 

用转换流实现上面的示例:

package com.leaf.iotest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class TranStreamDemo {

	public static void main(String[] args) throws IOException {
		//字节流
		InputStream in = System.in;
		//
		InputStreamReader isr = new InputStreamReader(in);
		
		BufferedReader bufr = new BufferedReader(isr);
		
		String line = null;
		
		while((line = bufr.readLine())!=null){
			if("over".equals(line))
				break;
			System.out.println(line.toUpperCase());
		}
	}
}

result:


ps

OutputStreamWriter这里不做演示,有兴趣的朋友可以自己设计一段代码,和上述代码相似。

 

转换流的综合应用:

package com.leaf.iotest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class TranStreamDemo {

	public static void main(String[] args) throws IOException {
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
		String line = null;
		while((line = bufr.readLine())!=null){
			if("over".equals(line))
				break;
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}
	}
}

result:


需求:将键盘录入的数据写入到一个文件中。

思路:

操作对象:字符,但是键盘读取的是System.in为字节流

源:键盘:使用缓冲流和转换流

目的:文件:FileOutputStream  转换流  缓冲流

package com.leaf.iotest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class TranStreamDemo1 {

	public static void main(String[] args) throws IOException {
		BufferedReader bufr = new BufferedReader(new InputStreamReader(
				System.in));
		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(
				new FileOutputStream("E:\\File\\1.txt")));
		String line = null;
		while ((line = bufr.readLine()) != null) {
			bufw.write(line);
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
		bufw.close();
	}
}

result:



其他的读取写入方法大同小异,那么可以看出什么规律吗?

流的操作规律:

我们在一开始就说过,java中的流对象太多了,又有很多为了增强功能进行设计的装饰类,开发的时候很容易就因此而头昏眼花,其实我们只要能按照以下的思路进行判断,流其实还是不难的。

1.      明确源和目的

源:InputStream Reader

目的:OutputStreamWriter

2.      明确数据是否为纯文本数据

源:

是纯文本:Reader

           否:InputStream

目的:

 是纯文本:writer

           否:OutputStream

3.      明确具体的设备

源设备:

硬盘:File

键盘:System.in

内存:数组Array

网络:Socket

目的设备:

硬盘:File

控制台:System.out

内存:数组Array

网络:Socket

4.      是否需要其他额外的功能

是否需要高效的读取和写入(缓冲区),BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream。

我们使用上面的思路进行设计程序

需求:复制一个文本文件

1、  明确源和目的。

源:InputStream Reader

目的:OutputStream Writer

2、  纯文本?

是!

源:reader

目的:writer

3、  明确具体设备

源:

硬盘:File

目的:

硬盘:File

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

FileWriter fw =new FileWriter(“1_copy.txt”);

4、需要额外功能吗?

需要

BufferedReaderbufr = new BufferedReader(new FileReader(“1.txt”));

BufferedWriterbufw = new BufferedWriter(new FileReader(“1_copy.txt”));

需求:读取键盘录入的信息,并写道一个文件中

1、     明确源和目的。

源:InputStream Reader

目的:OutputStreamWriter

2、  纯文本?

是!

源:reader

目的:writer

3、  明确具体设备

源:

键盘:System.in

目的:

硬盘:File

FileReader fr =new System.in;

FileWriter fw =new FileWriter(“1_copy.txt”);

4、需要额外功能吗?

需要

转换。将字节流转换成字符流

InputStreamReaderisr = new InputStreamReader(System.in);

FileWriter fw =new FileWriter(1_copy.txt);

还需要其他功能吗?

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

BufferedWriterbufw = new BufferedWriter(new FileWriter(1_copy.txt));

需求:将一个文本文件的数据显示在控制台上

1、     明确源和目的。

源:InputStream Reader

目的:OutputStreamWriter

2、  纯文本?

是!

源:reader

目的:writer

3、  明确具体设备

源:

硬盘:File

目的:

控制台:System.in

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

FileWriter fw = System.in;//PrintStream;

4、需要额外功能吗?

需要

转换。将字节流转换成字符流

FileReader fr =new FileLLReader(“1.txt”);

OutputStreamWirterosw = new OutputStreamWriter(System.in);

还需要其他功能吗?

BufferedReaderbufr = new BufferedReader(new FileReader(“1.txt”));

BufferedWriterbufw = new BufferedWriter(new OutputStreamWriter(System.out));

 

流和编码:

任何java识别的字符数据使用的都是Unicode码表,但是FileWriter写入本地文件使用的是本地编码,GBK。

而OutputStreamWriter可使用指定的编码将要写入流中的字符编码成字节。

示例:

package com.leaf.iotest;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class TranStreamDemo2 {

	public static void main(String[] args) throws IOException {
		//我们可以在转换流中定义编码表
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("e:\\File\\2.txt"),"UTF-8");
		osw.write("我来了!");
		osw.close();
	}
}
result:

运行前:

运行后:

ps:UTF-8编码,一个中文三个字节,编码解码的问题这里不做详细介绍,需要了解的朋友可以到网上去看。

读取刚刚2.txt文件中的数据:

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

public class TranStreamDemo2 {

	public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("E:\\File\\2.txt");
		char[] buf = new char[1024];
		int len = 0;
		while((len = fr.read(buf))!=-1){
			System.out.println(new String(buf,0,len));
		}
		fr.close();
	}
}

//我把Eclipse的编码改成utf-8的了,这里使用dos命令行来演示(默认编码为GBK)

从结果可以看出,读取的字符变成了乱码,和使用utf-8读取的结果不同。

 





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值