java IO流类结构图:
流:
流是一组有序的,有起点和终点的字节集合,是对数据传输的总称或抽象,即数据在两设备间的传输称为流。
IO流的分类:
根据处理数据类型的不同分为:字节流和字符流。
根据数据流向不同分为:输入流和输出流。
字节流和字符流:
字符流的由来:因为字符编码的不同,而有了对字符进行高效操作的流对象,本质就是基于字节流读取时,去查了指定的码表。
二者的区别:
读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读取多个字节。
处理对象不同:字节流能处理所有类型的数据(如图片、视频等),而字符流只能处理字符类型的数据。
输入流和输出流:(输入与输出是站在程序的角度而言的)
对输入流只能进行读操作, 对输出流只能进行写操作。
在java中,有很多流类,但我们常用的只有那么几个。
字节流:
1. FileInputStream与FileOutputStream:对文件系统中某个文件的读写操作。
一个文件拷贝的列子:
读取文件:
<span style="font-family:Microsoft YaHei;">public class FileCopyInputStream {
/**
* 读源文件,并将读取的结果保存到ArrayList中
*
* @param file
* 源文件
* @param isCut
* 是否剪切,true==剪切,false==复制
* @return 源文件内容,即ArrayList
*/
public ArrayList<DataBuffer> read(File file, boolean isCut) {
// 文件输入流
FileInputStream fis = null;
// ArrayList,用来保存读取的数据
ArrayList<DataBuffer> aList = new ArrayList<>();
try {
// 初始化文件输入流
fis = new FileInputStream(file);
// 文件容量大于0,就读
if (fis.available() > 0) {
// 读取的内容,为-1时表示文件读取结束
// 临时数组,用来保存read方法读到的数据
byte[] bytes = new byte[1024];
// read方法读到的数据的长度
int readLength = 0;
// 一直读到文件结尾
while ((readLength = fis.read(bytes)) != -1) {
// 创建一个DataBuffer对象,用来装载每一次read方法读出的数据
DataBuffer db = new DataBuffer();
// 读取的内容
db.setBytes(bytes);
// 读取的长度
db.setLength(readLength);
// 将DataBuffer对象添加到ArrayList中
aList.add(db);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 文件输入流不为空时,关闭输入流
if (fis != null) {
try {
// 关闭文件流
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 剪切,删除源文件
if (isCut) {
file.delete();
}
// 返回读取的内容
return aList;
}
}</span>
将读取的数据写入到文件:
<span style="font-family:Microsoft YaHei;">public class FileCopyOutputStream {
/**
* 将数据写入到一个新的文件中
*
* @param file
* 目标文件
* @param aList
* 数据源
*/
public void write(File file, ArrayList<DataBuffer> aList) {
// 文件输出流
FileOutputStream fos = null;
try {
// 初始化文件输出流
fos = new FileOutputStream(file);
// 迭代器,遍历ArrayList中的数据
Iterator<DataBuffer> iter = aList.iterator();
// 遍历
while (iter.hasNext()) {
// 取出数据
DataBuffer db = iter.next();
// 将数据写入到输出流
fos.write(db.getBytes(), 0, db.getLength());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 文件输出流不为空时,关闭输出流
if (fos != null) {
try {
// 清空文件输出流
fos.flush();
// 关闭文件输出流
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}</span>
DataBuffer辅助类:
<span style="font-family:Microsoft YaHei;">public class DataBuffer {
// byte数组,用来保存读出的数据
private byte[] bytes;
// 保存每次读出的数据长度
private int length;
/**
* 获取byte数组
*
* @return byte数组
*/
public byte[] getBytes() {
return bytes;
}
/**
* 设置byte数组的值
*
* @param bytes
* byte数组
*/
public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
/**
* 获取数据长度
*
* @return 数据长度
*/
public int getLength() {
return length;
}
/**
* 设置数据长度
*
* @param length
* 数据长度
*/
public void setLength(int length) {
this.length = length;
}
}</span>
测试类:
<span style="font-family:Microsoft YaHei;">public class FileCopyTest {
public static void main(String[] args) {
// 源文件
File src = new File("C:\\Users\\Administrator\\Desktop\\aa.txt");
// 目标文件目录
File desc = new File("src/com/wzd/filecopy/");
// 执行拷贝操作
copy(src, desc, false);
}
/**
* 拷贝
*
* @param src
* 源文件
* @param desc
* 目标目录
* @param isCut
* true,剪切 false,复制
*/
public static void copy(File src, File desc, boolean isCut) {
// 判断源文件是目录还是文件
if (src.isFile()) {
// 是文件
copyFile(src, desc, false);
} else {
// 是目录
coprDirectory(src, desc, false);
}
}
/**
* 拷贝目录
*
* @param src
* 源文件
* @param desc
* 目标目录
* @param isCut
* true,剪切 false,复制
*/
private static void coprDirectory(File src, File desc, boolean isCut) {
// 在目标目录下创建一个新文件对象
File newDirectory = new File(desc, src.getName());
// 创建目录
newDirectory.mkdirs();
// 拷贝该目录内的子文件
File[] files = src.listFiles();
// 遍历flies数组
for(File file : files) {
// 继续拷贝
copy(file, newDirectory, false);
}
// 剪切文件,则删除
if(isCut) {
src.delete();
}
}
/**
* 拷贝文件
*
* @param src
* 源文件
* @param desc
* 目标目录
* @param isCut
* true,剪切 false,复制
*/
private static void copyFile(File src, File desc, boolean isCut) {
// 在目标目录下创建一个新文件对象
File newFile = new File(desc, src.getName());
// 如果目标文件不存在,则创建一个目标文件
if (!newFile.exists()) {
try {
// 创建一个新文件
newFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
// 文件复制输入流
FileCopyInputStream input = new FileCopyInputStream();
// 读文件
ArrayList<DataBuffer> aList = input.read(src, false);
// 文件复制输出流
FileCopyOutputStream output = new FileCopyOutputStream();
// 写文件
output.write(newFile, aList);
}
}</span>
2. ObjectInputStream与ObjectOutputStream:对对象进行的读写操作,一般是将对象保存在文件中。
一个模拟数据库的列子:
此时需要注意:ObjectOutputStream的构造器会向输出流中写入头信息,因此在向文件中追加数据时,每一次都会写入头信息。
一种处理方式是自定义一个类,继承自ObjectOutputStream,重写writeStreamHeader()方法,该方法什么都不做,就不会再写入头信息。
自定义类,继承自ObjectOutputStream:
<span style="font-family:Microsoft YaHei;">public class DBObjectOutputStream extends ObjectOutputStream {
public DBObjectOutputStream() throws IOException, SecurityException {
}
public DBObjectOutputStream(OutputStream out) throws IOException {
super(out);
}
/**
* 写流的头信息
*/
@Override
protected void writeStreamHeader() throws IOException {
// 不实现该方法,则不会写入文件头信息
return;
}
}</span>
向文件中写入数据:<span style="font-family:Microsoft YaHei;">public class DBOutputStream<T> {
/**
* 向指定文件写入数据
*
* @param t
* 泛型对象
* @param file
* 目标文件
*/
public void write(T t, File file) {
// 创建一个FileOutputStream对象
FileOutputStream fos = null;
// 创建一个ObjectOutputStream对象
ObjectOutputStream oos = null;
try {
// 初始化FileOutputStream对象,append参数为true
fos = new FileOutputStream(file, true);
// 检测文件是否存在,若不存在,则创建文件
if (!file.exists()) {
// 创建新文件
file.createNewFile();
}
// 检测文件大小,确定是否是第一次操作文件
if (file.length() < 1) {
// 第一次操作文件。由于ObjectOutputStream每次都会向文件中写入文件头信息,
// 因此只在第一次操作文件时使用
oos = new ObjectOutputStream(fos);
} else {
// 不是第一次操作文件,则使用自己定义的DBObjectOutputStream
oos = new DBObjectOutputStream(fos);
}
// 向文件中写入对象
oos.writeObject(t);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 若oos不为空,则关闭该输出流
if (oos != null) {
// 刷新流缓冲
oos.flush();
// 关闭流
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}</span>
从文件中读取数据:
<span style="font-family:Microsoft YaHei;">public class DBInputStream {
/**
* 从指定文件中读取数据
*
* @param file
* 指定文件
* @return ArrayList<Object> 读取的结果
*/
public ArrayList<Object> read(File file) {
// 创建一个FileInputStream对象
FileInputStream fis = null;
// 创建一个ObjectInputStream对象
ObjectInputStream ois = null;
// 创建一个ArrayList<Object>
ArrayList<Object> aList = new ArrayList<>();
// 创建一个Object对象
Object object = null;
try {
// 判断文件是否存在
if (!file.exists()) {
return null;
}
// 初始化FileInputStream对象
fis = new FileInputStream(file);
// 初始化ObjectInputStream对象
ois = new ObjectInputStream(fis);
// 死循环读取文件内容,当抛出EOFException异常时,表示已经读至文件结尾
while (true) {
// 从文件里读对象
object = ois.readObject();
// 将对象放入ArrayList<Object>中
aList.add(object);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (EOFException e) {
// TODO: handle exception
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
// 若ois不为空,关闭输入流
if (ois != null) {
// 关闭流
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 返回ObjectInputStream读出的结果
return aList;
}
}</span>
dao类:
<span style="font-family:Microsoft YaHei;">public class StudnetInfoDAO {
// 接受控制台的输入信息
Scanner scanner = new Scanner(System.in);
// 保存学生对象的文件
File studentFile = new File("src/com/wzd/dbimitate/resource/student.obj");
// 保存课程对象的文件
File courseFile = new File("src/com/wzd/dbimitate/resource/course.obj");
// 保存成绩对象的文件
File gradeFile = new File("src/com/wzd/dbimitate/resource/grade.obj");
// ArrayList<Object>,保存DBInputStream读出的数据
ArrayList<Object> objects = new ArrayList<>();
// DBInputStream,从指定文件中读取数据
DBInputStream dbis = new DBInputStream();
/**
* 添加学生信息
*/
public void insertIntoStudent() {
// 提示输入格式
System.out.println("请输入学生信息(学生姓名,学生学号,学生电话):");
// 接收输入的信息
String str = scanner.next().trim();
// 分解字符串
String[] strs = str.split(",");
// 创建一个Student对象,并赋值
Student student = new Student();
student.setName(strs[0]);
student.setNumber(strs[1]);
student.setPhone(strs[2]);
// DBOutputStream<Student>输出流
DBOutputStream<Student> dbos = new DBOutputStream<>();
// 向学生文件中写数据
dbos.write(student, studentFile);
}
/**
* 添加课程信息
*/
public void insertIntoCourse() {
// 提示输入格式
System.out.println("请输入课程信息(课程名,课程号):");
// 接收输入的信息
String str = scanner.next().trim();
// 分解字符串
String[] strs = str.split(",");
// 创建一个Course对象,并赋值
Course course = new Course();
course.setName(strs[0]);
course.setNumber(strs[1]);
// DBOutputStream<Course>输出流
DBOutputStream<Course> dbos = new DBOutputStream<>();
// 向课程文件中写数据
dbos.write(course, courseFile);
}
/**
* 添加成绩信息
*/
public void insertIntoGrade() {
// 提示输入格式
System.out.println("请输入成绩信息(学生学号,课程号,成绩):");
// 接收输入的信息
String str = scanner.next().trim();
// 分解字符串
String[] strs = str.split(",");
// 创建一个Grade对象,并赋值
Grade grade = new Grade();
grade.setStudentNumber(strs[0]);
grade.setCourseNumber(strs[1]);
grade.setGrade(Double.parseDouble(strs[2]));
// DBOutputStream<Grade>输出流
DBOutputStream<Grade> dbos = new DBOutputStream<>();
// 向成绩文件中写数据
dbos.write(grade, gradeFile);
}
/**
* 通过学生姓名查询到所有的成绩信息
*/
public void selectGrade() {
// 提示输入信息
System.out.println("请输入学生姓名:");
// 接收输入的信息
String studentName = scanner.next().trim();
// 查询学生信息
Student student = selectStudentInfo(studentName);
// 查询成绩信息
ArrayList<Grade> grades = selectGradeInfo(student.getNumber());
// 创建一个课程对象数组
Course[] course = new Course[grades.size()];
// 遍历ArrayList<Grade>
for (int i = 0; i < grades.size(); i++) {
// 查询课程信息
course[i] = selectCourseInfo(grades.get(i).getCourseNumber());
}
// 打印头信息
System.out.println("学生姓名\t\t课程名\t\t分数");
// 遍历
for (int i = 0; i < grades.size(); i++) {
// 打印最终结果
System.out.println(student.getName() + "\t\t" + course[i].getName()
+ "\t\t" + grades.get(i).getGrade());
}
}
/**
* 通过学生姓名查询到所有的考试科目
*/
public void selectCourse() {
// 提示输入信息
System.out.println("请输入学生姓名:");
// 接收输入的信息
String studentName = scanner.next().trim();
// 查询学生信息
Student student = selectStudentInfo(studentName);
// 查询成绩信息
ArrayList<Grade> grades = selectGradeInfo(student.getNumber());
// 创建一个课程对象数组
Course[] course = new Course[grades.size()];
// 遍历ArrayList<Grade>
for (int i = 0; i < grades.size(); i++) {
// 查询课程信息
course[i] = selectCourseInfo(grades.get(i).getCourseNumber());
}
// 打印头信息
System.out.println("学生姓名\t\t考试科目");
// 遍历
for (int i = 0; i < course.length; i++) {
// 打印最终结果
System.out.println(student.getName() + "\t\t" + course[i].getName());
}
}
/**
* 通过学生姓名获取学生信息
*/
public void selectStudent() {
// 提示输入信息
System.out.println("请输入学生姓名:");
// 接收输入的信息
String studentName = scanner.next().trim();
// 查询学生信息
Student student = selectStudentInfo(studentName);
// 打印头信息
System.out.println("学生姓名\t\t学号\t\t电话号码");
// 打印最终结果
System.out.println(student.getName() + "\t\t" + student.getNumber()
+ "\t\t" + student.getPhone());
}
/**
* 通过课程号查询课程信息
*
* @param courseNumber
* 课程号
* @return 课程对象
*/
private Course selectCourseInfo(String courseNumber) {
// 创建一个Course对象
Course course = null;
// 从课程文件中读取数据
objects = dbis.read(courseFile);
// 遍历读取结果
for (Object object : objects) {
// 转换为Course对象
course = (Course) object;
// 若匹配
if (courseNumber.equals(course.getNumber())) {
// 返回课程对象
return course;
}
}
// 返回空
return null;
}
/**
* 通过学号查询成绩信息
*
* @param studentNumber
* 学号
* @return ArrayList<Grade> 成绩信息
*/
private ArrayList<Grade> selectGradeInfo(String studentNumber) {
// 创建一个Grade对象
Grade grade = null;
// 创建一个ArrayList<Grade>
ArrayList<Grade> grades = new ArrayList<>();
// 从成绩文件中读取数据
objects = dbis.read(gradeFile);
// 遍历读取结果
for (Object object : objects) {
// 转换为Grade对象
grade = (Grade) object;
// 若匹配
if (studentNumber.equals(grade.getStudentNumber())) {
// 将Grade对象添加到ArrayList<Grade>中
grades.add(grade);
}
}
// 返回结果
return grades;
}
/**
* 通过学生姓名查询学生信息
*
* @param studentName
* 学生姓名
* @return 学生对象
*/
private Student selectStudentInfo(String studentName) {
// 创建一个Student对象
Student student = new Student();
// 从学生文件中读取数据
objects = dbis.read(studentFile);
// 遍历读取结果
for (Object object : objects) {
// 转换为Student对象
student = (Student) object;
// 若匹配
if (studentName.equals(student.getName())) {
// 返回Student对象
return student;
}
}
// 返回空
return null;
}
}</span>
测试类:
<span style="font-family:Microsoft YaHei;">public class DBImitateTest {
public static void main(String[] args) {
// 创建一个StudnetInfoDAO对象
StudnetInfoDAO dao = new StudnetInfoDAO();
// 1.可以使用命令 存入学生成绩: 学生学号,学生姓名,学生成绩,科目,学生电话
// 添加学生信息
for (int i = 0; i < 5; i++) {
dao.insertIntoStudent();
}
// 添加课程信息
for (int i = 0; i < 4; i++) {
dao.insertIntoCourse();
}
// 添加成绩信息
for (int i = 0; i < 9; i++) {
dao.insertIntoGrade();
}
// 2.可以通过学生姓名查询到所有的成绩信息
dao.selectGrade();
// 3.可以通过学生姓名查询到所有的考试科目
dao.selectCourse();
// 4.可以通过学生姓名获取学生信息
dao.selectStudent();
}
}</span>
3. BufferedInputStream与BufferedOutputStream:缓冲流,里面包装其他的输入输出流,以提高读写的效率,其中BufferedInputStream支持mark和reset的使用。
示列如下:
<span style="font-family:Microsoft YaHei;">public class BufferedInputStreamTest {
public static void main(String[] args) {
//
try {
BufferedInputStream bis =
new BufferedInputStream(new FileInputStream("src/com/wzd/test/dec22/ReaderTest.java"));
// 判断该流是否可读
if(bis.available() > 0) {
// 判断该流是否可以被mark
if(bis.markSupported()) {
// mark一下,做一个标记,参数在此时没有使用,只要填写一个int值就行
// mark在一个流中只有一个有效,后面下的mark会覆盖掉前面的
System.out.print((char)bis.read());
System.out.println((char)bis.read());
bis.mark(0);
System.out.print((char)bis.read());
System.out.println((char)bis.read());
// 回到mark标记处
bis.reset();
System.out.print((char)bis.read());
System.out.println((char)bis.read());
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}</span>
4. DataInputStream与DataOutputStream:对java基本数据类型进行读写操作。
字符流:(字符流的操作与字节流基本类似)
1. FileReader与FileWriter:以字符的形式对文件进行读写操作。
2. BufferedReader与BufferedWriter:缓冲字符流。
3. InputStreamReader与OutputStreamWriter:
InputStreamReader是字节流通向字符流的桥梁:它使用charset读取字节并将其解码为字符。
OutputStreamWriter是字符流通向字节流的桥梁:可以使用指定的charset将要写入流中的字符编码成字节。