IO流
处理设备之间的数据传输
在本地电脑上,我们是站在内存角度 看流的流向
看电影 从硬盘中输入
写文件 从内存中输出
流 按照流向分 分为输入流和输出流
I InputStream 输入流
O outputStream 输出流
按照流读取文件的类型 我们分 字节流和字符流
字节流 读写任意类型文件 Stream
字符流 只能读取文本文件 Reader Writer
A、字节流
字节流继承体系 在java.io包下
字节流分 字节输入流InputStream和字节输出流OutputStream
1. 所有字节输入流父类 InputStream 抽象类
a.文件输入流FileInputStream
b. ByteArrayInputStream
c. ObjectInputStream
2. 所有字节输出流父类 OutputStream 抽象类
a.文件输出流FileOutputStream
b. ByteArrayOutputStream
c. ObjectOutputStream
1.文件输出流FileOutputStream
文件输出流是用于将数据写入 File
或 FileDescriptor
的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream
(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
FileOutputStream
用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用 FileWriter
。
a.构造方法
1.
FileOutputStream(File file);
File file = new File("a.txt");输出流关联的文件,如果文件不存在,那么运行时系统会自动帮你创建
FileOutputStream out = new FileOutputStream(file);
2.
FileOutputStream(String name);
FileOutputStream out = new FileOutputStream("a.txt");
3.
FileOutputStream(String name,boolean);
FileOutputStream(File file,boolean);
true代表追加写入 每运行一次程序在原来基础上再写一次
false代表不追加
b.写出数据
通过文件输出流,向其所关联的文件中,写出数据
FileOutputStream out = new FileOutputStream("a.txt");
wirte();//参数类型 int b / byte[] b / byte[] b,int off,int len
out.write();//一次写入一个字节
out.write(97);//打开文件后看到a
out.write(300);//超过一个字节的范围,会丢弃多余字节
byte[] bytes = {101,102,103,104};
out.write();//一次写入一个字节数组
out.write(bytes,2,2);//一次写入一个字节数组一部分,从2索引开始,写2个字节 结果 gh
String str = "杨家有女初长成";
byte[] bytes = str.getBytes();//将一个字符串转为字节数组
//UTF-8编码 一个汉字3个字节
out.write(bytes);
out.write("回眸一笑百媚生,六宫粉黛无颜色".getBytes());
out.write("\r\n".getBytes());//写入一个回车换行符
//每个平台系统的换行符都不一样
//windows平台 换行符 \r\n
//Linux \n
流用完之后必须释放资源,否则会有内存移除的风险
1.通知系统释放管理关联文件的资源
2.让IO流变成垃圾,等待垃圾回收器回收
out.close();//关闭流 释放资源
2.文件输入流FileInputStream
FileInputStream
从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。
FileInputStream
用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader
。
a.构造方法
FileInputStream(String name);
FileInputStream(File file);
File file = new File("e.txt");输入流所关联的文件如果不存在就会报错
FileInputStream in = new FileInputStream(file);//输入流所关联的文件如果不存在就会报错
FileInputStream in = new FileInputStream("a.txt");
b.读取文件中数据
read();//参数类型 byte b / byte[] b / byte[] b,int off,int len
FileInputStream in = new FileInputStream("a.txt");
//a.txt 内容 abcdef
int code = in.read();//一次读取一个字节 97
int code = in.read();//最后读不到时 返回-1
in.close();使用完后关闭释放资源
//创建一个空的字节数组充当缓冲区
byte[] bytes = new byte[1024];
int len = in.read(bytes);//将最多bytes长度个code数据存入bytes中 返回实际存入的code个数
//一次读取1024个字节,放到这个缓冲区中 len就是实际读取的长度 6
int len = in.read(bytes,0,3);//将最多3个数据存入bytes中,并将第一个数据存为bytes[0]
String str = new String(bytes,0,3);
in.close();
字符串转字节数组
byte[] bytes = "abc".getBytes();
字节数组转字符串
String str = new String(bytes);
3.文件复制
a.单字节读写
FileInputStream in = new FileInputStream("a.txt");
FileOutputStream out = new FileOutputStream("E:\\a.txt");
//一个字节一个字节读取 写入 复制文件效率太低
int len = 0;
while((len = in.read())!=-1){
out.write(len);
out.flush();//刷新
}
in.close();
out.close();
b.字节数组读写
//一次读取 写入一个字节数组
FileInputStream in = new FileInputStream("a.txt");
FileOutputStream out = new FileOutputStream("E:\\a.txt");
//定义字节数组充当缓冲区 数组长度过长 堆内存溢出不够用 OutOfMemoryError
byte[] bt = new byte[1024*8];//一般缓冲区大小为 1024*8 8字节
int len = 0;//记录每次读取到的有效字节个数
while((len = in.read(bt))!=-1){
out.write(bt,0,len);
out.flush();
}
in.close();
out.close();
c.高效的输入输出流
Ctrl H 可以看一个类的继承关系
new BufferedInputStream();//需要一个InputStream的参数 一个缓冲区大小参数(默认8字节 1024*8)
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file1));
new BufferedOutputStream();
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(file2));
int len = 0;
byte bytes = new byte[1024*8];
while((len = bin.read(bytes))!=-1){
bout.write(bytes,0,len);
bout.flush();//刷新
}
bin.close();
bout.close();
流的标准异常处理
ctrl alt T 对选定代码进行异常捕获处理
FileInputStream in = null;
FileOutputStream out = null;
try{
in = new FileInputStream("C:");
out = new FileOutputStream();
}catch(IOException e){
e.printStackTrace();
}finally{
}
B、字符流
一个中文汉字占两个/三个字节,如果使用字节流操作,需要对汉字进行拆分和合并,为了方便操作中文,java提供了字符流
但是,计算计上存储的最小单位就是字节,要操作数据,本质上还是对最小单位的操作,字符流底层应该还是字节流,因此要使用字符流,就还是要对中文汉字进行拆分和合并,而这就需要编码表作为依据
字符流 = 字节流 + 编码表;
字符流 两个顶层抽象父类 字符输入流Reader 字符输出流Writer
1.字符输入流Reader
a.InputStreamReader
b.BufferedReader
2.字符输出流Writer
a.OutputStreamWriter
b.BufferedWriter
1.编码和解码
编码:把字符串转换成字节数组
String str = "今晚老地方见面";
byte[] bytes = str.getBytes(); // 采用平台默认的码表进行编码
解码:把字节数组转换成字符串
String s = new String(bytes);//采用平台默认的码表进行解码
编码解码需要通过码表 无参构造就采用平台默认的码表 解码码表和编码码表不一致 就会产生乱码
String str = "今晚老地方见面";
byte[] bytes = str.getBytes(码表); //可以传入参数 使用指定码表
String s = new String(bytes,码表);//如果解码码表和编码码表不一致 就会产生乱码
2.字符转换流
InputStreamReader OutputStreamWriter
A、OutputStreamWriter
字符流通向字节流的桥梁 使用指定的码表
构造方法
//输出流所关联的文件如果不存在就会自动创建
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("a.txt"),码表);//也可以指定码表
out.writer('你');//写字符就行,不需要写转换字节 一次写一个字符
out.writer("今天是大暑"); //一次写一个字符串
out.writer("今天是大暑",0,5);//从0开始写5个
out.writer(new char[]{'n','好','5'}); //一次写入一个字符数组
out.writer(new char[]{'n','好','5'},0,2); //一次写入一个字符数组一部分
想追加就在传入的FileOutputStream传入true参数
B、InputStreamReader
字节流通过字符流的桥梁,使用指定码表 读取字节并将其转为字符
InputStreamReader(InputStream in);
//输入流关联的文件如果不存在就会报错
InputStreamReader in = new InputStreamReader(new FileInputStream(file));
int ch = in.read();//一次读取一个字符 返回该字符在编码表中的码数
System.out.println(ch);//如需看字符需要将ch通过查询编码表强转为char
int ch = in.read(); //读不到返回-1
char[] chars = new char[1024];//字符数组充当缓冲区
1.int len = in.read(chars);//返回值是实际读取到的字符个数
2.len = in.read(chars,0,3);//一次读取字符数组一部分装入缓冲区 字符数组从0开始装3个
String str = new String(chars);
String str = new String(chars,0,len);
String str = String.valueOf(chars,0,len);
C、采用字符流复制文本文件
InputStreamReader in = new InputStreamReader(new FileInputStringReader(file1));
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file2));
一次一个字符
int len = 0;
while((len = in.read())!=-1){时
out.writer(len);
out.flush();//字符流写入如果不刷新 不会写入
}
in.close();
out.close(); //此处也带有刷新功能 刷新并关闭
一次一个字符数组
char[] chars = new char[1000];//
int len = 0;//读取到的有效字符个数
while((len = in.read(chars))!=-1){
out.writer(chars,0,len);
out.flush();//字符流如果不刷新 不会写入
}
in.close();
out.close(); //此处也带有刷新功能 刷新并关闭
InputStreamReader 的便捷子类 FileReader (不能指定编码,只能使用平台默认编码)
OutputStreamWriter 的便捷子类 FileWriter (不能指定编码,只能使用平台默认编码)
FileReader(File file);
FileReader(String filename);
FileWirter(File file);
FileWirter(String filename);
FileWirter(File file,boolean append);//如果第二个参数为true 则将数据写到文件末尾处,而不是开头
FileWirter(String filename,boolean append);
两个子类方法与父类几乎一致,没有特有方法,都是从父类继承过来的 只是不能指定编码
4.高效字符流
BufferedReader 从字符输入流中读取中文,缓冲各个字符,从而实现字符、数组和行的高效读取,也可以指定缓冲区的大小
BufferedReader();//参数类型Reader r
BufferedWriter
BufferedWriter();//参数类型 Writer w
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
char[] chars = new char[1024];
int len = 0;
while((len = br.read(chars))!=-1){
bw.writer(chars,0,len);
bw.flush;
}
br.close();
bw.close();
br.readLine();//一次读取一行 包括空格行
bw.newLine();//写一个换行符 具有平台兼容性 //换行符占两个字节
//读一行写一行 复制文件
String line = null;
while((line = br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
br.close();
bw.close();
C.其他流
1.数据输入输出流 (字节流)
DataInputStream
DataOutputStream
特点:能够读写基本数据类型
DateInputStream(InputStream in);
DataOutputStream(OutputStream out);
DateOutputStream dos = new DateOutputStream(new FileOutputStream("a.txt"));
dos.writeBoolean(true);
dos.writeDouble(3.14);
dos.writeInt(100);
dos.writeUTF("你好世界");
dos.close();
//什么顺序写就什么顺序读
DateInputStream dis = new DateInputStream(new FileInputStream("a.txt"));
boolean b = dis.readBoolean();
double d = dis
2.内存操作流
特点:不关联任何文件,只在内存中对数据进行读写 关闭此流是无效的,此类方法在关闭此流后仍可使用,而不会产生IO流异常
操作字节数组
ByteArrayInputStream
ByteArrayOutputStream
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("横眉冷对千夫指".getBytes());
out.write("俯首甘为孺子牛".getBytes());
//取出缓冲区中的数据
byte[] b = out.toByteArray();
String s = new String(b);
//也可以直接通过此方法取 但只限于String类型
//String s = out.toString();
ByteArrayInputStream in = new ByteArrayInputStream(b);//自带一个缓冲区(数组)需要从
byte[] bytes = new byte[1024*8];
int len = in.read(bytes);
String s = new String(bytes,0,len);
操作字符数组
CharArrayWriter
CharArrayReader
CharArrayWriter cw = new CharArrayWriter();
cw.write("abc");
cw.write("cba");
cw.write("bac");
char[] chars = cw.toCharArray();
String s = new String(chars);
操作字符串
底层用StringBuffer为缓冲区
StringWriter
StringReader
StringWriter sw = new StringWriter();
sw.append("as");
sw.write("sd");
3.打印流
a.构造方法
PrintWriter(File file);
PrintWriter(OutputStream out);
PrintWriter(OutputStream out,boolean b);
PrintWriter(String fileName);
PrintWriter(String fileName,boolean b);
PrintStream(File file);
PrintStream(OutputStream out);
PrintStream(OutputStream out,boolean b);
PrintStream(String fileName);
PrintStream(String fileName,boolean b);
b.特点:
1.只操作目的地,不能操作数据源(不能读取数据) 只有输出流
2.可以操作任意数据类型的数据 使用print()方法可以写任意数据类型
3.如果我们启用自动刷新(构造方法第二个参数传入true ),那么在调用println、printf或format方法中的一个方法的时候,会自动完成刷新
public PrintWriter(OutputStream out,boolean b);
PrintWriter pw = new PrintWriter("abc.txt",true);//开启自动刷新
pw.println("abcde");
pw.println(100);
4.这个流可以直接对文件进行操作(可以直接操作文件的流:就是构造方法的参数可以传递文件或者文件路径)
c.字节打印流
PrintStream
PrintStream(File file);
PrintStream(OutputStream out);
PrintStream(OutputStream out,boolean b);
PrintStream(String fileName);
PrintStream(String fileName,boolean b);
//
PrintStream out = System.out;
PrintStream ps = new PrintStream(System.out);
ps.print(value);//将指定内容输出到显示器
ps.print(value);
ps.println();//换行
ps.println(value);//相当于ps.print(value)后再ps.println()
ps.write();
ps.append();
d.字符打印流
需要关联文件
PrintWriter pw = new PrintWriter(new FileOutputStream(“c.txt”),true);//可以加上true,再使用println()或printf()或format方法使其自动刷新
pw.write();
pw.flush();//write方法 需要刷新才生效
pw.println()//带有换行符
4.键盘录入的第二种方式
1.Scanner
2.BufferedReader 的 readLine()方法
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
5.随机访问流 RandomAccessFile
常用于 断点下载(第一次未下载完,第二次接着下) 和断点复制
特点 能读能写 支持读写基本数据类型
RandomAccessFile 类不属于流 是Object类的子类,融合了InputStream和OutputStream的功能
有一个文件指针 能够记录文件读写的位置
我们可以通过getFilePointer()方法获取文件指针,并且可以通过seek方法设置文件指针位置
构造方法
RandomAccessFile(File file,String mode);
RandomAccessFile(String name,String mode);
RandomAccessFile ra = new RandomAccessFile(new File("e.txt"),"rw");//参数2代表模式 "rw"可读可写
ra.writeBoolean();
6.序列化与反序列化
序列化 把对象保存到硬盘上
反序列化 把对象读取到内存中
a.序列化流
ObjectOutputStream
将java对象的基本数据类型和图形写入OutputStream,可以使用ObjectOutputStream读取(重构)对象。通过在流中使用文件可以实现对象的持久存储
把对象序列化到硬盘上 就要求该类实现Serializable接口 然后该类对象才能正常
注:Serializable 接口中没有任何方法 标记接口(给该类打标记 ,让jvm支持序列化 告诉虚拟机,我要序列化这个对象)
在实现了Serializable接口后,最好再写一个static final long serialVersionUID = 9746L,这样将对象存到硬盘上后,对象某个成员变量修饰符被修改,我们再次读取时会jvm会认为是同一个文件,就能读取成功
transient 对象的某个成员变量加上该关键字后 序列化该对象时不会将该对象的该成员变量一起序列化
如果要存储多个对象,我们可以将多个对象放到集合中,将集合序列化到硬盘,这样我们取某个对象时就很方便了
writeObject();//将指定对象写入ObjectOutputStream
b.反序列化流
ObjectInputStream
readObject();//从ObjectintputStream读取对象
7.Properties 属性集合
Properties 属性集合 经常用它来读写配置文件
属于双列集合 继承于Hashtable ,但不建议使用put和putAll方法,推荐使用setProperty()方法
规定 键值是String类型 不能设泛型
Properties类表示了一个持久的属性集,可以保存在流中或从流中加载。属性列表中的每一个键及其对应的值都是一个字符串。
一个属性列表可包含另一个属性列表作为它的“默认值”,如果未能在原有的属性列表中搜索到属性键,则搜索第二个属性类表
Properties p = new Properties();
//特有方法存储键值
p.setProperty("a","1");
String str = p.getProperty("a"); 键找值 如果键没有找到对应的值 则返回null
String st = p.getProperty("a","未找到"); 设置第二个参数 键没有找到对应的值 返回设置的默认值
//读取配置文件(将配置信息存到该集合中) 要求配置文件键值用等号 = 连接 Username="张三"
load() 按简单的面向行的格式从字符输入流中读取属性列表(键值对)
Password="123456"
p.load(new FileReader("a.txt"));
store(); 以 适合调用load()方法 的格式,将Properties集合中的键值对写入输出流
//将集合中信息写到文件中
p.setProperty("Username","王五");
p.setProperty("Password","1234565");
p.store(new FileWriter("用户信息.txt"),null);
8.顺序流
只有顺序输入流
顺序输入流 SequenceInputStream
表示其他输入流的逻辑串联 他从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
同时,在使用close()方法时,会将被合并的所有输入流一起关闭
//构造方法1 将两个输入流合并为一个输入流 先读取in1,在读取in2
SequenceInputStream si = new SequenceInputStream(InputStream in1,InputStream in2);
//构造方法2 该构造方法需要一个 存储元素为输入流的集合的迭代器 作为参数
SequenceInputStream si = new SequenceInputStream(Enumeration<? extends InputStream> e);