第十四章、IO流

IO流

1. 字节流

定义:是按照字节进行操作的,每次读取一个字节
字节流抽象基类:
	InputStream(字节输入流)/OutputStream(字节输出流)
子类:
    (1).FileInputStream
        public int read()
             从此输入流中读取一个数据字节。
        public int read(byte[] b)
             从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
        public int read(byte[] b,int off,int len)
             从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。off:目标数组 b 中的起始偏移量。

	(2).FileOutputStream
		public void write(int b)
		public void write(byte[] b)
		public void write(byte[] b,int off,int len)
		    指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。

	(3).BufferedInputStream:字节缓冲输入流
	(4).BufferedOutputStream:字节缓冲输出流
  • FileInputStream(字节输入流)使用方法

    package demo1;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    		
    public class InputStreamDemo {
    	public static void main(String[] args) throws Exception {
    		
    		// 1.创建字节输入流对象
    		InputStream is = new FileInputStream("斗破苍穹.txt");
    		
    		// 2.对文件进行读取
    		
    		System.out.println("----------第一种读取方式-----------");
    		
    		// read()方法  当文件读取到末尾的时候返回-1
    		
    		long startTime = System.currentTimeMillis();int num = 0;
    		while((num = is.read()) != -1){
    			System.out.print((char)num);
    		}
    		long endTime = System.currentTimeMillis();
    		System.out.println(endTime - startTime);
    				
    		System.out.println("----------第二种读取方式-----------");
    		
    		long startTime = System.currentTimeMillis();
    		// 缓冲区  比作一个可以放1024本书的书架
    		byte[] by = new byte[1024];
    		
    		int num = 0;
    		String str = new String();
    		while((num = is.read(by)) != -1){
    			// 将读取到的字节序列转换成字符序列
    			str = new String(by,0,num);
    		}
    		System.out.println(str);
    		
    		long endTime = System.currentTimeMillis();
    		System.out.println(endTime - startTime);
    		
    		System.out.println("----------第三种读取方式-----------");
    		
    		long startTime = System.currentTimeMillis();
    		// 缓冲区  比作一个可以放1024本书的书架
    		byte[] by = new byte[1024];
    		
    		int num = 0;
    		String str = new String();
    		while((num = is.read(by,0,by.length)) != -1){
    			// 将读取到的字节序列转换成字符序列
    			str = new String(by,0,num);
    		}
    		System.out.println(str);
    		
    		long endTime = System.currentTimeMillis();
    		System.out.println(endTime - startTime);
            
            // 关闭流并释放资源
            is.close();
    	}
    }
    
  • FileOutputStream(字节输出流)使用方法

    package demo1;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.OutputStream;
    
    public class OutputStreamDemo {
    	public static void main(String[] args) throws Exception {
    		// 创建字节流输出对象   如果存在就不会创建文件
    		// OutputStream os = new FileOutputStream("斗破苍穹.txt"); // 默认写入数据时会覆盖原来的数据
    		
    		// 向文件中追加内容
    		OutputStream os = new FileOutputStream("斗破苍穹.txt",true); // 追加
    		
    		// 第一种方式写入:public void write(int b)
    		os.write(97);
    		
    		// 第二种方式写入:public void write(byte[] b)
    		for (int i = 0; i < 3; i++) {
    			os.write("helloworld\n".getBytes());
    		}
    		
    		// 第三种方式写入:public void write(byte[] b,int off,int len)
    		os.write("helloworld".getBytes(), 0, 3);
    		
    		// 释放资源
    		// 关闭此文件输出流并释放与此流有关的所有系统资源
    		os.close();
    		
    		// 为什么一定要close呢?
    		// A.让流对象变成垃圾,这样就可以被垃圾回收机制回收了
    		// B.通知系统去释放该文件相关资源
    	}
    }
    
  • FileInputStream/FileOutputStream案例

    package demo1;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    
    /*
     * 1.实现对.txt结尾的文件进行复制
     * 2.实现对音频文件进行复制
     * 
     * 
     * 		文件复制的过程
     * 			1.先读取文件
     * 			2.再向指定目录输出文件
     */
    public class InToOutDemo {
    	public static void main(String[] args) throws Exception {
    		// 1.先读取文件
    		FileInputStream fis = new FileInputStream("斗破苍穹.txt");
    		
    		// 2.再向指定目录输出文件
    		FileOutputStream fos = new FileOutputStream("E:\\豆豆.txt");
    		
    		byte[] by = new byte[1024];
    		int num = 0;
    		
    		// 读取文件
    		while((num = fis.read(by)) != -1){
    			// 美都区一次向指定文件中写入一次
    			fos.write(by,0,num);
    			fos.flush(); // 刷新缓冲区 
    		}
    		
    		// 关闭流
    		fis.close();
    		fos.close();
    	}
    }
    
  • BufferedInputStream(字节缓冲输入流)使用方法

    package demo2;
    
    import java.io.BufferedInputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class bufferedInputStreamDemo {
    	public static void main(String[] args) throws Exception {
    		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("斗破苍穹.txt"));
    		
    		byte[] by = new byte[1024];
    		int num = 0;
    		
    		// 读取数据
    		while((num = bis.read(by)) != -1){
    			System.out.println(new String(by, 0, num));
    		}
    		
    		// 释放资源
    		bis.close();
    	}
    }
    
  • BufferedOutputStream(字节缓冲输出流)使用方法

    package demo2;
    
    import java.io.BufferedOutputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    
    public class bufferedOutputStreamDemo {
    	public static void main(String[] args) throws Exception {
    		// 复杂构造方法写法(属于脱裤子放屁)
    		FileOutputStream fos = new FileOutputStream("斗破苍穹.txt");
    		BufferedOutputStream bos = new BufferedOutputStream(fos);
    		
    		// 简单写法
    		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("斗破苍穹.txt"));
    		bos.write("hello world".getBytes());
    		
    		// 释放资源
    		bos.close();
    	}
    }
    
  • BufferedInputStream/BufferedOutputStream案例

    package demo2;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    
    /*
     * 需求:实现对音频文件复制
     * 
     * 		字节流四种方式复制文件
     * 			基本字节流一次读写一个字节:38549毫秒 
     * 			基本字节流一次读写一个字节数组:61毫秒
     * 			高效字节流一次读写一个字节:329毫秒
     * 			高效字节流一次读写一个字节数组:37毫秒
     */
    public class CopyMp4Demo {
    	public static void main(String[] args) throws Exception {
    		long startTime = System.currentTimeMillis();
    		method1("D:\\复活-乔任梁.mp3", "复活.mp3");
    		method2("D:\\复活-乔任梁.mp3", "复活.mp3");
    		method3("D:\\复活-乔任梁.mp3", "复活.mp3");
    		method4("D:\\复活-乔任梁.mp3", "复活.mp3");
    		long endTime = System.currentTimeMillis();
    		System.out.println("总耗时:"+(endTime - startTime));
    		
    	}
    	
    	// 基本字节流一次读写一个字节
    	public static void method1(String src,String dest) throws Exception{
    		FileInputStream fis = new FileInputStream(src);
    		FileOutputStream fos = new FileOutputStream(dest);
    		
    		int num = 0;
    		while((num = fis.read()) != -1){
    			fos.write(num);
    		}
    		
    		fis.close();
    		fos.close();
    	}
    	
    	// 基本字节流一次读写一个字节数组:
    	public static void method2(String src,String dest) throws Exception{
    		FileInputStream fis = new FileInputStream(src);
    		FileOutputStream fos = new FileOutputStream(dest);
    		
    		int num = 0;
    		byte[] by = new byte[1024];
    		
    		while((num = fis.read(by)) != -1){
    			fos.write(by,0,num);
    		}
    		
    		fis.close();
    		fos.close();
    	}
    	
    	// 高效字节流一次读写一个字节:
    	public static void method3(String src,String dest) throws Exception{
    		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
    		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
    		
    		int num = 0;
    		while((num = bis.read()) != -1){
    			bos.write(num);
    		}
    		
    		bis.close();
    		bos.close();
    	}
    	
    	// 高效字节流一次读写一个字节数组:
    	public static void method4(String src,String dest) throws Exception{
    		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
    		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
    		
    		int num = 0;
    		byte[] by = new byte[1024];
    		while((num = bis.read(by)) != -1){
    			bos.write(by,0,num);
    		}
    		
    		bis.close();
    		bos.close();
    	}
    }
    

2. 字符流

定义:是按照字符进行操作的,每次读取一个字符
字符流抽象基类:
	Reader(字符输入流)/Writer(字符输出流)
子类:
    (1).FileReader
       	public int read()
			读取单个字符
		public int read(char[] b)
			将字符读入数组
		public int read(char[] b,int off,int len)
			将字符读入数组的某一部分。

	(2).FileWriter
		public void write(String str)
         public void write(String str,int off,int len)
         public void write(int c)
         public void writer(char[] cbuf)
         public void write(char[] b,int off,int len)
         	指定 char 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。

	(3).BufferedReader:字符缓冲输入流
	(4).BufferedWriter:字符缓冲输出流
  • FileReader(字符输入流)使用方法

    package demo3;
    
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.Reader;
    
    public class ReaderDemo {
    	public static void main(String[] args) throws IOException {
    		Reader re = new FileReader("斗破苍穹.txt");
    		
    		int num = 0;
    		char[] ch = new char[1024];
    		
    		while((num = re.read(ch)) != -1){
    			
    			String str = new String(ch,0,num);
    			System.out.println(str);
    		}
    		re.close();
    	}
    }
    
  • FileWriter(字符输出流)使用方法

    package demo3;
    
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Writer;
    
    public class WriterDemo {
    	public static void main(String[] args) throws IOException {
    		
    		// 1.创建字符流输出流
    		Writer w = new FileWriter("斗破苍穹.txt");
    		w.write("第一章    世界之大,唯我独尊\n");
    		w.write("第二章    别bb了,赶紧上车\n");
    		
    		// 注意:如果不关闭流或者不刷新缓冲区,那么数据会一直存在内存中
    		w.flush();
    		w.close();		
    	}
    }
    
  • FileReader/FileWriter案例

    package demo3;
    
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Reader;
    import java.io.Writer;
    
    /*
     * 	字符流实现文件复制
     */
    public class ReadToWriteDemo {
    	public static void main(String[] args) throws IOException {
    		
    		// 1.利用字符流读取文件
    		Reader r = new FileReader("斗破苍穹.txt");
    		// 2.利用字符流写入文件
    		Writer w = new FileWriter("斗破2.txt");
    		
    		int num = 0;
    		// 读取 每次读取一个字符(2个字节)
    		while((num = r.read()) != -1){
    			// 写入指定文件
    			w.write(num);
    			w.flush(); // 刷新缓冲区
    		}
    		
    		r.close();
    		w.close();
    	}
    }
    
  • BufferedWriter/BufferedReader使用方法

    package demo4;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    /*
     * 利用你缓冲区市西南对文本文件的复制
     * 
     * 		BufferedWriter基本用法
     * 		BufferedReader基本用法
     * 			字符缓冲流复制文本文件
     * 
     * 特殊功能
     * 		BufferedWriter
     * 			void newLine()
     * 		BufferedReader
     * 			void readLine()
     * 		字符缓冲流特殊功能复制文本文件
     */
    public class BufferedWriterToBufferedReader {
    	public static void main(String[] args) throws IOException {
    		// 创建字符缓冲输入流  读取文件
    		BufferedReader br = new BufferedReader(new FileReader("斗破苍穹.txt"));
    		// 创建字符缓冲输入流  写入文件
    		BufferedWriter bw = new BufferedWriter(new FileWriter("新斗破苍穹.txt"));
    		
    		String str = null;
    		// newLine()  按行读取,依次读取一个文本行
    		// 读到末尾使返回null
    		while((str = br.readLine()) != null){
    			// 写入指定文件
    			bw.write(str);
    			// 换行
    			bw.newLine();
    			// 刷新缓存区
    			bw.flush();
    		}
    		// 释放资源
    		br.close();
    		bw.close();
    	}
    }
    

3. 字节流和字符流区别

字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓存区再操作文件。
下面以两个写文件的操作为主进行比较,字节和字符流操作完成后都不关闭输出流。

4. PrintStream流

(1)基本概念
	java.io.PrintStream类用于方便地打印各种格式的数据。
(2)常用的方法
	PrintStream(OutputStream out) 
		- 根据参数指定的引用构造对象
		- 其中OutputStream类是抽象类,实参需要传递子类的对象。
	void print(String s) 
		- 用于打印参数指定的字符串。
	void println(String x) 
		- 用于打印字符串并终止该行。

  • PrintStream流使用方法

    package demo5;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.PrintStream;
    
    public class TestPrintStream {
    	public static void main(String[] args){
    		// 1.构造PrintStream类型的对象与E:\\a.txt文件关联
    		PrintStream ps;
    		try {
    			ps = new PrintStream(new FileOutputStream("E:\\a.txt"));
    			// 2.向输出流中写入字符串内容
    			ps.print("hello");
    			System.out.println("写入数据成功!");
    			// 3.关闭流并释放有关的资源
    			ps.close();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
  • PrintStream流案例

    package demo5;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintStream;
    import java.util.Date;
    import java.text.SimpleDateFormat;
    
    /*
     * 练习:使用PrintStream和BufferedReader类来生成聊天记录
     * 要求:不断提示用户要输入发送的内容,判断是否为"bye",若是则结束
     * 输入:若不是"bye",则写入文件E:\\a.txt
     */
    
    public class TestChatMsg {
    	public static void main(String[] args) throws IOException{
    		
    		try {
    			// System.in 是InputStream类型的,代表键盘输入
    			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    			PrintStream ps = new PrintStream(new FileOutputStream("E:\\a.txt"));
    			boolean flg = true;
    			while(true){
    				// 1提示用户输入要发送的内容并记录到变量中
    				System.out.println("请"+(flg?"张三":"李四")+"输入要发送的内容:");
    				String str = br.readLine();
    				
    				// 2.判断用户输入的内容是否为"bye",若是则结束输入
    				if(str.equalsIgnoreCase("bye")){
    					System.out.println("聊天结束!");
    					break;
    				}
    				
    				// 获取当前的系统时间
    				Date d = new Date();
    				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    				String format = sdf.format(d);
    				
    				ps.println(format+(flg?"张三:":"李四:")+str);
    				flg = !flg;
    			}
    			
    			br.close();
    			ps.close();
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    

5. 内存操作流

操作字节数组
	ByteArrayInputStream
	ByteArrayOutputStream
操作字符数组
	CharArrayReader
	CharArrayWrite
操作字符串
	StringReader
	StringWriter
package demo7;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/*
 * 演示内存操作流
 * 		内存操作流一般用于处理临时文件,因为临时文件不需要保存,使用后就可以删除
 */
public class TestByteArray {
	public static void main(String[] args) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		
		bos.write("hello world".getBytes());
		
		byte[] bs = bos.toByteArray();
		
		ByteArrayInputStream bis = new ByteArrayInputStream(bs);
		
		int num = 0;
		while((num = bis.read()) != -1){
			System.out.println((char)num);
		}
		
		bos.close();
		bis.close();
	}
}

6. 序列化流

序列化流:指把Java对象转换为字节序列的过程为对象的
	ObjectOutputStream  
		writeObject(Object obj) 方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

反序列化流:指把字节序列恢复为Java对象的过程称为对象的反序列化
	ObjectInputStream  
		readObject()方法源输入流读取字节序列,再把它们反序列化成为一个对象,并将其返回。
		
使用transient关键字声明不需要序列化的成员变量

注意:只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常。
  • 实体对象

    package demo8;
    
    import java.io.Serializable;
    
    /*
     * Goods实体类
     */
    public class Goods implements Serializable{
    	private static final long serialVersionUID = -3195823797427025499L;
    	private int id;
    	private String name;
    	// transient 被他修饰的变量不参与序列化
    	private transient double price;
    	
    	public int getId() {
    		return id;
    	}
    
    	public Goods() {
    		super();
    	}
    	public Goods(int id, String name, double price) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.price = price;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public double getPrice() {
    		return price;
    	}
    	public void setPrice(double price) {
    		this.price = price;
    	}
    	
    	@Override
    	public String toString() {
    		return "Goods [id=" + id + ", name=" + name + ", price=" + price + "]";
    	}
    }
    
  • 实现类

    package demo8;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    /*
     * 演示序列化与反序列化
     * 		
     * 		序列化:ObjectOutputStream
     * 		反序列化:ObjectInputStream
     * 
     * 		需求:
     * 			1.创建Goods实体类
     * 			2.创建序列化流,将Goods对象序列化到项目object.txt文件中,做到数据持久化存储
     * 			3.利用反序列化将对象读取并打印出来
     * 
     * 		注意:使用序列化流被序列化的对象一定要实现序列化接口(Serializable)
     */
    public class TestObject {
    	public static void main(String[] args) throws Exception {
    		
    		Goods goods = new Goods(1001, "葵花宝典", 12.12);
    		
    		// 创建序列化流
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
    		
    		// 将Goods对象序列化到项目下object.txt中持久化存储
    		oos.writeObject(goods); // java.io.NotSerializableException:demo8.goods   对象类序列化异常
    		
    		oos.close();
    		
    		// 创建反序列化流
    		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
    		
    		// 读取文件中的对象信息 
    		Goods g = (Goods)ois.readObject();
    		ois.close();
    		System.out.println(g);
    	}
    }
    

7. 练习

  • 在D盘根目录下创建一个文件夹temp1,并创建300个文件,文件名从x000.txt到x299.txt,每个文件里写入一个10000以内的随机数。
  • 删除D:\temp1下奇数号的文件,如x001.txt、x003.txt…..x299.txt,留下偶数号的所有文件。
  • 读D:\temp1下所有的文件,计算其中所有数字的和,显示最终的计算结果。
  • 使用BufferReader和BufferWriter,读data.txt文件,并在所有行的数据前加上“行号+空格”后写到data1.txt文件中。
  • 创建一个元素是Book的ArrayList对象bookList1,并插入10个对象,数据随意。对这个ArrayList对象bookList1实现序列化,其中abs属性不参与序列化,存入文件D:\serial\book.dat。
posted @ 2020-12-13 20:33  今天捡到一百块钱  阅读(74)  评论(0)    收藏  举报