Java IO流详解
1. IO概述
IO(Input/Output)即输入和输出,指的是设备或环境之间进行数据的输入或输出。例如,键盘是输入设备,显示器是输出设备。在Java中,输入输出问题通过流(Stream)对象来解决。以程序为中心,读取文件是输入,写入文件是输出。
1.1 流的分类
Java中的IO流可以从两个角度进行分类:
-
从输入输出角度分类:
-
输入流:用于读取数据。
-
输出流:用于写入数据。
-
-
从数据的角度分类:
-
字符流:处理文本数据,如文章、Java文件等。
-
字节流:处理二进制数据,如图片、MP3文件等。
-
2. 字符流
字符流用于处理文本数据,如文章、Java文件等。字符流的类命名规则如下:
-
如果是输出流,类名以
Writer
结尾。 -
如果是输入流,类名以
Reader
结尾。
2.1 案例:使用字符流向文件写入"HelloWorld"
public class IOTest { public static void main(String[] args) { // 创建一个文件 File file = new File("test.txt"); Writer writer = null; try { // 创建输出流对象 writer = new FileWriter(file); // 写入数据 writer.write("HelloWorld"); } catch (IOException e) { e.printStackTrace(); } finally { // 释放资源 if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
2.2 文件的追加
public class IOTest4 { public static void main(String[] args) { Writer writer = null; try { // 追加模式 writer = new FileWriter("test1.txt", true); writer.write("liangliang"); } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
2.3 换行
不同操作系统下的换行符不同:
-
Windows:
\r\n
-
Linux:
\n
-
Mac:
\r
public class IOTest5 { public static void main(String[] args) { File file = new File("test.txt"); Writer writer = null; try { writer = new FileWriter(file); for (int i = 0; i < 100; i++) { writer.write("HelloWorld\r\n"); if (i % 10 == 0) { writer.flush(); } } } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
2.4 Writer的五种写入方法
public class IOTest6 { public static void main(String[] args) { File file = new File("test.txt"); Writer writer = null; try { writer = new FileWriter(file); char[] c = {'a', 'b', 'p', 'b', 'p'}; writer.write(c); // 写入整个数组 writer.write(c, 2, 2); // 写入数组的一部分 writer.write(97); // 写入单个字符 writer.write("helloworld", 2, 2); // 写入字符串的一部分 } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
2.5高效缓冲流
高效缓存字符输入流:BufferReader
高效缓存字符输出流:BufferWriter
高效缓存字节流:
BufferedInputStream,BufferedOutputStream
用法案例
package cn.tedu.buffer;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferWriterDemo {
public static void main(String[] args) {
//创建高效缓冲字符输出流
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter("IOTest.java"));
//写一行数据
writer.write("helloworld");
//换行
writer.newLine();
writer.write("helloworld");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//资源关闭
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.6 文件的复制
利用字符输入输出流 实现文件的拷贝功能
案例
package cn.tedu.demo1;
import java.io.*;
/**
* 利用字符输入输出流
* 实现文件的拷贝功能
*/
public class CopyDemo2 {
//主函数 主程序 入口
public static void main(String[] args) {
//1.创建需要的输出输入流对象
Reader reader=null;
Writer writer=null;
//2.创建要拷贝的文件file对象 以及要到达的新文件
File file=new File("test3.txt");
File file1=new File("test5.txt");
//3.利用字符输入流读取文件test3.txt
try {
//读取文件test3.txt
reader = new FileReader(file);
//创建字符输出流对象
writer=new FileWriter(file1);
//创建一个字符数组 减少读取的io次数
char [] cs = new char[1024];
//定义边界值
int len = -1;
//利用循环 找到边界值 读取文件
while((len = reader.read(cs)) != -1){
//此时读到的内容要写进新的文件通过字符输出流
//把输入流读取到的数据写入字符输出流
writer.write(cs, 0, len);
}
//为了解决传递压迫性能问题 使用flush操作
writer.flush();
//改成大异常 更方便
} catch (Exception e) {
e.printStackTrace();
}finally{
//最后把两个流关闭
try {
if(writer != null){
writer.close();
}
if(reader != null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. 字符流与字节流的桥梁
OutputStreamWriter
是字符流转向字节流的桥梁,可以指定编码格式。
public class ConverterDemo { public static void main(String[] args) { OutputStreamWriter ow = null; try { ow = new OutputStreamWriter(new FileOutputStream("b.txt"), "GBK"); ow.write("中"); ow.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (ow != null) { ow.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
4. Properties类
Properties
类表示一个持久的属性集,可以保存在流中或从流中加载。它继承自Hashtable
,是线程安全的键值对存储结构。
public class PropTest6 { public static void main(String[] args) { Properties prop = new Properties(); InputStream in = null; try { in = PropTest6.class.getClassLoader().getResourceAsStream("names.properties"); prop.load(in); System.out.println(prop); } catch (Exception e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
5. 序列化流
序列化流用于将对象写入输出流或从输入流中读取对象。
5.1 ObjectOutputStream
要序列化的对象需要实现Serializable
接口。
public class ObjectOutputStreamDemo { public static void main(String[] args) { try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"))) { oos.writeObject(new Person("张三", 20)); } catch (IOException e) { e.printStackTrace(); } } } class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
6. 字符编码
字符编码决定了字符在计算机中的存储和展示方式。常见的编码方式有ASCII、GBK、UTF-8等。
-
ASCII:最早的字符编码标准,仅支持英文字符。
-
GBK:用于存储中文字符的编码。
-
UTF-8:一种通用的字符编码,支持几乎所有字符。
注意:编码和解码需要使用相同的字符集,否则会出现乱码。
代码演示案例:
package cn.tedu.test;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Utf8Cladd {
public static void main(String[] args) {
String str="中";
try {
//str=new String(str.getBytes("UTF-8"),"GBK");
System.out.println(str);
byte[] bytes = str.getBytes();
System.out.println(Arrays.toString(bytes));
} catch (Exception e) {
e.printStackTrace();
}
}
}