IO流
IO流-字符流
字节流:适合复制文件等,不适合读写文本文件
字符流:适合读写文本文件内容
FileReader(文件字符输入流)
作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中来。
构造器 | 说明 |
public FileReader(File file) | 创建字符输入流管道与源文件接通 |
public FileReader(String pathname) | 创建字符输入流管道与源文件接通 |
方法名称 | 说明 |
public int read() | 每次读取一个字符返回,如果发现没有数据可读会返回-1. |
public int read(char[] buffer) | 每次用一个字符数组去读取数据,返回字符数组读取了多少个字符,如果发现没有数据可读会返回-1. |
/*
字符流
字符输入流 Reader--FileReader
字符输出流 Writer--FileWriter
FileReader
创建对象
public FileReader(File file);
public FileReader(string pathname);
读数据
public int read() 一次读一个字符,返回字符对应的字,读到文件末尾返回-1
public int read(char[] buffer) 一次读多个字符,返回本次读取的有效字节个数,读到文件末尾返回-1
*/
public class Demo1 {
// public static void main(String[] args) throws Exception {
File file = new File("day09-code\\a-1.txt");
FileReader reader = new FileReader(file);
// FileReader reader = new FileReader("day09-code/a-1.txt");
// int read;
// while ((read = reader.read()) != -1) {
// System.out.println((char) read);
// }
// reader.close();
// }
public static void main(String[] args) throws Exception{
FileReader reader = new FileReader("day09-code/a-1.txt");
char[] chars = new char[100];
int len;
while ((len = reader.read(chars)) != -1) {
System.out.println( "读取长度:" + len +",内容:" + new String(chars,0,len));
}
reader.close();
}
}
1、使用文件字符输入流,有啥好处?
读取中文不会出现乱码问题
2、每次读取一个字符,每次读取多个字符的方法是啥?它们各有什么特点?
int read() | 每次读取一个字符返回,如果发现没有数据可读会返回-1. |
int read(char[] buffer) | 每次用一个字符数组去读取数据,返回字符数组读取了多少个字符,如果发现没有数据可读会返回-1. |
FileWriter(文件字符输出流)
作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去。
构造器 | 说明 |
public FileWriter(File file) | 创建字节输出流管道与源文件对象接通 |
public FileWriter(String filepath) | 创建字节输出流管道与源文件路径接通 |
public FileWriter(File file,boolean append) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileWriter(String filepath,boolean append) | 创建字节输出流管道与源文件路径接通,可追加数据 |
方法名称 | 说明 |
void write(int c) | 写一个字符 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
/*
字符流
字符输入流 Reader--FileReader
字符输出流 Writer--FileWriter
FileWriter
创建对象
public FileWriter(File file) 创建字节输出流管道与源文件对象接通
public FileWriter(String filepath) 创建字节输出流管道与源文件路径接通
public FileWriter(File file,boolean append) 创建字节输出流管道与源文件对象接通,可追加数据
public FileWriter(String filepath,boolean append) 创建字节输出流管道与源文件路径接通,可追加数据
写数据
void write(int c) 写一个字符
void write(String str) 写一个字符串
void write(String str, int off, int len) 写一个字符串的一部分
void write(char[] cbuf) 写入一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
注意:
字符输出流写出数据后,必须刷新流(flush)或者关闭流(close),写出的数据才会生效,原因是有缓存区
如果要换行, 需要输出"\r\n"字符串
*/
public class Demo2 {
public static void main(String[] args) throws Exception{
//创建一个写文件
FileWriter writer = new FileWriter("day09-code/a-2.txt");//true,之前的数据会保存,不会覆盖
//IO操作
//写入字符
writer.write(98);
//写入换行
writer.write("\r\n");
//写一个字符串
writer.write("学厨师,就来新东方");
writer.write("\r\n");
String str = "遇到新东方厨师就嫁了吧";
writer.write(str);
writer.write("\r\n");
//写字符串的一部分
writer.write("我喜欢zjl", 3, 3);
writer.write("\r\n");
//写入一个字符数组
char[] chars = {'蓝', '翔', '挖', '掘', '机'};
writer.write(chars);
writer.write("\r\n");
//写入字符数组的一部分
writer.write(chars, 2,3);
writer.write("\r\n");
//关闭流
writer.close();
}
}
字符输出流使用时的注意事项
1、如何创建字符输出流,如何实现写数据的追加操作?
public FileWriter(String filepath,boolean append)
2、字符输出流写数据的方法有哪些?
void write(int c) |
void write(String str) |
void write(String str, int off, int len) |
void write(char[] cbuf) |
void write(char[] cbuf, int off, int len) |
3、字符输出流写完数据后,必须注意什么?
刷新或者关闭流
IO流-缓冲流
字节缓冲流
字节缓冲流的作用
提高字节流读写数据的性能
字节缓冲输入流自带了8KB缓冲池;字节缓冲输出流也自带了8KB缓冲池。
构造器 | 说明 |
public BufferedInputStream(InputStream is) | 把低级的字节输入流包装成一个高级的缓冲字节输入流,从而提高读数据的性能 |
public BufferedOutputStream(OutputStream os) | 把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能 |
/*
字节缓冲流
自带8KB缓冲区, 减少了内存和硬盘之间的交互,从而提高原始流读、写数据的性能
字节缓冲流
public BufferedInputStream(InputStream is); 底层提供8K的缓冲区,减少和内存和硬盘的交互,真正干活的还是字节输入流
public BufferedOutputStream(OutputStream os);底层提供8K的缓冲区,减少和内存和硬盘的交互,真正干活的还是字节输出流
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("D:\\ITheima\\zm.png");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("D:\\ITheima\\zm1.png");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int len;
byte[] bytes = new byte[1024];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes,0,len);
}
bos.close();
fos.close();
bis.close();
fis.close();
}
}
案例:
//目标:观察原始流和缓冲流的性能。
public class Demo2 {
// 复制的视频路径
private static final String SRC_FILE = "D:/upload/other/duotai.wmv";
// 复制到哪个目的地
private static final String DEST_FILE = "D:/upload/other/duotai2.wmv";
public static void main(String[] args) throws IOException {
copyUseFileInputStream();//不使用缓冲流
copyUseBufferedInputStream();//使用缓冲流
}
private static void copyUseFileInputStream() throws IOException {
long startTime = System.currentTimeMillis();
//1. 创建文件字节输入流、输出流
FileInputStream fis = new FileInputStream(SRC_FILE);
FileOutputStream fos = new FileOutputStream(DEST_FILE);
//2. 使用输入流读取内容,使用输出流写出内容
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
//3. 关闭流
fos.close();
fis.close();
long endTime = System.currentTimeMillis();
System.out.println("不使用缓冲流复制耗时:" + (endTime - startTime) + "ms");
}
private static void copyUseBufferedInputStream() throws IOException {
long startTime = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(SRC_FILE));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(DEST_FILE));
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bis.close();
bos.close();
long endTime = System.currentTimeMillis();
System.out.println("使用缓冲流复制耗时:" + (endTime - startTime)+ "ms");
}
}
1、字节缓冲流有几种?
字节缓冲输入流:BufferedInputStream
字节缓冲输出流:BufferedOutputStream
2、字节缓冲流为什么提高了字节流读写数据的性能?
字节缓冲流自带8KB缓冲区可以提高原始字节流、字符流读写数据的性能
字符缓冲流
自带8K的字符缓冲池,可以提高字符输入流读取字符数据的性能
BufferedReader(字符缓冲输入流)
方法 | 说明 |
public BufferedReader(Reader r) | 把低级的字符输入流包装成字符缓冲输入流管道,从而提高字符输入流读字符数据的性能 |
public String readLine() | 读取一行数据返回,如果没有数据可读了,会返回null |
BufferedWriter(字符缓冲输出流)
方法 | 说明 |
public BufferedWriter(Writer r) | 把低级的字符输出流包装成一个高级的缓冲字符输出流,从而提高字符输出流写数据的性能 |
public void newLine() | 换行 |
/*
字符缓冲流
public BufferedReader(Reader r) 把低级的字符输入流包装成字符缓冲输入流管道,从而提高字符输入流读字符数据的性能
public String readLine() 读取一行数据返回,如果没有数据可读了,会返回null
public BufferedWriter(Writer r) 把低级的字符输出流包装成一个高级的缓冲字符输出流,从而提高字符输出流写数据的性能
public void newLine() 换行
*/
public class Demo3 {
//使用字符缓冲流,讲b-3.txt内容写到b-4.txt
public static void main(String[] args) throws Exception {
BufferedReader reader = new BufferedReader(new FileReader("day09-code/b-3.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("day09-code/b-4.txt"));
String str;
while ((str = reader.readLine()) != null) {
writer.write(str);
writer.newLine();
}
writer.close();
reader.close();
}
//字符缓冲输入流
// public static void main(String[] args) throws Exception{
// BufferedWriter writer = new BufferedWriter(new FileWriter("day09-code/b-4.txt"));
// writer.write("hello");
// writer.newLine();
// writer.write(22);
// writer.close();
// }
//字符缓冲输入流
// public static void main1(String[] args) throws Exception {
// BufferedReader reader = new BufferedReader(new FileReader("day09-code/b-3.txt"));
//
// String str;
// while ((str = reader.readLine()) != null) {
// System.out.println(str);
// }
//
// reader.close();
// }
}
1、字符缓冲流有几种,好处是什么?
public BufferedReader(Reader r)
public BufferedWriter(Writer w)
字符缓冲流自带8K缓冲区,可以提高原始字符流读写数据的性能
2、两种字符缓冲流新增了哪些方法?
BufferedReader,多了readLine()按照行读取的功能
BufferedWriter,多了newLine()换行的功能
IO流-转换流
引出问题:不同编码读取时会乱码
/*
问题描述:
当程序使用的编码跟操作文档的编码不一致时,会出现乱码问题
此时可以使用字符转换流
它的思路是: 先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了
字符输入转换流
public InputStreamReader(InputStream is ,String charset) 把原始的字节输入流,按照指定字符集编码转成字符输入流
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
//需求: 从c-1.txt读取内容
//1.创建文件字节输入流
FileInputStream fis = new FileInputStream("day09-code/c-1.txt");
//2.创建一个字符输入转化流,并指定字符集
InputStreamReader isr = new InputStreamReader(fis,"GBK");
//3.将字符输入转化流包装为字符输出缓冲流
BufferedReader reader = new BufferedReader(isr);
//4.读取一行
String line = reader.readLine();
System.out.println(line);
//5.关闭
reader.close();
isr.close();
fis.close();
}
}
字符输入转换流
InputStreamReader(字符输入转换流)
- 解决不同编码时,字符流读取文本内容乱码的问题。
- 思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了
构造器 | 说明 |
public InputStreamReader(InputStream is ,String charset) | 把原始的字节输入流,按照指定字符集编码转成字符输入流 |
/*
问题描述:
当程序使用的编码跟操作文档的编码不一致时,会出现乱码问题
此时可以使用字符转换流
它的思路是: 获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了。
字符输出转换流
public OutputStreamWriter(OutputStream os,String charset) 可以把原始的字节输出流,按照指定编码转换成字符输出流
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
//需求: 向c-2.txt写出内容, 字符集为GBK
FileOutputStream fos = new FileOutputStream("day09-code/c-2.txt");
OutputStreamWriter ows = new OutputStreamWriter(fos,"GBK");
BufferedWriter writer = new BufferedWriter(ows);
writer.write("月薪过万");
writer.newLine();
writer.write("到黑马程序员");
writer.close();
ows.close();
fos.close();
}
}
1、字符输入转换流InputStreamReader的作用是啥?
可以解决字符流读取不同编码乱码的问题
public InputStreamReader(InputStream is,String charset):
可以指定编码把原始字节流转换成字符流,如此字符流中的字符不乱码
字符输出转换流
OutputStreamWriter字符输出转换流
- 作用:可以控制写出去的字符使用什么字符集编码。
- 获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了。
构造器 | 说明 |
public OutputStreamWriter(OutputStream os,String charset) | 可以把原始的字节输出流,按照指定编码转换成字符输出流 |
/*
JDK11开始,可直接使用字符流构造指定字符集,构造第二个参数,通过Charset的静态方法forName 指定字符集
public FileReader(String fileName, Charset charset) throws IOException
public FileWriter(String fileName, Charset charset) throws IOException
*/
public class Demo3 {
public static void main(String[] args) throws Exception {
BufferedWriter writer = new BufferedWriter(new FileWriter("day09-code/c-3.txt"));
}
}
1、字符输出转换流OutputStreamWriter的作用?
public OutputStreamWriter(OutputStream os,String charset)
可以指定编码把字节输出流转换成字符输出流,从而可以指定写出去的字符编码!
IO流-打印流
PrintStream/PrintWriter(打印流)
作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。
PrintStream和PrintWriter的区别
- 打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
- PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
- PrintWriter继承自字符输出流Writer,因此支持写字符数据出去。
import java.io.PrintStream;
/*
PrintStream/PrintWriter(打印流)
打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。
构造器
public PrintStream(OutputStream/File/String) 打印流直接通向字节输出流/文件/文件路径
public PrintWriter(OutputStream/Writer/File/String) 打印流直接通向字符输出流/文件/文件路径
常用方法
public void println(Xxx xx) 打印任意类型的数据出去
public void write(int/byte[]/byte[]一部分) 可以支持写字节数据出去(PrintStream)
public void write(int/String/char[]/..) 可以支持写字符数据出去(PrintWriter)
PrintStream和PrintWriter的区别
打印数据的功能上是一样的:都是使用方便,性能高效
PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法
PrintWriter继承自字符输出流Writer,因此支持写字符数据出去
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
//PrintStream
PrintStream printStream = new PrintStream("day09-code/d-1.txt");
printStream.println("遇到新东方厨师");
printStream.println(222);
printStream.println(true);
printStream.close();
//PrintWriter
}
}
IO流-数据流
DataOutputStream(数据输出流)
允许把数据和其类型一并写出去。
DataInputStream(数据输入流)
用于读取数据输出流写出去的数据。
/*
数据流
为了保存数据而用的一种数据流, 数据流输出的数据不是给人看的,是为了保存
数据输出流输出的数据,只能通过数据输入流读回程序
DataOutputStream(数据输出流)
public DataOutputStream(OutputStream out) 创建新数据输出流包装基础的字节输出流
public final void writeByte(int v) throws IOException 将byte类型的数据写入基础的字节输出流
public final void writeInt(int v) throws IOException 将int类型的数据写入基础的字节输出流
public final void writeDouble(Double v) throws IOException 将double类型的数据写入基础的字节输出流
public final void writeUTF(String str) throws IOException 将字符串数据以UTF-8编码成字节写入基础的字节输出流
public void write(int/byte[]/byte[]一部分) 支持写字节数据出去
DataInputStream(数据输入流)
public DataInputStream(InputStream is) 创建新数据输入流包装基础的字节输入流
Public final byte readByte() throws IOException 读取字节数据返回
public final int readInt() throws IOException 读取int类型的数据返回
public final double readDouble() throws IOException 读取double类型的数据返回
public final String readUTF() throws IOException 读取字符串数(UTF-8)据返回
public int readInt()/read(byte[]) 支持读字节数据进来
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
//创建数据输出流,写出数据到文件
DataOutputStream dos = new DataOutputStream(new FileOutputStream("day09-code/e-1.txt"));
dos.writeByte(100);
dos.writeInt(100);
dos.writeDouble(88.88);
dos.writeUTF("英文");
dos.close();
//创建数据输入流,读取数据到程序
//什么顺序写的,就什么顺序往外拿
DataInputStream dis = new DataInputStream(new FileInputStream("day09-code/e-1.txt"));
System.out.println(dis.readByte());
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readUTF());
dis.close();
}
}
IO流-序列化流
对象序列化:把Java对象写入到文件中去
对象反序列化:把文件里的Java对象读出来
ObjectOutputStream(对象字节输出流)
可以把Java对象进行序列化:把Java对象存入到文件中去。
方法 | 说明 |
public ObjectOutputStream(OutputStream out) | 创建对象字节输出流,包装基础的字节输出流 |
public final void writeObject(Object o) throws IOException | 把对象写出去 |
ObjectInputStream(对象字节输入流)
可以把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中来。
方法 | 说明 |
public ObjectInputStream(InputStream is) | 创建对象字节输入流,包装基础的字节输入流 |
public final Object readObject() | 把存储在文件中的Java对象读出来 |
注意:对象如果要参与序列化,必须实现序列化接口(java.io.Serializable)
/*
对象存储
用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可
注意:ArrayList集合已经实现了序列化接口!
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
//0. 准备一个Student对象的集合
List<Student> students = new ArrayList<>();
students.add(new Student("张三", 18));
students.add(new Student("李四", 19));
students.add(new Student("王五", 20));
//1. 序列化(f-1.txt)
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day09-code/f-1.txt"));
//写入文件
oos.writeObject(students);
//关闭
oos.close();
//2. 反序列化(f-1.txt)
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day09-code/f-1.txt"));
List<Student> obj = (List<Student>) ois.readObject();
System.out.println(obj);
ois.close();
}
}
1、对象序列化的含义是什么?怎么实现对象序列化?需要注意什么?
把对象数据存入到文件中去对象字节输出流
ObjectOutputStreampublic void write
Object(Object obj)对象必须实现序列化接口
2、 对象反序列化的含义是什么?怎么实现对象反序列化?
把对象数据存入到文件中去。
对象字节输入流ObjectInputStream
public Object readObject()
释放资源的方式
try-catch-finally
- finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止。
- 作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)。
/*
try-catch-finally
try {
...
} catch (IOException e) {
...
}finally{
...
}
注意:
1. finally代码块写在try-catch的最后面
2. 无论try的代码是否出现问题, 最后一定会执行finally中的代码,除非JVM终止
*/
public class Demo1 {
public static void main(String[] args) throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 创建文件字节输入流、输出流
fis = new FileInputStream("D:/upload/other/duotai1.wmv");
fos = new FileOutputStream("D:/upload/other/duotai2.wmv");
//2. 使用输入流读取内容,使用输出流写出内容
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3. 释放资源
try {
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
try-with-resource (JDK7开始提供)
- () 中只能放置资源,否则报错
- 什么是资源呢?
- 资源一般指的是最终实现了AutoCloseable接口。
/*
try-with-resource
JDK7开始提供的释放资源方式,编码更简洁
书写格式
try(资源对象1,资源对象2..){
...
}catch(Exception e){
处理异常的代码
}
注意:
try后面小括号中只能定义资源对象,最终底层会帮我们释放这些对象
*/
public class Demo2 {
public static void main(String[] args) throws IOException {
try (//1. 创建文件字节输入流、输出流
FileInputStream fis = new FileInputStream("D:/upload/other/duotai1.wmv");
FileOutputStream fos = new FileOutputStream("D:/upload/other/duotai2.wmv");
){
//2. 使用输入流读取内容,使用输出流写出内容
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
相关题目
/*
finally相关面试题
1. try-catch-finally 中哪个部分可以省略?
2. final,finally和finalize的区别
3. 关于finally在try-catch中的执行顺序
当try和catch代码块中有return语句时,finally仍然会被执行
执行try或catch代码块中的return语句之前,都会先执行finally语句
finally代码块中的return语句一定会执行
无论在finally代码块中是否有返回值,返回值都不会改变,仍然是执行finally代码块之前的值
*/
public class Demo3 {
public static void main(String[] args) {
//System.out.println(m1());
//System.out.println(m2());
//System.out.println(m3());
System.out.println(m4());
}
//当try和catch代码块中有return语句时,finally仍然会被执行
//执行try或catch代码块中的return语句之前,都会先执行finally语句
public static int m1() {
try {
return 1;
} finally {
System.out.println("执行finally模块");
}
}
public static int m2() {
try {
int a = 8 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.println("执行finally模块");
}
}
//finally代码块中的return语句一定会执行
public static int m3() {
try {
int a = 8 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.println("执行finally模块");
return 0;
}
}
//无论在finally代码块中是否有返回值,返回值都不会改变,仍然是执行finally代码块之前的值
public static int m4() {
int result = 0;
try {
return result;
} finally {
System.out.println("执行finally模块");
result = 1;
}
}
}
IO框架
什么是框架?
- 解决某类问题,编写的一套类、接口等,可以理解成一个半成品。
- 好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
- 框架的形式:一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去。
什么是IO框架?
封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等。
Commons-io
Commons-io是apache开源基金组织提供的一组有关IO操作的小框架,目的是提高IO流的开发效率。