Java IO

Scroll Down

Java IO流

JAVA_IO.png

分类

  • java按照将流连接类型分为

    • 节点流(低级流):是实际连接数据源与程序的“管道”,负责实际读写数据的流,读写一定是建立在节点流的基础上进行的
    • 处理流(高级流):不能独立存在,必须连接在其他流上,目的是当数据“流经”当前流时对其进行加工处理,简化我们读写时对数据的相应操作
  • 按流的方向分为

    • 输入流:InputStream
    • 输出流:OutputStream (所有字节流的超类)
  • 按照处理字节单位分为

    • 字节流:InputStream和OutputStream
    • 字符流:Reader和Writer

常用的IO流:

  1. 文件流 FileOutputStream(路径/File对象)-FIS 和FileInputStream(路径/File对象)-FOS
  2. 对象流 ObjectOutputStream(fos)-OOS 和ObjectInputStream(fis)-OIS
  3. 字节缓冲流 BufferedOutputStream(fos)-BOS 和BufferedInputStream(fos)-BIS
  4. 字符转换流 OutputStreamWriter(fos)-OSW 和InputStreamReader(fis)-ISR
  5. 字符缓冲流 BufferedWriter(osw)-BW 和BufferedReader(isr)-BR
  6. 字符缓冲加速流 PrintWriter(bw)-PW

1. 文件流

  • java.io.FileInputStream
  • java.io.FileOutputStream

常用的一类低级流的实现类,用来来接文件
对文件进行读写操作(功能上与RAF一致,但都有各自的优缺点)

文件输入流的常用构造方法

  • FileInputStream(File file)
  • FileOutputStream(String path)

上面两种构造方法创建的文件输入流为覆盖模式
即:若文件存在则文件数据清除,然后通过当前流写入的数据作为新数据保存

  • FileOutputStream(File file,boolean append)
  • FileOutputStream(String path,boolean append)

以上两种构造方法创建的文件输入流为追加模式
即:若文件存在,则数据保留,当前流写入的内容会顺序追加到文件末尾

示例1 利用文件输出流写文件

package io;

import java.io.FileOutputStream;
import java.io.IOException;

public class FOSDemo {
	public static void main(String[] args) throws IOException{
		FileOutputStream fos = new FileOutputStream("./fos.txt",true);
		String line = "记得双击嬷嬷哒";
		byte[] data = line.getBytes("gbk");
		
		fos.write(data);
		System.out.println("写入完毕");
		
		fos.close();
	}
}

示例2 利用文件输入流读文件

package io;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * 文件输入流,用于读取文件数据
 * 
 * 文件流与RAF放入区别
 * 1:RAF是基于指针的随机读写,读写方式更灵活
 * 并且可以对文件部分内容覆盖进行编辑操作
 * 而文件流则不行,文件流是基于java标准IO
 * 的操作方式,而IO读写为顺序读写,
 * 即只能向后读写操作不能回退
 * 
 * 2:文件流可以借助流连接完成复杂
 * 读写操作,这一点是RAF做不到的
 * @author QAIU
 *
 */
public class FISDemo {
    public static void main(String[] args) throws IOException{
        FileInputStream fis = new FileInputStream("fos.txt");
        byte[] data = new byte[100];
        int len = fis.read(data);
        System.out.println("实际读取到了:"+len+"字节");
        /*
         * String(byte[] data,int offset,int len,string csn)
         * 将给定的字符数组中
         * 将给定字符数组从下标offset
         * 处的连续len个字节按照指定的字符集转为字符串
         */
        String line = new String(data,0,len,"gbk");
        System.out.println(line);
    }
}

示例3 复制文件

package io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

使用文件流完成文件复制操作
@author QAIU

public class CopyDemo {
	public static void main(String[] args) throws IOException{
				1.创建文件输入流读取源文件
		2.创建文件输出流写复制文件
		3.循环读写,完成复制
		
		
		FileInputStream fis = new FileInputStream("\\jdk api 1.8_China");
		FileOutputStream fos = new FileOutputStream("out.exe");
		
		byte[] data = new byte[102];
		int len = -1;
		while((len = fis.read(data))!=-1) {
			fos.write(data, 0, len);
		}
		System.out.println("复制完毕");
	}
}

2. 字节缓冲流

  • java.io.BufferedOutputStream
  • java.io.BufferedInputStream

缓存字节输入输出流 是一对高级流,
在流连接的作用是提高读写效率(内部维护了一个8K的字节数组,
并将读写的数据转换成块读写从而提高效率)

使用缓冲流复制文件

package io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


public class CopyDemo2 {
	public static void main(String[] args) throws IOException{
		FileInputStream fis = new FileInputStream("D:\\jdk api 1.8_China\\使用说明.docx");
		FileOutputStream fos = new FileOutputStream("使用说明1.docx");
		
		BufferedInputStream bif = new BufferedInputStream(fis);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		
		int data = -1;
		while((data = bif.read())!=-1) {
			bos.write(data);
		}
		System.out.println("复制完毕!");
		bif.close();
		bos.close();
	}
}

3. 对象流

java.io.ObjectOutputStream
java.io.ObjectInputStream
对象流是一对高级流,作用是将java对象与字节
进行相互转换,便于我们读写java对象

对象输出流提供的方法:
void writeObject(Object obj)

将对象按照其结构转换一组字节并写出
写出对象所属的类必须实现Serializable接口
否则写出时会抛出异常
一个对象转出字节后会发现比该对象实际存储的数据要大
这是因为这组字节除了包含当前对象的数据外 还要记录对象的
结构信息以便于还原

这里涉及到两个专业术语:

  1. 将一个对象经过对象流写出时,对象流会按照其结构将该对象转换成一组字节,这个过程叫做对象序列化
  2. 这组被序列化后的字节在经过文件流写入文件(写入磁盘)做长久保存的过程 叫做数据持久化

对象输出流示例

Person.java:

package io;

import java.io.Serializable;
import java.util.Arrays;

/**
 * 使用当前类实例测试 对象流的读写操作
 * @author QAIU
 */
public class Person implements Serializable{
	
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private String gender;
	private transient String[] otherInfo;
	
	public Person(String name, int age, String gender, String[] otherInfo) {
		super();
		this.name = name;
		this.age = age;
		this.gender = gender;
		this.otherInfo = otherInfo;
	}
	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;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public String[] getOtherInfo() {
		return otherInfo;
	}
	public void setOtherInfo(String[] otherInfo) {
		this.otherInfo = otherInfo;
	}
	@Override
	public String toString() {
		return name+","+age+","+gender+","+Arrays.toString(otherInfo);
	}
	
}

OOSDemo.java:

package io;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class OOSDemo {
	public static void main(String[] args) throws IOException{
		String name = "小坤坤";
		int age = 18;
		String gender = "男";
		String[] outerInfo = {"学生","中国","喜欢唱跳篮球rap"};
		
		Person p = new Person(name, age, gender, outerInfo);
		FileOutputStream fos = new FileOutputStream("person.obj");
		ObjectOutputStream oos = new ObjectOutputStream(fos);

		oos.writeObject(p);
		System.out.println("写出完毕");
		oos.close();
		
	}
}

使用对象输入流 反序列化

package io;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class OISDemo {
	public static void main(String[] args) throws IOException, ClassNotFoundException{
		FileInputStream fis = new FileInputStream("person.obj");
		ObjectInputStream ois = new ObjectInputStream(fis);
		Object object = ois.readObject();
		System.out.println(object);
		ois.close();
	}
}

4. 转换流

java将按照读写单位分为:字节流和字符流
字节流:以字节为单位,一次至少读写8位二进制
字符流:以字符为单位读写数据,由实际读写的字节量与
指定的字符集与读写的字符数据有关 但是在java内部表示字符数据时都是用char表示(2字节)

  • java.io.Writer
  • java.io.Reader

上面两个类是抽象类,是所有字符输出流和字符输入流的超类
里面规定了读写字符相关的方法

转换流(字节流转字符流),他们是字符流的一对常用实现类,是一对高级流
实际开发中我们在读写文本数据时,它们是流连接中重要的一环
但是我们不会直接操
Java提供了这样一对转换流

  • OutputStreamWriter: 是字符流通向字节流的桥梁
  • InputStreamReader: 从字节流到字符流的桥梁

使用转换输出流写文本信息

package io;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class OSWDemo {
	public static void main(String[] args) throws IOException{
		FileOutputStream fos = new FileOutputStream("osw.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
		/*
		 * 转换流在创建时通过指定第二个参数来确
		 * 定字符集,这样通过当前流写出文本时
		 * 都会按照该字符集转换为字节后再做写出
		 */
		String string = "是兄弟就来砍我~~~~~~";
		osw.write(string);
		
		osw.close();
		System.out.println("输出完毕");
	}
}

使用转换输入流读文本信息

package io;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * java.io.InputStreamReader
 * 转换流--输入
 * 
 * InputStreamReader与OutputStreamWriter之所以
 * 称为转换流是因为:
 * 通常java中其他的高级字符流都只能连接在其他字符
 * 流上,都不能直接连接字节流.但是它们是可以连接在
 * 字节流上的,而本身又是字符流,这样就可以让其他的
 * 字符流与字节流衔接了,起到了"转换器"的作用.
 * 
 * @author QAIU
 *
 */
public class ISRDemo {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("./src/io/ISRDemo.java");
		
		InputStreamReader isr = new InputStreamReader(fis);
		
		int d = -1;
		while((d = isr.read())!=-1) {
			System.out.print((char)d);
		}
		isr.close();
	}
}

5. 字符缓冲流和缓冲加速(字符打印)流

  • java.io.BufferedWriter
  • java.io.BufferedReader

以上两个就是缓冲字符输入与输出流是一对高级流,内部有缓冲区,读写文本数据效率高

  • java.io.PrintWriter

内部总是连接BufferedWriter作为缓冲加速使用
并且PW还支持自动刷新功能,实际开发比较常用

PrintWriter提供了对文件直接写操作的构造器

  • PrintWriter(File file)
  • PrintWriter(String path)

并且上面的构造器还支持一个重载PrintWriter(String fileName, String csn),第二个参数
是字符集名称,这样可以按照指定的字符集名称写出文本数据

使用缓冲字符输入流读取文件

package io;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 缓冲字符输入流
 * 特点:块读写效率高,并且可以按行读取字符串
 * @author QAIU
 *
 */
public class BRDemo {
	public static void main(String[] args) throws IOException{
		FileInputStream fis = new FileInputStream("./src/io/BRDemo.java");
		
		BufferedReader br = new BufferedReader(new InputStreamReader(fis));
		/*
		 * String readline()
		 * 返回一行字符串,缓冲流会将一行字符串
		 * (到换行符"\n"为止,但不包括"\n")的内容
		 * 返回,若返回值为null,则表示流已经读取到了末尾
		 */
		String line = null;
		while((line = br.readLine())!=null) {
			System.out.println(line);
		}
		br.close();
	}
}

使用字符打印流写文件

package io;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;


public class PWDemo {
	public static void main(String[] args) throws IOException {
		PrintWriter pw = new PrintWriter("pw.txt","GBK");
		pw.println("写入换行");
		pw.print("不换行");
		pw.println("换行了");
		pw.close();
	}
}

在流连接中使用PW

package io;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class PWDemo2 {
	public static void main(String[] argv) throws IOException {
		FileOutputStream fos = new FileOutputStream("pw2.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos);
		BufferedWriter bw = new BufferedWriter(osw);
		PrintWriter pw = new PrintWriter(bw);
		pw.println(123*3);
		
		pw.close();
		
		
	}
}

缓冲输出流的缓冲区问题

package io;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BOS_flushDemo {
	public static void main(String[] args) throws IOException{
		FileOutputStream fos = new FileOutputStream("bos.txt");
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		
		String str = "测试文本";
		bos.write(str.getBytes("GBK"));
		/*
		 * void flush()
		 * 该方法是OutputStream定义的方法,
		 * 但并非所有字节输出流都实现了该方法的功能
		 * 只有缓冲流的该方法有实际意义
		 * 作用是一次性将缓冲区已存在的数据写出
		 * 之所以所有的字节流都有该方法是因为
		 * 流连接应用中缓冲流通常不是"终端流"(直接被我们操作的流)
		 * 为了传递刷新缓冲区功能才有
		 */
		bos.flush();
		System.out.println("写入完毕");
		bos.close();
		
	}
}

综合应用 简易记事本工具

package io;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Scanner;

/**
 * 简易记事本工具
 * 程序启动后要求用户输入文件名,之后将用户输入
 * 的每一行字符串按行写入该文件,单独输入exit
 * 时退出
 * 使用PW完成按行写入文本数据操作
 * 要求 独立完成流连接操作
 * @author QAIU
 *
 */
public class Note {
	public static void main(String[] args) throws IOException{
		Scanner scanner = new Scanner(System.in);
		System.out.println("请输入文件名");
		String filename = scanner.nextLine();

		PrintWriter pw = new PrintWriter(
				new BufferedWriter(
						new OutputStreamWriter(
                            new FileOutputStream(filename),"gbk")),true);

		System.out.println("请输入每一行文本,按exit退出");

		String str;
		while(!"exit".equals(str = scanner.nextLine())) {
			pw.println(str);
		}
		pw.close();


		System.out.println("已退出,欢迎下次使用");

	}
}