欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java基础-IO流

程序员文章站 2024-03-04 22:10:48
...

IO流结构

Java基础-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()

Java基础-IO流

总结:

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

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


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

Java基础-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();
}

执行结果:

Java基础-IO流

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