Java基础关键_027_IO流(五)
目 录
一、装饰器设计模式
1.说明
2.实例
3.实例类图
二、压缩流
1.GZipOutputStream
2.GZipInputStream
三、字节数组流
1.说明
2.实例
3.对象流装饰字节数组
四、对象克隆
1.clone() 的克隆
2.序列化和反序列化的克隆
3.字节数组流的深克隆
一、装饰器设计模式
1.说明
- 23 种设计模式之一,属于结构型设计模式;
- 通过编写大量子类来继承父类,重写父类中的方法,会导致子类数量翻倍,即类爆炸;
- 装饰器模式可以在不修改原代码的基础上完成功能扩展,符合 OCP 原则,避免了继承所带来的类爆炸问题;
- IO 流中大量使用了装饰器模式;
- 角色:
- 组件:定义一个对象接口,可以给这些对象动态地添加职责。它是被装饰者和装饰者共同实现的接口。
- 具体组件:实现了组件接口,是被装饰的原始对象,定义了基本的行为。
- 装饰器:持有一个组件对象的引用,并实现了组件接口。它的主要作用是为具体组件添加额外的功能。
- 具体装饰器:具体实现装饰器,负责向组件添加具体的额外功能。
- 装饰者和被装饰者应当实现相同接口 / 抽象类;
- 装饰者应含有被装饰者的引用,引用类型应该是抽象的、不是具体的。
2.实例
/**
* 接口
*/
public interface Eatable {
void eat();
}
/**
* 所有装饰者的父类
*/
public abstract class EatableDec implements Eatable {
protected Eatable eatable;
public EatableDec(Eatable eatable) {
this.eatable = eatable;
}
@Override
public void eat() {
}
}
/**
* 装饰者1
*/
public class DoDec extends EatableDec {
public DoDec(Eatable eatable) {
super(eatable);
}
@Override
public void eat() {
System.out.println(eatable + "开始吃饭!");
super.eat();
System.out.println(eatable + "吃饭结束!");
}
}
/**
* 装饰者2
*/
public class TimeDec extends EatableDec {
public TimeDec(Eatable eatable) {
super(eatable);
}
@Override
public void eat() {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(now) + eatable + "开始吃饭!");
super.eat();
}
}
/**
* 被装饰者1
*/
public class Man implements Eatable{
@Override
public void eat() {
System.out.println("Man eat");
}
}
/**
* 被装饰者2
*/
public class Woman implements Eatable {
@Override
public void eat() {
System.out.println("Woman eat");
}
}
public class Test {
public static void main(String[] args) {
Eatable e1 = new DoDec(new Man());
Eatable e3 = new TimeDec(new Man());
Eatable e2 = new DoDec(new Woman());
Eatable e4 = new TimeDec(new Woman());
e1.eat();
e3.eat();
e2.eat();
e4.eat();
}
}
3.实例类图
二、压缩流
1.GZipOutputStream
public class GZipOutputStreamTest {
public static void main(String[] args) {
try (FileInputStream fileInputStream = new FileInputStream("D:\\Test.txt");
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new FileOutputStream("D:\\Test.txt.gz"))) {
byte[] bytes = new byte[1024];
int readCount = 0;
while ((readCount = fileInputStream.read(bytes)) != -1) {
gzipOutputStream.write(bytes, 0, readCount);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
2.GZipInputStream
public class GZipInputStreamTest {
public static void main(String[] args) {
try (GZIPInputStream gzipInputStream = new GZIPInputStream(new FileInputStream("D:\\Test.txt.gz"));
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Java\\Test.txt")) {
byte[] bytes = new byte[1024];
int readCount = 0;
while ((readCount = gzipInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, readCount);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
三、字节数组流
1.说明
- 字节数组流是内存流;
- ByteArrayInputStream 和 ByteArrayOutputStream 都是内存操作流,无需 打开 / 关闭 文件等操作,能够方便地读写字节数组、图像数据等内存中的数据;
- ByteArrayInputStream 和 ByteArrayOutputStream 都是节点流。
2.实例
public class ByteArrayOutputStreamTest {
public static void main(String[] args) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(97);
byteArrayOutputStream.write(98);
byteArrayOutputStream.write(99);
byteArrayOutputStream.write(100);
byteArrayOutputStream.write(101);
byte[] byteArray = byteArrayOutputStream.toByteArray();
for (byte b : byteArray) {
System.out.print(b + "\t");
}
// 97 98 99 100 101
}
}
3.对象流装饰字节数组
注意:由于包装流有缓存,所以使用包装流就要手动刷新。
public class ByteArrayOutputStreamTest {
public static void main(String[] args) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
objectOutputStream.writeByte(1);
objectOutputStream.writeChar('a');
objectOutputStream.writeShort(100);
objectOutputStream.writeInt(6666);
objectOutputStream.writeLong(99999);
objectOutputStream.writeFloat(1.1f);
objectOutputStream.writeDouble(2.2);
objectOutputStream.writeBoolean(true);
objectOutputStream.writeObject("hello");
objectOutputStream.flush();
byte[] byteArray = byteArrayOutputStream.toByteArray();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
System.out.println(objectInputStream.readByte()); // 1
System.out.println(objectInputStream.readChar()); // a
System.out.println(objectInputStream.readShort()); // 100
System.out.println(objectInputStream.readInt()); // 6666
System.out.println(objectInputStream.readLong()); // 99999
System.out.println(objectInputStream.readFloat()); // 1.1
System.out.println(objectInputStream.readDouble()); // 2.2
System.out.println(objectInputStream.readBoolean()); // true
System.out.println(objectInputStream.readObject()); // hello
byteArrayInputStream.close();
objectInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
四、对象克隆
1.clone() 的克隆
之前在面向对象章节谈过的一种克隆方式,调用 Object 的 clone 方法默认是浅克隆,深克隆需要重写 clone 方法。参考链接如下:
Java基础关键_007_面向对象(二)https://mp.csdn.net/mp_blog/creation/editor/145369813
2.序列化和反序列化的克隆
在第三节谈到的序列化和反序列化也可以完成对象的克隆。参考链接如下:
Java基础关键_025_IO流(三)https://mp.csdn.net/mp_blog/creation/editor/146349119
3.字节数组流的深克隆
将要克隆的 Java 对象写到内存中的字节数组,再从内存中的字节数组读取,读取到的对象就是一个深克隆。
public class Student implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class Teacher implements Serializable {
@Serial
private static final long serialVersionUID = 0L;
private String name;
private Student student;
public Teacher(String name, Student student) {
this.name = name;
this.student = student;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", student=" + student +
'}';
}
}
/**
* 利用 ByteArrayOutputStream 和 ByteArrayInputStream 实现深拷贝
*/
public class BAOISClone {
public static void main(String[] args) {
Teacher teacher = new Teacher("鬼谷子", new Student("孙膑", 18));
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(teacher);
oos.flush();
byte[] byteArray = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
ObjectInputStream ois = new ObjectInputStream(bais);
Teacher teacherClone = (Teacher) ois.readObject();
System.out.println("原teacher:" + teacher);
System.out.println("克隆teacher" + teacherClone);
System.out.println("=====================================");
teacherClone.setName("孔子");
teacherClone.getStudent().setName("颜回");
teacherClone.getStudent().setAge(20);
System.out.println("原teacher:" + teacher);
System.out.println("克隆teacher" + teacherClone);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}