本篇将紧接上篇,同样以实现文件拷贝为例,探讨字节数组流ByteArrayInputStream 和 ByteArrayOutputStream 的使用方法,以及如何对资源释放进行封装,使我们处理资源释放问题时更加便捷。
一、概述
- 文件存储在硬盘上,Java虚拟机无权访问,需要借助操作系统来访问和操作,并在操作完成后通知操作系统释放资源。
- 而对于存有字节内容的字节数组,Java虚拟机可以直接访问。存储资源的内存可以在网络上,也可以在远程服务器等地方,关于资源的释放由Java内部的GC(垃圾自动回收机制)处理。
- 所有对象都可以转为字节数组,使得网络传输等操作更加方便。
二、字节数组流的使用
ByteArrayOutputStream
我们先写入内容,再输出。
ByteArrayInputStream
使用ByteArrayInputStream要将数据准备成字节数组,可新创建一个字节数组。
代码
以对接流实现文件与字节数组输入输出的转换为例:
1、图片到字节数组
文件输入流-字节数组输出流
- 文件到程序:FileInputStream
- 程序到字节数组:ByteArrayOutputStream
2、字节数组到图片
字节数组输入流-文件输出流
- 字节数组到程序:ByteArrayInputStream
- 程序到文件:FileOutputStream
注意:文件需要释放,字节数组流无需释放
主程序入口:
public static void main(String[] args) {
//图片转成字节数组
byte[] datas = fileToByteArray("BSandCS.png");
System.out.println(datas.length);
//字节数组转成图片
byteArrayTofile(datas,"dest.png");
}
图片读取到字节数组:
public static byte[] fileToByteArray(String filePath){
//1、创建源
File src = new File(filePath);
byte[] dest = null;
//2、选择流
InputStream is=null;
ByteArrayOutputStream os = null;
try {
is = new FileInputStream(src);
os = new ByteArrayOutputStream();
//3、传输操作:读
byte[] flush = new byte[1024*10]; //缓冲容器
int len=-1; //接收长度
while((len=is.read(flush))!=-1){
os.write(flush,0,len); //写出到字节数组中
}
//刷新一下
os.flush();
//获取数据
dest = os.toByteArray();
return dest;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{ //考虑异常处理,加上finally
//4、释放资源
try {
if(null!=is){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
字节数组写出到图片:
public static void byteArrayTofile(byte[] src,String filePath){
//1、创建流
File dest = new File(filePath);
//2、选择流
InputStream is=null;
FileOutputStream os=null;
try {
is = new ByteArrayInputStream(src);
os = new FileOutputStream(dest);
//3、传输操作:读
byte[] flush = new byte[1024]; //缓冲容器
int len=-1; //接收长度
while((len=is.read(flush))!=-1){
os.write(flush,0,len);
}
os.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if (null != os) {
os.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
以文件拷贝功能的实现举例:
public class FileUtils {
public static void main(String[] args) {
//文件到文件
try {
InputStream is = new FileInputStream("abc.txt");
OutputStream os = new FileOutputStream("abc_copy.txt");
copy(is,os);
} catch (Exception e) {
e.printStackTrace();
}
//文件到字节数组
byte[] datas = null;
try {
InputStream is = new FileInputStream("abc.txt");
ByteArrayOutputStream os = new ByteArrayOutputStream();
copy(is,os);
datas = os.toByteArray();
System.out.println(datas.length);
} catch (Exception e) {
e.printStackTrace();
}
//字节数组到文件
try {
InputStream is = new ByteArrayInputStream(datas);
OutputStream os = new FileOutputStream("abc_copy.txt");
copy(is,os);
} catch (Exception e) {
e.printStackTrace();
}
}
//对接输入输出流
public static void copy(InputStream is,OutputStream os){
try{
//操作:读入与写出内容
byte[] flush = new byte[1024]; //缓冲容器
int len=-1; //接收长度
while((len=is.read(flush))!=-1){
os.write(flush,0,len);
}
//刷新一下
os.flush();
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
close1(is,os);
//close2(is,os);
}
}
}
其中,close(is,os); 和 closed(is,os); 是我们接下来要探讨的两种实现释放资源的封装方法。
三、资源释放的封装
方法一
public static void close1(InputStream is,OutputStream os){
try {
if (null!=os) {
os.close();
}
} catch (Exception 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 (Exception e) {
e.printStackTrace();
}
}
}
方法三
在Java版本1.7之后可以不用手动释放资源,用try…with…resource,将声明与初始化写入 try () 中,删去 finally { } ,让操作系统自动释放资源。具体操作如下:
try (InputStream is = new FileInputStream("abc.txt");
OutputStream os = new FileOutputStream("abc_copy.txt");) {
//具体操作:写入与取出
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
总结
- 1、源转为字节数组
- 2、资源不用手动释放,此时close()是个空方法
- 3、一切对象都可以转为字节数组
- 4、开辟的存储空间不宜过大