IO流概述
1.什么是流
①.流是一种抽象的概念,可以理解为输入/输出的途径。
②.一个读取字节序列的对象被称为输入流,一个写入字节序列的对象称为输出流。输入流和输出流是相对于程序本身而言的。
2.流的分类
①.按照流向对流进行分类,分输入流和输出流。
②,按照处理的不同数据类型对流进行分类,分为字节流(Byte)和字符流(Character)。
3.字节流和字符流的区别
①.字节流读取的时候,读到一个字节就返回一个字节。字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在UTF-8码表中是3个字节)时。先去查指定的编码表,将查到的字符返回。
②.字节流可以处理所有类型数据,如:图片,MP3,AVI视频文件,而字符流只能处理字符数据。只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流
③.简单说:字节流+编码表。
IO流主要结构
字节流
1.字节流概述
Java的IO系统提供了InputStream和OutputStream两个抽象类实现字节数据(8位)的输入和输出。
2.InputStream类
(1)定义
InputStream类是个抽象类,作为字节输入流的直接或间接的父类,它定义了许多有用的方法,包括读取、移动指针、标记、复位、关闭等方法。
(2)常用方法
①.int available()返回此输入流方法的下一个调用方可以不受阻塞地从此输入流读取(或跳过)的字节数
②.void close() 关闭此输入流并释放与该流关联的所有系统资源。
③.abstract int read() 从输入流读取下一个数据字节。 如果到达流的末尾,则返回 -1。
④.int read(byte[] b)从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 中。 如果到达流的末尾,则返回 -1。
⑤.int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入字节数组。 如果到达流的末尾,则返回 -1。
⑥.long skip(long n) 跳过和放弃此输入流中的 n 个数据字节。
(3)FileInputStream类
①.FileInputStream类是InputStream的一个子类, 用于读取诸如图像数据之类的原始字节流。
②.它代表从文件中读取数据,其构造函数参数可以是一个文件对象或字符串(文件的路径)。
// 方式一,一次读一个字节
int by = 0;
// 读取,赋值,判断
while ((by = fis.read()) != -1) {
System.out.print((char) by);
}
// 方式二,一次读一个字节数组
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
System.out.print(new String(bys, 0, len));
}
3.OutputStream类
(1)定义
OutputStream类也是个抽象类,作为字节输出流的直接或间接的父类,它定义了许多有用的方法,包括读取、移动指针、标记、复位、关闭等方法。
(2)常用方法
①.void close() 关闭此输出流并释放与此流有关的所有系统资源。
②.void flush() 刷新此输出流并强制写出所有缓冲的输出字节。
③.void write(byte[] b) 将 b.length 个字节从指定的字节数组写入此输出流。
④.void write(byte[] b, int off, int len) 将指定字节数组中从偏移量 off 开始的 len 个字节写入此输出流。
⑤.abstract void write(int b) 将指定的字节写入此输出流。
(3)FileOutputStream类
①.FileOutputStream类是OutputStream的一个子类,是用于将数据写入File或FileDescriptor的输出流。
②.通过该输出流把数据写入文件,构造函数的参数可以是文件对象或者字符串(文件的路径)。
//一次写一个字节
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
//一次写一个字节数组
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
字符流
1.字符流概述
字符流主要是用于支持Unicode的文字内容,其中输入流Reader抽象类用来读取,输出流Writer抽象类用来写入。
2.Reader类
(1)定义
Reader类是读取字符流的父类,它是抽象类,所有继承自该类的子类都必须实现抽象方法reader()和close()。
(2)常用方法
①.abstract void close() 关闭该流。
②.void mark(int readAheadLimit) 标记流中的当前位置。
③.boolean markSupported() 判断此流是否支持mark()操作。
④.int read() 读取单个字符。如果已到达流的末尾,则返回-1 。
⑤.int read(char[] cbuf) 将字符读入数组。如果已到达流的末尾,则返回-1。
⑥.abstract int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。 如果已到达流的末尾,则返回-1。
⑦.int read(CharBuffer target) 试图将字符读入指定的字符缓冲区。 如果已到达流的末尾,则返回-1。
⑧.boolean ready() 判断是否准备读取此流。
⑨.void reset() 重置该流。
⑨+.long skip(long n) 跳过字符。
(3)FileReader类
①.FileReader类继承自InputStreamReader,读取字符流。
②.该类的构造函数默认采用了合适的字符编码和字节缓冲区大小。
③.该类通过指定一个File对象或一个文件名的条件下创建一个新的FileReader对象。
3.Writer类
(1)定义
Writer类是字符流输出类的父类,它是抽象类,所有继承自该类的子类都必须实现抽象方法writer()。
(2)常用方法
①.Writer append(char c) 将指定字符追加到此writer。
②.Writer append(CharSequence csq) 将指定字符序列追加到此writer。
③.Writer append(CharSequence csq, int start, int end) 将指定字符序列的子序列追加到此 writer.Appendable。
④.abstract void close() 关闭此流,但要先刷新它。
⑤.abstract void flush() 刷新此流。
⑥.void write(char[] cbuf) 写入字符数组。
⑦.abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。
⑧.void write(int c) 写入单个字符。
⑨.void write(String str) 写入字符串。
⑨+.void write(String str, int off, int len) 写入字符串的某一部分。
(3)FileWriter类
①.FileWriter类继承自OutputStreamWriter,写入字符流。
②.该类把字符写入文件,文件位置在构造函数中指定。
③.该类的构造函数接受默认的字符编码和默认的字节缓冲区大小。
//一次读写一个字符
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("d:\\a.txt");
FileWriter fw = new FileWriter("d:\\b.txt");
int ch = 0;
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
fr.close();
fw.close();
}
转换流
1.转换流概述
①.字节流操作中文数据不是特别的方便,所以就出现了转换流。 转换流的作用就是把字节流转换字符流来使用。
②.转换流其实是一个字符流,字符流 = 字节流 + 编码表
2.编码表
①由字符和对应的数值组成的一张表
3.常见的编码表
①.ASCII
②.ISO-8859-1
③.GB2312
④.GBK
⑤.GB18030
⑥.UTF-8
4.字符串中的编码问题
①.编码:String -- byte[]
②.解码:byte[] -- String
5.IO流中的编码问题
①.OutputStreamWriter
OutputStreamWriter(OutputStream os):默认编码,GBK
OutputStreamWriter(OutputStream os,String charsetName):指定编码
②.InputStreamReader
InputStreamReader(InputStream is):默认编码,GBK
InputStreamReader(InputStream is,String charsetName):指定编码
③.编码问题其实很简单
编码只要一致即可。
高效流
1.什么是高效流
①.高效流又称为带缓冲区的流,可以把数据先放入缓冲区,防止每次读、写数据时进行实际的操作,减少了数据实际访问的时间开销。
②.高效流分为:高效的字节流BufferedInputStream、BufferedOutputStream和高效的字符流BufferedReader、BufferedWriter。
2.BufferedInputStream类
①.本类间接继承自InputStream类,并实现了abstract int read()方法。此类没有特殊方法需要学习。
3.BufferedOutputStream类
①.本类间接继承自OutputStream类,并实现了abstract void write(int b)方法。此类也没有特殊方法需要学习。
// 高效字节流一次读写一个字节:
public static void method3(String srcString, String destString)
throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
srcString));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destString));
int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bos.close();
bis.close();
}
// 高效字节流一次读写一个字节数组:
public static void method4(String srcString, String destString)
throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
srcString));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destString));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
bis.close();
}
4.BufferedReader类
①.此类间接继承自Reader类,并实现了abstract void close()方法和abstract int read(char[] cbuf, int off, int len)方法。
②.此类有一个新方法
String readLine() 读取一个文本行。 如果已到达流末尾,则返回 null 。
5.BufferedWriter类
①.本类间接继承自Writer类,并实现了abstract void close()方法、abstract void flush()方法和abstract void flush()方法。此类也没有特殊方法需要学习。
②.此类有一个新方法
void newLine() 写入一个行分隔符。
// 一次读写一个字符数组
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
bw.write(chs, 0, len);
bw.flush();
}
// 释放资源
bw.close();
br.close();
// 一次读一行
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
// 读写数据
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
// 释放资源
bw.close();
br.close();
IO小结
其他流
1.打印流
(1).分类
①.PrintStream——字节打印流
②.PrintWriter——字符打印流
(2).特点
①.只操作目的地,不操作数据源
②.可以操作任意类型的数据
③.如果启动了自动刷新,在调用println()方法时,能够自动换行并刷新
④.打印流自动包装高效
2.标准输入输出流
(1).System类下面有这样两个字段
①.in标准输入流
②.out标准输出流
(2).键盘录入数据的三种方式
①.main方法的args接受参数
②.System.in通过BufferedReader进行包装
③.Scanner类
3.随机访问流
RandomAccessFile——随机访问流,该流可以按照文件指针的位置写数据和读数据。
4.合并流
SequenceInputStream——合并流,该流可以把多个输入流的数据写到一个输出流中。该流只提供了读的方法。
5.序列化流
ObjectOutputStream——序列化流,该流可以把对象写入文本文件或者在网络中传输。
(1).如何实现序列化呢?
①.让被序列化的对象所属类实现序列化接口。
②.该接口是一个标记接口,没有方法,没有功能需要实现。
(2).注意问题
①.把数据写到文件后,再去修改类会产生一个问题。
②.该如何解决这个问题呢?在类文件中,给出一个固定的序列化id值,serisversion UID= ;,这样也可以解决黄色警告线的问题。
③.transient用来声明不需要序列化成员变量。
(3).反序列化
ObjectInputStream被称为反序列化。
①. 对象 转换成 流数据 称为序列化,
②.流数据 转换成 对象 称为反序列化。
/*
* 序列化流:把对象按照流一样的方式存入文本文件或者在网络中传输。对象 -- 流数据(ObjectOutputStream)
* 反序列化流:把文本文件中的流对象数据或者网络中的流对象数据还原成对象。流数据 -- 对象(ObjectInputStream)
*/
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
// 由于我们要对对象进行序列化,所以我们先自定义一个类
// 序列化数据其实就是把对象写到文本文件
// write();
read();
}
private static void read() throws IOException, ClassNotFoundException {
// 创建反序列化对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"oos.txt"));
// 还原对象
Object obj = ois.readObject();
// 释放资源
ois.close();
// 输出对象
System.out.println(obj);
}
private static void write() throws IOException {
// 创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
"oos.txt"));
// 创建对象
Person p = new Person("林青霞", 27);
// public final void writeObject(Object obj)
oos.writeObject(p);
// 释放资源
oos.close();
}
}
6.Properties
Properties是一个集合,Hashtable(Map)的子类。它的属性是集合,但是能和IO流相结合。
(1).特有功能
①.String getProperty(String key) 用指定的键在此属性列表中搜索属性。
②.String getProperty(String key, String defaultValue) 用指定的键在属性列表中搜索属性。
③.Set<String> stringPropertyNames() 获取所有键的集合。
(2).和IO结合的方法
①.把键值对形式的文本文件内容加载到集合中
void load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。
②.把集合的数据存储到文本文件中
void store(OutputStream out, String comments) 以适合使用 load 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。