二、字节流
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()));
}
}
}
}
}
}