JAVA 笔记:I/O的简介
风萧萧兮易水寒,串流一去兮不复还
I/O 以连接的节点(Node)、传送的资料型态、以及输入/输出来区分
分作以下类别(Class)
档案 记忆体 Pipe |
X | 位元组(Byte)传输 字元(char)传输 字串(String)传输 |
X |
输入 输出 |
通常做位元组传输,会叫做Stream
以常见的字串传输,会叫做Reader/Writer
举例来说,FileInputStream是做档案位元输入的类别;InputStreamReader是做字串输入的类别
Pipe是比较特殊的用途,搭配在多执行续上使用,参照PipedInputStream、PipedOutputStream
其他的都直接继承了InputStream、OutputStream
不论是哪一种传输,都是以节点(Node)做为起点或终点,做出Stream这样的动作
Stream就如同字面上的意思,像水流一样,有开有关
当Read()或write()方法被呼叫时,串流就会开启,直到close()被呼叫为止
有时候串流会有保护同时只开放一个程序使用,因此必须记得close()串流,避免其他程序无法使用
在文章的第一行就有提到,串流是无法回头的
当一直Read()资料时,串流就一点一点的流去,不会回头
不过如果一定要让流过的串流再回头看一次,就要使用一些Buffer的方式,先把串流存起来,就像是蓄水槽一样
JAVA也有提供一些搭配使用的缓冲区类别,等一下会说到
通常我们会用到的串流,就是以档案为节点的串流了,读写档案都用的到
使用的方法十分简单,只需要开档后,建立起Stream的物件,在使用读写的方法就可以了
✎ 记事本 _ ❒ X | 1.在想要的目录中建立一个文字档 |
This is a test... |
|
import java.io.*; public class Test { public static void main(String[] args) { FileInputStream fin; try { fin = new FileInputStream("c:\\file.txt"); while (fin.available() > 0) System.out.print(fin.read()); } catch (IOException e) { e.printStackTrace(); } } } |
2.使用程式码来读档 |
◙ C:\Windows\System32\cmd.exe _ ❒ X |
3.执行的结果,很容易吧 |
This is a test... |
前面有提到,FileInputStream他是以Byte为传输单位
而英文字母和一般标点符号长度也恰好是一个Byte,因此显示出来都很正常
不过如果读写中文时就会出问题了
✎ 记事本 _ ❒ X | 1.这次的内容是中文 |
中文测试 |
|
◙ C:\Windows\System32\cmd.exe _ ❒ X |
2.执行结果 出现了世界末日的日期 (误 |
164164164229180250184213 |
如果想要顺利的读取中文,又想坚持使用FileInputStream的话,就要用到Byte[] 来做buffer,而且阵列大小必须是2的倍数,因为中文字占2个Byte
import java.io.*; public class Test { public static void main(String[] args) { FileInputStream fin; try { fin = new FileInputStream("c:\\file.txt"); byte buf[] = new byte[8];//建立一个大小8Byte的缓冲区 int bufSize;//用来计算读入的资料大小 while (fin.available() > 0) bufSize = fin.read(buf);//读取资料到buf内 System.out.print(new String(buf, 0, bufSize)); } catch (IOException e) { e.printStackTrace(); } } } |
1.修改一些程式码 | |
◙ C:\Windows\System32\cmd.exe _ ❒ X |
2.出现了正确的结果 | |
中文测试 |
不过要是中文字中间夹杂了1Byte大小的字元,读取就会错乱,这时候就没有办法再坚持使用FileInputStream了
FileReader就登场了!
import java.io.*;
public class Test {
public static void main(String[] args) {
FileReader fin;
try {
fin = new FileReader("c:\\file.txt");
int word;
while (fin.ready()){
word = fin.read();
System.out.print((char)word);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
跟FileInputStream几乎是一样的用法
FileReader或FileWriter都是以字元做传输的单位,因此不管是中文还是英文,都可以正常的传输
到这里为止,我们知道要如何读写档案文字
但是如果我们要读写的是数字、浮点数甚至是物件呢?
当然你可以把123或者0.01还有其他资料用String的方式写到档案中,就像刚才那样
不过如此写入的话,取出时,还要做字串分析等等繁杂的工作
虽然不是不可以,但是程式就不漂亮了
那么,该如何把真正的"资料型态"写到档案里面呢?
我们需要一些转接头,把资料型态与串流做个连结,称作chain
为了达到串流连结,我们要用到一些的类别,也就是转接头,称作Filter
Filter除了可以达到转换型态的功能外,还有些具有缓冲的功能,也就是让流去的串流可以被回朔
来看看JAVA提供那些Filter的类别
串流缓冲区 | BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter |
物件串流 | ObjectInputStream ObjectOutputStream |
基本资料型态串流 | InputStreamReader InputStreamWriter DataInputStream DataOutputStream |
计算行数 | LineNumberInputStream LineNumberReader |
堆叠串流 | PushbackInputStream PushbackReader |
列印串流 | PrintStream PrintWriter |
这么多种类转接头,看到都晕了…
不过常用的也不会很多,拿比较常用的DataOutputStream举例吧
DataOutputStream可以将输出的资料转换成各种基本资料型态:int, char, double, float...等等
要怎么转呢?看看↓
import java.io.*;
public class Test {
public static void main(String[] args) {
FileOutputStream fin;
try {
fin = new FileOutputStream("c:\\file.txt");
DataOutputStream dataWrite = new DataOutputStream(fin);
short sh[] = {1,2,3,4,5};
for(short i : sh){
dataWrite.writeShort(i);//这里的方法要搭配资料型态使用
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
这样就完成了写入short资料型态的数字到档案里了,当然也可以用类似方式写入整数、浮点数等等…
不过当好奇的用记事本打开档案一看…奇怪,啥都没有啊!
那是当然的了,因为那不是以"字串"的编码写入,而是以你指定的资料编码写入(上面例子就是short)
而记事本只会把资料以字串的编码来呈现,其中字串编码又分ASCII、UTF…数种
不过当你再写一段以short读入资料的程式之后,你就可以直接用short的变数来接收读入的资料了
超方便的!
刚才有提到,除了基本资料型态之外,物件也可以存进档案中
不过存进去的只限于"资料",也就是物件中的"属性(attribute)"
物件中的方法(method)是不会被存的
物件在要被丢到串流中之前,必须先序列化(Serialization),序列化其实也不是甚么很神秘的东西,只是把资料从物件中抽出来准备传送而已,这个部分电脑会帮你完成,你只需要在你想要被传送的类别上,implements Serializable就可以了
用到的Filter类别是ObjectInputStream、ObjectOutputStream,用法跟基本资料型态的一样,把FileOutputStream的物件挂上去(传入建构子)就可以了
同样的,读取物件时,用该物件来接收,你就可以得到之前存起来的物件了