java常用字节流总结

这篇博客详细总结了Java中的字节流,包括InputStream和OutputStream的常见子类,如ByteArrayInputStream、FileInputStream、BufferedInputStream、DataInputStream、PipedInputStream等,以及OutputStream的子类如ByteArrayOutputStream、FileOutputStream、BufferedOutputStream、PrintStream和DataOutputStream。内容涵盖了每个流的基本功能、源码分析以及应用场景,帮助读者深入理解Java字节流操作。
摘要由CSDN通过智能技术生成

前言:

流的浅显总结(总结了常用的流的操作和一些关键点):

之前我对流的概念理解很片面,认为所谓输入输出流,都是面对程序和文件的概念

但是,我发现其实比较片面,而且疏漏点很多,其实也有程序和非本程序范围内的内存之间,最终不是都结束于文件和外存,在对已经加载到了内存中的数据的读和写也占了很大的一部分。

如若不全,更多详细方法:请查找JDK文档。

首先请参考于:

什么是native method:

     wikeqi的博客CSDN

     http://blog.csdn.net/wike163/article/details/6635321

 

还有对于Input和Output中的概念,是对于程序来讲的,我们应该从程序的角度来看,我们从外部数据读取到程序中,为input;我们将程序中的数据输出到外部,为output。

InputStream

ByteArrayInputStream

ByteArrayInputStream把字节数组中的字节以流的形式读出

还是相对于程序来讲的: 从  字节数组 à程序(内存中的数据)

ByteArrayOutputStream把内存中的数据读到字节数组中,而ByteArrayInputStream又把字节数组中的字节以流的形式读出,实现了对同一个字节数组的操作. 

 

ByteArrayInputStream相对于ByteArrayOutputStream的(可以将程序中的数据读到自己内置的大小任意的缓冲区中,再写到一个Byte数组中)功能,显得比较鸡肋,在JDK介绍文档中介绍的都比较少(将字节数组中的数据,读到流中的缓冲区中,再反馈给程序)该缓冲区中包含从流中读取的字节。内部计数器跟踪read方法要提供的下一个字节。关闭ByteArrayInputStream无效(ByteArrayOutputStream相同)此类中的方法,在关闭此流之后,仍可以被调用,而不会产生IOException。

 

public ByteArrayInputStream(byte[] buf)

参数:

     buf - 输入缓冲区。


public ByteArrayInputStream(byte[] buf,
                            int offset,
                            int length)

参数:

     buf - 输入缓冲区。

     offset - 缓冲区中要读取的第一个字节的偏移量。

     length - 从缓冲区中读取的最大字节数。

用途:

    

/* 
在网络中读取数据包,用于数据包是定长的,我们可以先分配一个够大的byte数组,例如: byte[] buffer = new byte[1024];
然后再通过网络编程中的操作:
*/
     Socket s = …
      DataInputStream dis = new DataInputStream(s.getInputStream());
     // 先将所有的数据存到buffer中
     dis.read(buffer);
     // 把刚才的部分视为输入流
     ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
     DataInputStream dis_2 = new DataInputStream(bais);

     

现在就可以使用dis_2的各种read方法,读取制定的字节

比如: dis_2.readByte();

and so on

上面的示例两次包转,看起来多次一举,但是中间使用了ByteArrayInputStream的好处是关掉了流依然存在。

import java.io.ByteArrayInputStream;
import java.io.InputStream;
 
import java.io.ByteArrayInputStream;
import java.io.InputStream;
 
public class ByteArrayInputStreamDemo {
    public static void main(String[] args)throws Exception{
       Data data = new Data();
      
       byte[] temp = data.getData();
       InputStream inputStream = new ByteArrayInputStream(temp);
      
       byte[] arr =new byte[temp.length];
       inputStream.read(arr);
       System.out.println(new String(arr));
      
    }
}
 
class Data{
    byte[] data = "abc".getBytes();
    public byte[] getData() {
       returndata;
    }
}


FileInputStream

 三个核心方法,也就是Override(重写)了抽象类InputStream的read方法。intread() 方法,即:

1. publicint read() throws IOException

  代码实现中很简单,一个try中调用本地native的read0()方法,直接从文件输入流中读取一个字节。IoTrace.fileReadEnd(),字面意思是防止文件没有关闭读的通道,导致读文件失败,一直开着读的通道,会造成内存泄露。

 
/* 从此输入流中读取一个数据字节 */
    public int read() throws IOException {
        Object traceContext = IoTrace.fileReadBegin(path);
        int b = 0;
        try {
            b = read0();
        } finally {
            IoTrace.fileReadEnd(traceContext, b == -1 ? 0 : 1);
        }
        return b;
    }


2. int read(byte b[]) 方法,即

    public int read(byte b[]) throws IOException

  代码实现也是比较简单的,也是一个try中调用本地native的readBytes()方法,直接从文件输入流中读取最多b.length个字节到byte数组b中。

 
/* 从此输入流中读取多个字节到byte数组中。 */
    public int read(byte b[]) throws IOException {
        Object traceContext = IoTrace.fileReadBegin(path);
        int bytesRead = 0;
        try {
            bytesRead = readBytes(b, 0, b.length);
        } finally {
            IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead);
        }
        return bytesRead;
    }


3. int read(byte b[], int off, int len) 方法,即

    public int read(byte b[], int off, int len) throwsIOException

代码实现和 int read(byte b[])方法一样,直接从文件输入流中读取最多len个字节到byte数组b中。

 
/* 从此输入流中读取最多len个字节到byte数组中。 */
    public int read(byte b[], int off, int len) throws IOException {
        Object traceContext = IoTrace.fileReadBegin(path);
        int bytesRead = 0;
        try {
            bytesRead = readBytes(b, off, len);
        } finally {
            IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead);
        }
        return bytesRead;
    }


值得一提的native方法:

上面核心方法中为什么实现简单,因为工作量都在native方法里面,即JVM里面实现了。native倒是不少一一列举吧:

  

  native void open(String name) // 打开文件,为了下一步读取文件内容
    native int read0() // 从文件输入流中读取一个字节
    native int readBytes(byte b[], int off, int len) //从文件输入流中读取,从off句柄开始的len个字节,并存储至b字节数组内。
    native void close0() // 关闭该文件输入流及涉及的资源,比如说如果该文件输入流的FileChannel对被获取后,需要对FileChannel进行close。

FileInputStream和FileOutputStream的应用(后不再贴):

第一层文本文档copy:

packageday7.bytestream;
 
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
/**
 * 这里的路径没有写绝对路径使用的是相对路径
 * 这里的test.txt文档自动生成在我的teacher_yang目录下
 * 和src是同级的
 * 补充:在windows中.txt .doc等扩展名只是标志这个文件是什么类型的软件产生的
 * 但是其实这个扩展名是无关重要的,只是提示你用什么软件打开,一定能正常显示
 * 只是可能不同软件使用的编码方式不同,会导致文件出错,但是纠其本质,文件都
 * 是一串二进制的数字存储的。
 * @author半步疯子
 *     
 * 这个程序还有很多缺陷,就是这个程序进行程序拷贝的时候,是你的源文件有多大
 * 它就会去形成多大的临界缓冲区,假如我们这个文件过大的话,就会导致我们的内
 * 存占用过大,造成电脑死机等等问题。
 * (但是我们也得明白:缓冲区的大小越大,必然拷贝搬运文件的速度就越快
 *       但是缓冲区的大小最大就是需要拷贝文件的大小,再大的话拷贝的速度也不会再
 *   提升了)
 */
public classFileCopy1 {
         public static voidmain(String[] args) {
                  FileInputStreamfis = null;
                  FileOutputStreamfos = null;
                  try {
                          //源文件
                          fis= new FileInputStream("test.txt");
                          //目标文件
                           fos = newFileOutputStream("test1.txt");
                          //得到源文件的长度
                          intlen = fis.available();
                          //构造一个和源文件大小相同的buffer(临界区)
                          byte[]buffer =new byte[len];
                          //将源文件的数据读到程序中(buffer临界区内)
                          fis.read(buffer);
                          //将临界区buffer中的数据写到目标文件中去
                          fos.write(buffer);
                  } catch(FileNotFoundException e) {
                          //文件可能找不到的情况
                          e.printStackTrace();
                  }catch(IOException e) {
                          //输入输出异常
                          e.printStackTrace();
                  }finally {
                          if(fis!=null) {
                                   try {
                                            fis.close();
                                   }catch (IOException e) {
                                            e.printStackTrace();
                                   }
                          }
                          if(fos!=null) {
                                   try {
                                            fos.close();
                                   }catch (IOException e) {
                                            e.printStackTrace();
                                   }
                          }
                  }
         }
}


 

第二层自定义缓冲区,拷贝大文件:

packageday7.bytestream;
 
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
 
public classFileCopy2 {
         public static voidmain(String[] args) {
                  FileInputStreamfis = null;
                  FileOutputStreamfos = null;
                  try {
                           fis = newFileInputStream("E:\\安装包\\mysql-5.6.31-winx64.zip");
                           fos = newFileOutputStream("a.rar");
                           // 创建一个缓冲区
                           // 缓冲区越大,copy的速度越快,最快速度为缓冲区大小等于copy文件大小的时候
                           byte[] buffer =new byte[1024*8];
                          System.out.println("文件正在复制,请稍后...");
                          longbegin = System.currentTimeMillis();
                          while(true) {
                                   /*
                                    *  read方法返回的是一个int值
                                    * 表示读入缓冲区(buffer)的字节总数
                                    * 因为各种方面的原因,read到缓冲区中
                                    * 的字节数不一定每次都装满了len长度。
                                    * 如果当前源文件中不存在可以读的字节
                                    * 的时候,read的返回值为-1。
                                    */
                                   intlen = fis.read(buffer);
                                   if(len<0){
                                            break;
                                   }
                                   fos.write(buffer,0, len);
                                   /*
                                    *fos的flush方法是手动清空buffer(缓冲区,或者临界区)
                                    *在完成copy操作之后,会自动flush
                                    */
                                   fos.flush();
                          }
                          longend = System.currentTimeMillis();
                          System.out.println("文件复制完成,耗时:"+(end-begin)+"毫秒");
                  } catch(FileNotFoundException e) {
                          e.printStackTrace();
                  } catch(IOException e) {
                          e.printStackTrace();
                  } finally {
                          if(fos!=null) {
                                   try {
                                            fos.close();
                                   }catch (IOException e) {
                                            e.printStackTrace();
                                   }
                          }
                          if(fis!=null) {
                                   try {
                                            fis.close();
                                   }catch (IOException e) {                                    
                                            e.printStackTrace();
                                   }
                          }
                  }
                 
         }
}


源码:

 
/**
 * FileInputStream 从文件系统的文件中获取输入字节流。文件取决于主机系统。
 *  比如读取图片等的原始字节流。如果读取字符流,考虑使用 FiLeReader。
 */
public class FileInputStream extends InputStream{
    /* 文件描述符类---此处用于打开文件的句柄 */
    private final FileDescriptor fd;
    /* 引用文件的路径 */
    private final String path; 
    /* 文件通道,NIO部分 */
    private FileChannel channel = null;
    private final Object closeLock = new Object();
    private volatile boolean closed = false;
 
    private static final ThreadLocal<Boolean> runningFinalize =
        new ThreadLocal<>();
 
    private static boolean isRunningFinalize() {
        Boolean val;
        if ((val = runningFinalize.get()) != null)
            return val.booleanValue();
        return false;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值