• 1. Java IO流及装饰模式
  • 2. JAVA IO的概念分类所谓IO,也就是Input与Output的缩写,流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。12 根据处理数据类型的不同分为:字符流和字节流 根据数据流向不同分为:输入流和输出流
  • 3. JAVA IO 字符流和字节流字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别: (1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。 (2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。 (3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。 结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
  • 4. 对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。  输入/输出时,数据在通信通道中流动。所谓“数据流(stream)”指的是所有数据通信通道之中,数据的起点和终点。信息的通道就是一个数据流。只要是数据从一个地方“流”到另外一个地方,这种数据流动的通道都可以称为数据流。 输入/输出是相对于程序来说的。程序在使用数据时所扮演的角色有两个:一个是源,一个是目的。若程序是数据流的源,即数据的提供者,这个数据流对程序来说就是一个“输出数据流”(数据从程序流出)。若程序是数据流的终点,这个数据流对程序而言就是一个“输入数据流”(数据从程序外流向程序)。 Java输入流和输出流
  • 5. 对程序语言设计者来说,设计一个令人满意的I/O(输入输出)系统,是件极艰巨的 任务(thinging in java) 1.流的代码较长,不该省略的都不要省略,主要是因为作为一个新手需要养成良好的代码编写习惯 2.学习流的时候一定要出于跨平台性和健壮性考虑 3.代码中有些操作有多种执行方式,需要举一反三的学习 4.注重注释的使用 5.熟练使用java.io包中的相关类与接口进行I/O编程 6.掌握Java I/O 的设计原则与使用的设计模式(相对来说比较困难,必须学习设计模式,掌握规律) Java I/O 目标
  • 6.  文件(File)是 最常见的数据源之一,在程序中经常需要将数据存储到文件中,例如图片文件、声音文件等数据文件,也经常需要根据需要从指定的文件中进行数据的读取。当然, 在实际使用时,文件都包含一个的格式,这个格式需要程序员根据需要进行设计,读取已有的文件时也需要熟悉对应的文件格式,才能把数据从文件中正确的读取出 来。  文件的存储介质有很多,例如硬盘、光盘和U盘等,由于IO类设计时,从数据源转换为流对象的操作由API实现了,所以存储介质的不同对于程序员来说是透明的,和实际编写代码无关。   为了很方便的代表文件的概念,以及存储一些对于文件的基本操作,在java.io包中设计了一个专门的类——File类。 在File类中包含了大部分和文件操作的功能方法,该类的对象可以代表一个具体的文件或文件夹,所以以前曾有人建议将该类的类名修改成FilePath,因为该类也可以代表一个文件夹,更准确的说是可以代表一个文件路径 JAVA File类详解-文件处理类
  • 7. JAVA File类详解-特性 一个File类的对象,表示了磁盘上的文件或目录 File类提供了与平台无关的方法来对磁盘上的文件或目录进行操作 File类直接处理文件和文件系统。 File类没有指定信息怎样从文件读取或向文件存储(文件本身的内容,无法向文件中读写信息) File类描述了文件本身的属性 File对象用来获取或处理与磁盘文件相关的信息,例如权限,时间,日期和目录路径 File类还可以浏览子目录层次结构 通过API了解File类位于java.io
  • 8. java.io包中的File类提供了与具体平台无关的方式来描述目录和文件对象的属性功能。其中包含大量的方法可用来获取路径、目录和文件的相关信息,并对它们进行创建、删除、改名等管理工作。因为不同的系统平台,对文件路径的描述不尽相同。为做到平台无关,在Java语言中,使用抽象路径等概念。Java自动进行不同系统平台的文件路径描述与抽象文件路径之间的转换。 • File类的直接父类是Object类。 Java I/O File类
  • 9. File(File parent, String child) Creates a new File instance from a parent abstract pathname and a child pathname string. File(String pathname)           Creates a new File instance by converting the given pathname string into an abstract pathname. File(String parent, String child)           Creates a new File instance from a parent pathname string and a child pathname string. File(URI uri)           Creates a new File instance by converting the given file: URI into an abstract pathname.(编程理解File构造方法的使用FileTest1,FileTest2)Java I/O File类-构造方法
  • 10.  1 注意点 1)分隔符的使用,如果再window下使用D:\\代表一个\,第二种就是使用/来表示 2)为了更好的使系统的移植性,健壮性,推荐使用/,因为在Linux等其他操作系统下,只认/. 3)使用File的Separtor静态变量来实现window和非window系统的分隔符 JavaJava I/O File类-构造方法
  • 11.  File 类提供了两个方法去区分File和Directory isDirectory() isFile() 目录管理 – 目录操作的主要方法为: – c public n boolean ) mkdir() 根据抽象路径名创建目录 。 – c public ] String[] ) list() 返回抽象路径名表示路径中的文件名和目录名 。 • 文件管理 – 在进行文件操作时,常需要知道一个关于文件的信息。Jave的File类提供了方法来操纵文件和获得一个文件 的信息。另外,File类还可以对目录和文件进行删除、属性修改等管理工作 API编程理解(FileTest3) JavaI/O File-对于文件目录的区分
  • 12. 1、 boolean createNewFile():功能:当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。返回值:如果指定的文件不存在并成功地创建,则返回 true;如果指定的文件已经存在,则返回 false 。这与输出流不同,输出流一旦建立就会创建文件并将原有文件覆盖。 2static File suffix):首先注意该方法为static,功能:在默认目录中创建以prefix为前缀,以suffcreateTempFile(stringprefix,string ix为后缀名的文件。返回值:返回新建空文件的抽象路径名。 3、static File createTempFile(stringprefix,string suffix,File directory):首先注意该方法为static,功能:在directoryz为指定目录中创建以prefix为前缀,以suffix为后缀名的文件。返回值:返回新建空文件的抽象路径名。 4、 boolean  mkdir():创建此抽象路径名指定的目录。boolean mkdirs() :创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。 二、删除文件 1、 booleandelete()功能:删除此抽象路径名表示的文件或目录。返回值:删除成功返回true失败返回false。 2、void deleteOnExit()功能:在程序退出时删除指定文件。  三、判断文件(在判断之前应该首先判断文件是否存在) 1、Boolean canExecute():判断此抽象路径名表示的文件或目录是否可执行。 2、 Boolean  canRead():判断此抽象路径名表示的文件或目录是否可读。 3、 int  compareTo(File pathname):功能:按字母顺序比较两个抽象路径名。返回值:如果该参数等于此抽象路径名,则返回零;如果此抽象路径名在字母顺序上小于该参数,则返回小于零的值;如果此抽象路径名在字母顺序上大于该参数,则返回大于零的值 Java File常见的方法 
  • 13. 4、 boolean exists():判断文件是否存在。 boolean   isAbsolute():测试此抽象路径名是否为绝对路径名。 boolean   isDirectory():测试此抽象路径名表示的文件是否是一个目录。 boolean   isFile():测试此抽象路径名表示的文件是否是一个标准文件。 boolean   isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件。 四、获取文件信息(方法大部分以get开头) 1、File getAbsoluteFile() 返回此抽象路径名的绝对路径名形式。  2、StringgetAbsolutePath() 返回此抽象路径名的绝对路径名字符串。  3、FilegetCanonicalFile() 返回此抽象路径名的规范形式。  4、String  getName() 返回由此抽象路径名表示的文件或目录的名称。 5、long length()返回由此抽象路径名表示的文件的长度。 6、boolean  renameTo(File dest)重新命名此抽象路径名表示的文件。(可以实现类似剪切的功能) 五、文件列表 1、String[]list()返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。  2、String[]list(FilenameFilter filter)  返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。  3、File[]listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。 4、File[]listFiles(FileFilter filter) 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 5、File[]listFiles(FilenameFilter filter) 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 Java File常见方法
  • 14. java.io.FilenameFilter是文件名过滤器,用来过滤不符合规格的文件名,并返回合格的文件;我们一般地: (1)String[] fs = f.list(); (2)File[] fs = f.listFiles(); 这两个方法返回f下的所有文件或目录; FilenameFilter用来把符合要求的文件或目录返回; 因此可以调用: (1)String []fs = f.list(FilenameFilter filter);; (2)File[]fs = f.listFiles(FilenameFilter filter); Java I/O FilenameFilter类详解
  • 15. 文件名过滤器一般用法 1.实现FilenameFilter接口; 2.实现boolean accept(File dir,String name);   //dir表示文件的当前目录,name表示文件名; class MyFilter implements FilenameFilter{ private String type;            //type为需要过滤的条件,比如如果type=".jpg",则只能返回后缀为jpg的文件 public MyFilter(String type){ this.type = type; } public boolean accept(File dir,String name){           //返回true的文件则合格 } } API 详解 FilenameFilter 编程理解 Filetest 5   Java I/O FilenameFilter类详解
  • 16. Java程序通过流来完成输入/输出。流是生产或消费信息的抽象。流通过Java的输入/输出系统与物理设备链接。尽管与它们链接的物理设备不尽相同,所有流的行为具有同样的方式。这样,相同的输入/输出类和方法适用于所有类型的外部设备。这意味着一个输入流能够抽象多种不同类型的输入:从磁盘文件,从键盘或从网络套接字。同样,一个输出流可以输出到控制 台,磁盘文件或相连的网络。流是处理输入/输出的一个洁净的方法,例如它不需要代码理解键盘和网络的不同。Java中流的实现是在java.io包定义的类层次结构内部的。 JAVA I/O 流的分类详解1
  • 17. 输入/输出时,数据在通信通道中流动。所谓“数据流(stream)”指的是所有数据通信通道之中,数据的起点和终点。信息的通道就是一个数据流。只要是数据从一个地方“流”到另外一个地方,这种数据流动的通道都可以称为数据流。 输入/输出是相对于程序来说的。程序在使用数据时所扮演的角色有两个:一个是源,一个是目的。若程序是数据流的源,即数据的提供者,这个数据流对程序来说就是一个“输出数据流”(数据从程序流出)。若程序是数据流的终点,这个数据流对程序而言就是一个“输入数据流” ( 数据从程序外流向程序) 程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件 JAVA I/O 输入输出流
  • 18. 在java.io包中提供了60多个类(流)。 • 从功能上分为两大类:输入流和输出流。 • 从流结构上可分为字节流(以字节为处理单位或称面向字节)和字符流(以字符为处理单位或称面向字符)。(底层都是字节)首先是字节流的讲解 • 字节流的输入流和输出流基础是InputStream和OutputStream这两个抽象类,字节流的输入输出操作由这两个类的子类实现。字符流是Java 1.1 版后新增加的以字符为单位进行输入输出处理的流,字符流输入输出的基础是抽象类Reader和WriterJAVA I/O 输入输出流
  • 19. JAVA I/O 的输入输出流 
  • 20. Java 1.1以后定义了两种类型的流:字节流和字符流。字节流(byte stream)为处理字节的输入和输出提供了方便的方法。例如使用字节流读取或写入二进制数据。字符流(character stream)为字符的输入和输出处理提供了方便。它们采用了统一的编码标准,因而可以国际化(底层字节实现,当给予封装,对于用户操作字符提供了简便的方法)。当然,在某些场合,字符流比字节流更有效(具体问题具体分析) 在java1.0是不包括字符流的(了解)JAVA 字节流和字符流详解1
  • 21. 字节流类(Byte Streams) ) 字节流类用于向字节流读写8位二进制的字节。一般地,字节流类主要用于读写诸如图象或声音等的二进制数据,所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串; 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符 字符流类(Character Streams) 字符流类用于向字符流读写16位二进制字符。在程序中一个字符等于两个字节,字符流的操作比字节流操作好在一点,就是可以直接输出字符串了,不用再像之前那样进行转换操作了JAVA 字节流字符流详解1
  • 22. JAVA 输入输出流的操作原理输入流 1 打开流操作 2 判断是否有数据 3 如果有读数据 4 读完数据关闭流操作输出流 1 打开流操作 2 判断输出的目的地 3 向目的地写数据 4 写完数据关闭流操作
  • 23. 流的分类  按照功能的不同分,分节点流和处理流,节点流是直接从一个源读写数据的流(这个流没有经过包装和修饰),处理流是在对节点流封装的基础上的 一种流,FileInputStream是一个节点流,可以直接从文件读取数据,但是BufferedInputStream可以包装 FileInputStream,使得其有缓冲功能。 (就是直接作用于目标的流 如图1)   其实除了以上三种分类外,还有一些常常听到的一些分类比如:对象流、缓冲流、压缩流、文件流等等。其实都是节点流和处理流的子分类。当然你也可以创建新的流类型,只要你需要。(就是对节点流的封装 如图2)      JAVA IO流的分类详解2文件节点流java文件处理流java图1 节点流图2 处理流节点流
  • 24. JAVA InputStream详解1字节流类的输入流以InputStream 为顶层类,他是抽象类(abstract) 抽象类InputStream,最重要的方法是read(),对数据进行读操作,每个抽象类都有多个具体的子类,这些子类对不同的外设进行处理,例如磁盘文件,网络连接,甚至是内存缓冲区。 • 要使用流类,必须导入java.io包 三个基本的读方法
  • 25. abstract int read() :读取一个字节数据,并返回读到的数据,如果返回-1,表示读到了输入流的末尾。 int read(byte[] b) :将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流尾。 int read(byte[] b, int off, int len) :将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示到了输入流的末尾。off指定在数组b中存放数据的起始偏移位;len指定读取的最大字节数。 API详解Read的使用 思考:为什么只有第一个read方法是抽象的,而其余两个read方法都是具体的?JAVA InputStream详解
  • 26. FileInputStream详解处理流封装inputStream(API查看方法,read方法的实现) InputSteamTest1现场编程理解FileInputStream类的使用 固定模式编码方式(重要) InputStream is=new FileInputStream("D://aa.txt"); byte[] buffer=new byte[600]; //获取的文件读入的字节数组的长度 int length=0; while(-1!=(length=is.read(buffer, 0, 600)))//判断需要明白什么意思 结合API理解 { String str=new String(buffer,0,length); System.out.println(str); }JAVA InputStream详解2
  • 27. JAVA InputStream详解2
  • 28. 字节流类的输出流以OutputStream 为顶层类,他是抽象类(abstract),该类的所有方法返回一个void 值并且在出错情况下引发一个IOException异常 OutputStream中包含一套字节输出流需要的方法,可以完成最基本的输出数据到输出流的功能。当Java程序需要将数据输出到外设时,可根据数据的不同形式,创建一个适当的OutputStream子类类型的对象来完成与该外设的连接,然后再调用执行这个流类对象的特定输出方法来实现对相应外设的输出操作. OutputStream类子类对象也继承了OutputStream类的方法。常用的方法有:写数据的方法write(),关闭流方法close()等。JAVA OutputStream详解1
  • 29. 三个基本的写方法 abstract void write(int b) :往输出流中写入一个字节。 void write(byte[] b) :往输出流中写入数组b中的所有字节。 void write(byte[] b, int off,int len) :往输出流中写入数组b中从偏移量off开始的len个字节的数据 void flush() :刷新输出流,强制缓冲区中的输出字节被写出。 void close() :关闭输出流,释放和这个流相关的系统资源 JAVA OutputStream详解
  • 30. JAVA OutputStream详解2
  • 31. OutputStream.java 注意事项: 1 FileOutputStream文件如果不存在会自动创建 2 使用os的write方法会冲掉原来文件的内容 3 使用FileOutputStream的构造函数(File file,boolean append)JAVA OutputStream详解2
  • 32. 处理流的概念与作用 各种字节节点流类,它们都只具有读写字节内容的方法,以FileInputStream与FileOutputStream为例,它们只能在文件中读取或者向文件中写入字节,在实际应用中我们往往需要在文件中读取或者写入各种类型的数据,就必须先将其他类型的数据转换成字节数组后写入文件,或者从文件中读取到的字节数组转换成其他数据类型,很麻烦的!!通过FileOutputStream将一个浮点小数写入到文件中和将一个整数写入到文件时很困难的。 这时就需要处理类DataOutputStream。它提供了往各种输出流对象中写入各种类型的数据的方法。 传递一个FileOutputStream输出流对象给DataOutputStream实例对象和调用DataOutputStream实例对象的用于写入浮点小数的方法。JAVA 处理流(过滤流)
  • 33. DataOutputStream并没有对应到任何具体的流设备,一定要给它传递一个对应具体流设备的输出流对象,完成类似DataOutputStream功能的类就是一个处理类,也叫过滤流类或装饰流类。它对OutputStream流类进行了包装,使编程人员使用起来更方便。 数据输入流DataInputStream中定义了多个针对不同类型,数 据 的 读 方 法 , 如 readByte() 、 readBoolean() 、readShort()、readChar()、readInt()、readLong()、readFloat()、readDouble()、readLine()等。 数据输出流DataOutputStream中定义了多个针对不同类型数 据 的 写 方 法 , 如 writeByte() 、 writeBoolean() 、writeShort()、writeChar()、writeInt()、writeLong()、writeFloat()、writeDouble()、writeChars()等。 编程理解(DataInputStream.java)(不能直接跟目标打交道) JAVA 处理流(过滤流)
  • 34. 使用数据文件流的一般步骤 (1)建立字节文件流对象; (2)基于字节文件流对象建立数据文件流对象; (3)用流对象的方法对基本类型的数据进行输 DataInputStream类的构造方法如下 DataInputStream(InputStream in) 创建过滤流FilterInputStream 对象并为以后的使用保存InputStream 参数 in 。 DataOutputStream类的构造方法如下 DataOutputStream(OutputStream out) 创建输出数据流对象, , 写数据到指定的JAVA DataInput/outputStream小结
  • 35. BufferedInputStream与BufferedOutputStream类对IO进行缓冲,是一种常见的性能优化方式,缓冲流为I/o流增加了内存缓冲区,增加缓冲区有两个基本目的:允许Java程序一次不只操作一个字节,这样提高了程序的性能。 由于有了缓冲区,使得在流上执行skip、mark、和reset方法都成为可能。(使用它会提高系统的性能,避免与IO系统频繁交互) BufferedInputStream和BufferedOutputStream是Java提供的两个缓冲区包装类,不管系统是否使用了缓冲区,这两个类在自己的实例对象中创建缓冲区。这种缓冲区与底层系统提供的缓冲区是有区别的。底层系统提供的缓冲区直接与目标设备交换数据。包装类创建的缓冲区需要调用包装类所包装的那个输出流对象将这个缓冲区的数据写入到底层设备或底层缓冲区中。底层缓冲区就是一次向底层设备写入或者读取大量数据。而包装类缓冲区实现一次读取一行的作用(编程方式了解BufferStream.java)注意使用flush这个方法的使用 JAVA 处理流(过滤流) BufferedInputStream
  • 36. 对于字节流缓冲流(buffered stream)通过把内存缓冲区连到输入/输出流进而扩展一个过滤流类。该缓冲区允许Java对多个字节同时进行输入/输出操作,提高了程序性能。因为缓冲区可用,所以可以跳过、标记和重新设置流。 缓冲输入/输出是一个非常普通的性能优化。Java的BufferedInputStream 类允许把任何InputStream类“包装”成缓冲流并使它的性能提高 • BufferedInputStream 有两个构造方法 BufferedInputStream(InputStream inputStream)创建 BufferedInputStream 流对象并为以后的使用保存 InputStream 参数 ,并创建一个内部缓冲区数组来保存输入数据 BufferedInputStream(InputStream inputStream, int bufSize)用指定的缓冲区大小 size 创建BufferedInputStream 流对象。JAVA BufferedInput/OutputStream小结
  • 37. 使用DataOutputStream封装BufferedOutStream的使用 类DataBufferInputStream.java DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("data.txt"))); java.io.DataInputStream dis = new java.io.DataInputStream(new BufferedInputStream(new FileInputStream("data.txt"))); JAVA 处理流(过滤流)封装
  • 38. 1 、FileInputStream和FileOutputStream节点流 用于从文件中读取或往文件中写入字节流。如果在构造FileOutputStream时,文件已经存在则覆盖这个文件。 2、BufferedInputStream和BufferedOutputStream 过滤流,需要使用已经存在的节点流来构造,提供带缓冲的读写,提高了读写的效率。 3 、DataInputStream和DataOutputStream 过滤流,需要使用已经存在的节点流来构造,提供了读写Java中的基本数据类型的功能。JAVA 处理流(过滤流)总结
  • 39. 步骤 1 继承InputStream或者OutputStream 2 重写方法read()或者write()方法,一定根据自己的具体需求,比如读取网络的数据等 3 重写方法的实现(具体问题具体操作,读的是磁盘还是键盘还是网络) 4 调用类 具体的类( DefineStream.java) JAVA I/O 自定义流详解
  • 40. 字符流处理的为1个字节, 操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是 音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点. 所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列. 字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串; 2. 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。  字节流转换成字符流可以用 InputSteamReader OutputStreamWriter  JAVA I/O 字符流详解
  • 41. 字符流相对于字节流来说会简单一些,字节流是字符流的基础,在程序中一个字符等于两个字节,那么java提供了Reader、Writer两个专门操作字符流的类(API详解) 字符输出流:Writer Writer本身是一个字符流的输出类,此类的定义如下: public abstract class Writer extends Object implements Appendable,Closeable,Flushable 此类本身也是一个抽象类,如果要使用此类,则肯定要使用其子类,此时如果是向文件中写入内容,所以应该使用FileWriter的子类。 FileWriter类的构造方法定义如下: public FileWriter(File file)throws IOException 字符流的操作比字节流操作好在一点,就是可以直接输出字符串了,不用再像之前那样进行转换操作了。 字符输入流:Reader Reader是使用字符的方式从文件中取出数据,Reader类的定义如下: public abstract class Reader extends Objects implements Readable,Closeable Reader本身也是抽象类,如果现在要从文件中读取内容,则可以直接使用FileReader子类。 FileReader的构造方法定义如下: public FileReader(File file)throws FileNotFoundException JAVA I/O 字符流详解
  • 42. 由于Java采用16位的Unicode字符,因此需要基于字符的输入/输出操作。从Java1.1版开始,加入了专门处理字符流的抽象类Reader和Writer,前者用于处理输入,后者用于处理输出。这两个类类似于InputStream和OuputStream,也只是提供一些用于字符流的规定,本身不能用来生成对象 Reader和Writer类也有较多的子类,与字节流类似,它们用来创建具体的字符流象进行I/O操作。字符流的读写等方法与字节流的相应方法都很类似,但读写对象使用的是字符 Reader中包含一套字符输入流需要的方法,可以完成最基本的从输入流读入数据的功能。当Java程序需要外设的数据时,可根据数据的不同形式,创建一个适当的Reader子类类型的对象来完成与该外设的连接,然后再调用执行这个流类对象的特定输入方法,如read(),来实现对相应外设的输入操作 Writer中包含一套字符输出流需要的方法,可以完成最基本的输出数据到输出流的功能。当Java程序需要将数据输出到外设时,可根据数据的不同形式,也要创建一个适当的Writer子类类型的对象来完成与该外设的连接,然后再调用执行这个流类对象的特定输出方法,如write(),来实现对相应外设的输出操作 JAVA I/O Reader和Writer类
  • 43. JAVA I/O Reader和Writer类pushbackReader
  • 44. JAVA I/O Reader和Writer类Writer
  • 45. JAVA I/O InputStreamReader/OutputStreamWriter类InputStreamReader 是字符流Reader的子类,是字节流通向字符流的桥梁。你可以在构造器中指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如 GBK 等。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。 为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:  BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
  • 46. JAVA I/O InputStreamReader/OutputStreamWriter类OutputStreamWriter 是字符流Writer的子类,是字符流通向字节流的桥梁。每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积 为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:  BufferedWriter out  = new BufferedWriter(new OutputStreamWriter(System.out)); API详解
  • 47. JAVA I/O InputStreamReader/OutputStreamWriter类这是java.io包中用于处理字符流的基本类,用来在字节流和字符流之间搭一座“桥”。这里字节流的编码规范与具体的平台有关,可以在构造流对象时指定规范,也可以使用当前平台的缺省规范
  • 48. JAVA I/O InputStreamReader/OutputStreamWriter类InputStreamReader和OutputStreamWriter 类的主要构造方法如下 public InputSteamReader(InputSteam in) public InputSteamReader(InputSteamin,String enc) public OutputStreamWriter(OutputStreamout) public OutputStreamWriter(OutputStreamout,String enc)
  • 49. JAVA I/O InputStreamReader/OutputStreamWriter类其中in和out分别为输入和输出字节流对象,enc为指定的编码规范(若无此参数,表示使用当前平台的缺省规范,可用 getEncoding()方法得到当前字符流所用的编码方式)。 读写字符的方法read()、write(),关闭流的方法close()等与Reader和Writer类的同名方法用法都是类似的。 编程方式理解这两个类StreamReadTest1.java
  • 50. JAVA I/O InputStreamReader/OutputStreamWriter类编程方式理解键盘输入的处理 SystemIN.java(理解如何处理输入流)
  • 51. JAVA I/O FileReader/FileWriter类 FileReader类创建了一个可以读取文件内容的Reader类。FileReader继承于InputStreamReader。 它最常用的构造方法显示如下 FileReader(String filePath) FileReader(File fileObj) API详解
  • 52. JAVA I/O FileReader/FileWriter类2,主要方法 int read(); // 读取单个字符。返回作为整数读取的字符,如果已达到流末尾,则返回 -1。 int read(char []cbuf);//将字符读入数组。返回读取的字符数。如果已经到达尾部,则返回-1。 void close();//关闭此流对象。释放与之关联的所有资源。
  • 53. JAVA I/O FileReader/FileWriter类FileWriter类(字符输出流类) 构造方法: FileWriter fw = new FileWriter(StringfileName); 创建字符输出流类对象和已存在的文件相关联。文件不存在的话,并创建。 如:FileWriter fw = new FileWriter("C:\\demo.txt"); FileWriter fw = new FileWriter(String fileName,boolean append);//创建字符输出流类对象和已存在的文件相关联,并设置该该流对文件的操作是否为续写。 如:FileWriter fw = new FileWriter("C:\\demo.txt",ture); //表示在fw对文件再次写入时,会在该文件的结尾续写,并不会覆盖掉。
  • 54. JAVA I/O FileReader/FileWriter类主要方法: void write(String str)   //写入字符串。当执行完此方法后,字符数据还并没有写入到目的文件中去。此时字符数据会保存在缓冲区中。 此时在使用刷新方法就可以使数据保存到目的文件中去。 viod flush()  //刷新该流中的缓冲。将缓冲区中的字符数据保存到目的文件中去。 viod close() //关闭此流。在关闭前会先刷新此流的缓冲区。在关闭后,再写入或者刷新的话,会抛IOException异常。
  • 55. JAVA I/O FileReader/FileWriter类编程方式理解FileReader/FileWriter FileReader.java
  • 56. JAVA I/O CharArrayReader 类 CharArrayReader 是一个把字符数组作为源的输入流的实现。该类有两个构造函数,每一个都需要一个字符数组提供数据源: CharArrayReader(char array[ ]) CharArrayReader(char array[ ], int start, int numChars) 这里,array是输入源。第二个构造函数从你的字符数组的子集创建了一个Reader,该子集以start指定的索引开始,长度为numChars。(编程学习CharArrayReader.java)
  • 57. JAVA I/O CharArrayReader 类void close();   关闭此流 void mark(int readAheadLimit);  标记当前流读取的位置  void markSupport();     检测此流是否支持标记  int read();     读取一个字符、并以整数形式返回 int read(char[] c, int off, int len);   将buf中len个字符读取到下标从off开始的b中、返回读取的zi符个数 boolean ready();    查看CharArrayReader是否可读。  void reset();       将此流开始位置重置到最后一次调用mark是流的读取位置 long skip(long n);      丢弃buf中n个字符、返回实际丢弃的字符个数
  • 58. JAVA I/O 字符集编码方式 ASCII(American Standard Code for InformationInterchange,美国信息互换标准代码),是基于常用的英文字符的一套电脑编码系统。我们知道英文中经常使用的字符、数字符号被计算机处理时都是以二进制码的形式出现的。这种二进制码的集合就是所谓的ASCII码。每一个 ASCII码与一个8位(bit)二进制数对应。其最高位是0,相应的十进制数是0-127。如,数字“0”的编码用十进制数表示就是48。另有128个扩展的ASCII码,最高位都是1,由一些制表符和其它符号组成。ASCII是现今最通用的单字节编码系统。 GB2312:GB2312码是中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集-基本集》。主要用于给每一个中文字符指定相应的数字,也就是进行编码。一个中文字符用两个字节的数字来表示,为了和ASCII码有所区别,将中文字符每一个字节的最高位置都用1来表示。
  • 59. JAVA I/O 字符集编码方式 GBK:为了对更多的字符进行编码,国家又发布了新的编码系统GBK(GBK的K是“扩展”的汉语拼音第一个字母)。在新的编码系统里,除了完全兼容GB2312 外,还对繁体中文、一些不常用的汉字和许多符号进行了编码。 ISO-8859-1:是西方国家所使用的字符编码集,是一种单字节的字符集 ,而英文实际上只用了其中数字小于128的部分 。 Unicode:这是一种通用的字符集,对所有语言的文字进行了统一编码,对每一个字符都用2个字节来表示,对于英文字符采取前面加“0”字节的策略实现等长兼容。如 “a” 的ASCII码为0x61,UNICODE就为0x00,0x61。(在internet上传输效率较低) .UTF-8:Eight-bit UCS TransformationFormat,(UCS ,Universal Character Set ,通用字符集,UCS 是所有其他字符集标准的一个超集)。一个7位的ASCII码值,对应的UTF码是一个字节。如果字符是0x0000,或在0x0080与0x007f之间,对应的UTF码是两个字节,如果字符在0x0800与0xffff之间,对应的UTF码是三个字节(汉字为3个字节)。
  • 60. JAVA I/O 字符集编码方式 java对字符的处理 在java应用软件中,会有多处涉及到字符集编码,有些地方需要进行正确的设置,有些地方需要进行一定程度的处理。 1. getBytes(charset) 这是java字符串处理的一个标准函数,其作用是将字符串所表示的字符按照charset编码,并以字节方式表示。注意字符串在java内存中总是按unicode编码存储的。比如"中文",正常情况下(即没有错误的时候)存储为"4e2d 6587",如果charset为"gbk",则被编码为"d6d0 cec4",然后返回字节"d6 d0 ce c4"。如果charset为"utf8"则最后是"e4 b8 ad e6 96 87"。如果是"iso8859-1",则由于无法编码,最后返回 "3f 3f"(两个问号)。 2. new String(charset) 这是java字符串处理的另一个标准函数,和上一个函数的作用相反,将字节数组按照charset编码进行组合识别,最后转换为unicode存储。参考上述getBytes的例子,"gbk" 和"utf8"都可以得出正确的结果"4e2d 6587",但iso8859-1最后变成了"003f 003f"(两个问号)。 因为utf8可以用来表示/编码所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) === str,即完全可逆。 3. setCharacterEncoding() 该函数用来设置http请求或者相应的编码。
  • 61. JAVA I/O RandAccessFile类RandomAccessFile的唯一父类是Object,与其他流父类不同。是用来访问那些保存数据记录的文件的,这样你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。 RandomAccessFile是不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至都没有用InputStream和OutputStream已经准备好的功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。 基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream粘起来,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件,从这一点上看,假如RandomAccessFile继承了DataInputStream,它也许会干得更好。
  • 62. JAVA I/O RandAccessFile类Java的RandomAccessFile提供对文件的读写功能,与普通的输入输出流不一样的是RamdomAccessFile可以任意的访问文件的任何地方。这就是“Random”的意义所在 RandomAccessFile的对象包含一个记录指针,用于标识当前流的读写位置,这个位置可以向前移动,也可以向后移动。RandomAccessFile包含两个方法来操作文件记录指针。 long getFilePoint():记录文件指针的当前位置。 void seek(long pos):将文件记录指针定位到pos位置。 RandomAccessFile包含InputStream的三个read方法,也包含OutputStream的三个write方法。同时RandomAccessFile还包含一系列的readXxx和writeXxx方法完成输入输出。
  • 63. JAVA I/O RandAccessFile类RandomAccessFile的构造方法如下 RandomAccessFile(File file, String mode)           Creates a random access file stream to read from, and optionally to write to, the file specified by the File argument. RandomAccessFile(String name, String mode)           Creates a random access file stream to read from, and optionally to write to, a file with the specified name. mode的值有四个 "r":以只读文方式打开指定文件。如果你写的话会有IOException。 "rw":以读写方式打开指定文件,不存在就创建新文件。 "rws":不介绍了。 "rwd":也不介绍。 编程理解RandomAccessFile类(内存映射文件(memory-mapped files)"给取代)
  • 64. JAVA I/O RandAccessFile类特点: 1:该对象即可读取,又可写入。 2:该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组。 3:可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。 4:该对象操作的源和目的必须是文件。  5:其实该对象内部封装了字节读取流和字节写入流。 注意:实现随机访问,最好是数据有规律。
  • 65. JAVA I/O 序列化和反序列化序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象,序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将字节转换为对象。这两个过程结合起来,可以轻松地存储和传输数据,其目的有: 1 以某种存储形式使自定义对象持久化; 2、将对象从一个地方传递到另一个地方。 3、使程序更具维护性。 Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而产生。
  • 66. JAVA I/O 序列化和反序列化如何序列化一个对象 一个对象能够序列化的前提是实现Serializable或者Externalizable接口,Serializable接口没有方法,更像是个标记(Marker Interface)。有了这个标记的Class就能被序列化机制处理。API讲解Serializable(方法重要) 理论 需要注意的是Externalizable接口继承自Serializable接口。他们的区别是什么( Externalizable几乎用不到,了解)
  • 67. JAVA I/O 序列化和反序列化仅仅实现Serializable接口的类可应采用默认的序列化方式。比如String类。 假设有一个Customer类的对象需要序列化,如果这个类仅仅实现了这个接口,那么序列化和反序列化的方式如下:Object OutputStream采用默认的序列化方式,对于这个类的非static,非transient的实例变量进行序列化。Object InputStream采用默认的反序列化方式,对于这个类的非static,非transient的实例变量进行反序列化 如果这个类不仅实现了Serializable接口,而且定义了readObject(Object InputStream in)和 writeObject(Object OutputStream out)方法,那么将按照如下的方式进行序列化和反序列化:Object OutputStream会调用这个类的writeObject方法进行序列化,Object InputStream会调用相应的readObject方法进行反序列化。 实现Externalizable接口的类完全由自身来控制序列化的行为。而且必须实现writeExternal(Object Output out)和readExternal(Object Input in)。那么将按照如下的方式进行序列化和反序列化:ObjectOutputStream会调用这个类的writeExternal方法进行序列化,ObjectInputStream会调用相应readExternal方法进行反序列化。
  • 68. JAVA I/O 序列化和反序列化的特点假设一个被序列化的对象引用了其他对象,同样,其他对象又引用了更多的对象。这一系列的对象和它们的关系形成了一个顺序图表,他们都能被序列化和反序列化。 当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量。 如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。 如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。我们可以将这个引用标记为transient,那么对象仍然可以序列 总结 transient的变量不被序列化工具存储。同样,static变量也不被存储
  • 69. JAVA I/O 序列化和反序列化JDK类库中的序列化API java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。 (API详解类) 对象序列化包括如下步骤:   1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;   2) 通过对象输出流的writeObject()方法写对象。 对象反序列化的步骤如下:   1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;   2) 通过对象输入流的readObject()方法读取对象。
  • 70. JAVA I/O 序列化和反序列化编程方式学习序列化和反序列化( TestObjSerializeAndDeserialize.java类) 1 public class Person implements Serializable 2 private static void SerializePerson() throws FileNotFoundException 3 private static Person DeserializePerson() throws Exception, IOException
  • 71. JAVA I/O 序列化和反序列化serialVersionUID的作用 s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​ ​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量 private static final long serialVersionUID 实现Serializable接口的类如果类中没有添加serialVersionUID,那么就会出现如下的警告提示
  • 72. JAVA I/O 序列化和反序列化用鼠标点击就会弹出生成serialVersionUID的对话框,如下图所示:
  • 73. JAVA I/O serialVersionUID有两种生成方式serialVersionUID有两种生成方式: 采用Add default serial version ID这种方式生成的serialVersionUID是1L,例如: private static final long serialVersionUID = 1L; 采用Add generated serial version ID这种方式生成的serialVersionUID是根据类名,接口名,方法和属性等来生成的,例如(推荐使用) private static final long serialVersionUID = 4603642343377807741L;
  • 74. JAVA I/O serialVersionUIDserialVersionUID的取值 serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化. 类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。 显式地定义serialVersionUID有两种用途: 1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID; 2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
  • 75. JAVA I/O 序列化和反序列化的总结1. 一个类若想被序列化,则需要实现 java.io.Serializable 接口,该接口中没有定义任何方法,是一个标识性接口(Marker Interface) ,当一个类实现了该接口,就表示这个类的对象是可以序列化的。 2. 在序列化时,static 变量是无法序列化的;如果 A 包含了对 B 的引用,那么在序列化A 的时候也会将 B 一并地序列化;如果此时 A 可以序列化,B 无法序列化,那么当序列化 A 的时候就会发生异常,这时就需要将对 B 的引用设为 transient,该关键字表示变量不会被序列化
  • 76. 我们学习了字节流字符流(输入/输出流) 节点流(Fileinputstream/FileReader……) 处理流 (BufferedInPutStream/BufferedOutPutStream) 转换流 (InputStreamReader/OutputStreamWriter)字符流 数据流 (DataInputStream/DataOutputStream) 打印流 (PrintStream/PrintWriter) 对象流 (ObjectInputStream/ObjectOutputStream)序列化流 在编程中我们需要注意的是什么 1、将高级流“套接“在低级流上,这样起到缓冲的作用可以提高效率。(也就是处理流的使用)   2、将使用完的流关闭,释放资源。   3、读取如图片、声音、影像等文件用字节流。   4、读取如文本等文件用字符流。   5、根据具体的数据格式选择合适的读写方法、如按行读写、按照字节读写等 JAVA I/O 流总结
  • 77. JAVA I/O 流总结明白理论多练习
  • 78. 装饰模式的定义 装饰模式又名包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。(通过例子理解这句话BufferedStream) 装饰模式通过创建一个包装对象,也就是装饰,来包裹真实的对象 装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展 装饰模式把客户端的调用委派到被装饰类。装饰模式的关键在于这种扩展是完全透明的 装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。 通过例子理解装饰模式: JAVA I/O 设计模式-装饰模式
  • 79. 装饰模式的理解 让我们来假设一下,你正在寻找一个女朋友。有很多来自不同国家的女孩,比如:美国,中国,日本,法国等等,他们每个人都有不一样的个性和兴趣爱好,如果需要在程序当中模拟这么一种情况的话,假设每一个女孩就是一个Java类的话,那么就会有成千上万的类,这样子就会造成类的膨胀,而且这样的设计的可扩展性会比较差。因为如果我们需要一个新的女孩,就需要创建一个新的Java类,这实际上也违背了在程序开发当中需要遵循的OCP(对扩展开放,对修改关闭)原则。 让我们来重新做另外一种设计,让每一种个性或者兴趣爱好成为一种装饰从而可以动态地添加到每一个女孩的身上 JAVA I/O 设计模式-装饰模式
  • 80. JAVA I/O 设计模式-装饰模式
  • 81. 装饰模式的角色 抽象构件角色(Component):给出一个抽象接口,以规范准备接收附加责任的对象。 具体构件角色(Concrete Component):定义一个将要接收附加责任的类。 装饰角色(Decorator):持有一个抽象构件(Component)对象的引用,并定义一个与抽象构件接口一致的接口 具体装饰角色(Concrete Decorator):负责给具体构件对象“贴上”附加的责任。 JAVA I/O 设计模式-装饰模式
  • 82. 装饰模式的特点 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。 装饰对象包含一个真实对象的引用(reference)。 装饰对象接收所有来自客户端的请求,它把这些请求转发给真实的对象。 装饰对象可以在转发这些请求之前或之后附加一些功能。 这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。 JAVA I/O 设计模式-装饰模式
  • 83. 装饰模式与类继承的区别: 1)装饰模式是一种动态行为,对已经存在类进行随意组合,而类的继承是一种静态的行为,一个类定义成什么样的,该类的对象便具有什么样的功能,无法动态的改变。 2)    装饰模式扩展的是对象的功能,不需要增加类的数量,而类继承扩展是类的功能,在继承的关系中,如果我们想增加一个对象的功能,我们只能通过继承关系,在子类中增加两个方法。 3)    装饰模式是在不改变原类文件和使用继承的情况下,动态的扩展一个对象的功能,它是通过创建一个包装对象,也就是装饰来包裹真是的对象。 4)对于一个给定的对象,同时可能有不同的装饰对象,客户端可以通过它的需要选择合适的装饰对象发送消息 JAVA I/O 设计模式-装饰模式
  • 84. 编程理解装饰模式(decorator包下) JAVA I/O 设计模式-装饰模式
  • 85. 要点总结 1)继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。 2)在设计中,应该允许行为可以被扩展,而无须修改现有的代码。 3)组合和委托可用于在运行时动态地加上新的行为。 4)除了继承,装饰者模式也可以让我们扩展行为。 5)装饰者模式意味着一群装饰者类,这些类用来包装具体组件。 6)装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。 7)装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。 8)可以用无数个装饰者包装一个组件。 9)装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。 10)装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。 JAVA I/O 设计模式-装饰模式总结