zoukankan      html  css  js  c++  java
  • 搞定java.io


    一、java.io简介

      Java的核心库java.io提供了全面的IO接口。包括:文件读写、标准设备输出等。Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入。

      java.io通过数据流、序列化和文件系统提供系统输入和输出。


    二、先说文件操作

    文件File 位于java.io包中,用于描述文件和目录的操作。
    File类这个名字有一定的误导性;我们可能会认为它指代的是文件,但并非如此。它既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称。实际上FilePath(文件路径)对这个类来说是个更好的名字。
    创建文件对象如下:

    File file = new File("input.txt");//文件位于当前目录下
    File file = new File("/home/user","input.txt");//文件位于/home/user目录下
    

    除了上述的File构造方法之外,还有很多和File相关的方法如下:

    exists()                                  判断文件或目录是否存在
    mkdir()                                          创建目录
    isFile()/isDirectory()                    判断是文件还是目录
    delete()                                         删除文件
    getPath()                                 获取文件或者目录的路径
    list()                                    将目录中所有文件名保存在字符串数组中返回
    

    例 文件相关操作

    import java.io.*;
    public class TestAbstract {  
        public static void main(String args[]) throws IOException {  
            File dir = new File("D:/java");  
            File file1 = new File(dir, "fileTest001.txt");  
            File file2 = new File(dir, "fileTest002.java");   
            if (!dir.exists())  
                dir.mkdir();  
            if (!file1.exists())  
                file1.createNewFile();  
            if (!file2.exists())  
                file2.createNewFile();  
            System.out.println("file1's AbsolutePath=  " + file1.getAbsolutePath());  
            System.out.println("file2's AbsolutePath=  " + file2.getAbsolutePath());  
            System.out.println("file1 Canread=" + file1.canRead());  
            System.out.println("file1's len= " + file1.length());  
            String[] fileList = dir.list(); 
    
            System.out.println("there are " + fileList.length + " file(s) in D:");  
        }  
    
    }
    
    运行结果:
    D:java>java TestAbstract
    file1's AbsolutePath=  D:javafileTest001.txt
    file2's AbsolutePath=  D:javafileTest002.java
    file1 Canread=true
    file1's len= 0
    there are 133 file(s) in D:
    

    例 删除目录及文件

    import java.io.File;
     
    public class DeleteFileDemo {
      public static void main(String args[]) {
          // 这里修改为自己的测试目录
        File folder = new File("/tmp/java/");
        deleteFolder(folder);
      }
     
      //删除文件及目录
      public static void deleteFolder(File folder) {
        File[] files = folder.listFiles();
            if(files!=null) { 
                for(File f: files) {
                    if(f.isDirectory()) {
                        deleteFolder(f);
                    } else {
                        f.delete();
                    }
                }
            }
            folder.delete();
        }
    }
    

    三、什么是流?

    概念:

      流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。
      在计算机中,I/O流一般就是指输入输出过程中传输的的数据序列,是一个抽象的概念,即在输入输出设备之间的传输,一种有顺序的,有起点和终点的字节集合。

      流具有方向性,至于是输入流还是输出流则是一个相对的概念,一般以程序为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流。   Java把这些不同来源和目标的数据都统一抽象为数据流。Java语言的输入输出功能是十分强大而灵活的,美中不足的是看上去输入输出的代码并不是很简洁,因为你往往需要包装许多不同的对象。   在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流。

    四、流有哪些分类?

    按数据传输单位分:
    字节流: 以字节为单位传输数据的流
    字符流: 以字符为单位传输数据的流
    按流向分:
    输入流: 程序可以从中读取数据的流。(由InputStream(字节流)和Reader(字符流)作为基类)
    输出流: 程序能向其中写入数据的流。(由OutputStream(字节流)和Writer(字符流)作为基类)
    按功能分:
    节点流: 用于直接操作目标设备的流;节点流从一个特定的数据源读写数据,即节点流是直接操作文件,网络等的流,例如FileInputStream和FileOutputStream,他们直接从文件中读取或往文件中写入字节流。

    ![节点流][1]
    过滤流: 是对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能。
    ![过滤流][2]
    例如:
     public static void main(String[] args) throws IOException {
                // 节点流FileOutputStream直接以A.txt作为数据源操作
                FileOutputStream fileOutputStream = new FileOutputStream("A.txt");
                // 过滤流BufferedOutputStream进一步装饰节点流,提供缓冲写
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
                        fileOutputStream);
                // 过滤流DataOutputStream进一步装饰过滤流,使其提供基本数据类型的写
                DataOutputStream out = new DataOutputStream(bufferedOutputStream);
                out.writeInt(3);
                out.writeBoolean(true);
                out.flush();
                out.close();
                // 此处输入节点流,过滤流正好跟上边输出对应,读者可举一反三
                DataInputStream in = new DataInputStream(new BufferedInputStream(
                        new FileInputStream("A.txt")));
                System.out.println(in.readInt());
                System.out.println(in.readBoolean());
                in.close();
        }
    

    五、流结构介绍

    JDK所提供的所有流类位于java.io包中,都分别继承自以下四种抽象流类。

    字节流 字符流
    输入流 InputStream Reader
    输出流 OutputStream Writer

    InputStream:继承自InputStream的流都是用于向程序中输入数据的,且数据单位都是字节(8位)。
    OutputStream:继承自OutputStream的流都是程序用于向外输出数据的,且数据单位都是字节(8位)。
    Reader:继承自Reader的流都是用于向程序中输入数据的,且数据单位都是字符(16位)。
    Writer:继承自Writer的流都是程序用于向外输出数据的,且数据单位都是字符(16位)。

    提示:在最底层,所有的输入/输出都是字节形式的。基于字符的流只为处理字符提供方便有效的方法。

    字节流

    字节流的最顶层是两个抽象类:InputStream和OutputStream,其他关于处理字节的类都是它们的子类,这些子类对不同的外设进行处理,例如磁盘文件,网络连接,甚至是内存缓冲区。

            类名                                             含义
    BufferedInputStream                             缓冲输入流
    BufferedOutputStream                            缓冲输出流
    ByteArrayInputStream                       从字节数组读取的输入流
    ByteArrayOutputStream                      向字节数组写入的输出流
    DataInputStream                            包含读取Java标准数据类型方法的输入流
    DataOutputStream                           包含编写Java 标准数据类型方法的输出流
    FileInputStream                            读取文件的输入流
    FileOutputStream                           写文件的输出流
    FilterInputStream                          实现 InputStream
    FilterOutputStream                         实现 OutputStream
    InputStream                                描述流输入的抽象类
    OutputStream                               描述流输出的抽象类
    PipedInputStream                           输入管道
    PipedOutputStream                          输出管道
    PrintStream                                包含print( ) 和 println( )的输出流
    PushbackInputStream                        支持向输入流返回一个字节的单字节的“unget”的输入流
    RandomAccessFile                           支持随机文件输入/输出
    SequenceInputStream                        两个或两个以上顺序读取的输入流组成的输入流
    

    抽象类InputStream 和 OutputStream中定义了实现其他流类的关键方法read()和write(),它们分别对数据的字节进行读写。两种方法都是抽象方法,被子类重载。

    继承自InputStream/OutputStream的流都是用于向程序中输入/输出数据,且数据的单位都是字节(byte=8bit),如图,深色的为节点流,浅色的为过滤流。

     

    例:文件按字节流的方式拷贝

    import java.io.*;
    //文件按字节流的方式拷贝
    public class Main {
    
    	public static void main(String[] args) throws IOException {
    		FileInputStream in = null;
    		FileOutputStream out = null;
    		try{
    			in = new FileInputStream("input.txt");
    			out = new FileOutputStream("output.txt");
    			int c = 0;
    			while((c = in.read())!=-1)
    			{
    				out.write(c);
    			}
    		}finally{
    			if(in != null)
    			{
    				in.close();
    			}
    			if(out != null)
    			{
    				out.close();
    			}
    		}
    	}
    }
    

    上例中使用的是文件名来创建FileInoutStream和FileOutputStream,实际上可以还可以使用文件对象来创建输入输出流。字节流的每次操作都是一个数据单位——字节,假如input.txt文件中包含 Hello world ,那么它将复制完“H”之后,再复制“e”,接着就是“l”,如此类推直到其结束。in.read()每次从输入流中读取一个字节,如果达到文件末尾就返回-1。使用完了,还要关闭这些字节流,调用close()方法。

    字符流

    java是使用16-bits来存储字符数据的,涉及到的大多是字符操作,在程序中使用字符流会比字节流更加合适。类似于字节流,字符流的两个顶层抽象类是Reader和Writer,一下是它们的子类处理字符流。

     类名                                             含义
    BufferedReader                                 缓冲输入字符流
    BufferedWriter                                 缓冲输出字符流
    CharArrayReader                                从字符数组读取数据的输入流
    CharArrayWriter                                向字符数组写数据的输出流
    FileReader                                     读取文件的输入流
    FileWriter                                     写文件的输出流
    FilterReader                                   过滤读
    FilterWriter                                   过滤写
    InputStreamReader                              把字节转换成字符的输入流
    LineNumberReader                               计算行数的输入流
    OutputStreamWriter                             把字符转换成字节的输出流
    PipedReader                                    输入管道
    PipedWriter                                    输出管道
    PrintWriter                                    包含print( )和println( )的输出流
    PushbackReader                                 允许字符返回到输入流的输入流
    Reader                                         描述字符流输入的抽象类
    StringReader                                   读取字符串的输入流
    StringWriter                                   写字符串的输出流
    Writer                                         描述字符流输出的抽象类
    

    类似于字节,字符的抽象类Reader和 Writer中也定义了关键方法read()和write(),它们分别对字符进行读写。两种方法也都是抽象方法,被子类重载。

    继承自Reader/Writer的流都是用于向程序中输入/输出数据,且数据的单位都是字符(2byte=16bit),如图,深色的为节点流,浅色的为过滤流。

    例:文件按字符流的形式拷贝

    import java.io.*;
    public class CopyFileCharacter {
       public static void main(String args[]) throws IOException
       {
          FileReader in = null;
          FileWriter out = null;
          try {
             in = new FileReader("input.txt");
             out = new FileWriter("output.txt");
    
             int c;
             while ((c = in.read()) != -1) {
                out.write(c);
             }
          }finally {
             if (in != null) {
                in.close();
             }
             if (out != null) {
                out.close();
             }
          }
       }
    }
    

    对比发现,只有声明的I/O流的类名不同,这里使用的FileReader和FileWriter,它们操作的最小单位是一个字符16bits,而FileInputStream和FileOutputStream最小单位则是一个字节8bits.

    Java自定义的标准流

    标准输入流 System.in 读取标准输入设备数据,例如键盘输入(默认),其类型是InputStream,三个重要的读入方法:

    int read()  从输入流中读取数据的下一个字节,返回ASCII码。若,返回值=-1,说明没有读取到任何字节读取工作结束
    int read(byte[] b)      从输入流中读取一定数量的字节,并将这些数据存储到缓冲区数组b中
    int read(byte[] b, int off, int len)      将输入流中最多len个字节读入到字节数组b中
    

    标准输出流 System.out 向标准的输出设备写入数据,默认情况下指控制台,其类型是PrintStream,包含两个重要的方法:print()(不换行)和println()(输出之后换行)

    标准错误流 System.err 默认也是控制台,类型和System.out相同是PrintStream,

    提示:这些流都有默认的设备,但它们可以重定向到任何兼容的输入/输出设备。

    六、什么时候用字符流,什么时候用字节流,傻傻分不清?

    InputStream 和OutputStream,两个是为字节流设计的,主要用来处理字节或二进制对象,
    Reader和 Writer.两个是为字符流(一个字符占两个字节)设计的,主要用来处理字符或字符串.

    字符流处理的单元为2个字节的Unicode字符,操作字符、字符数组或字符串,
    字节流处理单元为1个字节,操作字节和字节数组。
    所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,
    所以它对多国语言支持性比较好!
    如果是音频文件、图片、歌曲,就用字节流好点,
    如果是关系到中文(文本)的,用字符流好点

    所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列
    字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;
    字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以
    字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的
    但实际中很多的数据是文本,
    又提出了字符流的概念,
    它是按虚拟机的encode来处理,也就是要进行字符集的转化
    这两个之间通过 InputStreamReader,OutputStreamWriter来关联,
    实际上是通过byte[]和String来关联
    在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的

    Reader类的read()方法返回类型为int :作为整数读取的字符(占两个字节共16位),范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1

    inputStream的read()虽然也返回int,但由于此类是面向字节流的,一个字节占8个位,所以返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。因此对于不能用0-255来表示的值就得用字符流来读取!比如说汉字.

    字节流和字符流的主要区别是什么呢?

    1. 字节流在操作时不会用到缓冲区(内存),是直接对文件本身进行操作的。而字符流在操作时使用了缓冲区,通过缓冲区再操作文件。
    2. 在硬盘上的所有文件都是以字节形式存在的(图片,声音,视频),而字符值在内存中才会形成。

    上面两点能说明什么呢?
    针对第一点,
      我们知道,如果一个程序频繁对一个资源进行IO操作,效率会非常低。此时,通过缓冲区,先把需要操作的数据暂时放入内存中,以后直接从内存中读取数据,则可以避免多次的IO操作,提高效率
    针对第二点,
      真正存储和传输数据时都是以字节为单位的,字符只是存在与内存当中的,所以,字节流适用范围更为宽广

    七、常用流类介绍

    节点流类型常见的有:

    对文件操作的字符流有FileReader/FileWriter,字节流有FileInputStream/FileOutputStream。

    过滤流类型常见的有:

    缓冲流:缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写效率,同时增加了一些新的方法。

      字节缓冲流有BufferedInputStream/BufferedOutputStream;
      字符缓冲流有BufferedReader/BufferedWriter,字符缓冲流分别提供了读取和写入一行的方法ReadLine和NewLine方法。

      对于输出地缓冲流,写出的数据,会先写入到内存中,再使用flush方法将内存中的数据刷到硬盘。所以,在使用字符缓冲流的时候,一定要先flush,然后再close,避免数据丢失。

    转换流:用于字节数据到字符数据之间的转换。

      仅有字符流InputStreamReader/OutputStreamWriter。其中,InputStreamReader需要与InputStream“套接”,OutputStreamWriter需要与OutputStream“套接”。

    数据流:提供了读写Java中的基本数据类型的功能。

      DataInputStream和DataOutputStream分别继承自InputStream和OutputStream,需要“套接”在InputStream和OutputStream类型的节点流之上。

    对象流:用于直接将对象写入写出。

      流类有ObjectInputStream和ObjectOutputStream,本身这两个方法没什么,但是其要写出的对象有要求,该对象必须实现Serializable接口,来声明其是可以序列化的。否则,不能用对象流读写。

      还有一个关键字比较重要,transient,由于修饰实现了Serializable接口的类内的属性,被该修饰符修饰的属性,在以对象流的方式输出的时候,该字段会被忽略。

  • 相关阅读:
    c#.net内部异常捕获问题
    jquery里面去判断一个DIV是否显示
    jquery调用asp.net 页面后台方法
    免费订阅天气并发送到手机
    LINQ语句之Select/Distinct和Count/Sum/Min/Max/Avg
    .net程序员应该掌握的常用类库
    jquery 图片放大镜例子
    MVC中关于Controller的最佳实践
    C#中foreach,for,while,DoWhile循环
    frameset框架滚动条的处理
  • 原文地址:https://www.cnblogs.com/hello-zy/p/6810129.html
Copyright © 2011-2022 走看看