Java_IO 流

1. File基本概念 

1). 基本概念

* File类用于表示文件(目录)

* File类只用于表示文件(目录)的信息(名称、大小), 能新建、删除、重命名文件和目录,但不能用于文件内容的访问,如果需要访问文件内容本身,则需要使用输入/输出流

* File对象可以作为参数传递给流的构造函数

2). 使用  
* 代码示例
    File file=new File("E:\\javaio");

    * 文件路径要用双斜杠分割(在UNIX中,此字段为‘/’,在Windows中,为‘\\’)

    * 参数可以使用字符串、uri

3). File操作的注意: 
    * 定义文件路径时,注意:可以用“/”或者“\\”。
    * 在写入一个文件时,如果目录下有同名文件将被覆盖。

    * 在读取文件时,必须保证该文件已存在,否则出异常

2 File常见的方法 
1). File的API使用  
  - exists()判断文件/文件夹是否存在,返回值boolean类型  
  - 代码示例

  File file=new File("E:\\javaio");
  System.out.println(file.exists());//存在为true,不存在为false           
  - mkdir()创建文件夹  
  - 代码示例:
  //声明文件对象
  File file=new File("E:\\javaio\\change");
    //判断是否存在
    if(!file.exists()){
//创建的时候父目录必须存在 即:E:\\javaio存在
boolean flag = file.mkdir();
//判断是否创建成功
if(flag){
    System.out.println("创建成功");
}else{
    System.out.println("创建失败");
}
    }
              
    - 代码示例:

    //声明文件对象
    File file=new File("E:\\javaio\\change.txt");
    //判断是否存在
    if(!file.exists()){
//创建的时候父目录必须存在 即:E:\\javaio存在
boolean flag = file.mkdir();
//判断是否创建成功
if(flag){
    System.out.println("创建成功");
}else{
    System.out.println("创建失败");
}
    }
  
  - mkdirs()创建多级文件夹  
  - 代码示例:

   //声明文件对象
    File file=new File("E:\\javaio\\change.txt");
    //判断是否存在
    if(!file.exists()){
//父目录不存在的情况
boolean flag = file.mkdirs();
//判断是否创建成功
if(flag){
    System.out.println("创建成功");
}else{
    System.out.println("创建失败");
}
    }  

  - delete() 删除文件  
  - 代码示例 : 

    file.delete();
  - isDirectory()判断是否是目录。返回值boolean类型,如果是目录返回true,如果不是目录或者目录不存在返回false  
  - 代码示例:


    file.isDirectory();
  - isFile()判断是否是一个文件,同理上个方法  
  代码示例:file.isFile();
    
  - createNewFile()创建文件  
  - 代码示例  

  File file=new File("javaio\\日记.txt");
    try {
        //父目录不存在,会出现IOException异常
        file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();

   }

  创建文件另一种方式 
   
  File file=new File("javaio","日记.txt");  

  - 直接输出File对象  
  - 代码示例:

  File file=new File("javaio\\change");
  System.out.println(file);
  - 结果:

  javaio\change

  - 返回的是File对象的toString();方法   
  - 内部源码:
   
  //重写Object中的toString方法
  public String toString() {
        return getPath();
  }

  - getAbsolutePath()返回的是File对象的路径  

  - 代码示例:

  File file=new File("javaio\\change");

  System.out.println(file.getAbsolutePath());

  - 结果:

  E:\EclipseWorkspase\day0805\javaio\change

  - getName()返回文件名字  
  - 代码示例:

    File file1=new File("javaio\\change");
    File file2=new File("javaio\\日记.txt");
    System.out.println(file1.getName());
    System.out.println(file2.getName());
            
  - getParent()返回文件父目录  
  - 代码示例:

    File file=new File("javaio\\change");
    System.out.println(file.getParent());
    System.out.println(file.getParentFile().getAbsolutePath());//返回父目录的绝对路径

* 2. 遍历File  
  * 案例1:遍历指定目录下的所有文件  
  * 工具类:

    public class FileUtils {
        public static void listDirectory(File dir) throws IOException{
            if(!dir.exists()){
                 throw new IllegalArgumentException(dir+"不存在");
            }
            if(!dir.isDirectory()){
                 throw new IllegalArgumentException(dir+"不是目录");
            }
            /*
             * 遍历指定文件下的文件名
             */
            //String[] fileNames=dir.list();
            //for(String fileName:fileNames){
            //          System.out.println(fileName);
            //}
             /*
              * 遍历指定文件下的文件
              */
             //File[] files=dir.listFiles();
             //for(File file:files){
             //          System.out.println(file);
             //}
               /*
                * 采用递归的方式,遍历指定目录下的所有子文件
                */
               File[] files=dir.listFiles();
                   if(files!=null&&files.length>0){
                       for(File file:files){
                           if(file.isDirectory()){
                               System.out.println(file);
                               listDirectory(file);                                         
                    }else{
                       System.out.println(file);
                    }
                 }
              }
          }

  测试类:

  public class FileTest {
      public static void main(String[] args) throws IOException {
          File file=new File("E:\\javaio");
          FileUtils.listDirectory(file);

      }

   }

 3. 流基本概念 
 - Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入。
 - 流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。 
 
3.1 流的分类

* 流向划分:  

  - 输入流:程序可以从中读取数据的流。
  - 输出流:程序能向其中写入数据的流。

* 传输单位划分:

  - 字节流:以字节为单位传输数据的流
  - 字符流:以字符为单位传输数据的流
  
* 按角色划分:
  
  - 节点流:用于直接操作目标设备的流
  - 处理流:“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。

3.2 字节流 

* 字节流主要操作byte类型数据,以byte数组为准,主要操作类有InputStream(字节输入流)、OutputSteam(字节输出流)由于IputStream和OutputStream都是抽象类。

* 1. 字节输出流:OutputStream  
  * OutputStream是一个抽象类,是所有字节流输出流的父类,要想使用它,必须通过子类来实例他  
  * OutputStream类的常用方法  
  
  1 、int write(int b)
  // 功能:将b的最低的一个字节写入此输入流,其他三个字节丢弃。
          
  2、int write(byte[] b)

  //功能:将指定的字节数组b写入此输入流

  3、int write(byte[] b, int off, int len)

  //功能:将指定byte数组中从偏移量off开始的len个字节写入输入流。

  4、flush()
  //功能:刷新此输入流并强制写出所有缓冲的输出字节数。

  5、close()
  //功能:关闭输出流,释放这个流的相关资源。

  *  案例2:向一个名为”hello.txt”的文本文件中写一个“HelloWorld"

    public static void main(String[] args) throws IOException {

        File file=new File("hello.txt");
        OutputStream os=new FileOutputStream(file);
        String str="Hello World";
        byte[] b=str.getBytes();
        os.write(b);
        os.close();

        os.flus

    }  

* 2. 字节输入流:InputStream
  * 与OutputStream类一样,InputStream本身也是一个抽象类,是所有字节输入流的子类,要想使用它,也必须依靠其子类。
  InputStream类的常用方法  

   1、int read() 

   //功能:读取下一个字节的数据,并且返回读到得数据,如果返回-1,则表示读到输入流的末尾。

   2、int read(byte[] b)
   //功能:从输入流中读取一定量的字节,并将其存储在字节数组b中,
   //返回实际读取的字节数,如果返回-1,则表示读到输入流的末尾。

   3、int read(byte[] b, int off, int len)
   //功能:将数据读入一个字节数组,同时返回读取的实际字节数,如果返回-1,
   //则表示读到输入流的末尾。off指定在数组b中存放数据的起始偏移位置,

   //len指定读取的最大字节数。  

   4、available()

   //功能:返回此输入流下一个方法调用可以不受阻塞地从此输入流读取或跳过的估计字节数

   5、close()
   //功能:关闭输入流,释放这个流的相关资源。

   * 案例3 :在上一个例子的基础上我们从文本中读取数据并且把它显示在控制台上  

    public static void main(String[] args) throws IOException {

        File file=new File("hello.txt");

        InputStream is = new FileInputStream(file);
        //只能读取1024字节
        byte[] b=new byte[1024];
        is.read(b);
        is.close();
        System.out.println(new String(b));
    }
 * 如果以这种方式打印,我们可以看到虽然控制台里是打印出了文本文件中的内容,但是后面跟了很多空格,这是我们不需要的,对于这种情况我们有两种解决方式 :
  
* 方法一:声明字节数组的时候指定字节数组的长度为文本内容的长度

byte[] b=new byte[(int)file.length()];

* 方式二:在将字节数组转换成字符串的调用String(byte b,int off,int len)这个构造函数

System.out.println(new String(b,0,(int)file.length()));

备注:   

1. 思考以上两种方式那个更好.  

2. 思考要是读取汉字会是什么样子尼?  

* 3. 字节流缓冲流  
  增加缓冲流,对文件的读取效率有很大的提高。  
  例如,单字节读取相当于将一个缸里的水,一滴一滴的送往另一个缸中;而缓冲读取的话,相当于先攒一桶水,然后再倒入另一个缸中。  
  
  - BufferedInputStream  
    带有缓冲区的字节输入流,通常使用它可以提高我们的读取效率  
    BufferedInputStream内部有一个缓冲区,默认大小为8KB,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源(譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区中,最后再将缓冲区中的内容部分或全部返回给用户.由于从缓冲区里读取数据远比直接从物理数据源(譬如文件)读取速度快,所以BufferedInputStream的效率很高!  
 源码中构造函数:  
    public BufferedInputStream(InputStream in) {

this(in, DEFAULT_BUFFER_SIZE);

    }  

  - BufferedOutputStream  
    带有缓冲区的字节输出流,提高了写入文件的效率  
   
   * 下面通过一个案例来进行演示  案例4:复制文件  

    public static void main(String[] args) throws IOException {

        File thisFile=new File("E:\\javaio\\1.mp3");

File copyFile=new File("E:\\javaio\\copyFile.mp3");
if(!thisFile.exists()){
    throw new IllegalArgumentException(thisFile+"未找到");
}
if(!copyFile.exists()){
    copyFile.createNewFile();
}
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(thisFile));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(copyFile));
byte[] b=new byte[8*1024];
int i;
while((i=bis.read(b, 0, b.length))!=-1){
    bos.write(b,0,i);
    bos.flush();
}
bis.close();
bos.close();
    }

 * 案例5:缓冲流的性能  
 * 通过复制1.mp3文件,计算两种复制方式哪种更快  
 * 工具类:  

 public class CopyUtil {
    public static void copyFile(File file1,File file2) throws IOException{
        FileInputStream fis=new FileInputStream(file1);
        FileOutputStream fos=new FileOutputStream(file2);
        int i;
        while((i=fis.read())!=-1){
    fos.write(i);
        }
        fis.close();
        fos.close();
    }
    public static void copyBuffer(File file1,File file2)throws IOException{
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file1));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(file2));
        int i;
while((i=bis.read())!=-1){
    bos.write(i);
    bos.flush();
}

bis.close();

        bos.close();

    }

}

* 测试类:

public class CopyTest {

    public static void main(String[] args)throws IOException {

        File file1=new File("E:\\javaio\\1.mp3");

File file2=new File("E:\\javaio\\copy.mp3");                  
//BufferedInputStream
long bufStart=System.currentTimeMillis();
CopyUtil.copyBuffer(file1, file2);
long bufEnd=System.currentTimeMillis();
System.out.println("使用BufferedInputStream拷贝文件的时间为:"+(bufEnd-bufStart)+"ms");
//FileInputStream
long fStart=System.currentTimeMillis();
CopyUtil.copyFile(file1, file2);
long fEnd=System.currentTimeMillis();
System.out.println("使用FileInputStream拷贝文件的时间为:"+(fEnd-fStart)+"ms");

    }

}

输出结果:

使用BufferedInputStream拷贝文件的时间为:23290ms

使用FileInputStream拷贝文件的时间为:36693ms

3.4 字符流 

字符流主要是用来处理文字的。Java采用16位的Unicode来表示字符串和字符,对应的字符流输入和输出分别称为Reader和Writer。

1. 字符输出流:Writer  
  * Writer是一个字符流,它是一个抽象类,所以要使用它,也必须通过其子类来实例化它后才能使用它。
  * Writer类的常用方法  

    public abstract void close() throws IOException //关闭输出流
    public void write(String str) throws IOException //将字符串输出
    public void write(char cbuf) throws IOException //将字符数组输出
    public abstract void flush() throws IOExcetpion //强制性清空缓存

  * 案例6: 向一个文本文件中通过字符输出流写入数据

    public static void main(String[] args) throws IOException {
        File file=new File("world.txt");
        Writer writer=new FileWriter(file);
        String str="Hello World";
        writer.write(str);
        writer.flush();
        writer.close(); 

    }

* 2. 字符输入流:Reader  
* Reader本身也是一个抽象类,同样,如果使用它,我们需要通过其子类来实例化它才可以使用它。  
* Reader类的常用方法  

    public abstract void close() throws IOException //关闭输入流
    public int read() throws IOException //读取内容,以数字的方式读取
    public int read(char c) throws IOException //将内容读到char数组,同时返回读入的个数

  * 案例7
  在上面的基础上把文本中的内容读出来,并且显示在控制台上

    public static void main(String[] args) throws IOException {
        File file=new File("world.txt");
Reader read=new FileReader(file);
char[] c=new char[1024];
int len=0;
int temp=0;
while((temp=read.read())!=-1){
    c[len]=(char)temp;
    len++;
}
read.close();
System.out.println(new String(c,0,len));
    }
* 3. 字符缓冲流  
- BufferedReader   
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值足够大。  
- readLine:  
提供了读取文本行的能力  
**案例8:**读取指定文本文件中第一行信息

public static void main(String[] args) throws IOException {
    BufferedReader br=new BufferedReader(new FileReader("E:\\javaio\\change.txt"));
        System.out.println(br.readLine());

}

- BufferedWriter   

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区的大小,或者接受默认的大小。   
newLine:    
它使用平台自己的行分隔符概念,并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。  
**案例9:**利用newLine方法将指定文本输出换行文本

public static void main(String[] args) throws IOException {
    File file=new File("E:\\javaio\\bw.txt");
        if(!file.exists()){
            file.createNewFile();
        }
        BufferedWriter bw=new BufferedWriter(new FileWriter(file));
        bw.write("111111111");
        bw.write("222222222");
        bw.newLine();
        bw.write("333333333");
        bw.flush();
        bw.close();

 }

3.5 字节流与字符流的区别 
1. 字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的
![](img/2.png)  
2. 字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容
思考:那开发中究竟用字节流好还是用字符流好呢?
在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会形成的,所以使用字节的操作是最多的

3.6 节点流 
* 节点流:可以从或向一个特定的地方(节点|数据源)读写数据。如 FileReader。 
* 常用的节点流:  

- 文 件 FileInputStream FileOutputStrean FileReader FileWriter 文件进行处理的节点流。
- 字符串 StringReader StringWriter 对字符串进行处理的节点流。
- 数 组 ByteArrayInputStream ByteArrayOutputStreamCharArrayReader CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)。
- 管 道 PipedInputStream PipedOutputStream PipedReaderPipedWriter对管道进行处理的节点流。
- 父 类 InputStream OutputStream Reader Writer

3.7 处理流 

处理流:是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。  
常用的处理流:  

  - 缓冲流:BufferedInputStrean BufferedOutputStream BufferedReader BufferedWriter增加缓冲功能,避免频繁读写硬盘。
  - 转换流:InputStreamReader OutputStreamReader实现字节流和字符流之间的转换。
  - 数据流 DataInputStream DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来。
  

* 流的方向 是相对于我们的应用程序的。

*输入流:程序要做读取操作
*输出流:程序要写入信息到数据流

* 字节流和字符流的区别

*字节流可以操作任意类型的数据类型文件。我们一般用它操作二进制数据文件。

*字符流可以操作字符单位的数据流。一般指文本文件。

* 流的关闭问题。
#####JDK1.6前标准写法。
    finally {
        try {
    fis.close();
} catch (IOException e) {
    e.printStackTrace();
}
    }

* jDK 1.7以后不用写。因为流对象实现了Closeable接口,只要将代码放到try块中。就能关闭了,就可以不写close()了。
*  6 参考资料 
《java从入门到精通》  第三版

猜你喜欢

转载自blog.csdn.net/qq_35325795/article/details/80557438