0.前言
本文会主要讲解:
Java中的I/O操作
若想要了解“类”的主要知识,可以去看前面的文章
《[Java]开发安卓,你得掌握的Java知识5》
《[Java]开发安卓,你得掌握的Java知识4》
《[Java]开发安卓,你得掌握的Java知识3》由于最终目的是要开发安卓app,
因此这里使用的IDE是AS(Android Studio)
(不会使用的可以参考下面这篇文章中的例子)
《[Java]开发安卓,你得掌握的Java知识2》
1.本文主要内容简介
-
I/O流的基础概念
-
I/O流的应用
-
一个实际的例子(思路、代码)
2.基础知识讲解
IO流.jpg2.1什么是流
简单来说,流就是一种数据的写入和读取的统一管理。
系统帮我们定义了将流中的内容写到不同的外部设备中,
编程时,只需要考虑如何将内存里面的数据写到流中流的方向的判定:
参考的是自己的内存空间输出流:是内存空间将数据写到外部设备
输入流:将外部数据读到内存中输出流就是写
输入流就是读
- 注意:图中只是大致概念,
源代码中并非完全按照这个关系进行继承
如图可见,流分为两种,字符流与字节流
简单来说,字节流就是以字节为单位进行传输的
而字符流则是以字符为单位进行传输的字节流可用于任何类型的对象,但它不能直接处理Unicode字符,而字符流就可以
字符流只能处理字符或者字符串.
什么时候改用什么流:
- 如果处理的是图片、视频等,就是用字节流
- 如果处理的是纯文本,那么就用字符流
===============================分割线=================================
2.2字节流
字节流的操作均继承于抽象类
InputStream与OutputStream一般对文件操作,则会使用
FileInputStream与FileOutputStream当然,如果想要存储一个对象,那么可以用ObjectInputStream与ObjectOutputStream
其中,Input表示输入,对内存空间输入,即“读”
Output表示输出,对内存空间输出,即“写”
===============================分割线=================================
- OutputStream操作思想:
(1)先创建File类对象,构造函数的参数要给出文件的路径
(2)创建FileOutputStream对象
(3)由于FileOutputStream的write方法需要一个字节数组,因此创建一个字节数组byte[],并赋值
(4)通过FileOutputStream对象调用write方法
(5)调用.close()来关闭输出流
让我们先来创建一个txt文件来测试
String path = "D:/test.txt";//创建在D盘根目录下面
File file = new Path();
FileInputStream fis = new FileInputStream(file);
if(!file.exists()){
//如果没有就创建文件
file.createNewFile();
}
- 输出流使用方法:
String path = "D:/test.txt";
File file = new Path();
if(!file.exists()){
//如果没有就创建文件
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
byte[] out = {'1','2','3'};
fos.write(out);
这样test里面就会被写入
123
===============================分割线=================================
- InputStream操作思想:
(1)先创建File类对象,构造函数的参数要给出文件的路径
(2)创建FileInputStream对象
(3)由于FileInputStream的write方法需要一个字节数组,因此创建一个字节数组byte[],用来装文件中的内容
(4)通过FileInputStream对象调用read方法
(5)调用.close()来关闭输出流
String path = "D:/test.txt";
File file = new Path();
FileInputStream fis = new FileInputStream(file);
if(!file.exists()){
//如果没有就创建文件
file.createNewFile();
}
//1024是可创建的最大空间,以此来避免不知道文件有多大
byte[] in = new byte[1024];
读取文件中内容
fis.read(in);
System.out.prinln(new String(in));
输出结果:
123
===============================分割线=================================
写入一个对象与保存一个对象
使用的是ObjectInputStream与ObjectOutputStream具体操作思路其实与上面的FileInputStream和FileOutputStream类似
不同点在于:
(1)首先得要有一个类(废话),然后这个类必须实现序列化接口
(2)ObjectInputStream的构造方法需要一个InputStream参数,可以使用多态的思想解决这个问题。
就是用父类声明变量,然后使用子类的构造方法
public class Person implements Serializable {
String name;
int age;
Password password;
}
- implements Serializable即实现了Serialzable这个接口
点进去会发现,这个接口其实没有任何内容- 但是要想将这个类保存起来,就必须实现这个接口
OutputStream os2 = new FileOutputStream(file);
//要写的是哪个对象
ObjectOutputStream oos = new ObjectOutputStream(os2);
oos.writeObject(xw);
oos.close();
os.close();
//从文件里面读取一个对象
InputStream is2 = new FileInputStream(file);
ObjectInputStream ois =new ObjectInputStream(is2);
Person xw3 = (Person)ois.readObject();
System.out.println(xw3);
ois.close();
is2.close();
其中,
OutputStream os2 = new FileOutputStream(file);
就是多态,用父类变量接收子类的构造方法这么做的原因在于new ObjectOutputStream()
括号中的参数类型得是OutputStreamInputSteam同理,不赘述
===============================分割线=================================
2.3字符流
字符流的操作均继承于抽象类
Reader与Writer一般对文件操作,则会使用
FileReader与FileWriter其实字符流和字节流的类的概念很相似,只是名字差别有点大
想要记忆的话,只要记住:流的方向以内存空间为参考
写(Writer)就是从内存空间写到别的地方,就是输出,
字节流中用的是OutputStream读(Reader)就是从外部将数据读到内存空间中,就是输入,字节流中用的是InputStream
===============================分割线=================================
- 注意:后面的代码统一省略file这个对象的创建,因为跟上面字节流的操作一样,直接复制上面的那部分就可以用了(从创建path变量到判断不存在文件就创建的)
===============================分割线=================================
Reader操作思想:
(1)先创建File类对象,构造函数的参数要给出文件的路径
(2)创建FileReader对象
(3)由于FileReader的read方法需要一个字符数组,因此创建一个字符数组char[],用来装文件中的内容
(4)通过FileReader对象调用read方法
(5)调用.close()来关闭输出流上面这段话其实跟InputStream的那段话基本一样,不同的就是把InputStream都换为了Reader
以及byte[]数组变为了char数组
//File file什么的就省略了,具体上面字节流部分代码有
char[] len2 = new char[1024];
Reader reader = new FileReader(file);
reader.read(len2);
System.out.println(new String(len2));
reader.close();
===============================分割线=================================
- Writer操作思想:
(1)先创建File类对象,构造函数的参数要给出文件的路径
(2)创建FileWriter对象
(3)由于FileWriter的write方法需要一个字节数组,因此创建一个字节数组char[],并赋值
(4)通过FileWriter对象调用write方法
(5)调用.close()来关闭输出流- 其实跟OutputStream以及Reader的操作都没啥区别,唯一不太一样的是,Writer有一个append方法,可以直接在文本后面追加内容
- append()方法接受的是String变量,所以就可以不用char[]
Writer writer = new FileWriter(file);
writer.append("hello ");
writer.append("world!");
//append()只能接受String类,write()只能接受char[]类
writer.close();
Writer writer = new FileWriter(file);
char[] len = {'1','2','3'};
writer.write(len);
writer.close();
3.进阶知识
如何通过输入输出流来实现文件的拷贝?
- 由于操作的文件是图片,因此需要使用字节流
- 首先,先确定源文件位置(原来图片在哪里)
然后,要明确图片要拷贝到哪里(目标为位置)
String sourcePath = "D:/1.jpg";
File sourcefile = new File(sourcePath);
if(sourcefile.exists()){
System.out.println("图片存在");
}
String desPath = "F:\\test\\1.jpg";
sourcePath就是源文件位置,desPath就是目标位置
- 这里比较有意思的是,两个path用了不同的斜杠写法
两种写法在windows系统中都可行- 由于\是转义符,即\x会被当成一个别的符号,而\的转义结果就是\本身,所以要用两个斜杠
而/这种斜杠则不用两个,因为它不是转义符号
===============================分割线=================================
//把原文件读进来
FileInputStream fis = new FileInputStream(sourcePath);
//把源文件读出去
FileOutputStream fos = new FileOutputStream(desPath);
//由于操作字节流,因此得使用字节数组
byte[] in = new byte[1024];
- 很多人可能不习惯这个字节数组,觉得不知道为啥,
其实这个字节数组就是自己定义的一片空间,起一个临时放东西的作用
===============================分割线=================================
while (true) {
int count = fis.read(in);//count指读的个数,读不到为0
if (count != -1) {
//读取到内容了
//将这一次读取的内容,写入到目标文件
//读一次,写一次
fos.write(in,0,count);
//从第0位开始读,读count个,那么写的话就写count个
}else{
break;
}
}
fis.close();
fos.close();
- 这里不断循环的原因,是因为一个图片应该是上百K的
而我们的字节数组只有1024的大小,1024Byte,就是1KB- 说白了,就是我们定义的用来放东西的空间,一次只能存1KB,但是图片却有几百KB,因此需要反复读,每次读1KB
- count就是每次读到的量,因此我们写的时候,就从0位开始,数count个,把这么多个写进去
===============================分割线=================================
如何提高字节流操作效率?
BufferedInputStream与BufferedOutputStream
上面讲解了字节流中的
FileInputStream与FileOutputStream
那这个名字前面带Buffered的有是什么呢?缓冲输入/输出流,它们的作用就是能够额外提供一个数组,来事先存放一部分文件内容
与FileInputStream与FileOutputStream不同,
缓冲输入输出流不再需要一边读流内容,再写出去而是:缓冲输入流类会把读进来的内容放在那个数组中,我们只需要去读那个数组即可
我们用普通字节流进行读写的时候,虽然最终结果可能是1024个Byte,但实际上每次都是1个字节1个字节读写的
而缓冲输入输出流,由于我们读写对象变为了那个数组,因此是1个字节数组1个字节数组来读写的,这就是读写变快的原因
InputStream is = new FileInputStream(sourcePath);
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream(desPath);
BufferedOutputStream bos = new BufferedOutputStream(os);
byte[] in = new byte[1024];
int count= 0;
while ((count = bis.read(in)) != -1) {
bos.write(in, 0, count);
}
bis.close();
bos.close();
- 总体的操作其实跟FileInput/OutputStream差不多,但总体速度却快了很多
4.总结
(1)本文讲解了Java中流的概念。流是Java知识点中十分重要的部分,可以说,如果不懂得如何操作文件,那代码的学习其实就缺少了很多意义
(2)我们从文中的那张流的继承图就可以看到,Java中的I/O流其实对于新手应该是十分头疼的,头疼的点主要在于
①看上去十分复杂的类名
②字节流字符流概念分不清
③分不清Input/OutputStream与Reader和Writer谁才是字节/字符流的方法
④文件操作的代码思路不像其他代码一样清晰明了
⑤新手可能头疼为啥要创建一个字符/字节数组,很多新手甚至在此之前没有用过(没见过)字节数组(byte[]),因此不想用
(3)但是其实,只要多看几遍文章,多用几遍,很自然地就能搞清楚以上问题,说白了,熟能生巧,多实践才是王道。
网友评论