11Java的字节数组流及其使用

IO字节数组流

字节数组流ByteArrayInputStream、ByteArrayOutputStream的区别: 源头换成电脑上的内存中的数据,字节数组,可以看成电脑上的内存,或者是网络上的内存,服务器上的内存。 对于电脑的内存,java虚拟机是可以直接访问的 ,与操作系统无关,释放资源是由垃圾回收机制gc释放,gc不需要人为干预,所以不用人为关闭,但是为了风格统一,关了也没事,只是一个空方法。上几节说的字节文件输入输出流,字符文件输入输出流都是操作存在硬盘上的数据,java虚拟机不能直接访问,必须通过操作系统OS来建立联系访问。总结:源头变成了电脑内存,网络或者数据库内存;不用释放系统资源操作。 使用字节数组的好处:字符串使用toByteArray()方法把字符串转为字节数组,所有的文件/数据类型都能转为字节数组,字节数组存的是二进制数据,方便后期进行网络的传输,多用于服务器,框架底层。 java虚拟机是在电脑内存中操作字节数组的,所以把数据转为字节数组的时候要注意尽量的使字节数组长度小,但是如何量化?

下面是字节数组输出流ByteArrayInputStream的实例程序:

package IO_Study;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 字节数组输入流ByteArrayInputStream
 * 操作流的步骤:
 * 1.确定源:字节数组输入流ByteArrayInputStream的源在内存中,字节数组不要太大,但是多少是大?
 * 2.选择流
 * 3.操作
 * 4.释放系统资源:不是必须,可有可无
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/03/22 21:24
 */

public class IOstudy08 {
          
   
    public static void main(String[] args) {
          
   
        //1.确定源
        byte[] src = "hello nihao".getBytes();//getBytes方法:字符串转为字节;toByteArray:字符串转为字节数组
        //2.选择流
        InputStream is = null;
        is = new ByteArrayInputStream(src);//字节数组流对接src源
        //3.操作
        byte[] flush = new byte[3];//中转字节数组,缓存flush
        int len = -1;
        try {
          
   
            while ((len = is.read(flush)) != -1) {
          
   
                String str = new String(flush, 0, len);
                System.out.println(str);
            }
        } catch (IOException e) {
          
   
            e.printStackTrace();
        } finally {
          
   
            try {
          
   //4.释放资源,可有可无
                if (null != is) {
          
   
                    is.close();
                }
            } catch (IOException e) {
          
   
                e.printStackTrace();
            }
        }
    }
}
运行结果:
由以上程序可以看出,操作字节数组输入流的步骤与操作文件字节输入输出流相似,只有两个区别: 一个是数据源头不需要从文件获取,从内存中获取;从内存中获取,存到中转字符数组flush[]中; 二是释放系统资源不是必须操作,但是为了风格统一做了也无妨。我想之所以不用释放系统资源是因为字节数组流是在内存中操作的,java虚拟机可以直接操作内存,这个操作并未与电脑系统打交道,所以也不用释放系统资源。

下面是字节数组输出流ByteArrayOutputStream的实例程序:

package IO_Study;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * 字节数组输出流ByteArrayOutputStream
 * 操作流的步骤:
 * 1.确定源:内部维护
 * 2.选择流:不关联源
 * 3.操作
 * 4.释放系统资源:不是必须,可有可无
 * 获取数据:字节数组输出流是输出到内存中,所以此时要向内存要数据,使用toByteArray方法
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/03/22 21:24
 */

public class IOstudy09 {
          
   
    public static void main(String[] args) {
          
   
        //1.确定源,实际上就是:如果是输入流就确定src文件,从src文件中获取数据;
        //如果是输出流,就是确定dest文件,把数据输出到dest文件中
        //此处其实不是目的地,只是为了显示而设置的数组,字节数组输出流是输出到内存中,不需要目标地址
        byte[] dest = null;
        //2.选择流
        ByteArrayOutputStream baos = null;
        baos = new ByteArrayOutputStream();//不用指定目标文件,API
        //3.操作
        try {
          
   
            String msg = "hello nihao";
            byte[] datas = msg.getBytes();//字符串转字节数组
            baos.write(datas, 0, datas.length);//向内存中写数据
            baos.flush();
            dest = baos.toByteArray();//把内存中的数据转换为字节数组存到自己的数组中
            System.out.println(new String(dest, 0, baos.size()));//输出从内存中获取的字节数据
        } catch (IOException e) {
          
   
            e.printStackTrace();
        } finally {
          
   
            try {
          
   
                if (null != baos) {
          
   
                    baos.close();
                }
            } catch (IOException e) {
          
   
                e.printStackTrace();
            }
        }
    }
}
运行结果:
关于ByteArrayInputStream有几点需要说明: 使用字节数组输出流不需要确定源,因为是把内存中的字节数组数据输出(使用toByteArray()方法),所以不用指定文件数据源; 再进行步骤二选择流时,前几节的选择流操作都需要用父类的引用指向子类的对象实例化需要用的数据流,但是字节数组输出流ByteArrayOutputStream不必使用它的父类进行实例化,因为ByteArrayOutputStream的父类OutputStream类没有所需的toByteArray()方法,所以就不适用多态了。 ByteArrayOutputStream类在实例化的时候不对接数据源,因为我是输出到内存中,不需要dest。

下面是使用程序作为中转,拷贝图片文件的程序

package IO_Study;

import java.io.*;

/**
 * 使用程序拷贝图片,使用对接流完成
 * 1.使用文件字节输入流FileInputStream读取图片
 * 2.使用字节数组输入流ByteArrayInputStream把读取到的字节存到字节数组中,这个字节数组是在内存中
 * 3.使用字节数组输出流ByteArrayOutputStream把内存中的字节数组输出到文件字节输出流
 * 4.使用文件字节输出流FileOutputStream把文件字节保存到文件中
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/01 21:14
 */
public class IOstudy10 {
          
   
    public static void main(String[] args) {
          
   
        byte[] file = FileToByteArray("src/IO_Study/javalearningways.jpg");//相对路径,从当前工程所在文件夹开																			//始搜索
        System.out.println(file.length);
        ByteArrayToFile(file, "src/IO_Study/copy.jpg");
    }

    /**
     * 文件到字节数组
     * 1. 使用文件字节输入流,读入分批读入到中转数组中
     * 2.使用字节数组输出流把中转数组中的文件字节输出到内存中
     *
     * @param filepath
     * @return
     */
    public static byte[] FileToByteArray(String filepath) {
          
   
        //1.选择源
        File src = new File(filepath);
        byte[] dest = null;//字节数组输出流是输出到内存中,这句可有可无
        //2.确定流
        InputStream is = null;
        ByteArrayOutputStream baos = null;//字节数组输出流不能使用多态
        try {
          
   
            is = new FileInputStream(src);
            baos = new ByteArrayOutputStream();
            //3.操作
            byte[] flush = new byte[1024 * 10];//中转数组
            int len = -1;
            while ((len = is.read(flush)) != -1) {
          
   //读到flush中
                baos.write(flush, 0, len);//把字节数组flush中的内容写到内存中的一个‘字节数组空间’中
            }
            baos.flush();
            return baos.toByteArray();
        } catch (IOException e) {
          
   
            e.printStackTrace();
        } finally {
          
   
            //4.释放资源。只需释放文件流的资源,节点流的释放不释放都行
            try {
          
   
                if (null != is) {
          
   
                    is.close();
                }
            } catch (IOException e) {
          
   
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 内存中的文件字节数组到文件
     * 1.使用字节数组输入流把内存中的字节数组文件分批读入到中转数组中
     * 2.使用文件字节输出流把中转数组分批输出到文件中
     *
     * @param src
     * @param destFilePath
     */
    public static void ByteArrayToFile(byte[] src, String destFilePath) {
          
   
        //1.选择源
        byte[] flush = null;
        File dest = new File(destFilePath);
        //2.确定流
        OutputStream os = null;
        InputStream bais = null;//字节数组输入流,相对于程序是输入,从内存中输入
        try {
          
   
            os = new FileOutputStream(dest);
            bais = new ByteArrayInputStream(src);
            //3.操作
            //中转数组,这里是每次从内存中读取数据的大小,FileToByteArray方法中文件字节输入流每次读入的大小是1024*10				字节,然后每次向内存中写1024*10
            //但是此处不必要与它相同,这里是每次从内存中读取的字节大小,按需设定即可
            flush = new byte[10];
            int len = -1;
            while ((len = bais.read(flush)) != -1) {
          
   
                os.write(flush, 0, len);
            }
            os.flush();
        } catch (IOException e) {
          
   
            e.printStackTrace();
        } finally {
          
   
            try {
          
   
                if (null != os) {
          
   
                    os.close();
                }
            } catch (IOException e) {
          
   
                e.printStackTrace();
            }
        }
    }
}
运行结果:
**其他文件或对象转成字节数组都需要流来对接,除了字符串,**如:
文件流一定要释放资源,节点流不需要释放资源。

下面是封装拷贝方法,关闭系统资源方法的程序

package IO_Study;

import java.io.*;

/**
 * 封装输入输出流,形成管道,只需要传入输入输出流即可
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/03 10:48
 */
public class FileUtils {
          
   
    public static void main(String[] args) {
          
   
        byte[] datas = null;
        //文件到文件拷贝
        try {
          
   
            InputStream is = new FileInputStream("001.jpg");
            OutputStream os = new FileOutputStream("001-copy.jpg");
            copy(is, os);
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
        //文件到字节数组拷贝
        try {
          
   
            InputStream is = new FileInputStream("001.jpg");
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            copy(is, os);
            datas = os.toByteArray();
            System.out.println(datas.length);;
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
        //字节数组到文件拷贝
        try {
          
   
            InputStream is = new ByteArrayInputStream(datas);
            OutputStream os = new FileOutputStream("001-copy2.jpg");
            copy(is, os);
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
    }

    public static void copy(InputStream is, OutputStream os) {
          
   
        try  {
          
   
            byte[] flush = new byte[1024];//每次读取1024字节byte,就是1KB的数据
            //length的作用主要是最后一次读取到的字节数组长度可能不是设定的长度,所以记录这个返回的长度值,避免写入的时候写入多余的数据
            int length = -1;
            //3.操作(读取)
            while ((length = is.read(flush)) != -1) {
          
   
                os.write(flush, 0, length);//读取过来的就是字节数组,可以直接使用文件字节写入流写入到文件中
                os.flush();
            }
        } catch (FileNotFoundException e) {
          
   
            e.printStackTrace();
        } catch (IOException e) {
          
   
            e.printStackTrace();
        } finally {
          
   
            close2(is, os);
        }
    }

    public static void close(InputStream is, OutputStream os) {
          
   //封装释放资源的方法
        try {
          
   
            if (null != os) {
          
   
                os.close();
            }
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
        try {
          
   
            if (null != is) {
          
   
                is.close();
            }
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
    }

    public static void close2(Closeable... ios) {
          
   //封装释放资源的方法
        for (Closeable io : ios) {
          
   
            try {
          
   
                if (null != io) {
          
   
                    io.close();
                }
            } catch (IOException e) {
          
   
                e.printStackTrace();
            }
        }
    }
}
这里就是把copy方法进行封装,进行拷贝时,只需要传入输入流和输出流即可。 需要说明的是,InputStream,OutputStream所具有的方法都一样,read,write,而且它们的返回值也一样,所以文件字节流和字节数组流都可以正常传入。

下面是使用try…with…resource…释放资源

package IO_Study;

import java.io.*;

/**
 * 封装输入输出流,形成管道,只需要传入输入输出流即可
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/03 10:48
 */
public class FileUtils2 {
          
   
    public static void main(String[] args) {
          
   
        try {
          
   
            InputStream is = new FileInputStream("src/IO_Study/javalearningways.jpg");
            OutputStream os = new FileOutputStream("src/IO_Study/copyjava.jpg");
            copy2(is,os);
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
    }

    /**
     * try...with...resource...JDK1.7出现,为了释放资源方便
     *
     * @param is
     * @param os
     */
    public static void copy2(InputStream is, OutputStream os) {
          
   
        try(is;os){
          
   //但是此处一直报错:Resource references are not supported at language level 8
            byte[] flush = new byte[1024];//每次读取1024字节byte,就是1KB的数据
            //length的作用主要是最后一次读取到的字节数组长度可能不是设定的长度,所以记录这个返回的长度值,避免写入的时候写入多余的数据
            int length = -1;
            //3.操作(读取)
            while ((length = is.read(flush)) != -1) {
          
   
                os.write(flush, 0, length);//读取过来的就是字节数组,可以直接使用文件字节写入流写入到文件中
            }
            os.flush();
        } catch (FileNotFoundException e) {
          
   
            e.printStackTrace();
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
    }
}
try(is;os){//但是此处一直报错:Resource references are not supported at language level ‘8’:语言级别“8”不支持资源引用 try…with…resource…,JDK7都有的用法,但是JDK8不能用,不知道为何 rlgl,用JDK8报错,改成JDK11就不报错了,能正常运行

当然也可以这样做:

package IO_Study;

import java.io.*;

/**操作IO流的步骤:
 * 1.确定源
 * 2.选择流
 * 3.操作
 * 4.释放系统资源
 *
 * @author 发达的范
 * @date 2021/03/21 19:34
 */
public class CopyFile {
          
   
    public static void Copy(File src, File dest) {
          
   
        try(InputStream is = new FileInputStream(src.getAbsoluteFile());
            OutputStream os = new FileOutputStream(dest.getAbsoluteFile())) {
          
   

            byte[] flush = new byte[1024];//每次读取1024字节byte,就是1KB的数据
            //length的作用主要是最后一次读取到的字节数组长度可能不是设定的长度,所以记录这个返回的长度值,避免写入的时候写入多余的数据
            int length = -1;
            //3.操作(读取)
            while ((length = is.read(flush)) != -1) {
          
   
                os.write(flush, 0, length);//读取过来的就是字节数组,可以直接使用文件字节写入流写入到文件中
                os.flush();
            }
//            while ((is.read(flush)) != -1) {
          
   
//                os.write(flush);//读取过来的就是字节数组,可以直接使用文件字节写入流写入到文件中
//                os.flush();
//            }
        } catch (FileNotFoundException e) {
          
   
            e.printStackTrace();
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
    }
}
本质上就是把使用的流的声明部分放到try括号里面
经验分享 程序员 微信小程序 职场和发展