黑马程序员——IO流

                                                    ------- android培训java培训、期待与您交流! ----------

一、文件File

       电脑中的数据最终体现形式为文件,在这个io包中唯一与文件相关的类就是File类,它是文件和目录路径名的抽象表示形式。根据面向对象思维,文件肯定有创建、删除、判断等等一些方法,我们可以查阅api文档来一一了解,在此就不赘述。我们用一个例子来演示File的用法,需求是列出指定路径下的所有后缀名为.java的文件。     

package cn.itheima.blog6;

import java.io.File;
import java.io.FilenameFilter;

/**
 * 将指定目录下的所有指定后缀名的文件打印到控制台
 *    运用到递归的思想,同时结合文件名过滤
 */

//文件名过滤器
class FilterByNames implements FilenameFilter {

	private String suffix;
	
	public FilterByNames(String suffix) {
		super();
		this.suffix = suffix;
	}

	@Override
	public boolean accept(File dir, String name) {
		return name.endsWith(suffix);
	}
}

public class FileFilterByJava {

	public static void main(String[] args) {
		//指定目录
		File f = new File("E:\\code\\io");
		//设定过滤器
		FilterByNames filter = new FilterByNames(".java");
		
		list(f, filter);
	}

	public static void list(File dir, FilterByNames filter){
		//列出所有文件
		File[] files = dir.listFiles();
		//遍历文件
		for(File file : files){
			//若是目录则继续过滤,即递归思想
			if(file.isDirectory()){
				list(file, filter);
			}
			//通过文件名过滤器过滤文件,递归的临界点
			if(filter.accept(file, file.getName())){
				//打印文件名
				System.out.println(file.getName());
			}
			
		}
	}
}
      

           上述代码用到了递归思想,递归是栈的应用。 

        递归:在方法定义中,直接或者简介地调用自身。

        适用时机:用分解技术,将大问题分解成规模较小的同类问题,通过不断分解,找到最小及其临界点,然后一步步解决。

 

二:IO流

        1、概述

         IO流:I (input) ,O(output)  其中用流来连接,用来处理设备之间的数据传输,Java用于操作流的对象都在IO包中。

    在程序中,所有的数据都是以流的方式进行传输或者保存的,程序需要数据是要适用输入流读取数据,而需要将一些数据保存起来时,就要适用输出流。

    流的分类:

    (1)按照流向分:输入流和输出流。

        判断流向是以内存作为参照物的,如将硬盘中的数据弄到内存中,就是输入,而将内存中的数据弄到硬盘上,就是输出。

    (2)按照数据分:字节流和字符流。

               计算机中所存在的均是二进制,用字节流就可以操作所有数据,但是为了方便纯文本数据的操作,因此引入了字符流。其实,字符流读取文本数据后,不直接操作,而是先查指定编码表,获取对应的文本,再对文本其进行操作。字符流 字节流 编码表。因此,只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都是用字节流。

          2、流的四大顶层抽象基类    

              字节输入输出流:InputStream,OutputStream

              字符读写流:Reader(输入),Writer(输出) 

              IO包中的由这四个类派生出来的子类名称都是以父类名作为后缀,子类的前缀名就是该对象的功能。几乎可以做到见名知意。

          3、流的继承体系

           (1)InputStream

             ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和文件中读取数据。

            SequenceInputStream用于对流进行有序的合并,无对应的OutputStream。

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

            PipedInputStream,管道输入流,应连接到管道输出流,一般用于多线程之间的通信。

            FilterInputStream的子类均为装饰类,提供特殊功能,如高效输入的BufferedInputStream,基本java数据输入流DataInputStream等。

           (2)OutputStream

             ByteArrayInputStream、FileInputStream 是两种基本的介质流,它们分别向Byte 数组、和文件中写入数据。

            ObjectOutputStream用于对象的持久化,其中的对象需要实现Serializable接口。

            PipedOutputStream 管道输出流,连接管道输入流,一般用于多线程之间的通信。     

             FilterOutputStream的子类均为装饰类,提供特殊功能,如高效写的BufferedOutputStream,基本java数据输出流DataOutputStream,打印流PrintStream,可以向文件、流中打印数据,同时可以保持数据的表现形式,还不抛出IOException等。

            (3)Reader

              CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据。     

              PipedReader 是从与其它线程共用的管道中读取数据。

              BufferedReader负责装饰其它Reader 对象,高效读。

              InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。

            (4)Writer

              CharWriter、StringWriter 是两种基本的介质流,它们分别将Char 数组、String中读取数据。     

              PipedWriter 是从与其它线程共用的管道中写出数据。

              BufferedWriter负责装饰其它Writer对象,高效写。

              InputStreamWriter是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileWriter就是实现此功能的具体类

              PrintWriter与PrintStream用法相似。

         4、流对象选择的四部曲

              (1)第一步:先明确源和目的
                          源:
                                 文本:用Reader
                                 字节:用InputStream
                      目的:
                                 文本:用Writer
                                 字节:用OutputStream
             
(2)第二步:明确是不是纯文本
                          是:用字符流;
                      不是:用字节流
            
 (3)第三步:明确流体系后,通过设备来明确具体使用哪个流对象
                   源设备:
                       键盘:System.in
                       硬盘:文件流File
                       内存:数组流ArrayStream
              

                目的设备:
                        键盘:System.out
                        硬盘:文件流File
                        内存:数组流ArrayStream        

                (4)第四步:明确是否需要其他功能

                  高效:Buffered+后缀名

                  合并:SequenceInputStream

                  序列化:ObjectInputStream
                  操作基本数据类型:DateInputStream,DateOutputStream

                  打印:PrintStream,PrintWriter

             5、流的应用示例
              需求1:将键盘输入的信息写入到文件中
  

package cn.itheima.blog6;

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

public class KeyBordToFile {

	/**
	 * 将控制台的信息写入到文件中 
	 * 1、源与目的:
	 *  源:InputStream, Reader 
	 * 目的:OutputStream, Writer
	 * 2、是否为纯文本:
	 *  源:否,用字节 
	 *  目的:无所谓,字符字节都行,用字符
	 * 3、具体设备 
	 * 源:键盘,System.in
	 * 目的:硬盘,File --->FileWriter
	 * 4、是否需要其他功能 
	 * 需要高效
	 * BufferedReader、BufferedWriter
	 * 需要转换
	 * InputStreamReader
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		
		//高效读封装,因为是System.in为字节流,所以转为字符流
		BufferedReader bufr = new BufferedReader(new InputStreamReader(
				System.in));
		
		//高效写入文件中
		BufferedWriter bufw = new BufferedWriter(new FileWriter("console.txt"));
		
		//流的常用操作,不断读,不断写
		String line = null;
		while ((line = bufr.readLine()) != null) {
			if (line.equals("over"))
				break;
			bufw.write(line);
			bufw.newLine();
		}
		
		//关流
		if(bufr != null)
			bufr.close();
		if(bufw != null)
			bufw.close();
	}

}

          

     需求2:对指定文件进行切割与合并

package cn.itheima.blog6;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

public class FileSpiltAndMerge {

	public final static int SIZE = 1048576;
	/**
	 * 文件的切割与合并,并且将切割的信息存入到配置文件中,
	 * 同时要根据配置信息进行合并。
	 * 
	 * 将流与Properties,集合,File结合起来
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
//		File file = new File("C:\\111.txt");
//		spiltFile(file);
		
		File dir = new File("C:\\partfiles");
		merge(dir);
	}

	//文件切割,并将切割的信息存入到配置文件中
	public static void spiltFile(File file) throws Exception{
		//若传入文件为路径,抛出异常
		if(file.isDirectory())
			throw new RuntimeException("不是文件!!!");
		//将文件关联上输入流
		InputStream in = new FileInputStream(file);
		//定义缓冲区
		byte[] buf = new byte[SIZE]; 
		int count = 0;
		int len = 0;
		OutputStream out = null;
		//设置切割后文件的位置,若文件夹不存在,则创建
		File dir = new File("C:\\partfiles");
		if(!dir.exists())
			dir.mkdirs();
		//将文件进行切割
		while((len = in.read(buf)) != -1){
			out = new FileOutputStream(new File(dir, (count++) + ".part"));
			out.write(buf, 0, len);
			out.close();
		}
		//将切割的信息存入config文件中
		Properties prop = new Properties();
		prop.setProperty("filepath", file.getAbsolutePath());
		prop.setProperty("filecount", count+"");
		
		FileOutputStream fos = new FileOutputStream(new File(dir, "config.property"));
		prop.store(fos, "spilt config");
		
		//关流
		if(fos != null)
			fos.close();
		if(in != null)
			in.close();
	}

	public static void merge(File dir) throws Exception{
		
		//不是路径,无法合并
		if(dir.isFile())
			throw new RuntimeException("不是路径!!!!");
		//得到.property文件
		File[] files = dir.listFiles(new FilterByName(".property"));
		//若配置文件不唯一,则无法合并
		if(files.length != 1)
			throw new RuntimeException("配置文件不唯一,没法合并");
		//新建Properties集合,用来读取设置信息
		Properties prop = new Properties();
		FileReader fr = new FileReader(files[0]);
		prop.load(fr);
		//读取文件路径
		String filepath = prop.getProperty("filepath");
		//读取文件个数
		int count = Integer.parseInt(prop.getProperty("filecount"));
		File[] parts = dir.listFiles();
		//若目录中文件数与配置中读取的文件数不等,则抛出异常
		if((parts.length - 1) != count)
			throw new RuntimeException("文件个数不一致");
		//将输入流添加到集合中
		List<InputStream> list = new ArrayList<InputStream>();
		FileInputStream fis = null;
		for(File part : parts){
			fis = new FileInputStream(part);
			list.add(fis);
		}
		//将集合中的流转化为枚举类,用于后面的合并
		Enumeration en = Collections.enumeration(list);
		//合并流
		SequenceInputStream sis = new SequenceInputStream(en);
		//流的一般操作,不断读取合并流,不断写入文件
		byte[] buf = new byte[1024];
		int len = 0;
		FileOutputStream fos = new FileOutputStream(filepath);
		while((len = sis.read(buf)) != -1){
			fos.write(buf, 0, len);
		}
		//关流
		if(fr != null)
			fr.close();
		if(fos != null)
			fos.close();
		if(fis != null)
			fis.close();
		if(sis != null)
			sis.close();
	}
}
package cn.itheima.blog6;

import java.io.File;
import java.io.FilenameFilter;

public class FilterByName implements FilenameFilter {

	private String suffix;
	
	FilterByName(String suffix) {
		super();
		this.suffix = suffix;
	}

	@Override
	public boolean accept(File dir, String name) {
		return name.endsWith(suffix);
	}

}

 

三、装饰设计模式

          当一个类的功能不能满足于现在的需求时,我们就需要对这些类的功能进行增强,我们首先会想到的是继承。假设这一功能为缓冲,IO流中许多都需要加入缓冲功能,这样,IO流的体系就越来越臃肿不够灵活。那我们是否可以将缓冲当做一种技术,不将他与具体的IO流对象结合,而是对其进行单独封装,那个对象需要缓冲技术,就将该流对象与缓冲关联。

          装饰设计模式的使用步骤:

              (1)写一个类,继承被增强对象的父类,实现与被增强对象相同的接口

              (2)定义一个变量,接受被增强的对象

              (3)定义一个构造函数,接受被增强对象

              (4)覆盖需要增强的方法

              (5)对于不想增强的方法,直接调用被增强对象的方法。

          以BufferedReader为例来了解装饰设计模式,BufferedReader用来装饰Reader用以高效写入,我们可以自定义一个高效类来模仿BufferedReader对象,代码如下   

package cn.itheima.blog6;

import java.io.IOException;
import java.io.Reader;

//(1)写一个类,继承被增强对象的父类,实现与被增强对象相同的接口
public class MyBufferedReader extends Reader{

	/**
	 * 高效读取字符
	 * 缓冲的原理:
     * 其实就是从源中获取一批数据装进缓冲区中。
     * 在从缓冲区中不断的取出一个一个数据。
	 * 
	 */
	
	//(2)定义一个变量,接受被增强的对象
	private Reader reader;
	
	//定义一个数组作为缓冲区。
	private char[] buf = new char[1024];
	
	//定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。	
	private int pos = 0;	
	
	//定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
	private int count = 0;
	
	//(3)定义一个构造函数,接受被增强对象
	public MyBufferedReader(Reader reader){
		this.reader = reader;
		
	}

	//(4)覆盖需要增强的方法
	public int myRead() throws IOException{
		
		if(count==0){
			count = reader.read(buf);
			pos = 0;
		}
		if(count<0)
			return -1;
		
		char ch = buf[pos++];
		count--;
		return ch;
	}
	//模仿BufferedReader的readLine()
	public String myReadLine() throws IOException{
			StringBuilder sb = new StringBuilder();
			
			int ch = 0;
			while((ch = myRead())!=-1){
				if(ch=='\r')
					continue;
				if(ch=='\n')
					return sb.toString();
				//将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
				sb.append((char)ch);
				
			}		
			
			if(sb.length()!=0)
				return sb.toString();
			return null;
		}
		
	//(5)对于不想增强的方法,直接调用被增强对象的方法。

		@Override
		public void close() throws IOException {
			reader.close();
		}
	
		@Override
		public int read(char[] cbuf, int off, int len) throws IOException {
			return 0;
		}
}

四、编码表             

         计算机中的文字全是二进制表示,但是为了适合人们阅读,就产生了编码表,将数值与文字一一对应,这样看不懂的二进制就变成了看得懂的明文。

       常见的编码表:
                 ASCII:美国标准信息交换码,用一个字节的七位表示
                 ISO8859-1:拉丁码表,欧洲码表,用一个字节的八位表示
                 GB2312:中文编码表,用两个字节表示
                 GBK:中文编码表升级,融合录入更多的中文字符,用两个字节表示,为避免与美国重复,故两字节的最高位都是1,即汉字都是用负数表示
                 Unicode:国际标准码,融合了多种文字,所有文字都用两个字节表示
                 UTF-8:用一个字节到三个字节表示。 

         编码与解码:

         编码:字符串变成字节数组:String-->getBytes()-->byte[]()
         解码:字节数组变成字符串:byte[]-->new String(byte[],0,len)-->String

        对于流的学习就像是一个非常漫长的过程,因为其中的对象太多,不好选择,但是有了毕老师的四部曲,让我们可以比较好地去选择,希望在这彷徨的同学可以去看毕老师的第22天的视频--流的操作基本规律。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值