一、流的简介
- 流是用来读写文件数据的,在Java程序中,对于数据的输入,输出操作以”流“的方式进行。
- 文件是硬盘上的一块存储空间,Java里面的类File是对其的抽象,封装了文件名。
- 类比一下水流,文件可以看成小桶。水从小桶流出,就是输出,反之就是输入。
- 输入与输出都是站在程序角度而言。
二、流的分类
-
字符流与字节流
字节流:InputStream OutputStream
字符流:Reader Writer -
节点流与处理流
-
网络操作:socket
-
对象操作:Serializable
三、编码与解码
编码就是把字符转换为字节,而解码是把字节重新组合成字符。如果编码与解码方式不一致,就可能会出现乱码。
- GBK编码中,中文字符占2个字节,英文字符占1个字节
- UTF-8编码中,中文字符占3个字节,英文字符占1个字节
- UTF-16be编码中,中文字符占2个字节,英文字符占2个字节
注:Java中的char类型是2个字节,16位,单个中文和英文都能存储
解决乱码问题:
Byte[] bytes = str.getBytes(String encodeCharName); //字符串编码为 byte 序列
str = new String(Byte[] bytes, String decodeCharsetName); //重新对bytes进行编码,创建新的字符串对象
//两者结合使用
str = new String(str.getBytes(String encodeName), String decodeCharsetName); //转码时getBytes必须以原有编码格式,否则依然乱码
// 创建指定字符集的 InputStreamReader
InputStreamReader(InputStream in, String CharsetName)
// 创建使用指定字符集的 OutputStreamWriter
OutputStreamWriter(OutputStream out, String CharsetName)
四、流的使用
一个简单的使用例子:
public static void main(String[] args) {
int count = 0;
String line = null;
FileInputStream in = null;
FileOutputStream out = null;
BufferedReader reader = null;
//读到数组中
byte[] buffer = new byte[1024];
System.out.println(buffer.length + " 长度");
try {
in = new FileInputStream("F:\\sparkTest\\io.txt"); //空格也算字节
out = new FileOutputStream("F:\\sparkTest\\io3.txt");
//windows默认文件以gbk存储,读取方式是utf-8
reader = new BufferedReader(new InputStreamReader(new FileInputStream("F:\\sparkTest\\io.txt"), "gbk"));
} catch (Exception e1) {
e1.printStackTrace();
}
try {
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
/*while ((count = in.read(buffer, 0 , buffer.length)) != -1) {
System.out.println(count);
out.write(buffer, 0, count);
}*/
/*while ((count = in.read()) != -1) { //返回的是字母代表的ASCII码
//中文乱码
System.out.println(count + " " + (char)count);
out.write(count);
}*/
in.close();
out.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
流的具体API略过
流的读写:
字节流:
int read() throws IOException; //单个字节读取,返回的数字是字符的ASCII
int read(byte b[]) throws IOException //读取字节到数组,返回实际字节数
int read(byte b[], int off, int len) throws IOException //off起始位置,len可读取的长度,返回值是实际读取字节数
注:写操作方法与此对应
字符流:
void write(int c) throws IOException;
write(char cbuf[]) throws IOException; //char数组
void write(char cbuf[], int off, int len);
处理流的类还是比较多的,有BufferReader(缓冲流),InputStreamReader(转换流),DataInputStream(数据流)等等,请仔细阅读API
类图:
五、流的关闭
流关闭的顺序:先开启的后关闭,和栈有点类似
注: 流的打开一定需要关闭,finally可以保证,但是多重判断比较麻烦,可以使用try-with-resources
//1.7之前流的关闭
finally {
if(in != null) {
in.close();
}
if(in != null) {
in.close();
}
if(in != null) {
in.close();
}
}
使用try-with-resources:
try (FileInputStream in = new FileInputStream("F:\\sparkTest\\io.txt");
FileOutputStream out = out = new FileOutputStream("F:\\sparkTest\\io3.txt");
BufferedReader reader =new BufferedReader(
new InputStreamReader(new FileInputStream("F:\\sparkTest\\io.txt"), "gbk"));) {
逻辑代码...
} catch(Exeception) {}
try-with-resources在try()创建流会自动按顺序关闭流,代码看起来简洁很多
try-with-resources为什么能够避免大量资源释放代码呢?答案是,由Java编译器来帮我们添加finally代码块。注意,编译器只会添加finally代码块,而资源释放的过程需要资源提供者提供。
小知识
try catch finally中都有return语句,执行顺序是怎么样的呢?
public int test() {
try {
int i = 1;
i = i / 0;
return i;
} catch (Exception e) {
return -1;
} finally{
return -2;
}
}
本例答案是-2
在i=i/0处代码抛出异常,进入catch方法,但是在return -1前会进入finall模块return -2
也就是finally会覆盖try和catch中的return语句