IO流用来处理设备之间的数据传输。
数据传输:源结点——目标结点(他们之间需要一条通道)
输入流 输出流
源文件-------程序-------目标文件
(对于程序而言:当程序读文件时,程序作为目标结点,通道为输入流,当程序写文件时,程序作为源节点,通道为输出流,输入/输出流是一个相对的概念)
流的分类:
输入流 输出流
字节流 InputStream OutputStream
字符流 Reader Writer
字节流:二进制字节为单位进行传输(8位)
字符流:Unicode字符(16位)进行传输
处理文件步骤:
1.创建流对象,打开指定文件,建立通道
2.处理文件
3.关闭流
读/写文件的详细步骤:
1.声明流对象引用并赋值为null
2.try-catch-finally在finally中执行关流操作
3.处理异常
4.在try中创建流对象,并赋值给引用,建立通道,利用read方法读数据,设置循环控制读完整个文件,在循环中,先处理已经读到的数据,再继续读剩下的数据。(-1为文件结束标识符)
程序分享:
/** * 输入/输出是对于程序而言,当用程序读取一个文件的时候,程序是输入点(具有read方法) * 当用程序写一个文件的时候,程序作为输出点(具有writer方法) */
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/* * 写一个类FileCopy.java,在main方法中把FileCopy.java文件复制为 * FileCopy.java.bak,确认复制的正确性 */
public class FileCopy {
public static void main(String[] args) {
//声明流对象
FileReader fReader = null;
FileWriter fWriter = null;
try {
//创建流对象,建立通道
fReader = new FileReader("./src/com/atguigu/javase/iotest/FileCopy.java");
fWriter = new FileWriter("FileCopy.java.bak");
//利用read方法读取字符
int ch = fReader.read();
//fWriter.write(ch); 若是在外边写,里面先读再写,会多写一个字节
//先对ch进行判断若不是-1再写
while(ch != -1) {
//先写读到的字节,先对读取到字符进行处理
fWriter.write(ch);
//继续读剩下的字节
ch = fReader.read();
}
} catch (Exception e) {
//处理异常
e.printStackTrace();
} finally {
//finally中执行关流操作,并处理异常
if(fReader != null) {
try {
fReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fWriter != null) {
try {
fWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.atguigu.javase.review;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.junit.Test;
public class IOTest {
/** * 1.用于处理文本文件 * 所有的read都是以-1为结束标志 * 字符输入流 * FileReader int read(); 从输入流中读一个字符,用非法字符-1表示流结束 * int read(char[] buf); 从输入流中读一些字符,读入到数组中,返回值为实际读了多少字符 ,这两个read都是以-1为结束标志 * 字符输出流 * FileWriter void write(int ch); 把参数中的字符写入到输出流中 * void write(char[] buf); 把参数中的字符数组全部写入到输出流中 * void write(char[] buf, offset, count); 把参数中字符数组的一部分写入输出流中,offset代表偏移,count代表要写入的字符数 * void flush(); 把数据刷新到硬盘上 */
/** *利用FileReader和FileWriter实现对某一文件的复制 */
@Test
public void Exer1() {
//声明输入流引用,并赋值为null
FileReader fr = null;
//声明输出流引用,并赋值为null
FileWriter fw = null;
try {
//创建输入流对象
fr = new FileReader("测试文件");
//创建输出流对象
fw = new FileWriter("测试文件copy");
//利用输入流读取一个字符
int ch = fr.read();
//设置循环读整个文件,结束标志为read()返回值为-1
while(ch != -1) {
//先处理读到的字符
fw.write(ch);
//继续读剩下的字符
ch = fr.read();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关流操作,分开关
if(fr != null) {
try {
fr.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(fw != null) {
try {
fw.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * copy当前文件,注意创建FileReader对象参数 */
@Test
public void Exer2() {
//声明输入流引用,并赋值为null
FileReader fr = null;
//声明输出流引用,并赋值为null
FileWriter fw = null;
try {
//创建输入流对象
fr = new FileReader("./src/com/atguigu/javase/review/IOTest.java");
//创建输出流对象
fw = new FileWriter("IOTest.java");
//利用输入流读取一个字符
int ch = fr.read();
//设置循环读整个文件,结束标志为read()返回值为-1
while(ch != -1) {
//先处理读到的字符
fw.write(ch);
//继续读剩下的字符
ch = fr.read();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关流操作,分开关
if(fr != null) {
try {
fr.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(fw != null) {
try {
fw.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * 设置缓冲区实现copy */
@Test
public void Exer3() {
//声明输入流引用,并赋值为null
FileReader fr = null;
//声明输出流引用,并赋值为null
FileWriter fw = null;
try {
//创建输入流对象
fr = new FileReader("测试文件");
//自定义创建一个缓冲区
char[] buf = new char[8192];
//创建输出流对象
fw = new FileWriter("测试文件copy2");
//利用输入流读取一个字符
int realCount = fr.read(buf);
//设置循环读整个文件,结束标志为read()返回值为-1
while(realCount != -1) {
//先处理读到的字符
fw.write(buf, 0, realCount);
//继续读剩下的字符
realCount = fr.read(buf);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关流操作,分开关,也可以不判断if
if(fr != null) {
try {
fr.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(fw != null) {
try {
fw.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * 2.用于处理字节文件,它与上边的FileReader/FileWriter都是节点流 * FileInputStream * int read(); 从输入流中读取一个字节0~255,用-1表示非法数据 * int read(byte[] buf); 从流中读取一些字节,读到byte[]数组中,返回值表示实际读到的字节数 * FileOutputStream * void write(int byte); * void write(byte[] buf); * void write(byte[] buf, offset, count); * 对于缓冲区的使用和FileReader/FileWriter类似 * 可以用来copy一个图片/电影/文本文件 * 这里直接一个较高级流来示例 */
@Test
public void Exer5() {
//声明基础输入流,较高级流
FileInputStream fis = null;
BufferedInputStream bis = null;
//声明基础输出流,较高级流
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
//创建输入流对象
fis = new FileInputStream("狼.jpg");
bis = new BufferedInputStream(fis);
//创建输出流对象
fos = new FileOutputStream("狼2copy.jpg");
bos = new BufferedOutputStream(fos);
//利用输入流对象读数据
int ch = bis.read();
while(ch != -1) {
bos.write(ch);
ch = bis.read();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bis != null) {
try {
bis.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(bos != null) {
try {
bos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * Buffered开头(缓冲流),属于处理流,把节点流或其他流包装起来,用以获取更大的处理能力 * 一般常用的形式,将低级流包装成高级流 * 利用较高级流,带有缓冲区的流Buffered开头的 * BufferedReader * String readLine(); 读一行字符,以String型返回 * BufferedWriter * void writer(String line); 写入一行字符 * void newLine(); 写一个空行 * BufferedInputStream * BufferedOutputStream * 实现copy */
@Test
public void Exer4() {
//声明输入流引用,并用高级流包装
FileReader fr = null;
BufferedReader bfr = null;
//声明输出流引用,并用高级流包装
FileWriter fw = null;
BufferedWriter bfw = null;
try {
//创建输入流对象,并包装
fr = new FileReader("测试文件");
bfr = new BufferedReader(fr);
//创建输出流对象,并包装
fw = new FileWriter("测试文件copy3");
bfw = new BufferedWriter(fw);
//读取一行字符
String string = bfr.readLine();
while(string != null) {
bfw.write(string);
bfw.newLine();
string = bfr.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bfr != null) {
try {
bfr.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(bfw != null) {
try {
bfw.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * Object开头(对象流),属于处理流,最高级流,可以直接写入基本数据类型和对象 * 只处理字节流 * ObjectOutputStream * //这样一次写一个基本数据类型,还是比较好的 * void writeXxx(xxx value); //Xxx代表8种基本数据类型 writeInt() writefloat() * void writeUTF(String str); //把字符串按照UTF-8方式进行编码,编码:把字符串变成字节数组的过程 * ObjectInputSteam * xxx readXxx(); //从流中把8种基本数据类型读出来 * String readUTF(); //把流中的字节数组以UTF8编码形式解码 * 对象流读出一个数据(整体性) * 对象流和基础的输入输出流区别在于它更强大,一次可以读任意基本数据类型的数据,并且可以读对象,(对于文件中一个数据的读出意思重大,而不是读出字节再拼接) * 而对于基础的输入输出流它只能读一个字节或者一些字节。 * 对于对象流的一个关键点:必须怎么写,怎么读,若是读写不一致,不能正确读出 */
@Test
public void Exer6() {
/** * 对于创建文件的对象流和读文件的对象流不能再一个程序中否则会报错 */
//声明输出流引用,并包装
FileOutputStream fos = null;
BufferedOutputStream bos = null;
ObjectOutputStream oos = null;
try {
//先利用输出流写一个对象流文件
fos = new FileOutputStream("对象流文件");
bos = new BufferedOutputStream(fos);
oos = new ObjectOutputStream(bos);
//写一个包含int型[0,20)的文件
for(int i = 0; i < 20; i++) {
//生成的是二进制文件
oos.writeInt(i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关流,只需要关高级流就好
if(oos != null) {
try {
oos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
@Test
public void Exer7() {
//这一也可以直接将其包装成Object流
//声明输入流引用,并包装
FileInputStream fis = null;
BufferedInputStream bis = null;
ObjectInputStream ois = null;
try {
//利用输入流阅读这个文件并输出在控制台上
fis = new FileInputStream("对象流文件");
bis = new BufferedInputStream(fis);
ois = new ObjectInputStream(bis);
//利用输入流读取这个文件,并查看
for(int i = 0; i < 20; i++) {
//每次读的是一个int型变量,而不是一个字节
//int value = ois.read(); //不能正确读出
//必须怎么写,怎么读,才可以正确实现
int value = ois.readInt();
System.out.print(value + " ");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关流,只需要关高级流就好
if(ois != null) {
try {
ois.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * 反例,当利用输出对象流写一个文件,并用输入对象流读这个文件时,这两个流的对象创建不能一起创建,必须等文件写好以后 * 输入流对象才可以创建,进行阅读,否则报异常,不建议下边的写法 * 当需要利用输出流写文件,利用输入流读文件时,这两个过程写在不同的类中 * 如下程序所示 */
@Test
public void Exer8() {
//声明输出流引用,并包装
FileOutputStream fos = null;
BufferedOutputStream bos = null;
ObjectOutputStream oos = null;
try {
//先利用输出流写一个对象流文件
fos = new FileOutputStream("对象流文件1");
bos = new BufferedOutputStream(fos);
oos = new ObjectOutputStream(bos);
//写一个包含int型[0,20)的文件
for(int i = 0; i < 20; i++) {
//生成的是二进制文件
oos.writeInt(i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关流,只需要关高级流就好
if(oos != null) {
try {
oos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
//声明输入流引用,并包装
FileInputStream fis = null;
BufferedInputStream bis = null;
ObjectInputStream ois = null;
try {
//读文件
//利用输入流阅读这个文件并输出在控制台上
fis = new FileInputStream("对象流文件");
bis = new BufferedInputStream(fis);
ois = new ObjectInputStream(bis);
for(int i = 0; i < 20; i++) {
//每次读的是一个int型变量,而不是一个字节
//注意:对象流文件必须怎么写入的就怎么读出,否则出错
//int value = ois.read(); 不能正确读出
int value = ois.readInt(); //正确读出
System.out.print(value + " ");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(ois != null) {
try {
ois.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * 对象流 * 序列化,和反序列化对于数据传输意义重大,现将对象序列化,然后写入到输出流中,在其他地方,利用输入流,将对象解析出来 * Java对象----->二进制流() 序列化 (注:数组,集合,单个对象都是对象) * 二进制----->Java对象 反序列化 * * 可以序列化单个对象,可以序列化数组,可以序列化集合 * 序列化要求:单个对象实现了Seriliable接口(标记型接口) * 注意事项: * 静态成员不能被序列化 * transient修饰的属性也不能被序列化 * 序列化和反序列化的类版本必须一致,否则出错 * * 可以利用对象流对一些集合,数组,String,基本数据类型进行传输。 */
@Test
/* * 序列化 */
public void Exer9() {
/** * 可以直接包装成Object流 */
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream("学生信息文件");
oos = new ObjectOutputStream(fos);
/* // 1.写单个学生对象 Student obj = new Student("张三", 23, "男"); oos.writeObject(obj); */
/* //2.同样的可以将学生对象保存在学生数组中,然后直接将学生数组序列化,(一个学生数组相当于一个学生数组对象) Student[] stus = { (new Student("张三", 23, "男")), (new Student("李四", 24, "男")), (new Student("小红", 23, "女")), (new Student("赵六", 26, "男")), (new Student("马艳", 21, "女"))}; oos.writeObject(stus); */
//3.将学生对象保存在集合中,直接将学生集合对象序列化
// 同样的学生对象若保存在set集合中需要重写equals和hashCode方法
// 保存在TreeSet中要实现Comparable接口,对于对象保存在集合中的要求不变
Set<Student> set = new HashSet<Student>();
set.add(new Student("张三", 23, "男"));
set.add(new Student("李四", 24, "男"));
set.add(new Student("小红", 23, "女"));
set.add(new Student("赵六", 26, "男"));
set.add(new Student("马艳", 21, "女"));
oos.writeObject(set);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(oos != null) {
try {
oos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
@SuppressWarnings("unchecked")
@Test
public void Exer10() {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("学生信息文件");
ois = new ObjectInputStream(fis);
/* //1.读取单个学生对象,注意要进行强转,返回值就是一个学生对象 Student stu = (Student)(ois.readObject()); System.out.println(stu); */
/* //2.读取一个学生数组对象,怎么写怎么读,返回值就是一个数组对象,直接将返回值强转为目标对象 Student[] stus = (Student[])(ois.readObject()); for (Student student : stus) { System.out.println(student); } */
//3.读取一个学生集合对象,返回值是一个集合,强转为集合
Set<Student> set = (Set<Student>)(ois.readObject());
Iterator<Student> iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(ois != null) {
try {
ois.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * 转换流也属于处理流,当你需要按照某一指定的编码形式写文件是,需要用到转换流,例如以UTF8编码形式写文件 * 若是不指定编码形式是以本地编码形式进行编码的,即GBK * 若是不指定编码形式写出的文件,在eclipse中可以认出来(默认),若是用UTF8生成的文件认不出来 * 字节流,看不懂的,全是数字 * 只能从字节流--->字符流 * InputStreamReader * OutputStreamWriter */
@Test
public void Exer11() {
//声明输出流
FileOutputStream fos = null;
//转化流包装
OutputStreamWriter osw = null;
//升级包装
BufferedWriter bfw = null;
try {
fos = new FileOutputStream("UTF8测试文件");
//第二个参数指定编码类型,用什么编码就要用什么类型解码否则乱码
osw = new OutputStreamWriter(fos, "UTF8");
bfw = new BufferedWriter(osw);
//写文件
bfw.write("转换流可以指定编码方式");
bfw.newLine();
bfw.write("Eclipse使用的是默认编码方式");
bfw.newLine();
bfw.write("若是不指定,使用的是默认编码方式");
bfw.newLine();
bfw.write("使用UTF8写的文件在Eclipse乱码");
bfw.newLine();
bfw.write("字节流咱们看不懂,所有内容都是用数字表示的");
bfw.newLine();
bfw.write("用什么形式编码的就得用什么形式解码否则乱码");
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bfw != null) {
try {
bfw.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
@Test
public void Exer12() {
/* * 对上边的UTF8测试文件进行解析并输出到控制台上 */
FileInputStream fis = null;
InputStreamReader isr = null;
BufferedReader bfr = null;
try {
fis = new FileInputStream("UTF8测试文件");
//用什么编码就得用什么解码
isr = new InputStreamReader(fis, "UTF8");
bfr = new BufferedReader(isr);
//每次阅读一行,返回值为一个字符串
String str = bfr.readLine();
while(str != null) {
//处理已经阅读到的
System.out.println(str);
//继续阅读剩余的
str = bfr.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bfr != null) {
try {
bfr.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * 打印流也属于处理流 * PrintStream * PrintWriter * 从键盘获取20个数字,并保存在"数字文件",若是不是数字给出提示 * try-catch相当于另一种分支 */
@Test
public void Exer13() {
//输入流引用
InputStreamReader isr = null;
BufferedReader bfr = null;
//输出流引用
//FileOutputStream fos = null;
OutputStreamWriter osw = null;
BufferedWriter bfw = null;
try {
//创建输入流对象
isr = new InputStreamReader(System.in);
bfr = new BufferedReader(isr);
//创建输出流对象
osw = new OutputStreamWriter(System.out);
bfw = new BufferedWriter(osw);
//实现从键盘输入,从控制台输出
while(true) {
String str = bfr.readLine();
//当从键盘接受到"quit"时终止循环
if(str.equals("quit")) {
break;
}
bfw.write(str);
bfw.newLine();
//写完以后需要刷下
bfw.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bfr != null) {
try {
bfr.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(bfw != null) {
try {
bfw.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
@Test
public void Exer14() {
//输入流引用
InputStreamReader isr = null;
BufferedReader bfr = null;
//输出流引用
FileOutputStream fos = null;
OutputStreamWriter osw = null;
BufferedWriter bfw = null;
try {
//创建输入流对象
isr = new InputStreamReader(System.in);
bfr = new BufferedReader(isr);
//创建输出流对象
fos = new FileOutputStream("数字文件");
osw = new OutputStreamWriter(fos);
bfw = new BufferedWriter(osw);
//实现从键盘输入,从控制台输出
for(int i = 0; i < 20; i++) {
String str = bfr.readLine();
//try-catch相当于一种分支,每次有异常的时候,捕获处理,执行catch,程序继续执行
//若是在循环中程序执行下一次循环
try {
//每次都尝试判断一下,是不是数字,若不是执行catch
//并且继续执行下一次循环
Double.parseDouble(str);
bfw.write(str);
bfw.newLine();
} catch (Exception e) {
System.out.println("输入的不是数字!");
i--;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bfr != null) {
try {
bfr.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(bfw != null) {
try {
bfw.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/** * 数据流也属于处理流 * DataInputStream * DataOutputStream */
}