“你好,阿米戈!今天,我要告诉你有关 BufferedInputStream 类的一些有趣的事情,但是我们要从‘包装类’和‘一袋糖’说起。”

“你所说的‘包装类’和‘一袋糖’是什么意思?”

“这是隐喻。听好。嗯...”

“包装类”(或“装饰类”)设计模式是一种相当简单方便的机制,用于在不使用继承的情况下扩展对象功能。

BufferedInputStream - 1

假设我们有一个 Cat 类,该类包含两个方法:getName 和 setName:

Java 语言代码 说明
class Cat
{
 private String name;
 public Cat(String name)
 {
  this.name = name;
 }
 public String getName()
 {
  return this.name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
}
Cat 类包含两个方法:getName 和 setName
public static void main(String[] args)
{
 Cat cat = new Cat("Oscar");

 printName(cat);
}

public static void printName(Cat cat)
{
 System.out.println(cat.getName());
}
有关如何使用它的示例。

将在控制台上显示“奥斯卡”。

假设我们需要拦截对 cat 对象的方法调用,并可能进行一些小的更改。为此,我们需要将其包装在它自己的包装类中。

如果我们想要围绕某个对象的方法调用“包装”我们自己的代码,则需要:

1) 创建我们自己的包装类,并从与包装对象相同的类/接口继承。

2) 将包装对象传递给我们的类的构造方法。

3) 重写新类中的所有方法。在每个重写的方法内部调用包装对象的方法。

4) 完成所需的任何更改:更改方法调用的操作,更改其参数和/或执行其他操作。

在下面的示例中,我们拦截对 Cat 对象的 getName 方法的调用,并稍微更改其返回值。

Java 语言代码 说明
class Cat
{
 private String name;
 public Cat(String name)
 {
  this.name = name;
 }
 public String getName()
 {
  return this.name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
}
Cat 类包含两个方法:getName 和 setName。
class CatWrapper extends Cat
{
 private Cat original;
 public CatWrapper (Cat cat)
 {
  super(cat.getName());
  this.original = cat;
 }

 public String getName()
 {
  return "A cat named " + original.getName();
 }

 public void setName(String name)
 {
  original.setName(name);
 }
}
包装类。除了对原始对象的引用之外,该类不存储任何数据。
该类能够“抛出”对传递给构造方法的原始对象 (setName) 的调用。它还可以“捕获”这些调用并修改其参数和/或结果
public static void main(String[] args)
{
 Cat cat = new Cat("Oscar");
 Cat catWrap = new CatWrapper (cat);
 printName(catWrap);
}

public static void printName(Cat named)
{
 System.out.println(named.getName());
}
有关如何使用它的示例。

“猫的名字叫奥斯卡”。
将在控制台上显示

换句话说,我们用包装类对象悄悄地替换了每个原始对象,包装类对象将会收到指向原始对象的链接。对包装类的所有方法调用都被转发给原始对象,并且一切顺利运行。

“我喜欢它。这种解决方法既简单又实用。”

“我还会告诉你有关‘一袋糖’的事情。这是一个隐喻,而不是设计模式。是对词语缓冲区和缓冲的隐喻。什么是缓冲,我们为什么需要它?”

BufferedInputStream - 2

假设今天轮到里希做饭,而你正在帮忙。里希还没来,但我想喝茶。我请你给我一匙糖。你去地下室找到一袋糖。你可以把整袋糖给我,但我不需要一袋糖。我只需要一匙糖。然后,作为一个善良的机器人,你舀了一匙糖并递给我。我把糖加到茶里,但是还不够甜。于是我请你再给我一些糖。你再次去地下室给我拿来一匙糖。这时艾莉来了,我请你给艾莉拿来一些糖...整个过程花费太长时间并且效率低下。

里希来了,他看到这一切,于是请你给他拿来一个装满糖的糖罐。然后艾莉和我开始向里希要糖。他直接从糖罐里拿糖给我们,就这样。

里希出现后发生的事情称为缓冲:糖罐是缓冲区。借助于缓冲,“客户”可以从小分量的缓冲区读取数据,而为了节省时间和精力,缓冲区则从大分量的源中读取数据。

“这个示例太酷了,金。我完全懂了。对一匙糖的请求就像从流中读取一个字节一样。”

“没错。BufferedInputStream 类是缓冲包装类的经典示例。它对 InputStream 类进行包装。它从原始 InputStream 中以大块的形式将数据读取到缓冲区中,然后在我们从中读取数据时一点一点地从缓冲区中拉出数据。”

“很好。一切都很清楚。有用于写入的缓冲区吗?”

“哦,当然有。”

“可以举个例子吗?”

“想象一个垃圾桶。你不必每次都出去将垃圾放入焚化炉中,只需将其扔进垃圾桶即可。然后布巴会每两周将垃圾桶拿出去一次。一个典型的缓冲区。”

“太有趣了!顺便说一句,它比一袋糖的比喻要清楚得多。”

“而 flush() 方法就像立即取出垃圾。你可以在客人来之前使用它。”