Java.io包中的流的输入输出详解1(字节流)

由于流在java中的重要性,和本身自己在流这方面存在混乱的原因。我将会对流进行一次很清晰的总结和记录下来我的学习过程。第一次开始了发表博客,我很高兴可以讲自己的理解和大家进行分享。有不好和不对的地方请给我指出。你的鼓励就是我的动力。

注:在下面的分类由于发表后格式有更改,显的有点乱。需要慢慢看了。

 

File类(流的输入输出必须使用到的文件类)

新建文件:(构造方法)

   创建文件可以根据相对路径和相对绝对路径

       相对路径

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

绝对路径:

File file=new File("D://a.txt");

 

File类中的常用方法

File.exists()  //文件或者目录是否存在

File.isFile()  //是否是文件

File.isDirectory(); //是否是目录

File.getName(); //取文件或者目录的名字

File.getPath(); //取文件或者目录的路径

File.getAbsolutePath(); //取文件绝对路径

File.lastModified(); //最后修改日期

File.length(); //文件或者目录的子节大小

File.createNewFile(); //创建新文件

File.canRead()   //测试文件是否可以读取

File.canWrite()  //测试文件是否可以写入

还有很多的方法,这里就不一一介绍。可以从JDK中可以查询的到。实际上,在开发过程中,这些方法比较少用。使用流也是一般都是创建一个新文件而已

 

流的概念

  Java中对文件的操作是以流的体例进行的。流是Java内存中的一组有序数据序列。Java将数据年夜源(文件、内存、键盘、收集)读入到内存中,形成了流,然后将这些流还可以写到此外的目的地(文件、内存、节制台、收集),之所以称为流,是因为这个数据序列在分歧时刻所操作的是源的分歧部门。

 

   流的分类

  初学者对流分类是很模糊的。我也不例外。我记得让我最头疼的是。InputStream,FileInputStream,BufferedInputStream ,DataInputStream等等很多的流不知道如何使用。看到别人的代码有时候读取图片只用到InputStream,但是有些时候使用FileInputStream和BufferedInputStream,这个就是对流的整体的分类不清楚导致的

 

                                                                ----------输入流(InputStream)

                                                                                                                             这两个流就是字节流的基类了。不管字节流怎么变化,都是继承了他们。以InputStream为例,下面

                                        --------字节流:                                                             ByteArrayInputStream PipedInputStream,StringBufferedInputStream,FileInputStream。大家是否

                                                                                                                                有点眼前一亮,发现了常用的FileInputStream,对,只有记住这个流就好了。FileInputStream是

                                                                                                                             DataInputStream(常用),BufferedInputStream(常用),LineNumberInputStream等的父类

                                                              ------------输出流(OutputStream)      OutputSteam 和InputStream总是对应的。这里就不一一写出来的。有输入流就有输出流,他们结构一样

按照流的单位可以分:                                               

                                                                                                                                  ---------Reader

                               ----------字符流(常用于文本类型的数据或字符串流)                 像上面一样,Reader和Writer就是字符流的基类,不管字符流这么变化,都是继承

                                                                                                                                             了他们。Reader下面CharArryReader,FilerReader,PipedReader  BufferedReader(常用),InputStreamReader(常用),只要记住这两个常用的字符流子类就好。InputStreamReader下面有FileReader(重要)

                                                                                                                          -----------Writer    读流和写流当然也是成对的了。

 

 

 

分类的截图如下:(如果看上面混乱那就直接看截图吧)

 

除了以上的流的分类外,还有人应该听说过 过滤流

 

那么什么是过滤流(又称包装流)呢?

大家请看上面的输入流,输入流InputStream子类的FileInputStream的子类DataInputStream(常用),BufferedInputStream(常用),LineNumberInputStream就是所谓的过滤流了。上面有说BufferedInputStream,DataInputStream比较常用,之后我会给出一些案例,也是介绍常用的流的使用。

 

那么,为什么需要过滤流呢?

 

不适用过滤流可以实现一些文件的读取吗?答案是可以的。这个就是为什么有时候我们看到别人的代码中有的时候BufferedInputStream和BufferedOutputStream,有些时候,看到代码中只使用了FileInputStream就可以读取和写入。

有朋友问,如果仅当InputStream和OutputStream流可以不可以呢?

当然是不可以的。我们要知道,InputStream和OutputStream都是抽象类。那么他们是不能直接使用的。但是他们有什么用呢?即使其他的操作类的基类。

 

介绍如下:

在需要想文件中写入或者读取文件内容各种数据类型的时候,一般是先将其他类型的数据转换为字节数组写入文件。或者将从文件独有的字节数组置换为其他类型。现在需要构造一个中间类,这个中间类提供了读/写各种类型数据的方法。通俗的讲,就是读取和写入一些字节数据的时候,这些过滤流为中间人,先转换成字节数组。

 

使用方法:

过滤流必须建立在基本的字节流之上,意思就是说,过滤流是一个高层流(如BufferedInputStream),他必须和低层流(InputStream)连接。通俗讲,低层流(InputStream)的对象作为高层流(如BufferedInputStream)的参数。对其中的数据进行了某些加工和处理。并提供一些方法供用户进行输入和输出操作以及流操作。

例如:BufferedInputStream可以对任何种类的输入流进行带缓冲区的封装以达到性能的改善。

 

对于字符流也来个总结吧。字符流我们学习什么呢?Reader和Writer。Reader和Writer下面的BufferedReader和BufferedWriter,InputStreamReader和OutputStreamWriter。

inputStreamReader ,outputStreamWriter下面的FileReader和FileWriter。有没有发现和上面字节流的有些结构上的混乱,让我们记忆也有点混乱,在上面Buffered是在InputStream...outputStream....下面的,但是在字符流中,他们是同级别的。但是Reader和Writer的直接子类的。但是呢,InputStream和OutputStream下面既然是FileReader和FileWriter。如果按照常理来说,如果子类的名字叫做FileInputStreamReader和FileOutputStreamWriter就好了。我们可以认为创造者怕名字长了,不按常理出牌。哈哈,开玩笑

 

实际上,我在学习流的时候,最让我对流的分类混乱的就是这里了。因为他们名字好像不按照常理出牌之后,记忆就经常混乱。时间久了我总是记成Buffered.....在InputStream...下面的。FileReader,FileWriter是和InputStreamReader。。。OutputStreamWriter同级的。

 

为了解决上面的问题,好像除了记忆好没什么好办法。记忆混乱的时候只能通过查找追究他们的继承关系了。不过个人觉得最好的记忆方法就是直接使用它们多几次。

 

好了,继承关系理清楚了。我们开始案例的编写了。

 

第一个学习的是:文件

import java.io.*;
import java.util.Date;

public class FileDemo {
 public static void main (String[] args) {
                //相对路径
     File file=new File("D://a.txt");
     if(file.exists()){
      System.out.println ("a.txt文件存在");
     }else{
      System.out.println ("a.txt文件不存在");
     }
     //获得文件名
     String fileName=file.getName();
     System.out.println ("文件名:"+fileName);
     String path=file.getAbsolutePath();
     System.out.println ("完整路径文件名:"+path);
     //获得文件的长度(以字节为单位)
     long l=file.length();
     System.out.println ("文件的长度为:"+l+"字节");
     
     //获得文件创建或最后修改的时间
     long time=file.lastModified();
     System.out.println ("修改的时间毫秒数:"+time);
     Date date=new Date(time);
     System.out.println ("文件修改的时间:"+date.toLocaleString());
    }
}

执行后会在D盘根目录下生成一个a.txt文件的

 

学会了创建文件之后,我们就开始了流的学习的

 

首先,先从字节流的最经常使用的FileOutputStream和FileInputStream开始了

 

在学习之前,我们先了解下几个方法(基类中的read和write最为重要)

 

FileInputStream中方法

close()
          关闭此输入流并释放与该流关联的所有系统资源。

read()
          从输入流中读取数据的下一个字节。

read(byte[] b)
          从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。

read(byte[] b, int off, int len)
          将输入流中最多 len 个数据字节读入 byte 数组。b为读入的缓冲区。off 为输入b中写入数据的初始化偏移量 Len为要读取的最大字节数

read方法都是返回的是Int类型,如果因为已经到达流末尾而没有可用的字节的话就会返回-1

 

FileOutputStream中的方法

close()
          关闭此输出流并释放与此流有关的所有系统资源。

flush()
          刷新此输出流并强制写出所有缓冲的输出字节。

write(byte[] b)
          将 b.length 个字节从指定的 byte 数组写入此输出流。

write(byte[] b, int off, int len)
          将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。   里面的参数和上面的read里面的参数是一个意思的,这里不写出了

write(int b)
          将指定的字节写入此输出流。

 

 

在学习读和写之前,我们先来学习如果写文件

 

将字节数组中的内容写入到b.txt中  那么b.txt中就会有65,66,67

public class FileOutputStreamDemo {
 public static void main (String[] args) {
  try{
      File file=new File("D://b.txt");
      FileOutputStream fos=new FileOutputStream(file);
      byte[] bytes={65,66,67};
      
      fos.write(bytes);
      fos.close();   
  }catch(IOException ex){
   ex.printStackTrace();
  }

    }
}

 

接下来那就要操作读和写了

 

案例:使用FileInputStream和FileOutputStream复制文件(这个就有了读和写两个过程了)

import java.io.*;
public class BufferInputOutputStreamDemo {
 public static void main (String[] args) {
  try{
//可以直接在创建流的时候创建文件,所以这里不需要new File了
     FileInputStream fis=new FileInputStream("myFile.txt");    
         FileOutputStream fos=new FileOutputStream("myFile2.txt");
      //定义字节数组
      byte[] buffer=new byte[1024];
      int num=-1;
      while(true){
       num=fis.read(buffer);
       if(num==-1){//是否知道了返回-1的作用了?
        break;
       }
      //read和write是有几种形式的,上面你可以看到 
       fos.write(buffer,0,num);

    // fos.write(buffer);      //照样可以是实现,不同的是,上面规定了最大的字节数
      }
      bis.close();
      fis.close();   
  }catch(FileNotFoundException ex){
   ex.printStackTrace();
  }catch(IOException ex){
   ex.printStackTrace();
  }
 
    } 
}

 

读写操作案例二:

 

复制图片文件

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;


public class File_In_Out_putStream {
  public static  void main(String []args){
   //设定字节数组长度为100*1024个字节,即是100k
   byte[]b=new byte[100*1024];
   try {
    //图片路径
  FileInputStream fis=new FileInputStream("d://Winter.jpg");
  //复制后图片所在路径
  FileOutputStream fos=new FileOutputStream("d://Winter2.jpg");
  //read方法需要捕获IOException异常
  //从输入流fis中读取一定数量的字节并将其存储在缓冲区数组b中
  fis.read(b);
  //将b.length个字节从指定的字节数组b写入输入流
  fos.write(b);
  //必须记得关闭流  不然你会碰到一些问题的
  fis.close();
  fos.close();
 } catch (FileNotFoundException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }catch(IOException ex){
  ex.printStackTrace();
 }
  }
}

 

这样的话,你就会发现,既然复制了一个图片喔。但是,你会发现了没有,这个图片如果大的话,既然图片没有完整。那是为什么呢?如果你细心的话,你会发现,第一个复制文件我使用了循环。第二个文件我既然没有使用循环。不使用循环就会导致了一次读进去多少字节就多少字节。所以图片是不完整的。

 

我们应该修改。

 

修改后的代码如下:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;


public class File_In_Out_putStream2 {
   public static  void main(String []args){
    //设定字节数组长度为100*1024个字节,即是100k
    byte[]b=new byte[100*1024];
    try {
     //图片路径
   FileInputStream fis=new FileInputStream("d://Winter.jpg");
   //复制后图片所在路径
   FileOutputStream fos=new FileOutputStream("d://Winter2.jpg");
   int num=-1;
   while(true){
    //read方法需要捕获IOException异常
    //从输入流fis中读取一定数量的字节并将其存储在缓冲区数组b中
    num=fis.read(b);
    if(num==-1){
     break;
    }
    //将b.length个字节从指定的字节数组b写入输入流
    fos.write(b,0,num);
   } 
   //必须记得关闭流  不然你会碰到一些问题的
   fis.close();
   fos.close();
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }catch(IOException ex){
   ex.printStackTrace();
  }
   }

}

 

这样你会发现了一个问题,就是你的图片就是完整的了。那么,你懂了吗?

 

那好,以下我们就开始使用过滤流来做“第三者”了。鼓励大家使用这种,为什么呢?好处就是:可以提高性能和速度

 

为什么会提高了性能呢?

 

因为使用了缓冲输入和输出流之后,可以从此流中成批读取字符,而不会每次都直接对数据源的读操作。数据输入时,首先会放到缓冲区,随后的读操作就是对缓冲区中的内容进行访问。那必须快的了。

 

BufferedInputStream和BufferedOutputStream的学习

 

案例:上面的复制文件的案例的改进版本

import java.io.*;
public class BufferInputOutputStreamDemo {
 public static void main (String[] args) {
  try{
      FileInputStream fis=new FileInputStream("myFile.txt");
      //创建缓冲输入流
      BufferedInputStream bis=new BufferedInputStream(fis);
    
         FileOutputStream fos=new FileOutputStream("myFile2.txt");
      //创建缓冲输出流
      BufferedOutputStream bos=new BufferedOutputStream(fos);
      //定义字节数组
      byte[] buffer=new byte[1024];
      int num=-1;
      while(true){
       num=bis.read(buffer);
       if(num==-1){//如何读到文件结尾
        bos.flush();//清空缓冲到输出流
        break;
       }
       bos.flush();
       bos.write(buffer,0,num);
      }
      bos.close();
      fos.close();
      bis.close();
      fis.close();   
  }catch(FileNotFoundException ex){
   ex.printStackTrace();
  }catch(IOException ex){
   ex.printStackTrace();
  }
 
    } 
}

 

这样和上面实现的效果一模一样。但是有朋友说,怎么看不到性能的提高了呢?那是因为我们的文件是小文件。看不出效果而已

 

使用BufferedInputStream和BufferedOutputStream复制文件(同样是上面的案例的改进)

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;


public class Buffered_In_Out_putStream {

  public static  void main(String []args){
    //设定字节数组长度为100*1024个字节,即是100k
    byte[]b=new byte[100*1024];
    try {
     //图片路径
   FileInputStream fis=new FileInputStream("d://Winter.jpg");
   //低层流作为高层流的参数 即是让他们建立连接
   BufferedInputStream bis=new BufferedInputStream(fis);
   //复制后图片所在路径
   FileOutputStream fos=new FileOutputStream("d://Winter2.jpg");
   BufferedOutputStream bos=new BufferedOutputStream(fos);
   int num=-1;
   while(true){
    //read方法需要捕获IOException异常
    //从输入流fis中读取一定数量的字节并将其存储在缓冲区数组b中
    num=bis.read(b);
    if(num==-1){
     bos.flush();//清空缓冲输出流
     break;
    }
    //将b.length个字节从指定的字节数组b写入输入流
    bos.flush();
    fos.write(b,0,num);
   } 
   //必须记得关闭流  不然你会碰到一些问题的
   //先关闭高层流再关闭底层流
   fis.close();
   fos.close();
   bis.close();
   bos.close();
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }catch(IOException ex){
   ex.printStackTrace();
  }
   }

}

 

嘻嘻。。。。那么。。。。思路很清晰吗?如果到这里你还是一知半解的话,建议多看几遍了。

 

那么,接下来我们学习DataInputStream和DataOutputStream吧


从前面我们知道,当向流中写入数据时,首先必须要将数据转换成byte数组或者char数组

(字符流)。如果数据比较少,比较简单,则向流中吸入数据还不麻烦。但是当写入数据

比较多的时候,这种转换就会出现问题了。


DataInputStream和DataOutputStream的作用:简化流数据的读写


使用方法:
如实使用了DataOutputStream流格式写入数据,那么读取时必须使用DataInputStream进

行读取。因为,在DataOutputStream吸入数据时,还写入了特定的数据格式,必须使用

DataInputStream来读取才能保持类型一样。

 

写案例之前,我们先来了解下他们的常用方法吧。


DataInputStream中方法

int readInt()
          从输入流中读取整数

double readDouble()
          从输入流中读取double型小数

 float readFloat()
          从输入流中读取Float型小数

 char readChar()
          从输入流中读取一个字符

readUTF()
          参见 DataInput 的 readUTF 方法的常规协定。

read(byte[] b)
          从包含的输入流中读取一定数量的字节,并将它们存储到缓冲区数组 b 中。

 int read(byte[] b, int off, int len)
          从包含的输入流中将最多 len 个字节读入一个 byte 数组中。

 

DataOutputStream中方法

 flush()
          清空此数据输出流。

 void writeInt(int v)
          将一个 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。

 void writeFloat(float v)
          使用 Float 类中的 floatToIntBits 方法将 float 参数转换为一个 int 值,然后将该 int

值以 4-byte 值形式写入基础输出流中,先写入高字节。

 void writeDouble(double v)
          使用 Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值,然

后将该 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。

 void writeChar(int v)
          将一个 char 值以 2-byte 值形式写入基础输出流中,先写入高字节。

 void writeChars(String s)
          将字符串按字符顺序写入基础输出流。

 void writeUTF(String str)
          以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。 

 void write(int b)
          将指定字节(参数 b 的八个低位)写入基础输出流。

 void write(byte[] b, int off, int len)
          将指定 byte 数组中从偏移量 off 开始的 len 个字节写入基础输出流。

 


那么案例还是以前的案例,这样更容易理解


复制图片
import java.io.*;

public class DataStreamDemo {
 public static void main (String[] args) {
  try{
   
      FileInputStream fis=new FileInputStream("Winter.jpg");
      //创建高层输入流
      DataInputStream dis=new DataInputStream(fis);
      
      //创建输出流
      FileOutputStream fos=new FileOutputStream("Winter2.jpg");
      DataOutputStream dos=new DataOutputStream(fos);

 //发现了没有,我们不再需要创建一个byte数组了
      int temp=0;
 //因为没有数组了所有这里就是read()方法,没有参数了,返回的是一个整数,这个整数以二进制返回,所以可以直接dos.write(temp);
      while((temp=dis.read())!=-1){
       //写入文件
       dos.write(temp);
      }
      dis.close();
      fis.close();
      dos.close();
      fos.close();
      System.out.println ("文件复制成功");   
  }catch(FileNotFoundException ex){
   System.out.println ("文件不存在");
   ex.printStackTrace();
  }catch(IOException ex){
   System.out.println ("读写异常");
   ex.printStackTrace();
  }
    } 
}

 

那么,字节流我们就学完了常用的了。其他的如果读者有兴趣的话,自己去研究了。接下来我还会在我的博客更新上字符流的部分。

 

你可能感兴趣的:(Java,InputStream,outputSt,IO操作包,java,Java,JAVA,输入流输出流)