Java input and output is defined in terms of an abstract concept called a “stream”, which is a sequence of data. There are 2 kinds of streams.
- Byte streams (8 bit bytes) -> Abstract classes are: InputStream and OutputStream
- Character streams (16 bit UNICODE) -> Abstract classes are: Reader and Writer
Design pattern: java.io.* classes use the decorator design pattern. The decorator design pattern attaches responsibilities to objects at runtime. Decorators are more flexible than inheritance because the inheritance attaches responsibility to classes at compile time. The java.io.* classes use the decorator pattern to construct different combinations of behavior at runtime based on some basic classes.
|Attaching responsibilities to classes at compile time using subclassing.||Attaching responsibilities to objects at runtime using a decorator design pattern.|
|Inheritance (aka subclassing) attaches responsibilities to classes at compile time. When you extend a class, each individual changes you make to child class will affect all instances of the child classes. Defining many classes using inheritance to have all possible combinations is problematic and inflexible.||By attaching responsibilities to objects at runtime, you can apply changes to each individual object you want to change.
File file = new File(“c:/temp”);
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
Decorators decorate an object by enhancing or restricting functionality of an object it decorates. The decorators add or restrict functionality to decorated objects either before or after forwarding the request. At runtime the BufferedInputStream (bis), which is a decorator (aka a wrapper around decorated object), forwards the method call to its decorated object FileInputStream (fis). The “bis” will apply the additional functionality of buffering around the lower level file (i.e. fis) I/O.