A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 白小托 中级黑马   /  2019-8-1 16:01  /  909 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

近期学习了Java的IO流,尝试着总结一下。
java.io 包下的IO流很多:
其中,以Stream结尾的为字节流,以Writer或者Reader结尾的为字符流。所有的输入流都是抽象类IuputStream(字节输入流)或者抽象类Reader(字符输入流)的子类,所有的输出流都是抽象类OutputStream(字节输出流)或者抽象类Writer(字符输出流)的子类。字符流能实现的功能字节流都能实现,反之不一定。如:图片,视频等二进制文件,只能使用字节流读写。
1、字符流FileReader和FileWriter
FileReader类
构造方法摘要
[url=]FileReader[/url]([url=]File[/url] file)
          在给定从中读取数据的 File 的情况下创建一个新 FileReader。
[url=]FileReader[/url]([url=]FileDescriptor[/url] fd)
          在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。
[url=]FileReader[/url]([url=]String[/url] fileName)
          在给定从中读取数据的文件名的情况下创建一个新 FileReader。
FileWriter类
构造方法摘要
[url=]FileWriter[/url]([url=]File[/url] file)
          根据给定的 File 对象构造一个 FileWriter 对象。
[url=]FileWriter[/url]([url=]File[/url] file, boolean append)
          根据给定的 File 对象构造一个 FileWriter 对象。
[url=]FileWriter[/url]([url=]FileDescriptor[/url] fd)
          构造与某个文件描述符相关联的 FileWriter 对象。
[url=]FileWriter[/url]([url=]String[/url] fileName)
          根据给定的文件名构造一个 FileWriter 对象。
[url=]FileWriter[/url]([url=]String[/url] fileName, boolean append)
          根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
使用FileReader和FileWriter类完成文本文件复制:
CopyFile
2、字符缓冲流BufferedReader和BufferedWriter
字符缓冲流具备文本特有的表现形式,行操作
public class BufferedReader extends [url=]Reader[/url]
(1)从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
(2)可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
(3)通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如,
BufferedReader in   = new BufferedReader(new FileReader("foo.in"));
(4)将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。
public class BufferedWriter extends [url=]Writer[/url]
(1)将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
(2)可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
(3)该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。
(4)通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如,
PrintWriter out   = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));
(5)缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。

使用BufferedReader和BufferedWriter完成文件复制
CopyFile2
缓冲区的工作原理:1、使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内。2、通过缓冲区的read()方法从缓冲区获取具体的字符数据,这样就提高了效率。3、如果用read方法读取字符数据,并存储到另一个容器中,直到读取到了换行符时,将另一个容器临时存储的数据转成字符串返回,就形成了readLine()功能。
3、字节流FileInputStream和FileOutputStream
public class FileInputStream extends [url=]InputStream[/url]
(1)FileInputStream 从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。
(2)FileInputStream 用于读取诸如图像数据之类的原始字节流。
构造方法摘要
[url=]FileInputStream[/url]([url=]File[/url] file)
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
[url=]FileInputStream[/url]([url=]FileDescriptor[/url] fdObj)
          通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。
[url=]FileInputStream[/url]([url=]String[/url] name)
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
public class FileOutputStream extends [url=]OutputStream[/url]
(1)文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。(2) FileOutputStream 用于写入诸如图像数据之类的原始字节的流。
构造方法摘要
[url=]FileOutputStream[/url]([url=]File[/url] file)
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
[url=]FileOutputStream[/url]([url=]File[/url] file, boolean append)
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
[url=]FileOutputStream[/url]([url=]FileDescriptor[/url] fdObj)
          创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。
[url=]FileOutputStream[/url]([url=]String[/url] name)
          创建一个向具有指定名称的文件中写入数据的输出文件流。
[url=]FileOutputStream[/url]([url=]String[/url] name, boolean append)
          创建一个向具有指定 name 的文件中写入数据的输出文件流。
例:使用字节流复制图片
[backcolor=rgb(245, 245, 245) !important][url=][/url]
1 import java.io.FileInputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4  5 public class CopImg { 6     public static void main(String[] args) throws IOException { 7         FileInputStream fin=new FileInputStream("C:\\Users\\Administrator\\Desktop\\Img.jpg"); 8         FileOutputStream fout=new FileOutputStream("C:\\Users\\Administrator\\Desktop\\ImgCopy.jpg"); 9         int len=0;10         byte[] buff=new byte[1024];11         while((len=fin.read(buff))!=-1) {12             fout.write(buff, 0, len);13         }14         fin.close();15         fout.close();16     }17 }[backcolor=rgb(245, 245, 245) !important][url=][/url]


4、字节缓冲流BufferedInputStream和BufferedOutputStream
public class BufferedInputStream extends [url=]FilterInputStream[/url]
BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。
public class BufferedOutputStream extends [url=]FilterOutputStream[/url]
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
例:使用字节缓冲流实现图片的复制
[backcolor=rgb(245, 245, 245) !important][url=][/url]
1 import java.io.BufferedInputStream; 2 import java.io.BufferedOutputStream; 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6  7 public class CopyImg { 8     public static void main(String[] args) throws IOException { 9         BufferedInputStream bfin=new BufferedInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\Img.jpg"));10         BufferedOutputStream bfout=new BufferedOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\ImgCopybuff.jpg"));11         int len=0;12         byte[] buff=new byte[1024];13         while((len=bfin.read(buff))!=-1) {14             bfout.write(buff, 0, len);15         }16         bfin.close();17         bfout.close();18     }19 }[backcolor=rgb(245, 245, 245) !important][url=][/url]


5、转换流:InputStreamReader和OutputStreamWriter
InputStreamReader和OutputStreamWriter是字符和字节的桥梁,也可称之为字符转换流。原理:字节流+编码。
FileReader和FileWriter作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接使用子类完成操作,简化代码。
一旦要指定其他编码时,不能使用子类,必须使用字符转换流。

public class InputStreamReader extends [url=]Reader[/url]
(1)InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 [url=]charset[/url] 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
(2)每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。
(3)为了达到最高效率,可以考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));//重要
InputStreamReaderDemo
public class OutputStreamWriter extends [url=]Writer[/url]
(1)OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 [url=]charset[/url] 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
(2)每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。
(3)为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));//重要
例如:利用标准输出流将文本输出到命令行
OutputStreamWriterDemo
TransStreamDemo
6、打印流PrintWriter和PrintStream
public class PrintWriter extends [url=]Writer[/url]
(1)向文本输出流打印对象的格式化表示形式。此类实现在 [url=]PrintStream[/url] 中的所有 print 方法。不能输出字节,但是可以输出其他任意类型。
(2)与 [url=]PrintStream[/url] 类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。
(3)此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用 [url=]checkError()[/url] 是否出现错误。
[backcolor=rgb(245, 245, 245) !important][url=][/url]
1 import java.io.FileWriter; 2 import java.io.IOException; 3 import java.io.PrintWriter; 4 /** 5  * 注意:创建FileWriter对象时boolean参数表示是否追加; 6  *              而创建打印流对象时boolean参数表示是否自动刷新 7  */ 8 public class PrintWriterDemo { 9     public static void main(String[] args) throws IOException {10         //PrintWriter pw=new PrintWriter("print.txt");11         PrintWriter pw=new PrintWriter(new FileWriter("print.txt"),true);12         pw.write("测试打印流");13         pw.println("此句之后换行");14         pw.println("特有功能:自动换行和自动刷新");15         pw.println("利用构造器设置自动刷新");16         pw.close();17     }18 }[backcolor=rgb(245, 245, 245) !important][url=][/url]


使用字符打印流复制文本文件:
PrintWriterDemo
public class PrintStream extends [url=]FilterOutputStream[/url]implements [url=]Appendable[/url], [url=]Closeable[/url][url=]
[/url](1)PrintStream 为其他输出流添加了功能(增加了很多打印方法),使它们能够方便地打印各种数据值表示形式(例如:希望写一个整数,到目的地整数的表现形式不变。字节流的write方法只将一个整数的最低字节写入到目的地,比如写fos.write(97),到目的地(记事本打开文件)会变成字符'a',需要手动转换:fos.write(Integer.toString(97).getBytes());而采用PrintStream:ps.print(97),则可以保证数据的表现形式)。
1 //PrintStream的print方法 2  public void print(int i) {3         write(String.valueOf(i));4  }
(2)与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。
另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 ('\n')。
(3)PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 [url=]PrintWriter[/url] 类。  
使用字节打印流复制文本文件:
PrintStreamDemo
7、对象操作流ObjectInputStream和ObjectOutputStream
public class ObjectOutputStream extends [url=]OutputStream [/url]implements [url=]ObjectOutput[/url],[url=]ObjectStreamConstants[/url]
(1)ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。只能使用 ObjectInputStream 读取(重构)对象。
(2)只能将支持 java.io.Serializable 接口的对象写入流中。
(3)writeObject 方法用于将对象写入流中。所有对象(包括 String 和数组)都可以通过 writeObject 写入。可将多个对象或基元写入流中。必须使用与写入对象时相同的类型和顺序从相应 ObjectInputstream 中读回对象。
构造方法:[url=]ObjectOutputStream[/url]([url=]OutputStream[/url] out)    创建写入指定 OutputStream 的 ObjectOutputStream。
public class ObjectInputStream extends [url=]InputStream [/url]implements [url=]ObjectInput[/url],[url=]ObjectStreamConstants[/url]
(1)ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
(2)只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
(3)readObject 方法用于从流读取对象。应该使用 Java 的安全强制转换来获取所需的类型。在 Java 中,字符串和数组都是对象,所以在序列化期间将其视为对象。读取时,需要将其强制转换为期望的类型。
例:对象读写:
Student
ObjectOperate
ObjectOperate2
序列化接口Serializable的作用:没有方法,不需要覆写,是一个标记接口为了启动一个序列化功能。唯一的作用就是给每一个需要序列化的类都分配一个序列版本号,这个版本号和类相关联。在序列化时,会将这个序列号也一同保存在文件中,在反序列化时会读取这个序列号和本类的序列号进行匹配,如果不匹配会抛出java.io.InvalidClassException.
注意:静态数据不会被序列化,因为静态数据在方法区,不在对象里。
或者使用transient关键字修饰,也不会序列化。
8、SequenceInputStream
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
案例:媒体文件切割与合并
CutFile
mergeFile
9、用于操作数组和字符串的流对象
ByteArrayInputStream  ByteArrayOutputStream
CharArrayReader   CharArrayWriter
StringReader    StringWriter
关闭这些流都是无效的,因为这些都未调用系统资源,不需要抛IO异常。

View Code

10、RandomAccessFile
View Code
11、File类:
File: 文件和目录路径名的抽象表示形式,File类的实例是不可改变的
(1)File类常用功能
[backcolor=rgb(245, 245, 245) !important][url=][/url]
File: 文件和目录路径名的抽象表示形式,File类的实例是不可改变的 * File类的构造方法: *             File(String pathname) 将指定的路径名转换成一个File对象 *             File(String parent,String child) 根据指定的父路径和文件路径创建对象 *             File(File parent,String child) * File类常用功能: *             创建:boolean createNewFile():当指定文件(或文件夹)不存在时创建文件并返回true,否则返回false,路径不存在则抛出异常 *                 boolean mkdir()  :当指定文件(或文件夹)不存在时创建文件夹并返回true,否则返回false *                 boolean mkdirs() :创建指定文件夹,所在文件夹目录不存在时,则顺道一块创建
         *             删除:boolean delete():删除文件            注意:要删除一个目录,需要先删除这个目录下的所有子文件和子目录 *             获取:File getAbsoluteFile() *                 File getParentFile() *                 String getAbsolutePath() *                 String getParent() *                 String getPath() *                 long lastModified() *             判断: boolean exists(); *                 boolean isAbsolute()  *                 boolean isDirectory()  *                 boolean isFile()  *                 boolean isHidden()     *             修改:boolean renameTo(File dest): 将当前File对象所指向的路径修改为指定File所指的路径 (修改文件名称)    [backcolor=rgb(245, 245, 245) !important][url=][/url]


案例:打印指定文件(夹)及其所有子目录
[backcolor=rgb(245, 245, 245) !important][url=][/url]
1 import java.io.File; 2 /** 3  *打印目录 4  */ 5 public class FileDemo1 { 6     public static void main(String[] args){ 7         File f=new File("E:\\BaiduNetdiskDownload"); 8         printTree( f,0); 9     }10     11     public static void printTree(File f,int level) {12         for(int j=0;j<level;j++) {13             System.out.print("\t");14         }15         System.out.println(f.getAbsolutePath());16         if(f.isDirectory()) {17             level++;18             File strs[]=f.listFiles();19             for(int i=0;i<strs.length;i++) {20                 File f0=strs;21                 printTree(f0,level+1);22             }23         }24     }25 }[backcolor=rgb(245, 245, 245) !important][url=][/url]


(2)File类重要方法之过滤器
String[] list()String[] list(FilenameFilter)File[] listFiles()File[] listFiles(FilenameFilter)File[] listFiles(FileFilter filter)
File类的list方法可以获取目录下的各个文件,传入过滤器还能按照特定需求取出需要的文件。下面来看一下过滤器怎么用的。首先看
String[] list(FilenameFilter)
查看FilenameFilter源码,发现其实是一个接口:
[backcolor=rgb(245, 245, 245) !important][url=][/url]
public interface FilenameFilter {    /**     * Tests if a specified file should be included in a file list.     *     * @param   dir    the directory in which the file was found.     * @param   name   the name of the file.     * @return  <code>true</code> if and only if the name should be     * included in the file list; <code>false</code> otherwise.     */    boolean accept(File dir, String name);}[backcolor=rgb(245, 245, 245) !important][url=][/url]


那么我们要想使用过滤器,应该先实现接口,假设我们要找某个文件夹下的txt文件:
[backcolor=rgb(245, 245, 245) !important][url=][/url]
1 import java.io.File; 2 import java.io.FilenameFilter; 3  4 public class FileDemo { 5     public static void main(String[] args) { 6         File file = new File("C:\\Test"); 7         if(file.isDirectory()) { 8             String[] list=file.list(new FilenameFilterbytxt());//传入过滤器 9             for(String l:list) {10                 System.out.println(l);11             }12         }13     }14 }15 16 class FilenameFilterbytxt implements FilenameFilter{17     @Override18     public boolean accept(File dir, String name) {19         // TODO Auto-generated method stub20         return name.endsWith(".txt");//当文件名以.txt结尾时返回true.21     }22     23 }[backcolor=rgb(245, 245, 245) !important][url=][/url]


但是我们看到第8行代码只是传入了过滤器,那么accept方法是如何被调用的呢?查看
String[] list(FilenameFilter) 的源码:
[backcolor=rgb(245, 245, 245) !important][url=][/url]
1  /*list(FilenameFilter)源码解析*/ 2 public String[] list(FilenameFilter filter) { 3         String names[] = list();//调用list()方法获取所有名称 4         if ((names == null) || (filter == null)) { 5             return names;  6         } 7         List<String> v = new ArrayList<>();//用于保存过滤后的文件名 8         for (int i = 0 ; i < names.length ; i++) {//遍历 9             //调用filter的accept方法,传入当前目录this和遍历到的名称names10             if (filter.accept(this, names)) {11                 v.add(names);//满足过滤器条件的添加到集合中12             }13         }14         return v.toArray(new String[v.size()]);//将集合转成数组返回,限定增删操作15     }                        [backcolor=rgb(245, 245, 245) !important][url=][/url]


也就是说,我们实现的accept方法是在构造器中被调用的。
类似地,FileFilter 也是一个接口,采用匿名内部类的方式实现接口,使用File[] listFiles(FileFilter filter)获取目录下所有文件夹:
[backcolor=rgb(245, 245, 245) !important][url=][/url]
1 import java.io.File; 2 import java.io.FileFilter; 3  4 public class FileDemo { 5     public static void main(String[] args) { 6         File file = new File("C:\\Test"); 7         if(file.isDirectory()) { 8             File[] list=file.listFiles(new FileFilter() { 9                 @Override10                 public boolean accept(File pathname) {11                     // TODO Auto-generated method stub12                     return pathname.isDirectory();13                 }14                 15             });16             17             for(File f:list) {18                 System.out.println(f);19             }20         }21     }22 }[backcolor=rgb(245, 245, 245) !important][url=][/url]


File[] listFiles(FileFilter filter) 方法的源码如下:
[backcolor=rgb(245, 245, 245) !important][url=][/url]
1 /*File[] listFiles(FileFilter filter)源码解析*/ 2  public File[] listFiles(FileFilter filter) { 3         String ss[] = list();//调用list()获取所有的名称数组 4         if (ss == null) return null;//健壮性判断,数组为null则返回 5         ArrayList<File> files = new ArrayList<>();//创建File类型集合 6         for (String s : ss) {//遍历 7             File f = new File(s, this);//private File(String child, File parent)私有构造调用 8             if ((filter == null) || filter.accept(f))//条件判断 9                 files.add(f);//添加到集合10         }11         return files.toArray(new File[files.size()]);//集合转成数组返回12     }[backcolor=rgb(245, 245, 245) !important][url=][/url]


12、IO流使用规律总结:
(1)明确要操作的数据是数据源还是数据目的(要读还是要写)
      源:InputStream  Reader
      目的:OutputStream  Writer
(2)明确要操作的设备上的数据是字节还是文本
      源:
          字节:InputStream
          文本:Reader
      目的:
          字节:OutputStream
          文本:Writer
(3)明确数据所在的具体设备
      源设备:
        硬盘:文件 File开头
        内存:数组,字符串
        键盘:System.in
        网络:Socket
      目的设备:
        硬盘:文件 File开头
        内存:数组,字符串
        屏幕:System.out
        网络:Socket
(4)明确是否需要额外功能?
    需要转换——转换流 InputStreamReader OutputStreamWriter
    需要高效——缓冲流Bufferedxxx
    多个源——序列流 SequenceInputStream
    对象序列化——ObjectInputStream,ObjectOutputStream
    保证数据的输出形式——打印流PrintStream Printwriter
    操作基本数据,保证字节原样性——DataOutputStream,DataInputStream

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马