Java输入输出流(IO流)(从装饰者模式理解java.io)

Java输入输出流(IO流)(从装饰者模式理解java.io)

概述

  1. 程序运行时,将数据从硬盘或网络输入内存进行处理,再将数据从内存输出到硬盘或网络。Java对数据的输入输出依靠IO流实现。
  2. Java的IO流分为两类:
    字节流:传输数据的最小单位为byte(字节)
    字符流:传输数据的最小单位char(字符)
  3. 对字节流的输入输出使用InputStream和OutputStream。
  4. 对字符流的输入输出使用Reader和Writer,其实字符也是由字节组成,使用Reader和Writer传输数据时,也是对字节数据的传输,可以将Reader和Writer看为带有编解码功能的InputStream和OutputStream,可以将字节数据解释为字符。(借鉴了廖雪峰老师对Reader和Writer的理解
    -------------------------------------------分割线(6月16日更新)------------------------------------------------------
  5. java.io包与装饰者设计模式

inputStream/OutputStream(字节流)

  1. InputStream/OutputStream都是抽象类.
  2. InputStream抽象类提供的重要函数:
int read()
int read(byte[] b)
int read(byte[] b, int off, int len)

int read():一次读取一个字节,返回值为字节的数值,当返回值为-1时,表示没有内容可以被读取.
int read(byte[] b):一次读取多个字节到数组b中,返回值为实际读取到字节的个数,当返回值为-1时,表示没有内容可以被读取.
int read(byte[] b, int off, int len):off为偏移量,len为一次读取长度,返回值为实际读取到字节的个数,当返回值为-1时,表示没有内容可以被读取.
将InputStream的一个实现类FileInoutStream做为例子,如下:

import java.io.*;
import java.util.*;

public class IOPractice{
	public static void main(String[] args) {
		InputStream input = null;
		try {
			input = new FileInputStream("test");
			Byte n = 0;
			List<Byte> b = new ArrayList<>();
			while((n = (byte)input.read()) != -1) {
				b.add(n);
			}
			//将读取到的字节数组转化为字符串
			byte[] array = new byte[b.size()];
			for(int i = 0;i < b.size();i++) {
				array[i] = b.get(i);
			}
			String s = new String(array);
			System.out.println(s);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			if(input != null) {
				try {
					input.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

上述代码运行结果:
在这里插入图片描述

  1. OutputStream类提供的重要函数
    void wirte(int b); //向输出流写入一个字节(b的低8位)
    void write(byte[] b); //向输出流写入一个字节数组
    void write(byte[] b, int off, int len); //向输出流写入一个字节数组
    void flush(); //强制将缓冲区中的内容输出
  2. OutputStream的使用例子如下(使用了OutputStream的一个实现类FileOutputStream)
import java.io.*;

public class IOExceptionPractice {
	public static void main(String[] args) {
		try {
			OutputStream output = new FileOutputStream("test.txt");
			output.write("hellow java".getBytes("UTF-8"));
			output.close();
		}catch (IOException e) {
			System.out.println(e);
		}
		finally {
			System.out.println("finally");
		}
	}
}

Reader/Writer(字符流)

  1. Reader是一个抽象类,Reader提供常用函数如下:
    int read() //返回读取到的字符,当没有数据可读取时,返回-1
    int read(char[] arg) //一次读取多个字符,返回读取到的字符数
    int read(char[] arg, int off, int len) //off为开始存放读取到的字符的位置,len为个数,返回值为读取到的字符数
  2. java中的字符:java中的char占两个字节,采用Unicode编码。如果读取的字符为UTF-8编码,该编码一个中文字符占3个字节,如果直接用Char来方法,会导致乱码,故在读取时需要主要编码。
  3. Reader的使用例子如下(使用了Reader的一个实现类FileReader)
import java.io.*;
import java.nio.charset.StandardCharsets;

public class CharStream{
	public static void main(String[] args) {
		try {
			Reader reader = new FileReader("test.txt");	//注意要将该文件的编码方式改为ANSI(windows下)否则会出现乱码
			char[] buf = new char[10];
			reader.read(buf);
			System.out.println(buf);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
  1. Writer提供的常用函数如下:
    void write(int c) //写入一个字节
    void write(char[] arg) //一次写入多个字节
    void write(String s) //写入一个字符串
  2. writer的使用例子如下(使用了Writer的一个实现类FileWriter)
import java.io.*;
import java.nio.charset.StandardCharsets;

public class CharStream{
	public static void main(String[] args) {
		try {
			Writer myWriter = new FileWriter("test.txt");
			String s = new String("你好java");
			myWriter.write(s);
			myWriter.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

--------------------------分割线(6月16日更新)--------------------------------------------

java.io与装饰者设计模式

  1. 装饰者模式(如果没有接触过,可能刚开始有点晕,但是我会举例尽量讲得明白)。
    装饰者模式可以实现:动态地将功能附加到对象上。
    一般来说,装饰者模式中有四种角色,分别是抽象组件,具体组件,抽象装饰者,具体装饰者(可能具体实现会有一些出入)。这四种角色的关系如下图所示。
    装饰者模式示意图
    现在举一个具体的例子:对于饮料店的饮料,有不同种类,如黑咖啡,拿铁等。顾客在点完一杯饮料后,还可以往饮料中加入一些配料,如抹茶,奶盖等。那么在这个场景中,饮料就是一个抽象组件;黑咖啡、拿铁就是具体组件;配料就是抽象装饰者;抹茶、奶盖就是具体装饰者。他们之间的关系如下图所示。(这个例子取自于Head First 设计模式)
    饮料店-装饰者设计模式
    针对饮料店的例子,我们实现的代码如下(为了简单,没有实现Latte类):
//总共有6个源文件,主函数在StarBucks.java中

/*
* Beverage.java
* 饮料类-抽象组件
*/
public abstract class Beverage{
	String description = "unknown Beverage";
	
	public String getDescription() {
		return this.description;
	}
	
	public abstract double cost();
}

/*
* DarkRoast.java
* 黑咖啡类-具体组件
* 继承Beverage
*/
public class DarkRoast extends Beverage{
	Beverage beverage;
	
	public String getDescription() {
		return "DarkRoast coffee";
	}
	
	public double cost() {
		return 0.88;
	}
}

/*
* CondimentDecorator.java
* 配料类-抽象装饰者
* 继承Beverage
*/
public abstract class CondimentDecorator extends Beverage{
	public abstract String getDescription();
}

/*
* Mocha.java
* 抹茶类-具体装饰者
* 继承CondimentDecorator
*/
public class Mocha extends CondimentDecorator{
	Beverage beverage;
	
	Mocha(Beverage beverage){
		this.beverage = beverage;
	}
	
	public double cost() {
		return 0.2f + this.beverage.cost();
	}
	
	@Override
	public String getDescription() {
		return this.beverage.getDescription() + ", mocha";
	}
}

/*
* Naigai.java
* 奶盖类-具体装饰者
* 继承CondimentDecorator
*/
public class Naigai extends CondimentDecorator{
	Beverage beverage;
	
	Naigai(Beverage beverage){
		this.beverage = beverage;
	}
	
	public double cost() {
		return 0.5f + this.beverage.cost();
	}
	
	@Override
	public String getDescription() {
		return this.beverage.getDescription() + ", naigai";
	}
}

/*
* StartBucks.java
* 主类
*/
public class Starbucks{
	public static void main(String[] args) {
		Beverage coffee1 = new DarkRoast();
	    System.out.println(coffee1.getDescription() + " . cost = " + coffee1.cost());
	    //coffee2:黑咖啡 + 抹茶、奶盖
	    Beverage coffee2 = new Naigai(new Mocha(new DarkRoast()));
	    System.out.println(coffee2.getDescription() + " . cost = " + coffee2.cost());
	}
}
  1. 装饰者模式和java.io的关系
    我们使用java.io读取文件时,经常会用到BufferedInputStream类,该类使用缓冲区来增强读文件的性能,并且支持readline()函数来一次读取一行的数据。一般定义BufferedInputStream对象的代码如下:
File filename = new File(fllePath);
BufferedInputStream in = new BufferedInputStream(new FileInputStream(filename));

这里定义BufferedInputStream对象的代码和上面例子中定义coffee2(黑咖啡+抹茶+奶盖)的代码有木有很相似啊。
其实:FileInputStream就是一个具体组件;BufferedInputStream是一个具体装饰者。
java.io中类的关系如下图所示:
Java.IO示意图
总结:在使用java.io库时,面对库中的类,只要明白它们大多数都是具体装饰者而已,这样一切都会变得简单很多。
最后,通过实现一个自己的java.io装饰者来加深印象。

/*
* LowerCaseInputStream.java
* LowerCaseInputStream-具体装饰者
* 继承FilterInputStream
* 将读取到的内容中的大写字母转化为小写字母
*/
import java.io.*;

public class LowerCaseInputStream extends FilterInputStream{
	
	public LowerCaseInputStream(InputStream in){
		super(in);
	}
	
	public int read() throws IOException {
		int c = super.read();
		if(c == -1) {
			return c;
		}else {
			return Character.toLowerCase((char)c);
		}
	}
	
	public int read(byte[] b,int offset,int len) throws IOException {
		int result = super.read(b,offset,len);
		for(int i = offset;i < offset + result;i++) {
			b[i] = (byte) Character.toLowerCase((char)b[i]);
		}
		return result;
	}
}

/*
* MyInputStream.java
* 主类
*/
import java.io.*;

public class MyInputStream{
	public static void main(String[] args) {
		InputStream in;
		try {
			in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("test.txt")));
			int c;
			while((c = in.read()) != -1) {
				System.out.print((char)c);
			}
		}catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
}

最近在通过看设计模式学java,《Head First 设计模式》真的是一本好书啊

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值