IO流
流的概念:
流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源设备的流,这个数据源设备可以是文件、内存或网络连接。流是含有流质具有方向的抽象管道。
流的特点:
流有两个最基本的特性:一是它含有流质,而是它有方向
对流的读或写就是针对设备进行信息的输入或输出。我们可以将流理解为传送数据的管道。管道的一段是固定的,就是系统的内存;管道的另一端连的是不同的设备。
流的作用:
用于应用程序和内外部进行数据通讯。
**
流的分类:
方向:
-
输入流inputStream 继承自抽象类InputStream或Reader
-
输出流outputStream继承自抽象类OutputStream或Writer
流的数据单位:
1.字节流 1个字节 8位二进制数继承自抽象类InputStream或OutputStream。
2.字符流 char字符(2个字节)继承自抽象类Reader或者Writer流的功能:
低级流(节点流)节点流是可以直接从/向一个特定的数据源(例如磁盘文件、内存、网络)读/写数据的流。
高级流(处理流不直接连接到设备,而是连接在已存在的流(节点流或处理流))
文件类
File既代表文件又代表目录
File.exist()
File.isFile();
低级字符流:
低级字符流实现文件的复制:
String fromPath="E:"+File.separator+"helloWorld.java";
File file=new File(fromPath);
String toPath="D:"+File.separator+"test"+File.separator+"helloWorld.java";
File file02=new File(toPath);
if(file.exists()) {
FileReader r=null;
FileWriter w=null;
try {
r=new FileReader(file);
w=new FileWriter(file02);
char[] data=new char[1024];
int length=0;
while((length=r.read(data))!=0) {
w.write(data, 0, length);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
try {
r.close();
w.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
低级字节流
无论是什么类型的文件都可以使用字节流。InputStream和OutputStream。
音频、视频图片等文件使用字节流。
文本型文件一般使用字符流。
低级字符流
文本文件,适合使用字符流来读取、写入
以下是通过低级字节流,把一个文件内容复制到另一个文件:
String fromPath = "C:" + File.separatorChar +"1" + File.separatorChar + "MainEnter.java";
**//路径根据本地地址更改**
//File.separatorChar代表路径中的”/“
File file = new File(fromPath);//新创一个文件通过被复制文件路径
String toPath = "C:" + File.separatorChar +"2" + File.separatorChar + "MainEnter.java";
//复制到的路径
File file02 = new File(toPath);//新创一个文件,代表复制到的文件
//判断文件是否存在
if(file.exists()) {
//无论文件是什么类型的文件,都可以采用字节流
//推荐:对于字节码文件,或者图片,或音频,视频等等文件,我们读取时,一般采用字节流
//而文本文件,采用字符流
FileInputStream in = null;
FileOutputStream out = null;
byte[] data = new byte[1024];//定义一个数据缓冲区
try {
in = new FileInputStream(file);//针对该文件,创建一个对应字节输入流
//false代表文件采用覆盖的方式,来写入内容,true代表文件采用尾部追加的方式,来写入内容
out = new FileOutputStream(file02,false);//针对该文件,创建一个对应的字节输出流
int lenth = 0;//代表单次读取内容的长度
while((lenth = in.read(data)) != -1) {
out.write(data,0,lenth);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {//关流
try {
in.close();
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
高级字节流
缓冲流:BufferedInputStream和BufferedOutputStream利用缓冲区来提高读写数据的效率。
BufferedReader和BufferedWriter类,与BufferedInputStream和BuffedOutputStream类似,可以利用缓冲区来提高读写数据的效率,用来装饰其它的字符流。比BufferedInputStream和BuffedOutputStream更强大的是,这两个类可以整行读写字符。
以下是通过高级字节流,复制一个文件的内容到另一个文件:
String fromPath = "C:" + File.separatorChar + "1" + File.separatorChar + "1.txt";
File file = new File(fromPath);
String toPath = "D:" + File.separatorChar + "test" + File.separatorChar + "2.txt";
File file02 = new File(toPath);
// 判断文件是否存在
if (file.exists()) {
// 定义低级流
FileInputStream in = null;
FileOutputStream out = null;
// 定义高级流
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
// 实例化低级流
in = new FileInputStream(file);
out = new FileOutputStream(file02);
// 实例化高级流
bis = new BufferedInputStream(in);
bos = new BufferedOutputStream(out);
// 读写文件
byte[] data = new byte[1024];
int lenth = 0;
while ((lenth = bis.read(data)) != -1) {
bos.write(data, 0, lenth);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
try {
bos.flush();// 向文件中,写入数据
in.close();
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
数据流:DataInputStream和DataOutputStream。与计算机无关,非文件。
字节打印输出流
PrintStream
可以让我们写入以默认字符编码集显示
String[] names = { "张帅", "徐帅", "袁帅", "胡帅", "帅胡" };// 名字
int[] age = { 20, 18, 29, 19, 91 };// 年龄
boolean[] marry = { true, false, true, true, false };// 结没结婚
// 将上面的这些数据,写入到文件中
String toPath = "C:" + File.separatorChar + "2" + File.separatorChar + "Person02.txt";
File file = new File(toPath);
//定义低级字节流
FileOutputStream os = null;
//定义字节,字符转换流
OutputStreamWriter osw = null;
//定义高级字符流
PrintWriter out = null;
try {
//实例化各种流
os = new FileOutputStream(file);
osw = new OutputStreamWriter(os, "utf-8");//指定文本文件,采用什么编码方式,转换字符
out = new PrintWriter(osw);
//通过高级字符流向文件中输出“特定编码方式”内容
int lenth = names.length;
for(int i = 0; i < lenth; i ++) {
out.print(names[i] + " ");
out.print(age[i] + " ");
out.print(marry[i] + " ");
out.println();//换行
}
out.flush();//刷新缓冲区
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
try {
os.close();//关流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
字符流:处理文本文件、音频、视频
Reader类可以将数据源中采用其他编码的字符转换为Unicode编码;writter会将Unicode字符转换为其他编码的字符。
字符流中常用的高级流包括:
缓冲流:包括BufferedReader和BufferedWriter类,利用缓冲区来提高读写数据的效率。
String fromPath = "C:" + File.separatorChar + "1" + File.separatorChar + "MainEnter.java";
File file = new File(fromPath);
String toPath = "C:" + File.separatorChar + "2" + File.separatorChar + "MainEnter.java";
File file02 = new File(toPath);
//判断文件是否存在
if(file.exists()) {
//声明字符输入流,字符输出流
FileReader r = null;
FileWriter w = null;
BufferedReader reader = null;
BufferedWriter writer = null;
try {
//实例化字符流入流,和字符输出流
r = new FileReader(file);
w = new FileWriter(file02);
reader = new BufferedReader(r);
writer = new BufferedWriter(w);
//使用BufferedReader类可以缓冲数据,在读取的可以1个字符1个字符的读,也可以1行行的读
String data = "";
while((data = reader.readLine()) != null) {
System.out.println(data);
writer.write(data + "\n");
// writer.newLine();//换行
}
writer.flush();//将缓冲区中的数据推送到文件中去,重新清空缓冲区
// char[] data = new char[1024];
// int lenth = 0;
// while((lenth = br.read(data)) != -1) {
// System.out.println(Arrays.toString(data));
// }
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}finally {
try {
r.close();
w.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
转换流:用于字节数据到字符数据之间的转换,包括InputStreamReader和OutputStreamWriter。
String fromPath = "C:" + File.separatorChar + "1" + File.separatorChar + "MainEnter.java";
File file = new File(fromPath);
String toPath = "C:" + File.separatorChar + "2" + File.separatorChar + "MainEnter.java";
File file02 = new File(toPath);
if(file.exists()) {
// 定义低级输入流
FileInputStream in = null;
FileOutputStream out = null;
//定义 转换流
InputStreamReader isr = null;
OutputStreamWriter osw = null;
//定义字符缓冲流
BufferedReader reader = null;
try {
//实例化输入流
in = new FileInputStream(file);
out = new FileOutputStream(file02);
isr = new InputStreamReader(in);//作用:将字节流数据转换为字符流数据
osw = new OutputStreamWriter(out, "UTF-8");
reader = new BufferedReader(isr);//再转换为缓冲流,方便我们大家可以使用readLine()
System.out.println("文件的编码集:" + isr.getEncoding());
String data = "";
while((data = reader.readLine()) != null) {
System.out.println(data);
osw.write(data + "\n");
}
osw.flush();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
打印输出流:包括PrintWriter类,允许将基本类型数据打印输出到字符串流中,PrintWriter带有带自动刷新(Flush)功能。
Java既支持字节流,也支持字符流,但是有时候需要在字节流和字符流之间转换。Java I/O库中提供了InputStreamReader和OutputStreamWriter这两个类用于字节数据到字符数据之间的转换。
对象流
ORM(Object relationshipDB Mapping)对象关系数据库映射
ObjectInputStream/ObjectOutputStream之所以能完成基于对象的读写,是因为Java提供了一套对象序列化机制。序列化机制就是一套向流写入或读取对象数据的自动机制,这套机制将读写的实现封装在对象自身的实现之中。
序列化:对象转换为流数据
反序列化:流数据转换为对象
如果需要传输的对象类作了改动(加了新属性或删除了属性)只要serialVersionUID没有变,原来保存的对象还是可以读取。当然改动之后的类要与原来的对象兼容
JavaBeans
package com.gezhi.javaoo23.bean;
import java.io.Serializable;
/**
* 学生实体类
*
*
*/
public class StudentBean implements Serializable{
/**
* 序列化版本号(作用:比较原来Java的序列化机制,
* 是通过在运行时判断类的serialVersionUID来验证版本的一致性的。在进行反序列化时,
* JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较。)
*/
private static final long serialVersionUID = 5453124311904581252L;
/**
* 数据的唯一标识
*/
private String id;
/**
* 学生名字
*/
private String stuName;
/**
* 学生年龄
*/
private int age;
/**
* 学生性别(0-女,1-男,-1 不男不女)
*/
private int gender;
/**
* 学生学号
*/
private String stuNo;
/**
* 提供无参构造 (可以显式写出,也可以不写,编译器会自动添加)
*/
public StudentBean() {
super();
// TODO Auto-generated constructor stub
}
public StudentBean(String id) {
super();
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
@Override
public String toString() {
return "StudentBean [id=" + id + ", stuName=" + stuName + ", age=" + age + ", gender=" + gender + ", stuNo="
+ stuNo + "]";
}
}
对象流:
package com.gezhi.javaoo23.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import com.gezhi.javaoo23.bean.StudentBean;
/**
- 对象流
*/
public class ObjectStream {
public static void main(String[] args) {
// TODO Auto-generated method stub
//从硬盘的文件中,读取对象
input();
//向文件中持久化对象
// output();
}
private static void input() {
// TODO Auto-generated method stub
String fromPath = "C:" + File.separatorChar + "2" + File.separatorChar + "students.data";
File file = new File(fromPath);
if(file.exists()) {
//定义低级字节流
FileInputStream in = null;
//定义高级对象输入流
ObjectInputStream ois = null;
try {
//实例化
in = new FileInputStream(file);
ois = new ObjectInputStream(in);
//从文件中读取多个JAVA对象
Object obj = null;
while((obj = ois.readObject()) != null) {
if(obj instanceof StudentBean) {
StudentBean stu = (StudentBean) obj;
System.out.println(stu);
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private static void output() {
// TODO Auto-generated method stub
// 创建一个学生对象(代表何汉峰的相关数据)
StudentBean stu = new StudentBean("9527");
stu.setStuName("何汉峰");
stu.setAge(18);
// stu.setGender(1);
StudentBean stu02 = new StudentBean("9528");
stu02.setStuName("小哥");
stu02.setAge(68);
// stu02.setGender(1);
// System.out.println(stu);
// 此时我们需要将该对象“持久化”到硬盘上去
String toPath = "C:" + File.separatorChar + "2" + File.separatorChar + "students.data";
File file = new File(toPath);
// 定义低级流
FileOutputStream out = null;
// 定义对象高级流
ObjectOutputStream oos = null;
try {
// 实例化
out = new FileOutputStream(file);
oos = new ObjectOutputStream(out);
oos.writeObject(stu);// 将学生对象丢入“对象流”中
oos.writeObject(stu02);
oos.flush();// 刷新流
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
**
流的使用原则:
**
对Java I/O库中的流类有如下的使用原则:
一、按数据源分类
- 如果数据源是非文本文件文件,使用字节流, FileInputStream和FileOutputStream;对于文本文件,使用字符流, FileReader和 FileWriter。
- 如果数据源是字节数组byte[],则使用ByteArrayInputStream和ByteArrayOutputStream。
- 如果数据源是字符数组Char[],则使用CharArrayReader和CharArrayWriter。
- 如果数据源是String对象,对于字节流,则使用StringBufferInputStream和StringBufferOuputStream;对于字符流,则使用StringReader和StringWriter。
- 如果数据源是网络数据流,对于字节流,使用InputStream和OutputStream;对于字符流,使用Reader和Writer。
二、按是否格式化输出分:要格式化输出,则使用PrintStream或PrintWriter。
三、按是否要缓冲分,要缓冲的话,对于字节流使用BufferedInputStream和BufferedOutputStream;对于字节流,使用BufferedReader和BufferedWriter。
四、按数据格式分 - 二进制格式(只要不能确定是纯文本的):使用InputStream、OutputStream 及其所有带 Stream结束的子类。
- 纯文本格式(含纯英文与汉字或其他编码方式):使用Reader、Writer 及其所有带 Reader、Writer 的子类。
五、按输入输出分 - 输入:使用Reader、InputStream 类型的子类。
- 输出:使用Writer、OutputStream 类型的子类。
六、特殊需要 - 从Stream到Reader、Writer的转换类:InputStreamReader、OutputStreamWriter。
- 对象输入输出:ObjectInputStream、ObjectOutputStream。
- 线程间通信:PipeInputStream、PipeOutputStream、PipeReader、PipeWriter。
- 合并输入:SequenceInputStream。
- 更特殊的需要:PushbackInputStream、PushbackReader、LineNumberInputStream、LineNumberReader。
在不考虑特殊需求的情况,决定使用哪个类以及它的构造进程的一般准则如下:
首先,考虑最原始的数据格式是什么:原则四。
第二,是输入还是输出:原则五。
第三,是否需要转换流:原则六第1点。
第四,数据源是什么:原则一。
第五,是否要缓冲:原则三(特别注明:一定要注意的是readLine()是否有定义,有什么比 read()、write()更特殊的输入或输出方法)。
第六,是否要格式化输出:原则二。