第十四章、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。