I/O(Input / Output):用于设备之间的数据传输,如读写文件,网络通讯等。
1. 分类
1.1 字节流、字符流
按操作数据单位不同分类:字节流(8 bit)、字符流(16 bit)
1.1.1 字节流
以字节为单位,处理2进制文件,如图片、声音、影像等文件
抽象基类:InputStream、OutputStream
1.1.2 字符流
以字符为单位,读文本、数字等类型的文件
抽象基类:Reader、Writer
1.2 节点流、处理流
按流的角色的不同分类:节点流(作用在文件上)、处理流(作用在已有流外层的流)
1.3 输入流、输出流
按数据流向不同分类:输入流、输出流(以程序/内存为视点)
2. 节点流(文件流)
FileInputStream、FileOutputStream
FileReader、FileWriter
(最基本的流,效率略差,开发中并不常用)
public class FileReaderWriterTest {
// 空参read(),每次搬运一个字符
@Test
public void test01() {
FileReader fr = null;
try {
// 1.指明要操作的文件
File file = new File("hello.txt");
// 2.提供具体的流(流的实例化)
fr = new FileReader(file);
// 3.读取数据,返回一个字符或-1
// int read = fr.read();
// while (read != -1){
// System.out.println((char)read);
// read = fr.read();
// }
int read;
while ((read = fr.read()) != -1){
System.out.println((char)read);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(fr != null) {
try {
// 4.关闭流
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 带数组参数的read(),每次搬运数组长度个数据(例:字符)
@Test
public void test02(){
FileReader fr = null;
try {
// 1.指明要操作的文件
File file = new File("hello.txt");
// 2.提供具体的流(流的实例化)
fr = new FileReader(file);
// 3.读取数据,返回每次读取到的字符个数或-1
int len;
char[] chBuff = new char[5];
while ((len = fr.read(chBuff)) != -1){
// for (int i = 0; i < len; i++) {
// System.out.println(chBuff[i]);
// }
String str = new String(chBuff,0,len);
System.out.println(str);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(fr != null) {
try {
// 4.关闭流
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void test03() {
FileWriter fileWriter = null;
try {
// 1.指明要操作的文件
File file = new File("hello.txt");
// 2.提供具体的流(流的实例化)
fileWriter = new FileWriter(file,true);
// 3.读取数据,返回每次读取到的字符个数或-1
fileWriter.write("Hello IO \n");
fileWriter.write("Hello WRITE");
}catch (IOException e){
e.printStackTrace();
}finally {
// 4.关闭流
if (fileWriter != null) {
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void test04(){
FileReader fr = null;
FileWriter fw = null;
try {
// 1.指明要操作的文件
File readFile = new File("hello.txt");
File writeFile = new File("hello1.txt");
// 2.提供具体的流(流的实例化)
fr = new FileReader(readFile);
fw = new FileWriter(writeFile,true);
// 3.读取和写入操作
char[] cbuf = new char[5];
int len;
while ((len = fr.read(cbuf)) != -1){
fw.write(cbuf,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
// 4.关闭流
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test05(){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
// 1.指明要操作的文件
File readFile = new File("a.jpg");
File writeFile = new File("b.jpg");
// 2.提供具体的流(流的实例化)
fis = new FileInputStream(readFile);
fos = new FileOutputStream(writeFile,true);
// 3.读取和写入操作
byte[] bbuf = new byte[5];
int len;
while ((len = fis.read(bbuf)) != -1){
fos.write(bbuf,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
// 4.关闭流
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. 处理流
处理流是对已有流的包装。
3.1 缓冲流
缓冲流的作用:内部提供缓冲区,先将读取到的数据放到缓冲区,然后一次写出,从而可以提高文件的读写效率。
BufferedInputStream、BufferedOutputStream
BufferedReader、BufferedWriter
public class BufferedTest {
@Test
public void bufferedCharStreamTest(){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
// 1.指明要操作的文件
File readFile = new File("a.jpg");
File writeFile = new File("c.jpg");
// 2.提供具体的流(流的实例化)
// 2.1 节点流
FileInputStream fis = new FileInputStream(readFile);
FileOutputStream fos = new FileOutputStream(writeFile,true);
// 2.2 缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
// 3.读取和写入操作
byte[] bbuf = new byte[5];
int len;
while ((len = bis.read(bbuf)) != -1){
bos.write(bbuf,0,len);
//bos.flush(); // 刷新缓冲区,写出,默认是缓冲区满了之后自动刷新(输出)
}
}catch (IOException e){
e.printStackTrace();
}finally {
// 4.关闭流(先关闭外层再关闭内层,由于关闭外层会自动关闭内存,所以内存关闭可省略)
try {
if(bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void bufferedByteStreamTest(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
// 1.指明要操作的文件
// 2.提供具体的流(流的实例化)
// 2.1 节点流
// 2.2 缓冲流
br = new BufferedReader(new FileReader(new File("hello.txt")));
bw = new BufferedWriter(new FileWriter(new File("hello2.txt"),true));
// 3.读取和写入操作
char[] bbuf = new char[5];
int len;
while ((len = br.read(bbuf)) != -1){
bw.write(bbuf,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
// 4.关闭流(先关闭外层再关闭内层,由于关闭外层会自动关闭内存,所以内存关闭可省略)
try {
if(br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bw != null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.2 转换流
字节流和字符流之间的转换。
public void test01(){
InputStreamReader isr = null;
OutputStreamWriter osw = null;
try {
// 字节流读入
FileInputStream fis = new FileInputStream("hello.txt");
// 字节流输出
FileOutputStream fos = new FileOutputStream("hello_gbk.txt");
// 字节流转字符流(解码)
isr = new InputStreamReader(fis); // 默认字符集(IDE),自定义字符集(fis,"UTF-8"),取决于读取的文件保存类型
// 字符流转字节流(编码)
osw = new OutputStreamWriter(fos,"gbk");
char[] cbuf = new char[5];
int len;
while ((len = isr.read(cbuf)) != -1){
String str = new String(cbuf,0,len);
osw.write(cbuf,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(isr != null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(osw != null){
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4. 对象流
用于存储和读取基本类型或对象的处理流。
4.1 序列化
序列化:将数据从内存保存到数据源;ObjectOutputStream
反序列化:将数据从数据源还原成内存对象;ObjectInputStream
对象的序列化机制:
允许把内存中Java 对象转换成与平台无关的二进制流,从而允许把这种二进制流持久化保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点,当其他程序获取了这种二进制流,就可以恢复成原来的Java 对象
4.2 代码实现
public class ObjectInputOutputTest {
/**
* 序列化:Java 对象保存到硬盘或通过网络传输
* ObjectOutputStream
*/
@Test
public void objectOutputStreamTest(){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
oos.writeObject(new String("Hello ObjectInputOutputTest"));
oos.writeObject(new Person("chengyu",30));
oos.flush(); // 刷新操作
}catch (IOException e){
e.printStackTrace();
}finally {
if(oos != null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 反序列化:硬盘或通过网络传输的对象还原为Java 对象
* ObjectInputStream
*/
@Test
public void objectInputStreamTest(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
String str = (String)ois.readObject();
Person p = (Person)ois.readObject();
System.out.println(str);
System.out.println(p);
}catch (IOException e){
e.printStackTrace();
}catch (ClassNotFoundException e){
e.printStackTrace();
}finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 1. 实现Serializable 可序列化标识接口
// ※序列化对象的所有属性也要求是可序列化
class Person implements Serializable{
// 2.提供全局常量(唯一标识)
// 若不定义的情况下,系统会自动生成,但存在问题,若序列化后更改了序列化对象类,直接反序列化将出现问题,因为修改后的序列化对象的唯一标识也自动更新了
private static final long serialVersionUID = -1149794470754667710L;
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
补充:static 和transient 修饰的成员变量无法序列化,static 为全局的,而transient 则用于标记指定内容不允许序列化
5. 随机存取文件流
RandomAccessFile 类
既可以做输入流也可以做输出流,直接继承于Object 类;写文件时覆盖文件部分内容。
public class RandomAccessFileTest {
@Test
public void test01(){
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
raf1 = new RandomAccessFile("hello.txt","r");// 访问模式r:只读;rw:读写;rwd:读写,同步文件内容;rws:读写,同步文件内容及元数据
raf2 = new RandomAccessFile("hello3.txt","rw");
byte[] buffer = new byte[1024];
int len;
while ((len = raf1.read(buffer)) != -1){
raf2.write(buffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(raf2 != null){
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(raf1 != null){
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void test02(){
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("hello3.txt","rw");
raf.seek(5); // 从目标文件的角标为5 的位置进行覆盖
raf.write("abc".getBytes());
}catch (IOException e){
e.printStackTrace();
}finally {
if(raf != null){
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
6. NIO.2 (java 7)
6.1 介绍
NIO是Java 1.4推出的一套新的IO API,支持面向缓冲区的(IO 是面向流的)、基于通道的IO 操作,更加高效的方式进行文件的读写操作。但使用上并不方便,在Java 7发布了NIO2。
早期Java 只提供了一个File 类来访问文件系统,但File 类的功能比较有限,性能也不高,大多数出错时也并不会提供异常信息;NIO.2 引入Path 结构,替换原有的File 类。
6.2 核心 API
记得操作文件、目录要使用Path 类和Files 类即可。
6.2.1 Path
用于替换File 类。
6.2.2 Files
用于操作文件或目录的工具类。
7. 其他流
标准输入输出流(了解)
System.in:键盘输入
System.out:控制台输出
打印流(了解)
将基本数据类型的数据格式转化为字符串输出。
PrintStream
PrintWriter
数据流(了解)
操作基本数据类型和String 类型的数据。持久化到文件中或从文件中读取。
DataInputStream
DataOutputStream