Java基础-IO流

IO流结构


流的概念和作用
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
IO流的分类
根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:
读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
字节流:一次读入或读出是8位二进制。
字符流:一次读入或读出是16位二进制。
设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
输入流和输出流
输入流只能进行读操作,输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。
输入字节流 InputStream
InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
输出字节流 OutputStream
OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
PipedOutputStream 是向与其它线程共用的管道中写入数据。
ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
总结:
输入流:InputStream或者Reader:从文件中读到程序中;

输出流:OutputStream或者Writer:从程序中输出到文件中;

节点流
节点流:直接与数据源相连,读入或读出。
直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。
常用的节点流
父 类 :InputStream 、OutputStream、 Reader、 Writer
文 件 :FileInputStream 、 FileOutputStrean 、FileReader 、FileWriter 文件进行处理的节点流
数 组 :ByteArrayInputStream、 ByteArrayOutputStream、 CharArrayReader 、CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)
字符串 :StringReader、 StringWriter 对字符串进行处理的节点流
管 道 :PipedInputStream 、PipedOutputStream 、PipedReader 、PipedWriter 对管道进行处理的节点流
处理流
处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
常用的处理流
缓冲流:BufferedInputStream 、BufferedOutputStream、 BufferedReader、 BufferedWriter 增加缓冲功能,避免频繁读写硬盘。
转换流:InputStreamReader 、OutputStreamReader实现字节流和字符流之间的转换。
数据流: DataInputStream 、DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来。

三种复制文件的方法:

下面代码为了能尽量清晰展示每种方法就把异常抛出去了,实际生产环境能处理的尽量处理一下。

public class Test {
	public static void main(String[] args) throws Exception {
//		copyFile1();
		copyFile2();
//		copyFileUsingFileChannels();
	}
	/**
	 * 简单的文件复制,不使用处理流
	 * 
	 * @throws Exception
	 */
	public static void copyFile1() throws Exception {
		// 文件复制,b预先不存在,所以new了下
		File file1 = new File("d:a.jpg");
		File file2 = new File("d:b.jpg");
		file2.createNewFile();
		FileInputStream ins = new FileInputStream(file1);
		FileOutputStream out = new FileOutputStream(file2);
		byte[] b = new byte[1024];
		int n = 0;
		while ((n = ins.read(b)) != -1) {
			out.write(b, 0, n);
		}
		ins.close();
		out.close();
		System.out.println("copy over!");
	}
	/**
	 * 使用缓冲流
	 * @throws Exception
	 */
	public static void copyFile2() throws Exception {
		// 文件复制,b预先不存在,所以new了下
		File file1 = new File("d:a.jpg");
		File file2 = new File("d:b.jpg");
		file2.createNewFile();
		FileInputStream ins = new FileInputStream(file1);
		FileOutputStream out = new FileOutputStream(file2);
		BufferedInputStream bufferInput = new BufferedInputStream(ins);
		BufferedOutputStream bufferOutput = new BufferedOutputStream(out);
		byte[] bb = new byte[1024];// 用来存储每次读取到的字节数组  
        int n;// 每次读取到的字节数组的长度  
        while ((n = bufferInput.read(bb)) != -1) {  
        	bufferOutput.write(bb, 0, n);// 写入到输出流  
        }  
        bufferInput.close();
        bufferOutput.close();
		ins.close();
		out.close();
		System.out.println("copy over!");
	}
	
	
	/**
	 * 使用nio的filechannel
	 * @throws Exception
	 */
	private static void copyFileUsingFileChannels() throws Exception {
		// 文件复制,d预先不存在,所以new了下
		File file1 = new File("d:c.jpg");
		File file2 = new File("d:d.jpg");
		file2.createNewFile();
		FileChannel inputChannel = null;
		FileChannel outputChannel = null;
		try {
			inputChannel = new FileInputStream(file1).getChannel();
			outputChannel = new FileOutputStream(file2).getChannel();
			outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
		} finally {
			inputChannel.close();
			outputChannel.close();
		}
		System.out.println("copy over!");
	}

}

对象流:

/**
	 * 对象流使用
	 * @throws Exception
	 */
	private static void copyObject() throws Exception {
		User user1 = new User();
		user1.setName("我是对象1");
		// 保存的地址
		File file2 = new File("d:a.obj");
		file2.createNewFile();
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file2));
		oos.writeObject(user1);
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file2));
		User user2 = (User) ois.readObject();
		System.out.println(user2);
		oos.close();
		ois.close();
	}

	/**
	 * 注意这个类必须实现Serializable接口
	 * 
	 * @author Administrator
	 * 
	 */
	public static class User implements Serializable {
		private String name;
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		@Override
		public String toString() {
			return "User [name=" + name + "]";
		}

	}

我对我们的复制文件做了性能比较,同时复制一个70m的文件,使用了缓冲流的方法用时是节点流的1/3;

具体就是调用上面的copyFile1()和copyFile2()


总结:

1.上面例子没有使用字符流,是因为字节流可以替代字符流,只是操作文本文件的时候字符流会快;

2.尽量使用处理流,因为节点流每次操作的粒度有限导致性能没有处理流快;


昨日翻看关于io的资料,发现他是使用了设计模式的装饰器模式,我便通过源码画了一个类图


上图结构简单,但是从图上面看我发现一个比较奇怪的现象,FileterInputStream的属性居然有父类InputStream,InputStream居然是一个抽象类不是接口。翻阅资料说io流使用了装饰器模式,于是再研究一下装饰器模式。

装饰器模式:

允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到特定的目的。

适用场景:
扩展一个类的功能。
动态增加功能,动态撤销。
优点:
装饰类和被装饰类可以独立发展,不会相互耦合
动态的将责任附加到对象身上。
缺点:
多层装饰比较复杂。

装饰器模式由组件和装饰者组成。
抽象组件(Component):需要装饰的抽象对象。 
具体组件(ConcreteComponent):是我们需要装饰的对象 
抽象装饰类(Decorator):内含指向抽象组件的引用及装饰者共有的方法。 
具体装饰类(ConcreteDecorator):被装饰的对象。

例子:

使用一个装饰手机的例子

public class DecoratorTest {

	public static void main(String[] args) {
		Phone phone = new Phone();
		System.out.println("装饰手机开始:");
		Decorateable phoneDecoreat = new BeautifulPhone(phone);
		phoneDecoreat.des();
		phoneDecoreat.decorate();

		phoneDecoreat = new MuchAppPhone(phone);
		phoneDecoreat.des();
		phoneDecoreat.decorate();

	}
}

class BeautifulPhone extends AbstractPhone {
	public BeautifulPhone(Phone phone) {
		super(phone);
	}

	@Override
	public void decorate() {
		System.out.println("我开始装饰了:贴了很多贴画在背面,然后买了一些挂坠挂着,然后买了一个卡通套套着。");
	}

	@Override
	public void des() {
		System.out.println("我是一个很美的手机。");

	}
}

class MuchAppPhone extends AbstractPhone {
	public MuchAppPhone(Phone phone) {
		super(phone);
	}

	@Override
	public void decorate() {
		System.out.println("我开始装饰了:我装饰的是手机内部,我下载了支付宝,淘宝,下载了微信,下载了麻将游戏,还下载了酷狗等等一共100多款手机应用。");
	}

	@Override
	public void des() {
		System.out.println("我是一个功能很多的手机");
	}
}

class Phone {
}

abstract class AbstractPhone implements Decorateable {
	private Phone phone;

	public AbstractPhone(Phone phone) {
		this.phone = phone;
	}

}

/**
 * 包装一个物品
 * 
 * @author Administrator
 * 
 */
interface Decorateable {
	/**
	 * 装饰
	 */
	void decorate();

	/**
	 * 描述
	 */
	void des();
}

执行结果:


例子中不直接使用手机这个对象,而是将手机放到一个父类,然后子类去继承父类来操作手手机,这样,想做怎么装饰手机随便你,这样应用多的手机和很美的手机相互无关。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值