黑马程序员-IO总结


-----------------------
android培训 java培训 、java学习型技术博客、期待与您交流!----------------------

题外话:
这近十天来.为了赶进度,越来越发现自己没有花多少时间去总结回顾之前的知识了.突然发现IO流已经学习完几天了.竟然一次也没复习过.甚至于提到IO某些知识都有种莫名的陌生感. 的确时间不等人,但是某些必要的时间还是得划出来.更不能因为周边某些人的急功近利,反而乱了自己的节奏.
---
不断的戒掉旧习,却又不断的有新的恶习.
想优秀,要努力.
---

1.I/O概述

1.1.I/O系统

顾名思义:输入/输出系统.
I/O之所以公认最重要,就是因为作为输入输出,就是输入输出的源端和想要与之通信的接收端(对应Input,Output)存在各种形式.而且还需要考虑各种不同的数据类型作为源和接收端之间的数据能否互通问题.

2.准备工作-File类

2.1.File类的概念

一个实用类库工具,用于帮助处理文件内部数据外的文件,文件夹,路径等属性.
其实就是将文件系统中的文件和文件夹封装成了对象.提供了更多的属性和行为可以对这些文件和文件夹进行操作.这些是流对象办不到的,因为流只操作数据.

2.2.File类的常用构造和方法

File类



(更新下,今天用到了一个相当常用,当时学习File没有写进去,罪过)

重命名

boolean f1.renameTo(Filedest);//可以实现移动的效果.剪切+重命名.

注意:

这里的renameTo()方法,改变了需要改变的文件夹后,他的f1封装的还是以前未改的路径,就不是在指向改后的文件或文件夹了!!!

在这里栽了很蛋疼的跟头.谨记.!!

2.3.案例

这里最经典就是用递归思想操作一个目标文件下,包括子文件的所有文件及文件夹.
我的代码:
/*
 * 需求:
 * 编写程序,将指定目录下所有.java文件拷贝到另一个目的中,并将扩展名改为.txt
 * 
 * 思路:
 * 封装原始与目的路径对象,利用递归的方式获取原始目录下java文件路径对象.
 * 然后每当获取到java文件路径对象,通过IO流中的字符流在目的路径创建java文件对应txt文件进行复制.
 * 
 * 步骤:
 * 1.键盘录入初始目录路径,目标目录路径,并进行封装.
 * 2.自定义复制java文件方法,按需求,复制.java文件并改后缀名为.txt.
 * 3.自定义遍历方法,利用递归的方式,获取初始目录下所有java文件路径对象.
 * 4.每当获取到java文件路径对象,调用自定义复制方法,进行复制.
 * 5.反馈操作是否成功.
 * */

public class Test9 {
	public static void main(String[] args){
		Scanner in =  new Scanner(System.in);
		System.out.println("您将进行如下操作:\n\t将指定目录下所有.java文件拷贝到另一个目的中,并将扩展名改为.txt\n请输入初始路径:\t(例如:D:/itheima/javaCode)");
		String fromStr=in.nextLine();
		System.out.println("请输入目标路径:\t(例如:E:/itheima/txtCode)");
		String targetStr=in.nextLine();
		//封装初始路径,目标路径.
		File from=new File(fromStr);
		File target=new File(targetStr);
		//判断目标路径是否存在,否则创建.
		if (!target.exists()) {
			System.out.println("目标路径不存在,自动创建.");
			target.mkdirs();
		}
		//调用方法,复制重命名文件.
		copyAll(from, target);
		System.out.println("拷贝并重命名操作成功!");
	}
	
	//定义遍历方法,用递归方式遍历目录下所有java文件,并调用自定义复制方法进行复制.
	private static void copyAll(File from,File target){
		File[] fileList=from.listFiles();
		for(File temp : fileList){
			if(temp.isDirectory()){//如果该路径对象是文件夹,则递归.
				File targetTemp=new File(target,temp.getName());
				targetTemp.mkdirs();
				copyAll(temp,targetTemp);
			}else{
				if(temp.getName().endsWith(".java")){//如果该路径对象是文件,判断是否为java文件,是则调用方法,自定义复制.
					try {
						myCopy(temp,target);
					} catch (IOException e) {
						System.exit(0);
					}
				}
			}
		}
	}
	
	//定义复制java文件方法,包括将文件后缀改为txt操作.
	private static void myCopy(File from,File target) throws IOException {
		File targetFile=new File(target,from.getName().replace(".java", ".txt"));//修改复制后文件后缀为txt.
		BufferedReader br=null;
		BufferedWriter bw=null;
		try {
			br=new BufferedReader(new FileReader(from));
			bw=new BufferedWriter(new FileWriter(targetFile));
			String temp=null;
			while((temp=br.readLine())!=null) {//运用字符缓冲流,复制java文件进入目标路径下同名txt文件.
				bw.write(temp);
				bw.newLine();
			}
		}
		catch (IOException e) {
			throw e;
		}
		finally{//关闭资源.
			if(bw!=null)
				try {
					bw.close();
				} catch (IOException e) {
					throw e;
				}
				finally{
					if(br!=null)
					try {
						br.close();
					} catch (IOException e) {
						throw e;
					}
				}
		}
	}
}

3.IO流体系

3.1.IO流分类

流的概念:可以理解数据的流动,就是一个数据流.IO流最终要以对象来体现.对象都存在IO包中.

流的分类两种依据
处理方式不同:分为输入流(读)和输出流(写).
处理的数据不同:分为字节流和字符流.(下面是四大基类)
字节流:InputStream  OutputStream

字符流:Reader  Writer

字节流:

处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

字符流:

因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。

3.2.字符流:

Reader:用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。

|---BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

|---InputStreamReader:是字节流通向字符流的桥梁:它使用指定的字符集读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

|---FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。

Writer:写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。

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

|---OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的字符集将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

|---FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。

|---PrintWriter:字符打印流.

3.3.字节流

InputStream:是表示字节输入流的所有类的超类。

|--- FileInputStream:从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。

|--- BufferedInputStream:该类实现缓冲的字节输入流。

|--- ObjectInputStream:用于序列化的操作流.将对象存入字节文件中进行存储或者传输.

|--- PipedInputStream:管道流.

OutputStream:此抽象类是表示输出字节流的所有类的超类。

|--- FileOutputStream:文件输出流是用于将数据写入FileFileDescriptor 的输出流。

|--- FilterOutputStream:此类是过滤输出流的所有类的超类。

|--- BufferedOutputStream:该类实现缓冲的输出流。

|--- PrintStream:字节打印流.

|--- ObjectOutputStream:用于反序列化的操作流.将存入文件中的对象数据读取出来获得该对象实例.

|--- PipedOutputStream:管道输出流.

3.4.流的操作规律:

1,明确源和目的。

数据源:就是需要读取,可以使用两个体系:InputStream、Reader;

数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer;

2,操作的数据是否是纯文本数据?

如果是:数据源:Reader

数据汇:Writer

如果不是:数据源:InputStream

数据汇:OutputStream

3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?

明确操作的数据设备。

数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)

数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。

4,需要在基本操作上附加其他功能吗?比如缓冲。

如果需要就进行装饰。


4.缓冲区(或者叫高效流)

4.1.理解原理

缓冲区其实就是底层用数组,将需要读取的数据进行缓存,当达到一定量后进行写入或者读取.

说白了就是读一大片,写一大片.

4.2.模拟自定义BufferedReader

(BufferedInputStream等其他高效流类似)

我的代码:

/*
需求:
利用装饰设计模式,模仿jvm底层实现BufferedReader或者BufferedWriter的方式,自定义读写缓冲区.
思路:
缓冲区其实就是对默认的字符输入输出流(FileReader和FileWriter)字节输入输出流进行功能修饰.
用到了装饰设计模式.
步骤:
1.自定义方法,参数列表为需要的输入或者输出流.
2.将作为参数传递的输入输出流Read或者Writer方法进行缓冲区功能性加强.
3.封装自定义方法类.作为自定义缓冲区类.
*/

import java.io.*;

class MyBufferedReader{

	//为了让整个类都能调用fr的方法进行装饰.
	private FileReader fr;

	MyBufferedReader(FileReader fr){//让该类一旦实例化,必然存在一个FileReader对象.
		this.fr = fr;
	}
	
	//定义读取一行的方法.
	public String readLine()throws IOException{
		int ch = 0;
		StringBuilder line=new StringBuilder();
		while((ch=fr.read())!=-1){
			if (ch=='\r'){
				if ((ch=fr.read())=='\n'){//同时遇到
					return line.toString();
				}else{
	//				line.append('\r');	//如果单单遇到\r而不是\r\n,就把\r放进读取的字符串中.	
				}
			}
			line.append((char)ch);
		}
		if (line.length()!=0)//当读到结尾,line中还存在数据的话,就返回这些数据.
			return line.toString();
		return null;
	}

	//定义关闭资源方法.
	public void close()throws IOException{
		fr.close();
	}
}

class MyBufferedReaderTest
{
	public static void main(String[] args)throws IOException{
		MyBufferedReader mbr = new MyBufferedReader(new FileReader("abc.txt"));
		String line = null;
		while((line=mbr.readLine())!=null){
			System.out.println(line);
		}
		mbr.close();
	}
}

4.3.注意:

字符流的缓冲流中提供了readLine()和newLine()方法.

而字节流的缓冲流中没有提供.需要注意.


5.转换流

5.1.转换流概念

转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。

转换流最重要的内容就是:字节流 + 编码表 --->字符流.事实上,没有转换流,就没有字符流。

InputStreamReader----------->底层为字节流

|--FileReader------------------->我们应用为字符流

OutputStreamWriter---------->底层为字节流

|--FileWrier--------------------->我们应用为字符流

所以, 想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了.所以操作文件的流对象只要继承自转换流就可以读取一个字符了.

但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。

FileReaderfr = new FileReader("a.txt");    等同于    InputStreamReaderisr = new InputStreamReader(newFileInputStream("a.txt"),"gbk");

如果仅仅使用平台默认码表,就可以直接使用:FileReader fr = new FileReader("a.txt"); 

如果需要制定码表,必须用转换流.

转换流 = 字节流+编码表.

转换流的子类File = 字节流 + 默认编码表(GBK).

5.2.代码实例:

案例:
		//键盘录入数据
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferddWriter bw = new BufferedWriter(new FileWriter("a.txt"));

		String line = null
		while((line=br.readLine())!=null)
		{
			if("over".equals(line))
			{
				break;
			}

			bw.write(line);
			bw.newLine();
			bw.flush();
		}

		bw.close();
		br.close();

		//把文本文件的数据通过流对象在控制台显示
		BufferedReader br = new BufferedReader(new FileReader("a.txt"));
		BufferddWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

		String line = null
		while((line=br.readLine())!=null)
		{
			bw.write(line);
			bw.newLine();
			bw.flush();
		}

		bw.close();
		br.close();


6.打印流

6.1.打印流概念:打印流是写数据的.

6.2.特点:

A:可以写任意数据类型的数据

B:如果启动了自动刷新,能自动刷新,还会换行。println()
C:可以直接往文件写数据

注意:看构造方法,一个流对象的构造中如果同时有String和File的构造参数,那么该流对象就可以读写文件。

6.3.用打印流改进复制文本文件的操作(PrintStream可应用于二进制文件)

//数据源:
	//c:\\a.txt
//目的地:
	//d:\\b.txt
		BufferedReader br = new BufferedReader(new FileReader("c:\\a.txt"));
		PrintWriter pw = new PrintWriter(new FileWriter("d:\\b.txt"),true);

		String line = null;
		while((line=br.readLine())!=null)
		{
			pw.println(line);
		}

		pw.close();
		br.close();


7.字符编码

7.1.编码表:

就是字符和对应的数据组成的一张表。

7.2.常见的编码表:

ASCII
ISO-8859-1
GBK
GB2310
GB18030
UTF-8
BIG5

7.3.转换流中的编码问题

A:字符流 = 字节流+编码表
B:通过转换流写入数据,指定为UTF-8编码,并通过UTF-8编码读取。


8.存储程序对象信息.

8.1.通过Properties,序列化存储.

Properties:

通过键值对的形式,将对象的属性名,属性值作为键值对映射的方式存入Map中.(Properties:为Map的实现类)

序列化:

通过ObjectInputStream流将实现了序列化接口的类实例化对象以二进制数据的形式写入文本文件中

A: setProperty(String key,String value)
B: getProperty(String key)

getProperty(String key,String defaultValue)
C: Set<String> stringPropertyNames()
和IO流结合的方法:
A: list 通过打印流把数据保存到文件中
B: load 通过任意流把文本中的数据加载到集合中
C: store 通过任意流把数据保存到文件中



----------------------- android培训 java培训 、java学习型技术博客、期待与您交流!----------------------

详情请查看:http://edu.csdn.net/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值