刚开始学java的io流时,一直傻傻分不清楚,到底哪个时候使用inputStream,什么时候使用outputStream,这几天就抽了个时间好好琢磨了下。
之前自学时,看的视频,黑马的老师讲的是看程序本身,魔乐科技的老师讲的是操作对象,一直觉得讲的不够细致,自己慢慢地查找各位大佬的博客,总结出一条好理解的说法。
看内存,为什么看操作的对象是内存呢,因为程序是跑在内存中的。只有理解操作对象是内存后,两位老师的讲解才说得通了。当程序需要将字符串输入到磁盘的某个文件时,文件的传输方向是 内存 --> 磁盘。下面列个代码大家理解下
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestOutputStreamDemo {
public static void main(String[] args) throws Exception {
//1.根据文件路径 创建File对象
File file = new File("E:"+File.separator+"hello.txt");
//安全起见 ---- 避免异常
if(!file.getParentFile().exists()) {//必须保证父目录的存在
file.getParentFile().mkdirs();//不存在 则创建父目录
}
//2.根据字节流或字符流的子类 实例化父类对象 意味着只能进行文件处理
//OutputStream os = new FileOutputStream(file);//文件内容的覆盖
//如果需要进行文件内容的追加,不是覆盖
OutputStream os = new FileOutputStream(file,true);//文件内容的追加
//3.数据的读写操作 这里是向文件中 写数据
//String msg = "hello java";
//如果需要对文件进行加入并换行操作 单独的 \n不会去进行换行操作
String msg = "hello.java\r\n";
//msg.getBytes() 只是说明是内存中的数据信息 需要将内存中的数据信息写入到磁盘中去所以使用的是out
os.write(msg.getBytes());
//4.关闭流 避免资源的消耗
os.close();
}
}
这里使用的是outputStream下的子类 文件输出流(操作的是文件!)
当我们需要将磁盘中的某个文件读取到内存中时,此时的流的方向是 磁盘--->内存 所以使用inputStream这个抽象类,还是举个代码例子说明下。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class TestInputStreamDemo {
public static void main(String[] args) throws Exception {
File file = new File("E:"+File.separator+"hello.txt");
System.out.println(file.getName());
if(file.exists()) {//必须保证文件本身存在
FileInputStream fileInputStream = new FileInputStream(file);
byte[] data = new byte[1024];//吃饭用的勺子 ----- 每次可以读取到的最大数量
//1.一满勺,刚好装够最大数据,这时read返回的长度为定义的容器大小
//2.不够一勺,返回能够读取到的字节的长度
//3.没有了,返回-1
int len = fileInputStream.read(data);//此时的数据读取到了数组中
System.out.println(len);
//输出的数组中所有的内容(数组中总长度1024 装不完 导致数据打印有" ")
//System.out.println("读取内容【"+new String(data)+"】");
System.out.println("读取内容【"+new String(data,0,len)+"】");
fileInputStream.close();
}
}
}
上面的注释很好理解啦 哈哈,我喜欢将代码结合现实生活来看问题。
上面的两个代码段已经很好说明问题了,相信和我一样纠结不知道怎么用io的小伙伴,对输入输出流有了个好的理解了吧,下面再说一个输入流和输出流结合使用,拷贝文件的代码,一个是一个字节一个字节读再写的操作,一个是刚开始创建一个集装箱,装满一定数量的数据后再写的操作,大家可以尝试运行下看时间比,不多说,先上代码
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 大文件的拷贝 两种方式时间对比
*
* @author 76519
*
*/
public class TestDemo {
public static void main(String[] args) {
// 创建两个独立的线程 分别测试
new Thread(() -> {
String path1 = "E:" + File.separator + "download" + File.separator + "SecureCRT_6.5.3.490_XiaZaiBa.exe";
String path2 = "E:" + File.separator + "wanwan" + File.separator + "demo1" + File.separator
+ "SecureCRT_6.5.3.490_XiaZaiBa.exe";
try {
copyOne(path1, path2);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
String path1 = "E:" + File.separator + "download" + File.separator + "SecureCRT_6.5.3.490_XiaZaiBa.exe";
String path2 = "E:" + File.separator + "wanwan" + File.separator + "demo2" + File.separator
+ "SecureCRT_6.5.3.490_XiaZaiBa.exe";
try {
copyTwo(path1, path2);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
/**
* 文件拷贝方式一
*
* @param onePath
* @param twoPath
* @throws Exception
*/
public static void copyOne(String onePath, String twoPath) throws Exception {
File fileOne = new File(onePath);
File fileTwo = new File(twoPath);
if (!fileOne.getParentFile().exists()) {
fileOne.getParentFile().mkdirs();
}
if (!fileTwo.getParentFile().exists()) {
fileTwo.getParentFile().mkdirs();
}
// 首先需要将磁盘中某个文件内容读取到内存中
InputStream inputStream = new FileInputStream(fileOne);
// 读取到的文件资源 写入到新的磁盘的某个文件中
OutputStream outputStream = new FileOutputStream(fileTwo);
// 方式一的读取
long startTime = System.currentTimeMillis();
// 每次读取一个字节的文件
int len = 0;
while ((len = inputStream.read()) != -1) {
// 将读取到的 一个字节文件 从内存中 放入磁盘中
outputStream.write(len);
}
long endTime = System.currentTimeMillis();
System.out.println("方式一总共花费时间:" + (endTime - startTime));
inputStream.close();
outputStream.close();
}
/**
* 文件拷贝方式二
*
* @param onePath
* @param twoPath
* @throws Exception
*/
public static void copyTwo(String onePath, String twoPath) throws Exception {
File fileOne = new File(onePath);
File fileTwo = new File(twoPath);
if (!fileOne.getParentFile().exists()) {
fileOne.getParentFile().mkdirs();
}
if (!fileTwo.getParentFile().exists()) {
fileTwo.getParentFile().mkdirs();
}
// 首先需要将磁盘中某个文件内容读取到内存中
InputStream inputStream = new FileInputStream(fileOne);
// 读取到的文件资源 写入到新的磁盘的某个文件中
OutputStream outputStream = new FileOutputStream(fileTwo);
// 方式一的读取
long startTime = System.currentTimeMillis();
// 创建一片独立的载体 每次多读取指定长度的数据 存在车厢里运输
byte[] bytes = new byte[1024];
// 每次读取一个字节的文件
int len = 0;
while ((len = inputStream.read(bytes)) != -1) {
// 将读取到的 一个字节文件 从内存中 放入磁盘中
outputStream.write(bytes, 0, len);
}
long endTime = System.currentTimeMillis();
// new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date(endTime -
// startTime))
System.out.println("方式二总共花费时间:" + (endTime - startTime));
inputStream.close();
outputStream.close();
}
}
我随便找的一个exe文件,java的这个拷贝代码可以拷贝任意格式的文件。
为什么创建两个线程,是不想再main主线程中做一个耗时的操作,造成显示和两个代码运行的影响。java8新特性,lamada表达式。
运行结果
方式二总共花费时间:51
方式一总共花费时间:31053
为什么时间差上面会有如此大的差距呢,举个栗子,就好比你吃饭,是一勺子一勺子吃快点还是一粒米一粒米吃快点。
代码方式二中,添加一个byte[] bytes = new byte[1024];然后再while循环中(len = inputStream.read(bytes),这里不是读数组(我以前学的不好,老是理解为读数组),这个操作叫做读取数据放入数组中暂存。
就先说到这里啦,有问题还望各位大佬指出,小白不胜感激!