一、IO流 
用于读写文件中的数据(可以读写文件,或网络中的数据…) 
1. 什么是IO流 
1 2 3 -  存储和读取数据的解决方案-  I:Input		O:output**-  流:像水流一样传输数据
 
2. IO流的作用 
用于读写数据(本地文件,网络) 
3 IO流向划分 
输出流: 程序 ----> 文件  
输入流::文件 ----> 程序  
 
4. IO按照文件类型划分 
字节流:可以操作所有类型的文件 
 
字符流:只能操作纯文本文件 
1 纯文本文件:Windows记事本可以打开并且能够读懂的文件 
 
 
 
5. IO体系划分 
 
二、字节流 
1. 字节输出流 
操作本地文件的字节输出流,可以将程序中的数据写入到本地文件中 
java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
public void close() :关闭此输出流并释放与此流相关联的任何系统资源。 
public void flush()  :刷新此输出流并强制任何缓冲的输出字节被写出。 
public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。 
public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。 
public abstract void write(int b) :将指定的字节输出流。 
 
小贴士:
close方法,当完成流的操作时,必须调用此方法,释放系统资源。
 
FileOutPutStream类 
形参可以是字符串或是File对象 
如果文件不存在可以创建新的文件,但是要却道父级路径存在 
如果文件存在会清空文件并且覆盖 
write方法中写入整数按照的是ASCII值 
每次使用完流都需要释放资源 
 
构造方法 
public FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件。 
public FileOutputStream(String name): 创建文件输出流以指定的名称写入文件。 
 
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。
1 2 3 4 5 6 7 8 9 10 public  class  FileOutputStreamConstructor  throws  IOException {    public  static  void  main (String[] args)  {    	 	         File  file  =  new  File ("a.txt" );         FileOutputStream  fos  =  new  FileOutputStream (file);                         FileOutputStream  fos  =  new  FileOutputStream ("b.txt" );     } } 
 
写出字节数据 
写出字节 :write(int b) 方法,每次可以写出一个字节数据,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  FOSWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileOutputStream  fos  =  new  FileOutputStream ("fos.txt" );            	       	fos.write(97 );        	fos.write(98 );        	fos.write(99 );        	         fos.close();     } } 输出结果: abc 
 
小贴士:
虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。 
流操作完毕后,必须释放系统资源,调用close方法,千万记得。 
 
 
写出字节数组 :write(byte[] b),每次可以写出数组中的数据,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  FOSWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileOutputStream  fos  =  new  FileOutputStream ("fos.txt" );            	       	byte [] b = "黑马程序员" .getBytes();       	       	fos.write(b);       	         fos.close();     } } 输出结果: 黑马程序员 
 
写出指定长度字节数组 :write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  FOSWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileOutputStream  fos  =  new  FileOutputStream ("fos.txt" );            	       	byte [] b = "abcde" .getBytes(); 		         fos.write(b,2 ,2 );       	         fos.close();     } } 输出结果: cd 
 
数据追加续写 
经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?
public FileOutputStream(File file, boolean append): 创建文件输出流以写入由指定的 File对象表示的文件。 
public FileOutputStream(String name, boolean append): 创建文件输出流以指定的名称写入文件。 
 
这两个构造方法,参数中都需要传入一个boolean类型的值,true 表示追加数据,false 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,代码使用演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  FOSWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileOutputStream  fos  =  new  FileOutputStream ("fos.txt" ,true );            	       	byte [] b = "abcde" .getBytes(); 		         fos.write(b);       	         fos.close();     } } 文件操作前:cd 文件操作后:cdabcde 
 
写出换行 
Windows系统里,换行符号是\r\n 。把
以指定是否追加续写了,代码使用演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  class  FOSWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileOutputStream  fos  =  new  FileOutputStream ("fos.txt" );         	       	byte [] words = {97 ,98 ,99 ,100 ,101 };       	         for  (int  i  =  0 ; i < words.length; i++) {           	             fos.write(words[i]);           	             fos.write("\r\n" .getBytes());         }       	         fos.close();     } } 输出结果: a b c d e 
 
回车符\r和换行符\n :
回车符:回到一行的开头(return)。 
换行符:下一行(newline)。 
 
 
系统中的换行:
Windows系统里,每行结尾是 回车+换行 ,即\r\n; 
Unix系统里,每行结尾只有 换行 ,即\n; 
Mac系统里,每行结尾是 回车 ,即\r。从 Mac OS X开始与Linux统一。 
 
 
 
 
2. 字节输入流 
java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
public void close() :关闭此输入流并释放与此流相关联的任何系统资源。 
public abstract int read(): 从输入流读取数据的下一个字节。 
public int read(byte[] b): 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。 
 
小贴士:
close方法,当完成流的操作时,必须调用此方法,释放系统资源。
 
java.io.FileInputStream 类是文件输入流,从文件中读取字节。
构造方法 
FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。 
FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。 
 
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException 。
1 2 3 4 5 6 7 8 9 10 public  class  FileInputStreamConstructor  throws  IOException{    public  static  void  main (String[] args)  {    	 	         File  file  =  new  File ("a.txt" );         FileInputStream  fos  =  new  FileInputStream (file);                         FileInputStream  fos  =  new  FileInputStream ("b.txt" );     } } 
 
读取字节数据 
读取字节 :read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public  class  FISRead  {    public  static  void  main (String[] args)  throws  IOException{       	        	FileInputStream  fis  =  new  FileInputStream ("read.txt" );       	         int  read  =  fis.read();         System.out.println((char ) read);         read = fis.read();         System.out.println((char ) read);         read = fis.read();         System.out.println((char ) read);         read = fis.read();         System.out.println((char ) read);         read = fis.read();         System.out.println((char ) read);       	        	read = fis.read();         System.out.println( read); 		         fis.close();     } } 输出结果: a b c d e -1  
 
循环改进读取方式,代码使用演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  class  FISRead  {    public  static  void  main (String[] args)  throws  IOException{       	        	FileInputStream  fis  =  new  FileInputStream ("read.txt" );       	         int  b ;                  while  ((b = fis.read())!=-1 ) {             System.out.println((char )b);         } 		         fis.close();     } } 输出结果: a b c d e 
 
小贴士:
虽然读取了一个字节,但是会自动提升为int类型。 
流操作完毕后,必须释放系统资源,调用close方法,千万记得。 
 
 
使用字节数组读取 :read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1 ,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  class  FISRead  {    public  static  void  main (String[] args)  throws  IOException{       	        	FileInputStream  fis  =  new  FileInputStream ("read.txt" );        	         int  len ;                  byte [] b = new  byte [2 ];                  while  (( len= fis.read(b))!=-1 ) {            	             System.out.println(new  String (b));         } 		         fis.close();     } } 输出结果: ab cd ed 
 
错误数据d,是由于最后一次读取时,只读取一个字节e,数组中,上次读取的数据没有被完全替换,所以要通过len ,获取有效的字节,代码使用演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  class  FISRead  {    public  static  void  main (String[] args)  throws  IOException{       	        	FileInputStream  fis  =  new  FileInputStream ("read.txt" );        	         int  len ;                  byte [] b = new  byte [2 ];                  while  (( len= fis.read(b))!=-1 ) {            	             System.out.println(new  String (b,0 ,len));         } 		         fis.close();     } } 输出结果: ab cd e 
 
小贴士:
使用数组读取,每次读取多个字节,减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用。
 
3.文件拷贝 
初级 
一次读写一个字节,速度慢 
1 2 3 4 5 6 7 8 9 10 FileInputStream  fis  =  new  FileInputStream ("DIR\\a.txt" );FileOutputStream  fos  =  new  FileOutputStream ("DIR\\b.txt" );int  b;while  ((b = fis.read()) != -1 ){    fos.write(b); } fis.close(); fos.close(); 
 
中级 
一次多一个字节数组的数据,每次读取会尽可能把数组装满(1024) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package  com.yyh.iodemo;import  java.io.FileInputStream;import  java.io.FileNotFoundException;import  java.io.FileOutputStream;import  java.io.IOException;public  class  IODemo03  {    public  static  void  main (String[] args)  throws  FileNotFoundException {         FileInputStream  fis  =  new  FileInputStream ("F:\\燕云十六声.mp4" );         FileOutputStream  fos  =  new  FileOutputStream ("DIR\\燕云十六声.mp4" );         try  (fis; fos) {             int  len;             byte [] bytes = new  byte [1024  * 1024  * 6 ];             while  ((len = fis.read(bytes)) != -1 ) {                 fos.write(bytes, 0 , len);             }         } catch  (IOException e) {             e.printStackTrace();         }     } } 
 
三、字符集 
1. 字符编码和字符集 
字符编码 
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码  。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码  。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
编码:字符(能看懂的)–字节(看不懂的)
解码:字节(看不懂的)–>字符(能看懂的)
字符集 
字符集 Charset :也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。 
 
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。
可见,当指定了编码 ,它所对应的字符集 自然就指定了,所以编码 才是我们最终要关心的。
ASCII字符集  :
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。 
基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。 
 
 
ISO-8859-1字符集 :
拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。 
ISO-8859-1使用单字节编码,兼容ASCII编码。 
 
 
GBxxx字符集 :
GB就是国标的意思,是为了显示中文而设计的一套字符集。 
GB2312 :简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。 
GBK :最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。 
GB18030 :最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。 
 
 
Unicode字符集  :
Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。 
它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。 
UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则:
128个US-ASCII字符,只需一个字节编码。 
拉丁文等字符,需要二个字节编码。 
大部分常用字(含中文),使用三个字节编码。 
其他极少使用的Unicode辅助字符,使用四字节编码。 
 
 
 
 
 
2. 英文存储 
3.中文存储 
4. Unicode万国码 
英文采用一个字节进行编码 
中文采用三个字节进行编码 
Unicode字符集的UTF-8编码格式 
Unicode 
Unicode 是一个国际标准(ISO/IEC 10646),旨在为全世界所有的书写系统中的每个字符提供一个唯一的码点(code point)。Unicode 提供了一种将文本表示为数字代码的方式,使得计算机可以准确地存储、处理和交换文本数据。
Unicode 能够表示超过 100,000 个字符,涵盖了现代和古代的脚本、符号、表情符号等。每个字符在 Unicode 中都有一个唯一的码点,通常是用 U+ 加上一个十六进制数来表示,比如拉丁字母 ‘A’ 的 Unicode 码点是 U+0041。
UTF-8 
UTF-8(8-bit Unicode Transformation Format)是 Unicode 的一种实现方式,它使用一至四个字节来表示一个字符。UTF-8 的特点是:
兼容性 :UTF-8 完全兼容 ASCII 编码,这意味着所有 ASCII 字符在 UTF-8 中都是单字节的,这使得它能够无缝地与基于 ASCII 的系统进行交互。
 
变长编码 :UTF-8 使用不同长度的字节序列来表示不同的字符。ASCII 字符(U+0000 至 U+007F)使用单个字节,而其他字符则使用两个、三个或四个字节。
 
自同步 :UTF-8 的编码方式保证了字节序列的自同步特性,即不管从哪个字节开始读取,都能确定当前字符的开始和结束位置。
 
无字节顺序标记(BOM) :UTF-8 不需要字节顺序标记(Byte Order Mark, BOM),这简化了文本文件的处理。
 
网络优化 :UTF-8 在网络传输中表现良好,因为它避免了使用字节的高八位,这在某些网络协议中可能有特殊含义。
 
 
由于这些优点,UTF-8 成为了互联网和多数操作系统的默认字符编码方式。它也被广泛用于编程语言和数据库系统中。
总结 
Unicode  是一个字符集和编码标准,提供了一个字符到数字码点的映射。 
UTF-8  是 Unicode 的一种实现,使用一到四个字节来编码 Unicode 中的字符,具有很好的兼容性和效率。 
 
在编程和文本处理中,使用 Unicode 和 UTF-8 可以确保跨平台和跨语言的文本准确性和一致性。
5.乱码 
原因1:读取数据时未读完整个汉字 
1 比如字节流一次读取一个字节,但是中文是以三个字节或者两个字节保存,那么就读不出来,因为中文是以1开头的所以解码为负数,显示乱码 
 
 
原因2:编码和解码的所用规则不一样 
 
 
解决乱码 
不要使用字节流读取文本文件  
编码解码时使用同一张码表,同一种编码方式  
 
扩展 
字节流读取会乱码,但是拷贝不会乱码 
1 数据不会丢失,记事本打开的时候解码规则如果和编码规则一样是不会乱码的 
 
 
 
6. Java中编码解码 
在IDEA中,使用FileReader 读取项目中的文本文件。由于IDEA的设置,都是默认的UTF-8编码,所以没有任何问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码。
1 2 3 4 5 6 7 8 9 10 11 12 public  class  ReaderDemo  {    public  static  void  main (String[] args)  throws  IOException {         FileReader  fileReader  =  new  FileReader ("E:\\File_GBK.txt" );         int  read;         while  ((read = fileReader.read()) != -1 ) {             System.out.print((char )read);         }         fileReader.close();     } } 输出结果: ��� 
 
那么如何读取GBK编码的文件呢?
四、字符流 
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
字符流底层是字节流 
 
输入流:一次读一个字节,遇到中文时候,一次读多个字节 
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中 
1. 字符输入流【Reader】 
java.io.Reader抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
public void close() :关闭此流并释放与此流相关联的任何系统资源。 
public int read(): 从输入流读取一个字符。 
public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。 
 
FileReader类 
java.io.FileReader 类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
小贴士:
字符编码:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。
idea中UTF-8
 
字节缓冲区:一个字节数组,用来临时存储字节数据。
 
 
 
构造方法 
FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象。 
FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称。 
 
当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。
1 2 3 4 5 6 7 8 9 10 public  class  FileReaderConstructor  throws  IOException{    public  static  void  main (String[] args)  {    	 	         File  file  =  new  File ("a.txt" );         FileReader  fr  =  new  FileReader (file);                         FileReader  fr  =  new  FileReader ("b.txt" );     } } 
 
读取字符数据 
读取字符 :read方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  class  FRRead  {    public  static  void  main (String[] args)  throws  IOException {       	        	FileReader  fr  =  new  FileReader ("read.txt" );       	         int  b ;                  while  ((b = fr.read())!=-1 ) {             System.out.println((char )b);         } 		         fr.close();     } } 输出结果: 黑 马 程 序 员 
 
小贴士:虽然读取了一个字符,但是会自动提升为int类型。
 
使用字符数组读取 :read(char[] cbuf),每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1 ,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  class  FRRead  {    public  static  void  main (String[] args)  throws  IOException {       	        	FileReader  fr  =  new  FileReader ("read.txt" );       	         int  len ;                   char [] cbuf = new  char [2 ];                  while  ((len = fr.read(cbuf))!=-1 ) {             System.out.println(new  String (cbuf));         } 		         fr.close();     } } 输出结果: 黑马 程序 员序 
 
获取有效的字符改进,代码使用演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  class  FISRead  {    public  static  void  main (String[] args)  throws  IOException {       	        	FileReader  fr  =  new  FileReader ("read.txt" );       	         int  len ;                  char [] cbuf = new  char [2 ];                  while  ((len = fr.read(cbuf))!=-1 ) {             System.out.println(new  String (cbuf,0 ,len));         }     	         fr.close();     } } 输出结果: 黑马 程序 员 
 
2. 字符输出流【Writer】 
java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
void write(int c) 写入单个字符。 
void write(char[] cbuf) 写入字符数组。 
abstract  void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数。 
void write(String str) 写入字符串。 
void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。 
void flush() 刷新该流的缓冲。 
void close() 关闭此流,但要先刷新它。 
 
FileWriter类 
java.io.FileWriter 类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
构造方法 
FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象。 
FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称。 
 
当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。
1 2 3 4 5 6 7 8 9 10 public  class  FileWriterConstructor  {    public  static  void  main (String[] args)  throws  IOException {    	 	         File  file  =  new  File ("a.txt" );         FileWriter  fw  =  new  FileWriter (file);                         FileWriter  fw  =  new  FileWriter ("b.txt" );     } } 
 
基本写出数据 
写出字符 :write(int b) 方法,每次可以写出一个字符数据,代码使用演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  class  FWWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileWriter  fw  =  new  FileWriter ("fw.txt" );            	       	fw.write(97 );        	fw.write('b' );        	fw.write('C' );        	fw.write(30000 );               	              } } 输出结果: abC田 
 
小贴士:
虽然参数为int类型四个字节,但是只会保留一个字符的信息写出。 
未调用close方法,数据只是保存到了缓冲区,并未写出到文件中。 
 
 
关闭和刷新 
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush 方法了。
flush :刷新缓冲区,流对象可以继续使用。 
close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。 
 
代码使用演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  class  FWWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileWriter  fw  =  new  FileWriter ("fw.txt" );                  fw.write('刷' );          fw.flush();         fw.write('新' );          fw.flush();              	         fw.write('关' );          fw.close();         fw.write('闭' );          fw.close();     } } 
 
小贴士:即便是flush方法写出了数据,操作的最后还是要调用close方法,释放系统资源。
 
写出其他数据 
写出字符数组  :write(char[] cbuf) 和 write(char[] cbuf, int off, int len) ,每次可以写出字符数组中的数据,用法类似FileOutputStream,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  class  FWWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileWriter  fw  =  new  FileWriter ("fw.txt" );            	       	char [] chars = "黑马程序员" .toCharArray();              	       	fw.write(chars);           		         fw.write(b,2 ,2 );               	         fos.close();     } } 
 
写出字符串 :write(String str) 和 write(String str, int off, int len) ,每次可以写出字符串中的数据,更为方便,代码使用演示: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  class  FWWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileWriter  fw  =  new  FileWriter ("fw.txt" );            	       	String  msg  =  "黑马程序员" ;              	       	fw.write(msg);         		         fw.write(msg,2 ,2 );	       	                  fos.close();     } } 
 
续写和换行 :操作类似于FileOutputStream。 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  class  FWWrite  {    public  static  void  main (String[] args)  throws  IOException {                  FileWriter  fw  =  new  FileWriter ("fw.txt" ,true );            	         fw.write("黑马" );       	       	fw.write("\r\n" );       	   		fw.write("程序员" );       	         fw.close();     } } 输出结果: 黑马 程序员 
 
小贴士:字符流,只能操作文本文件,不能操作图片,视频等非文本文件。
当我们单纯读或者写文本文件时  使用字符流 其他情况使用字节流
 
3.字符流原理解析 
3.1 字符输入流 
3.2 字符输出流 
只有在flush或close之后才能将缓冲区的数据输出到文件中 
 
五、缓冲流 
昨天学习了基本的一些流,作为IO流的入门,今天我们要见识一些更强大的流。比如能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等等。这些功能更为强大的流,都是在基本的流对象基础之上创建而来的,就像穿上铠甲的武士一样,相当于是对基本流对象的一种增强。
1. 概述 
缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:
字节缓冲流 :BufferedInputStream,BufferedOutputStream 
字符缓冲流 :BufferedReader,BufferedWriter 
 
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
2. 字节缓冲流 
构造方法 
public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。 
public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流。 
 
构造举例,代码如下:
1 2 3 4 BufferedInputStream  bis  =  new  BufferedInputStream (new  FileInputStream ("bis.txt" ));BufferedOutputStream  bos  =  new  BufferedOutputStream (new  FileOutputStream ("bos.txt" ));
 
效率测试 
查询API,缓冲流读写方法与基本的流是一致的,我们通过复制大文件(375MB),测试它的效率。
基本流,代码如下: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  class  BufferedDemo  {    public  static  void  main (String[] args)  throws  FileNotFoundException {                	long  start  =  System.currentTimeMillis(); 		         try  (         	FileInputStream  fis  =  new  FileInputStream ("jdk9.exe" );         	FileOutputStream  fos  =  new  FileOutputStream ("copy.exe" )         ){         	             int  b;             while  ((b = fis.read()) != -1 ) {                 fos.write(b);             }         } catch  (IOException e) {             e.printStackTrace();         } 		         long  end  =  System.currentTimeMillis();         System.out.println("普通流复制时间:" +(end - start)+" 毫秒" );     } } 十几分钟过去了... 
 
缓冲流,代码如下: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  class  BufferedDemo  {    public  static  void  main (String[] args)  throws  FileNotFoundException {                	long  start  =  System.currentTimeMillis(); 		         try  (         	BufferedInputStream  bis  =  new  BufferedInputStream (new  FileInputStream ("jdk9.exe" )); 	     BufferedOutputStream  bos  =  new  BufferedOutputStream (new  FileOutputStream ("copy.exe" ));         ){                      int  b;             while  ((b = bis.read()) != -1 ) {                 bos.write(b);             }         } catch  (IOException e) {             e.printStackTrace();         } 		         long  end  =  System.currentTimeMillis();         System.out.println("缓冲流复制时间:" +(end - start)+" 毫秒" );     } } 缓冲流复制时间:8016  毫秒 
 
如何更快呢?
使用数组的方式,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  class  BufferedDemo  {    public  static  void  main (String[] args)  throws  FileNotFoundException {       	         long  start  =  System.currentTimeMillis(); 		         try  ( 			BufferedInputStream  bis  =  new  BufferedInputStream (new  FileInputStream ("jdk9.exe" )); 		 BufferedOutputStream  bos  =  new  BufferedOutputStream (new  FileOutputStream ("copy.exe" ));         ){           	             int  len;             byte [] bytes = new  byte [8 *1024 ];             while  ((len = bis.read(bytes)) != -1 ) {                 bos.write(bytes, 0  , len);             }         } catch  (IOException e) {             e.printStackTrace();         } 		         long  end  =  System.currentTimeMillis();         System.out.println("缓冲流使用数组复制时间:" +(end - start)+" 毫秒" );     } } 缓冲流使用数组复制时间:666  毫秒 
 
3. 字符缓冲流 
构造方法 
public BufferedReader(Reader in) :创建一个 新的缓冲输入流。 
public BufferedWriter(Writer out): 创建一个新的缓冲输出流。 
 
构造举例,代码如下:
1 2 3 4 BufferedReader  br  =  new  BufferedReader (new  FileReader ("br.txt" ));BufferedWriter  bw  =  new  BufferedWriter (new  FileWriter ("bw.txt" ));
 
特有方法 
字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。
BufferedReader:public String readLine(): 读一行文字。 
BufferedWriter:public void newLine(): 写一行行分隔符,由系统属性定义符号。 
 
readLine方法演示,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  class  BufferedReaderDemo  {    public  static  void  main (String[] args)  throws  IOException {       	          BufferedReader  br  =  new  BufferedReader (new  FileReader ("in.txt" )); 		         String  line   =  null ;       	         while  ((line = br.readLine())!=null ) {             System.out.print(line);             System.out.println("------" );         } 		         br.close();     } } 
 
newLine方法演示,代码如下:
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  class  BufferedWriterDemo  throws  IOException {    public  static  void  main (String[] args)  throws  IOException  {       	 		BufferedWriter  bw  =  new  BufferedWriter (new  FileWriter ("out.txt" ));       	         bw.write("黑马" );       	         bw.newLine();         bw.write("程序" );         bw.newLine();         bw.write("员" );         bw.newLine(); 		         bw.close();     } } 输出效果: 黑马 程序 员 
 
4. 练习:文本排序 
请将文本信息恢复顺序。
1 2 3 4 5 6 7 8 9 3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。 8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。 4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。 2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。 1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。 9.今当远离,临表涕零,不知所言。 6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。 7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。 5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。 
 
案例分析 
逐行读取文本信息。 
把读取到的文本存储到集合中 
对集合中的文本进行排序 
遍历集合,按顺序,写出文本信息。 
 
案例实现 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public  class  Demo05Test  {    public  static  void  main (String[] args)  throws  IOException {                  ArrayList<String> list = new  ArrayList <>();                  BufferedReader  br  =  new  BufferedReader (new  FileReader ("10_IO\\in.txt" ));                  BufferedWriter  bw  =  new  BufferedWriter (new  FileWriter ("10_IO\\out.txt" ));                  String line;         while ((line = br.readLine())!=null ){                          list.add(line);         }                  Collections.sort(list, new  Comparator <String>() {                          @Override              public  int  compare (String o1, String o2)  {                                  return  o1.charAt(0 )-o2.charAt(0 );             }         });                  for  (String s : list) {                          bw.write(s);                          bw.newLine();         }                  bw.close();         br.close();     } } 
 
5. 缓冲流总结 
六、转换流 
是字符流和字节流之间的桥梁,可以将字节流转成字符流,并可以使用其里面的方法 
字节流读取中文的时候会乱码,所以用转换流
转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法 
InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。 
InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。 
 
构造举例,代码如下:
1 2 InputStreamReader  isr  =  new  InputStreamReader (new  FileInputStream ("in.txt" ));InputStreamReader  isr2  =  new  InputStreamReader (new  FileInputStream ("in.txt" ) , "GBK" );
 
指定编码读取 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  class  ReaderDemo2  {    public  static  void  main (String[] args)  throws  IOException {       	         String  FileName  =  "E:\\file_gbk.txt" ;       	         InputStreamReader  isr  =  new  InputStreamReader (new  FileInputStream (FileName));       	         InputStreamReader  isr2  =  new  InputStreamReader (new  FileInputStream (FileName) , "GBK" ); 		         int  read;       	         while  ((read = isr.read()) != -1 ) {             System.out.print((char )read);          }         isr.close();              	         while  ((read = isr2.read()) != -1 ) {             System.out.print((char )read);         }         isr2.close();     } } 
 
2. OutputStreamWriter类 
转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法 
OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。 
OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。 
 
构造举例,代码如下:
1 2 OutputStreamWriter  isr  =  new  OutputStreamWriter (new  FileOutputStream ("out.txt" ));OutputStreamWriter  isr2  =  new  OutputStreamWriter (new  FileOutputStream ("out.txt" ) , "GBK" );
 
指定编码写出 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  class  OutputDemo  {    public  static  void  main (String[] args)  throws  IOException {       	         String  FileName  =  "E:\\out.txt" ;       	         OutputStreamWriter  osw  =  new  OutputStreamWriter (new  FileOutputStream (FileName));                	osw.write("你好" );          osw.close();       	 		 		String  FileName2  =  "E:\\out2.txt" ;      	         OutputStreamWriter  osw2  =  new  OutputStreamWriter (new  FileOutputStream (FileName2),"GBK" );                	osw2.write("你好" );         osw2.close();     } } 
 
转换流理解图解 
转换流是字节与字符间的桥梁! 
3. 练习:转换文件编码 
将GBK编码的文本文件,转换为UTF-8编码的文本文件。
案例分析 
指定GBK编码的转换流,读取文本文件。 
使用UTF-8编码的转换流,写出文本文件。 
 
案例实现 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public  class  TransDemo  {   public  static  void  main (String[] args)  {           	      	String  srcFile  =  "file_gbk.txt" ;         String  destFile  =  "file_utf8.txt" ; 		     	         InputStreamReader  isr  =  new  InputStreamReader (new  FileInputStream (srcFile) , "GBK" );     	         OutputStreamWriter  osw  =  new  OutputStreamWriter (new  FileOutputStream (destFile)); 		     	         char [] cbuf = new  char [1024 ];     	         int  len;     	         while  ((len = isr.read(cbuf))!=-1 ) {                        	osw.write(cbuf,0 ,len);         }     	         osw.close();         isr.close();   	} } 
 
4. 字节流使用缓冲字符流 
1 new  BufferReader (new  InputStreamReader (new  FileInputStream ("DIR\\yyh.txt" )))
 
5. 转换流总结 
转换流的名称
字符转换输入流:InputStreamReader 
字符转换输出流:OutputStreamWriter 
 
 
转换流的作用
指定字符集读写数据(JDK11之后已淘汰) 
字节流想要使用字符流中的方法了 
 
 
 
七、序列化 
 
1. 概述 
Java 提供了一种对象序列化 的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存 了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化 。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化: 
2. ObjectOutputStream类 
java.io.ObjectOutputStream  类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
构造方法 
public ObjectOutputStream(OutputStream out) : 创建一个指定OutputStream的ObjectOutputStream。 
 
构造举例,代码如下:
1 2 FileOutputStream  fileOut  =  new  FileOutputStream ("employee.txt" );ObjectOutputStream  out  =  new  ObjectOutputStream (fileOut);
 
序列化操作 
一个对象要想序列化,必须满足两个条件: 
 
该类必须实现java.io.Serializable  接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException 。 
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。 
 
1 2 3 4 5 6 7 8 public  class  Employee  implements  java .io.Serializable {    public  String name;     public  String address;     public  transient  int  age;      public  void  addressCheck ()  {       	System.out.println("Address  check : "  + name + " -- "  + address);     } } 
 
2.写出对象方法
public final void writeObject (Object obj) : 将指定的对象写出。 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  class  SerializeDemo {   	public  static  void  main (String [] args)    {     	Employee  e  =  new  Employee ();     	e.name = "zhangsan" ;     	e.address = "beiqinglu" ;     	e.age = 20 ;      	try  {       		           ObjectOutputStream  out  =  new  ObjectOutputStream (new  FileOutputStream ("employee.txt" ));         	         	out.writeObject(e);         	         	out.close();         	fileOut.close();         	System.out.println("Serialized data is saved" );          } catch (IOException i)   {             i.printStackTrace();         }    	} } 输出结果: Serialized data is saved 
 
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
构造方法 
public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream。 
 
反序列化操作1 
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:
public final Object readObject () : 读取一个对象。 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public  class  DeserializeDemo  {   public  static  void  main (String [] args)    {         Employee  e  =  null ;         try  {		                            FileInputStream  fileIn  =  new  FileInputStream ("employee.txt" );              ObjectInputStream  in  =  new  ObjectInputStream (fileIn);                            e = (Employee) in.readObject();                            in.close();              fileIn.close();         }catch (IOException i) {                            i.printStackTrace();              return ;         }catch (ClassNotFoundException c)  {         	              System.out.println("Employee class not found" );              c.printStackTrace();              return ;         }                  System.out.println("Name: "  + e.name);	         System.out.println("Address: "  + e.address);          System.out.println("age: "  + e.age);      } } 
 
对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。 
反序列化操作2  
**另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。**发生这个异常的原因如下:
该类的序列版本号与从流中读取的类描述符的版本号不匹配 
该类包含未知数据类型 
该类没有可访问的无参数构造方法 
 
Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
1 2 3 4 5 6 7 8 9 10 11 12 public  class  Employee  implements  java .io.Serializable {           private  static  final  long  serialVersionUID  =  1L ;      public  String name;      public  String address;            public  int  eid;       public  void  addressCheck ()  {          System.out.println("Address  check : "  + name + " -- "  + address);      } } 
 
4. 练习:序列化集合 
将存有多个自定义对象的集合序列化操作,保存到list.txt文件中。 
反序列化list.txt ,并遍历集合,打印对象信息。 
 
案例分析 
把若干学生对象 ,保存到集合中。 
把集合序列化。 
反序列化读取时,只需要读取一次,转换为集合类型。 
遍历集合,可以打印所有的学生信息 
 
案例实现 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public  class  SerTest  {	public  static  void  main (String[] args)  throws  Exception { 		 		Student  student  =  new  Student ("老王" , "laow" ); 		Student  student2  =  new  Student ("老张" , "laoz" ); 		Student  student3  =  new  Student ("老李" , "laol" ); 		ArrayList<Student> arrayList = new  ArrayList <>(); 		arrayList.add(student); 		arrayList.add(student2); 		arrayList.add(student3); 		 		 		 		 		ObjectInputStream  ois   =  new  ObjectInputStream (new  FileInputStream ("list.txt" )); 		 		ArrayList<Student> list  = (ArrayList<Student>)ois.readObject(); 		       	for  (int  i  =  0 ; i < list.size(); i++ ){           	Student  s  =  list.get(i);         	System.out.println(s.getName()+"--" + s.getPwd());       	} 	} 	private  static  void  serializ (ArrayList<Student> arrayList)  throws  Exception { 		 		ObjectOutputStream  oos  =  new  ObjectOutputStream (new  FileOutputStream ("list.txt" )); 		 		oos.writeObject(arrayList); 		 		oos.close(); 	} } 
 
5. JavaBean中的版本号 
在JavaBean中,版本号通常是指用于标识类的不同版本的一个字段,它主要用于序列化和反序列化过程中保持兼容性。版本号也被称为序列化ID或 serialVersionUID。
当你的JavaBean实现了Serializable接口时,Java运行时环境会使用这个版本号来验证序列化和反序列化的对象是否兼容。如果发送方序列化对象的类和接收方反序列化对象的类具有不同的版本号,那么在反序列化过程中会抛出InvalidClassException异常。
版本号是一个名为serialVersionUID的静态常量字段,通常位于实现Serializable接口的类的顶部。它是一个long类型的值,可以由开发者手动指定,也可以由编译器自动生成。如果你希望类的序列化版本保持稳定,你应该手动指定一个版本号。
以下是一个包含版本号的JavaBean示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import  java.io.Serializable;public  class  MyBean  implements  Serializable  {    private  static  final  long  serialVersionUID  =  1L ;     private  String name;     private  int  age;          @Override      public  String toString ()  {         return  "MyBean{"  +                 "name='"  + name + '\''  +                 ", age="  + age +                 '}' ;     } } 
 
在这个例子中,serialVersionUID字段被设置为1L。如果你修改了MyBean类的结构,比如添加或删除字段,你应该更新serialVersionUID的值,以反映类的新版本。
版本号是确保序列化兼容性的重要机制,它有助于避免在不同版本的类之间进行对象传输时出现问题。如果你不关心序列化兼容性,或者你的类永远不会改变,那么可以省略版本号,让编译器自动生成。但是,在实际开发中,为了确保类的序列化兼容性,手动指定版本号是一种良好的实践。
八、打印流 
1. 概述 
平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
2. PrintStream类 
字节打印流 
构造方法 
public PrintStream(String fileName)  : 使用指定的文件名创建一个新的打印流。 
 
构造举例,代码如下:
1 PrintStream  ps  =  new  PrintStream ("ps.txt" );
 
改变打印流向 
System.out就是PrintStream类型的,只不过它的流向是系统规定的,打印在控制台上。不过,既然是流对象,我们就可以玩一个"小把戏",改变它的流向。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  PrintDemo  {    public  static  void  main (String[] args)  throws  IOException { 		         System.out.println(97 );        		         PrintStream  ps  =  new  PrintStream ("ps.txt" );       	       	         System.setOut(ps);       	         System.out.println(97 );     } } 
 
成员方法 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package  com.itheima.myprintstream;import  java.io.FileNotFoundException;import  java.io.FileOutputStream;import  java.io.PrintStream;import  java.nio.charset.Charset;public  class  PrintStreamDemo1  {    public  static  void  main (String[] args)  throws  FileNotFoundException {                       PrintStream  ps  =  new  PrintStream (new  FileOutputStream ("myio\\a.txt" ), true , Charset.forName("UTF-8" ));                  ps.println(97 );         ps.print(true );         ps.println();         ps.printf("%s爱上了%s" ,"阿珍" ,"阿强" );                  ps.close();     } } 
 
3. PrintWrite类 
字符打印流 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package  com.itheima.myprintstream;import  java.io.FileWriter;import  java.io.IOException;import  java.io.PrintWriter;public  class  PrintStreamDemo3  {    public  static  void  main (String[] args)  throws  IOException {                PrintWriter  pw  =  new  PrintWriter (new  FileWriter ("myio\\a.txt" ),true );                  pw.println("今天你终于叫我名字了,虽然叫错了,但是没关系,我马上改" );         pw.print("你好你好" );         pw.printf("%s爱上了%s" ,"阿珍" ,"阿强" );                  pw.close();     } } 
 
4. 打印流应用场景 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package  com.itheima.myprintstream;import  java.io.PrintStream;public  class  PrintStreamDemo4  {    public  static  void  main (String[] args)  {                                    PrintStream  ps  =  System.out;                           ps.println("123" );                  ps.println("你好你好" );         System.out.println("456" );     } } 
 
九、 压缩流和解压缩流 
压缩流:
	负责压缩文件或者文件夹
解压缩流:
	负责把压缩包中的文件和文件夹解压出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public  class  ZipStreamDemo1  {    public  static  void  main (String[] args)  throws  IOException {                  File  src  =  new  File ("D:\\aaa.zip" );                  File  dest  =  new  File ("D:\\" );                  unzip(src,dest);     }          public  static  void  unzip (File src,File dest)  throws  IOException {                           ZipInputStream  zip  =  new  ZipInputStream (new  FileInputStream (src));                           ZipEntry entry;         while ((entry = zip.getNextEntry()) != null ){             System.out.println(entry);             if (entry.isDirectory()){                                  File  file  =  new  File (dest,entry.toString());                 file.mkdirs();             }else {                                  FileOutputStream  fos  =  new  FileOutputStream (new  File (dest,entry.toString()));                 int  b;                 while ((b = zip.read()) != -1 ){                                          fos.write(b);                 }                 fos.close();                                  zip.closeEntry();             }         }         zip.close();     } } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public  class  ZipStreamDemo2  {    public  static  void  main (String[] args)  throws  IOException {                           File  src  =  new  File ("D:\\a.txt" );                  File  dest  =  new  File ("D:\\" );                  toZip(src,dest);     }          public  static  void  toZip (File src,File dest)  throws  IOException {                  ZipOutputStream  zos  =  new  ZipOutputStream (new  FileOutputStream (new  File (dest,"a.zip" )));                           ZipEntry  entry  =  new  ZipEntry ("aaa\\bbb\\a.txt" );                  zos.putNextEntry(entry);                  FileInputStream  fis  =  new  FileInputStream (src);         int  b;         while ((b = fis.read()) != -1 ){             zos.write(b);         }         zos.closeEntry();         zos.close();     } } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public  class  ZipStreamDemo3  {    public  static  void  main (String[] args)  throws  IOException {                           File  src  =  new  File ("D:\\aaa" );                  File  destParent  =  src.getParentFile();                  File  dest  =  new  File (destParent,src.getName() + ".zip" );                  ZipOutputStream  zos  =  new  ZipOutputStream (new  FileOutputStream (dest));                  toZip(src,zos,src.getName());                  zos.close();     }          public  static  void  toZip (File src,ZipOutputStream zos,String name)  throws  IOException {                  File[] files = src.listFiles();                  for  (File file : files) {             if (file.isFile()){                                  ZipEntry  entry  =  new  ZipEntry (name + "\\"  + file.getName());                 zos.putNextEntry(entry);                                  FileInputStream  fis  =  new  FileInputStream (file);                 int  b;                 while ((b = fis.read()) != -1 ){                     zos.write(b);                 }                 fis.close();                 zos.closeEntry();             }else {                                  toZip(file,zos,name + "\\"  + file.getName());                              }         }     } } 
 
十. 工具包(Commons-io) 
介绍:
	Commons是apache开源基金组织提供的工具包,里面有很多帮助我们提高开发效率的API
比如:
	StringUtils   字符串工具类
	NumberUtils   数字工具类
	ArrayUtils   数组工具类
	RandomUtils   随机数工具类
	DateUtils   日期工具类
	StopWatch   秒表工具类
	ClassUtils   反射工具类
	SystemUtils   系统工具类
	MapUtils   集合工具类
	Beanutils   bean工具类
	Commons-io io的工具类
	等等…
其中:Commons-io是apache开源基金组织提供的一组有关IO操作的开源工具包。
作用:提高IO流的开发效率。
使用方式:
1,新建lib文件夹
2,把第三方jar包粘贴到文件夹中
3,右键点击add as a library
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package  com.itheima.mycommonsio;import  java.io.IOException;public  class  CommonsIODemo1  {    public  static  void  main (String[] args)  throws  IOException {                                        } } 
 
十一. 工具包(hutool) 
介绍:
	Commons是国人开发的开源工具包,里面有很多帮助我们提高开发效率的API
比如:
	DateUtil  日期时间工具类
	TimeInterval  计时器工具类
	StrUtil  字符串工具类
	HexUtil   16进制工具类
	HashUtil   Hash算法类
	ObjectUtil  对象工具类
	ReflectUtil   反射工具类
	TypeUtil  泛型类型工具类
	PageUtil  分页工具类
	NumberUtil  数字工具类
使用方式:
1,新建lib文件夹
2,把第三方jar包粘贴到文件夹中
3,右键点击add as a library
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public  class  Test1  {    public  static  void  main (String[] args)  {                             List<String> list = FileUtil.readLines("D:\\a.txt" , "UTF-8" );         System.out.println(list);     } }