I/O流(2)

二、字节流

1.InputStream抽象类

     字节流类用于向字节流读写8位二进制的字节。一般地,字节流主要用于读写诸如图像或声音等的二进制数据。字节流类以InputStream和OutputStream为顶层类,它们都是抽象类。

(1)InputStream是定义了字节输入流的抽象类,其中定义的方法如下:

      ① public abstract int read();从输入流中读取数据的下一个字节

      ② public int read(byte[] b);从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中

      ③ public int read(byte[] b, int off, int len);

      ④ public long skip(long n);跳过和丢弃此输入流中数据的几个字节

      ⑤ public int available();此输入流有多少个字节可读

      ⑥ public void close();关闭输入流

2.OutputStream抽象类

(1)OutputStream是定义了字节输出流的抽象类,该类所有方法的返回值为void,在出错的情况下抛出IOException异常,其中定义的方法如下:

      ① public abstract void write();将指定的字节写入此输入流

      ② public void write(byte[] b);

      ③ public void write(byte[] b, int off, int len);

      ④ public void flush();强制刷新清空缓冲区,并强制写出缓冲区数据

      ⑤ public void close();关闭输出流

3.FileInputStream和FileOutputStream

     每个抽象类都有多个具体的子类,这些子类对不同的外设进行处理,例如:磁盘文件,网络连接,甚至是内存缓冲区。

(1)FileInputStream类表示能从文件读取字节的InputStream类。常用的构造方法:

       ① public FileInputStream(String filePath);

       ② public FileInputStream(File fileObj);

(2)FileOutputStream表示能向文件写入字节的OutputStream类,构造方法如下:

     ① public FileOutputStream(String filePath);

     ② public FileOutputStream(File fileObj);

     ③ public FileOutputStream(String filePath, boolean append);

// 拷贝一个指定文件到指定目录当中
public class FileInputStreamOutputStreamDemo {
	public static void main(String[] args) {
		try {
			FileCopyUtil.copyFile(new File("f:\\src\\test.mp3"), new File("f:\\dst\\dst.mp3"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
/**
 * 总结:对文件进行读写操作,使用缓冲区会大大提高文件读写的效率。
 */
class FileCopyUtil {
	public static void copyFile(File src, File dst) throws IOException {
		FileInputStream inputStream = new FileInputStream(src);
		FileOutputStream outputStream = new FileOutputStream(dst);
		// 创建一个1M大小的缓冲区,用来存放输入流中的字节数据
		byte[] buf = new byte[1024 * 1024];
		int len = 0;
		long timeStart = System.currentTimeMillis();
		while((len = inputStream.read(buf)) != -1) {
			outputStream.write(buf, 0, len);
		}
		inputStream.close();
		outputStream.close();
		long timeEnd = System.currentTimeMillis();
		System.out.println("复制完成,共花费:" + (timeEnd - timeStart) + "毫秒");
	}
}
=======================================================================================
// 显示文本文件内容
public class ShowFileContents {
	public static void main(String[] args) {
		// 创建File对象
		File file = new File("f:\\a.txt");
		try {
			FileInputStream fis = new FileInputStream(file);
			int read = fis.read();
			while(read != -1) {
				System.out.print((char)read);
				read = fis.read();
			}
			fis.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

4.ByteArrayInputStream和ByteArrayOutputStream

(1)ByteArrayInputStream是把字节数组当成源的输入流,两个构造方法,每个都需要一个字节数组提供数据源

      ① ByteArrayInputStream(byte array[]);

      ② ByteArrayInputStream(byte array[],int start, int numBytes);

(2)ByteArrayOutputStream是把字节数组当作目标的输出流(即把内存中的数据写到对应的字节数组当中),有两个构造方法:

      ① ByteArrayOutputStream();创建一个新的byte数组输出流(默认长度32)

      ② ByteArrayOutputStream(int numBytes);创建一个新的byte数组输出流,具有指定大小缓冲区(字节为单位)

public class ByteArrayInputOutputStream {
	public static void main(String[] args) {
		String str = "hello,beijing";
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(str.getBytes());
		int data = -1;
		while((data = byteArrayInputStream.read()) != -1) {
			System.out.print((char)data);
		}
		
		System.out.println("\n=======================我是最美的分割线========================");
		
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		byteArrayOutputStream.write(65);
		byteArrayOutputStream.write(97);
		try {
			byteArrayOutputStream.write("hello,world".getBytes());
			// 创建一个新分配的byte数组
			byte[] buff = byteArrayOutputStream.toByteArray();
			for (byte strData : buff) {
				System.out.print((char)strData);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("\n=======================我是最美的分割线========================");
		
		try {
			// 方法的第二个参数为true,则代表在源文件后面追加或拼接;为false,则表示覆盖源文件
			FileOutputStream fileOutputStream = new FileOutputStream("f:\\a.txt", false);
			// 把byteArrayOutputStream内部缓冲区中的数据写到对应的文件输出流中
			byteArrayOutputStream.writeTo(fileOutputStream);
			fileOutputStream.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

5.过滤流介绍

(1)过滤流(Filtered Stream)仅仅是为底层透明地提供扩展功能的输入流(输出流)的包装。这些流一般由普通类的方法(即过滤流的一个父类)访问。我们往往把过滤流称之为包装流,主要功能就是对基本的输入流(输出流)进行功能的扩展。(过滤流是使用一个已经存在的输入流或输出流连接创建的,不用手动创建缓冲区,底层有封装,而且缓冲区的大小可以自定义。)

(2)过滤字节流FilterInputStream和FilterOutputStream,构造方法:

      ① FilterInputStream(InputStream is);

      ② FilterOutputStream(OutputStream os);

这些类提供的方法和InputStream及OutputStream类的方法相同,常用的过滤流BufferedInputStream和BufferedOutputStream(带缓冲区的字节输入/输出流)、DataInputStream和DataOutputStream(对基本数据类型进行读写操作)

(3)BufferedInputStream和BufferedOutputStream

       需要使用已经存在的节点流来构造,提供带缓冲的读写,提高了读写的效率。

文件→从文件中获取输入字节(FileInputStream)→增加字节缓冲区功能(BufferedInputStream)→数据

数据→提供数据写入到缓冲区(BufferedOutputStream)→将数据以字节写入到文件中(FileOutputStream)→文件

(4)DataInputStream和DataOutputStream

    数据输入输出流允许应用程序读写基本java数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取。读写顺序要保持一致。

public class BufferedInputOutputStreamDemo {
	public static void main(String[] args) {
		try {
			FileUtil.copyFile(new File("f:\\a.txt"), new File("e:\\a.txt"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
class FileUtil {
	public static void copyFile(File src, File dst) throws IOException {
		FileInputStream fis = new FileInputStream(src);
		FileOutputStream fos = new FileOutputStream(dst);
		BufferedInputStream bis = new BufferedInputStream(fis);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		int data = 0;
		long startTime = System.currentTimeMillis();
		while((data = bis.read()) != -1) {
			bos.write(data);
		}
		bis.close();
		bos.close();
		long endTime = System.currentTimeMillis();
		System.out.println("复制完成,共花费:" + (endTime - startTime) + "毫秒");
	}
}
public class DataInputOutputStreamDemo {
	public static void main(String[] args) {
		String name = "zhangsan";
		int age = 10;
		boolean flag = true;
		char sex = 'm';
		double money = 100.56;
		
		try {
			DataOutputStream dos = new DataOutputStream(new FileOutputStream("e:\\e.txt"));
			dos.writeUTF(name);
			dos.writeInt(age);
			dos.writeBoolean(flag);
			dos.writeChar(sex);
			dos.writeDouble(money);
			dos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// 读的顺序必须和写的顺序一致
		try {
			DataInputStream dis = new DataInputStream(new FileInputStream("e:\\e.txt"));
			System.out.println(dis.readUTF());
			System.out.println(dis.readInt());
			System.out.println(dis.readBoolean());
			System.out.println(dis.readChar());
			System.out.println(dis.readDouble());
			dis.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

6.装饰模式

  (1)装饰模式概念

        装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

(2)装饰模式中的角色有:

     ① 抽象构件(Component)角色:给出一个抽象接口(方法),以规范准备接收附加责任的对象(好比InputStream)

     ② 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类(好比FileInputStream(继承了InputStream))

     ③ 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。(也就是说装饰角色中的方法与抽象构件中的方法是一样的,它类似FilterInputStream(过滤字节输入流))。

     ④ 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。(好比BufferedInputStream)

public class Test {
	public static void main(String[] args) {
		ConcreteComponent concreteComponent = new ConcreteComponent();
		concreteComponent.doThingA();
		ConcreteDecorator concreteDecorator = new ConcreteDecorator(concreteComponent);
		concreteDecorator.doThingA();
	}
}
// 类似于InputStream
interface Component{
	public void doThingA();
}
// 类似于FileInputStream
class ConcreteComponent implements Component{

	@Override
	public void doThingA() {
		System.out.println("do A thing");
	}
	
}
// 类似于FilterInputStream
class Decorator implements Component{
	
	private Component component = null;
	
	public Decorator(Component component) {
		this.component = component;
	}

	@Override
	public void doThingA() {
		// 调用被装饰的方法
		component.doThingA();
	}
	
}
// 类似于BufferedInputStream
class ConcreteDecorator extends Decorator {

	public ConcreteDecorator(Component component) {
		super(component);
	}
	
	@Override
	public void doThingA() {
		// 调用被包装类的方法
		super.doThingA();
		doThingB();
	}

	private void doThingB() {
		System.out.println("do thing B");
	}
}

案例:使用字节流复制一个文件中的所有内容到指定的文件夹中。

public class CopyDirDemo {
	public static void main(String[] args) {
		try {
			CopyDirUtil.copyDir(new File("f:\\src"), new File("e:\\test"));
			System.out.println("拷贝完成!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

class CopyDirUtil{
	public static void copyDir(File src, File dst) throws IOException {
		// 创建目标文件夹(包括所有必须但不存在的父目录,即嵌套文件夹 d:\\zz\\cc)
		dst.mkdirs();
		if (src != null) {
			File[] files = src.listFiles();
			if (files != null) {
				for (File file : files) {
					if (file.isFile()) {
						// 复制文件
						FileInputStream fis = new FileInputStream(file);
						FileOutputStream fos = new FileOutputStream(
								dst.getAbsolutePath() + File.separator + file.getName());
						byte[] buff = new byte[1024*1024];
						// 保存的是实际读到的字节个数
						int len = 0;
						while( (len = fis.read(buff)) != -1) {
							fos.write(buff, 0, len);
						}
						fis.close();
						fos.close();
					} else {
						copyDir(file, new File(dst.getAbsolutePath() + File.separator + file.getName()));
					}
				}
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_42051619/article/details/82844509