黑马程序员__JAVA基础__IO流(二)

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

1.装饰设计模式

    概述

    装饰模式(Decorator)又名包装(Wrapper)模式,装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
    装饰模式可以在不创造更多的子类的模式下,将对象的功能加以扩展。当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类称为装饰类。

    装饰类的特点:装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。

    与继承的区别

    假如一开始我们定义一个读取数据的类MyReader,其基本体系如下:
    MyReader  
    |---MyTextReader  
    |---MyMediaReader  
    |---MyDataReader  
    过一段时间,出现了新的技术(缓冲区),如果是使用继承,那么其他体系结构如下
    MyReader  
    |---MyTextReader  
        |---MyBufferedTextReader  
    |---MyMediaReader  
        |---MyBufferedMediaReader  
    |---MyDataReader  
        |---MyBufferedDataReade  
    于是上面发现有缺陷,于是我们单独定义一个MyBufferReader类

class MyBufferReader
{
	MyBufferReader(MyTextReader text)
	{}
	MyBufferReader(MyMediaReader media)
	{}
}
    但是发现其体系臃肿,而且每定义一个新的子类都要再定义一个新的基于它的MyBuffered子类。那么可以考虑在设计体系时,将实现新技术的类与之前类体系的关系由继承关系转为组合,使用装饰模式。
    好处:装饰模式比继承要灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系
装饰类因为是增强已有对象,具备的功能和已有对象的功能是相同的,只不过是提供了更强的功能,所以装饰类和被装饰类通常是都属于一个体系中
    设计时,可以写继承,但如果过于臃肿,可以考虑采用装饰设计模式。

    最终体系如下:

    MyReader  
    |---MyTextReader  
    |---MyMediaReader  
    |---MyDataReader  

    |---MyBufferReader

装饰设计模式示例:

class Person
{
	public void chifan()//基本功能吃饭
	{
		System.out.println("吃饭");
	}
}

class SuperPerson 
{
	private Person p ;
	SuperPerson(Person p)//通过构造方法接收被装饰的对象
	{
		this.p = p;
	}
	public void superChifan()//增强功能
	{
		System.out.println("开胃酒");
		p.chifan();
		System.out.println("甜点");
		System.out.println("来一根");
	}
}

class  PersonDemo
{
	public static void main(String[] args) 
	{
		Person p = new Person();//建立一个对象
		p.chifan();//基本的吃饭功能

		SuperPerson sp = new SuperPerson(p);//将对象作为参数传入,修饰基本功能
		sp.superChifan();//调用增强之后的功能

	}
}

2.LineNumberReader类

java.lang.Object
----java.io.Reader
    ----java.io.BufferedReader
        -----java.io.LineNumberReader
    LineNumberReader也是一个包装类,它在BufferedReader的基础上增加了一个记录行号的功能,而记录行号是在readLine方法中操作的,所以它继承了BufferedReader并复写了readLine方法,同时定义了方法setLineNumber(int)getLineNumber(),它们可分别用于设置和获取当前行号。 

用法示例:

import java.io.*;

public class LineNumberReaderDemo 
{
	public static void main(String[] args)throws IOException 
	{
		FileReader fr = new FileReader("D:\\123.txt");//建立读取流对象

		LineNumberReader lnr = new LineNumberReader(fr);

		String line = null;//定义用作临时存储的字符串变量
		lnr.setLineNumber(100);//设置起始行号,设置为100,从101开始
		while((line=lnr.readLine())!=null)
		{
			System.out.println(lnr.getLineNumber()+":"+line);//连同行号一起打印
		}
		//打印结果为前面带着行号的数据
		lnr.close();//关键资源
	}
}

3.字节流

    字节流的抽象基类:
    |---->InputStream:字节输入流
    |---->OutputStream:字节输出流

    它的操作与字符流类似,可以参与字符流的定义、读取、写入、处理异常的格式,只不过是处理的数据不同,因为对于非字符的数据,比如图片、视频、音频文件(例如mp3)等,这些文件只能用字节流对之进行操作。

    FileInputStream

    FileInputStream是InputStream的一个子类,用于读取诸如图像数据之类的原始字节流

    构造方法:

    |--->FileInputStream(File file) 
            通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 
    |--->FileInputStream(FileDescriptor fdObj) 
            通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。 
    |--->FileInputStream(String name) 
            通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。 

    常用方法:

    |--->int available()返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 
    |--->void close()关闭此文件输入流并释放与此流有关的所有系统资源。 
    |--->int read()从此输入流中读取一个数据字节。 
    |--->int read(byte[] b)从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 
    |--->int read(byte[] b, int off, int len)从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 

字节读取流代码示例:

import java.io.*;
public class InputStreamDemo {

	public static void main(String[] args)
	{
		read1();
	}
	//第一种读取方式:按字节来读取
	public static void read1()
	{
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("D:\\Demo\\demo.java");//新建字节读取的对象,明确源文件
			int x = 0;
			while((x = fis.read())!=-1)
			{
				System.out.print((char)x);
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally//执行关闭资源的操作
		{
			if(fis!=null)
			{
				try{
					fis.close();
				}catch(IOException e2){
					e2.printStackTrace();
				}
			}
		}
	}
	//第二种读取方式:按字节数组读取
	public static void read2()
	{
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("D:\\Demo\\demo.java");//新建字节读取的对象,明确源文件
			int len = 0;
			byte[] buff = new byte[1024];//定义一个字节数组,用于存储字节
			while((len=fis.read(buff))!=-1)//每次将读取到的字节存储进buff数组
			{
				System.out.println(new String(buff,0,len));//将字节数组转换成字符串输出
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally//执行关闭资源的操作
		{
			if(fis!=null)
			{
				try {
					fis.close();
				} catch (IOException e2) {
					e2.printStackTrace();
				}
			}
		}
	}
}

    注意available()这个方法的用法,取文件字节数,然后定义一个刚好大小的字节数组。代码如下:

fis =new FileInputStream(file);
byte [] ch =newbyte[fis.available()];
fis.read(ch);
System.out.println(new String(ch));
    但是如果文件过大,会造成内存溢出

    FileOutputStream

    FileOutputStream是OutputStream的一个子类,用于写入诸如图像数据之类的原始字节的流。

    构造方法:

    |--->FileOutputStream(File file) 
            创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
    |--->FileOutputStream(File file,boolean append
            创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
    |--->FileOutputStream(FileDescriptor fdObj) 
            创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 
    |--->FileOutputStream(String name) 
            创建一个向具有指定名称的文件中写入数据的输出文件流。 
    |--->FileOutputStream(String name, boolean append) 
            创建一个向具有指定 name 的文件中写入数据的输出文件流。 

    常用方法:

    |--->close():关闭此文件输出流并释放与此流有关的所有系统资源。  
    |--->write(byte[] b):将 b.length 个字节从指定byte数组写入此文件输出流中。 
    |--->write(byte[] b, int off, int len):将指定byte数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 
    |--->write(int b):将指定字节写入此文件输出流。 

字节写入流代码示例:

import java.io.*;
public class OutputStreamDemo {

	public static void main(String[] args)
	{
		FileOutputStream fos = null;
	    try 
	    {
			fos =  new FileOutputStream("D:\\Demo\\fos.txt");
			//定义一个字符串,因为字节流只能以字节或字节数组的形式读取
			String str = "努力努力";
			byte [] by =str.getBytes();//转成字节数组形式
			fos.write(by);//不用刷新
		} 
	    catch (Exception e) 
	    {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    finally
	    {
			if(fos!=null)
			{
				try 
				{
					fos.close();
				} 
				catch (IOException e) 
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

练习:通过字节流对图片文件的复制操作

/*
思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。
*/

import java.io.*;
class  CopyPic
{
	public static void main(String[] args) 
	{
		FileOutputStream fos = null;
		FileInputStream fis = null;
		try
		{
			fos = new FileOutputStream("c:\\2.bmp");//明确复制目的地
			fis = new FileInputStream("c:\\1.bmp");//关联一个源图片文件
			byte[] buf = new byte[1024];//用于临时存储的字节数组

			int len = 0;//定义变量用于判断是否是结尾处
			while((len=fis.read(buf))!=-1)
			{
				fos.write(buf,0,len);
			}
		}
		catch (IOException e)//捕获文件复制中的异常
		{
			throw new RuntimeException("复制文件失败");
		}
		finally//执行关闭流的操作
		{
			try
			{
				if(fis!=null)
					fis.close();//关闭读取流
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			try
			{
				if(fos!=null)
					fos.close();//关闭写入流
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入关闭失败");
			}
		}
	}
}

4.字节流缓冲区

    字节流缓冲区同样是提高了字节流的读写效率。

    对应类:
        BufferedOutputStream (extends OutputStream)
        BufferedInputStream (extends InputStream)

练习:利用缓冲区的技术,拷贝Mp3文件

import java.io.*;
public class copyMp3 {

	public static void main(String[] args)
	{
		BufferedInputStream buis=null;
		BufferedOutputStream buos=null;
		try
		{
			buis=new BufferedInputStream(new FileInputStream("D:\\play.mp3"));
			buos=new BufferedOutputStream(new FileOutputStream("D:\\play_copy2.mp3"));
			
			//第一种复制方式
			int num=0;
			while((num=buis.read())!=-1)
			{
				buos.write(num);
			}
			
			//第二种复制方式
			byte[] buff = new byte[1024];
			int len = 0;
			while((len=buis.read(buff))!=-1)
			{
				buos.write(buff,0,len);
			}	
		}
		catch(IOException e)
		{
			throw new RuntimeException("复制失败");
		}
		finally
		{
			if(buis!=null)
			{
				try {
					buis.close();
				} catch (Exception e2) {
					throw new RuntimeException("关闭读取流失败");
				}
			}
			if(buos!=null)
			{
				try {
					buos.close();
				} catch (Exception e2) {
					throw new RuntimeException("关闭写入流失败");
				}
			}
		}	  
	}
}

5.转换流

    转换流包括:

        InputStreamReader:字子流通向字符流的桥梁

        OutputStreamWriter:字符流通向字节流的桥梁
    转换流的由来:是字节流和字符流的桥梁;方便了字节流和字符流之间的操作
    转换流的应用:字节流中的数据都是字符时,转成字符流操作更高效

    InputStreamReader

    是字节流通向字符流的桥梁。
    每次调用 InputStreamReader 中的一个 read()方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。
    构造方法:
    InputStreamReader(InputStream in)
    InputStreamReader(InputStream in, String charsetName)

    示例:BufferedReader in = new BufferedReader(new InputStreamReader(System.in));(必须记住)

    OutputStreamWriter 

    是字符流通向字节流的桥梁,将要写入流中的字符编码成字节。

    每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。
    构造方法:
    OutputStreamWriter(OutputStream out)
    OutputStreamWriter(OutputStream out, String charsetName)
    示例:
    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    System.in 默认设备为键盘
        System.setIn(InputStream in)\\ 重新分配“标准”输入流。
    System.out 默认设备为控制台
        System. setOut(PrintStream out) \\重新分配“标准”输出流。


6.标准输入与输出

    System类中的字段:in,out,它们各代表了系统标准的输入和输出设备,默认输入设备是键盘,输出设备是显示器。
    System.in的类型是InputStream.
    System.out的类型是PrintStream
        java.lang.Object
        |--java.io.OutputStream
           |--java.io.FilterOutputStream
              |---java.io.PrintStream

    示例:

    例:获取键盘录入数据,然后将数据流向显示器,那么显示器就是目的地。
    通过System类的setIn,setOut方法对默认设备进行改变:
        System.setIn(new FileInputStream(“1.txt”));//将源改成文件1.txt。
        System.setOut(new FileOutputStream(“2.txt”));//将目的改成文件2.txt
    因为是字节流处理的是文本数据,可以转换成字符流,操作更方便。
    BfferedReader bufr =new BufferedReader(new InputStreamReader(System.in));
    BufferedWriter bufw =new BufferedWriter(new OutputStreamWriter(System.out));


个人总结

    本节又学习了一个java的设计模式:装饰设计模式,个人理解是定义一个新的类,将原有类对象作为参数传入,然后提供比原来对象的更强大的功能。避免了继承体系的臃肿,强化了类与类之间的关系。字节流是专门用于操作各种媒体文件的流对象,字符流是专门用于操作字符类文件的流对象,二者尽量避免交叉使用,有可能会出现操作失败等错误(比如用字符流去操作mp3文件的话,就有可能mp3里面的字节发生改变,从而文件使用出错)。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值