IO流体系中流的分类
IO流体系
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedOuputStream | BufferedReader | BufferedWriter |
转换流 | InputStreamReader | OutputStreamWriter | ||
对象流 | ObjectnputStream | ObjectOutputStream | ||
FilterInputStream | FiterOutputStream | FilterReader | FilterWriter | |
打印流 | PrintStream | PrintWriter | ||
推回输入流 | PushbacklnputStream | PushbackReader | ||
特殊流 | DatalnputStream | DataOutputStream |
常用的流
抽象基类 | 节点流(或文件流) | 缓冲流(处理流的一种 |
InputStream | FileInputStream(read(byte[] buffer)) | BufferedInputStream(read(byte[] buffer)) |
OutputStream | FileOutputStream (write(byte[] buffer,0,len)) | BufferedOutputStream (write(byte[] buffer,0,len)) / flush() |
Reader | FileReader (read(char[] cbuf)) | BufferedReader (read(char[] cbuf)) / readLine() |
Writer | FileWriter (write(char[] cbuf, 0, len)) | BufferedWriter (write(char[] cbuf, 0, len)) / flush() |
1,FileReader读入数据的基本操作
例:将项目下的hello.txt文件内容读入程序中,并输出到控制台
//程序中出现的异常需要使用try-catch-finally处理
@Test
public void testFileReader() throws IOException {
//1.实例化File类的对象,指明要操作的文件
File file = new File("hello.txt");
//2.提供具体的流
FileReader fileReader = new FileReader(file);
//3.数据的读入,read():返回读入的一个字符串,如果达到文件末尾,返回-1
int data = fileReader.read();
while (data != -1){
System.out.println((char)data);
data = fileReader.read();
}
//4.流的关闭操作
fileReader.close();
}
1.1,FileReader中使用read(char[] cbuf)读入数据
//对read()操作升级,使用read重装方法
@Test
public void testFileReader1(){
//1,File类的实例化
File file = new File("hello.txt");
//2,FileReader流的实例化
FileReader fileReader = null;
try {
fileReader = new FileReader(file);
//3,读入的操作
//read(char[] cbuf):返回每次读入cbuf数据中的字符的个数,如果达到文件末尾返回-1
char[] cbuf = new char[5];
int len;
while ((len = fileReader.read(cbuf)) != -1){
//方式一:错误的写法
// for (int i = 0;i<cbuf.length;i++){
// System.out.print(cbuf[i]);
// }
//正确的写法
for (int i = 0;i<len;i++){
System.out.print(cbuf[i]);
}
//方式二
String str = new String(cbuf,0,len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4.流的关闭操作
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2,FileWriter写出数据的操作
例:从内存中写出数据到硬盘的文件里
说明:
1,输出操作,对应的File可以不存在的。并不会报异常
2,File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件
File对应的硬盘中的文件如果存在:
如果流使用的构造器是FileWriter(file,flase)/FileReader(file):对原有文件的覆盖
如果流使用的构造器是FileWriter(file,true):不会对原有文件的覆盖,而是在原文件中添加
@Test
//程序中出现的异常需要使用try-catch-finally处理
public void testFileReader2() throws IOException {
//1,提供File类的对象,指明写出到的文件
File file = new File("hello.txt");
//2,提供FileWriter的对象,用于数据的写出
FileWriter fileWriter = new FileWriter(file,false);
//3,写出的操作
fileWriter.write("梦想");
fileWriter.write("是用来实现的");
//4,流资源的关闭
fileWriter.close();
}
3,使用FileReader喝FileWriter实现文本文件的复制
@Test
public void testFileReader3() {
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
//1,创建File类的对象,指明读写的文件
File src = new File("hello.txt");
File dest = new File("hello1.txt");
//2,创建输入流喝输出流的对象
fileReader = new FileReader(src);
fileWriter = new FileWriter(dest);
//3,数据的读入和写出操作
char[] cbuf = new char[5];
int len;//记录每次读入到cbuf数组中的字符的个数
while ((len = fileReader.read(cbuf)) != -1){
//每次写出的len个字符
fileWriter.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4,流资源的关闭
try {
if (fileReader != null){
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fileWriter != null){
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
不能使用字符流处理图片
总结:
①,对于文本文件(.txt,.java, .c, .cpp),使用字符流处理(复制的时候可以使用字节流,读写不可以)
②,对于非文本文件(.jpg, .mp3, .avi, .doc, .ppt, …),使用字节流处理
4,缓冲流(字节型)实现非文本文件的复制
作用:提高流的读取,写入的速度,原因:内部提供了一个缓冲区
@Test
public void testFileReader3() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1,造文件
File src = new File("hello.txt");
File dest = new File("hello1.txt");
//2,造流(造节点流)
FileInputStream fileInputStream = new FileInputStream(src);
FileOutputStream fileOutputStream = new FileOutputStream(dest);
//3.造缓存流
bis = new BufferedInputStream(fileInputStream);
bos = new BufferedOutputStream(fileOutputStream);
//3,复制细节,读取,写入
byte[] buffer = new byte[1024];
int len;//记录每次读入到buffer数组中的字符的个数
while ((len = bis.read(buffer)) != -1){
//每次写出的len个字符
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4,流资源的关闭,关闭外层流的同时,内层流也会关闭
try {
if (bis != null){
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bos != null){
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
例:实现图片加密操作
int b=0;
while((b = fis.read()) != -1){
fos .write(b^5);
}
获取文本上每个字符出现的次数
提示:遍历文本的每一个字符:字符及出现的次数保存在map中:将map中数据写入文件
@Test
public void testFileReader5() {
FileReader fileReader = null;
BufferedWriter bufferedWriter = null;
try {
//1,创建map集合
Map<Character, Integer> map = new HashMap<>();
//2,遍历每一个字符,每一个字符出现的次数写入map中
fileReader = new FileReader("hello.txt");
int c;
while ((c = fileReader.read()) != -1){
//int 还原char
char ch = (char)c;
//判断char是否在map中第一次出现
if(map.get(ch) == null){
map.put(ch,1);
}else {
map.put(ch,map.get(ch)+1);
}
}
//3,把map中数据存在文件count.txt,创建writer
bufferedWriter = new BufferedWriter(new FileWriter("count.txt"));
//遍历map,再写入数据
Set<Map.Entry<Character,Integer>> entrySet = map.entrySet();
for (Map.Entry<Character,Integer> entry : entrySet) {
switch (entry.getKey()){
case ' ':
bufferedWriter.write("空格 = "+entry.getValue());
break;
case '\t':
bufferedWriter.write("tab键 = "+entry.getValue());
break;
case '\r':
bufferedWriter.write("回车 = "+entry.getValue());
break;
case '\n':
bufferedWriter.write("换行 = "+entry.getValue());
break;
default:
bufferedWriter.write(entry.getKey()+" = "+entry.getValue());
break;
}
bufferedWriter.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null){
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bufferedWriter != null){
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5,转换流的使用
①,转换流:属于字符流
InputStreamReader:将一个字节的输入流转换为字符的输入流
outputStreamWriter:将一个字符的输出流转换为字节的输出流
②,作用:提供字节流与字符流之间的转换
③,解码:
字节,字节数组 —> 字符数组,字符串
编码:字符数组,字符串 —> 字节,字节数组
//程序中出现的异常需要使用try-catch-finally处理
@Test
public void demo01() throws IOException {
FileInputStream fileInputStream = new FileInputStream("hello.txt");
//参数指明的字符集,具体使用那个字符集,取决于文件hello.txt保存时使用的字符集,不填使用系统默认的字符集
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"UTF-8");
char[] cbuf = new char[20];
int len;
while ((len = inputStreamReader.read(cbuf)) != -1){
String str = new String(cbuf, 0, len);
System.out.print(str);
}
inputStreamReader.close();
}
- 字符流
ASCII:美国标准信息交换码,用一个字节的7位可以表示
ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表示
GB2312:中国的中文编码表。最多两个字节编码所有字符
GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
Unicode:国际标准码。融合了目前人类使用的所有字符。为每个字符分配唯一的字符码
UTF-8:变长的编码方式,可用1-4个字节来表示一个字符
- 字符流输入、输出的标准化过程
输入过程
①,创建File类的对象,指明读取的数据的来源。(要求此文件 - -定要存在)
②,创建相应的输入流,将File类的对象作为参数,传入流的构造器中
③,具体的读入过程: 创建相应的byte[]或char[]。
④,关闭流资源
说明:程序中出现的异常需要使用try-catch-finally处理。
输出过程
①,创建File类的对象,指明写出的数据的位置。(不要求此文件一 定要存在)
②,创建相应的输出流,将File类的对象作为参数,传入流的构造器中
③,具体的写出过程:write( char[]/byte[] buffer,0,1en)
④,关闭流资源
说明:程序中出现的异常需要使用try-catch-finally处理。
5,对象流:ObjectInputStream和ObjectOutputStream
概念:
用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来
- 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
- 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
- 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
- 注意:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
@Test
public void demo02() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
oos.writeObject(new Person("阿里", 11));
oos.flush();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.dat"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
随机存取文件流
- RandomAccessFile的使用
①,RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
②,RandomAccessFile即可是输入流,也可是输出流
③,如果RandomAccessFile作为输出流时:
写出到文件如果不存在,则在执行过程中自动创建
写出的文件存在,则会对原有文件内容进行覆盖,(默认情况下,从头覆盖)raf.seek(3);//将指针调到角标为3的位置
④,RandomAccessFile可以实现一个多线程断点下载的功能,下载前会建立两个临时文件,一个是与被下载文件大小相同的空文件,另一个是记录文件指针的位置文件,每次暂停的时候会保存上一次的指针,然后断点下载的时候,会继续从上一次的地方下载
构造器
public RandomAccessFile(File file, String mode)
- mode参数------->
r:以只读方式打开
rw:打开以便读取和写入
rwd:打开以便读取和写入,同步文件内容的更新
rws:打开以便读取和写入,同步文件内容和元数据的更新
@Test
public void demo03() throws IOException {
RandomAccessFile raf1= new RandomAccessFile(new File("1.jpg"),"r");
RandomAccessFile raf1= new RandomAccessFile(new File("2.jpg"),"rw");
byte[] buffer = new byte[1024]'
int len;
while((len = raf1.read(buffer)) != -1){
raf2.write(buffer, 0, len);
}
raf1.close();
raf2.close();
}
例:在文件中指定的位置插入内容
@Test
public void demo04() throws IOException {
RandomAccessFile raf1= new RandomAccessFile(new File("hello.txt"),"rw");
raf1.seek(3);//将指针调到角标为3的位置(0开始)
//保存指针3后面的所有数据到StringBuilder中
StringBuilder builder = new StringBuilder((int)new File("hello.txt").length());
byte[] buffer = new byte[20];
int len;
while ((len = raf1.read(buffer)) != -1){
builder.append(new String(buffer, 0, len));
}
//调回指针,写入zxc
raf1.seek(3);
raf1.write("123".getBytes());
//将StringBuilder中的数据写入到文件中
raf1.write(builder.toString().getBytes());
raf1.close();
}
下一章,(30)Java中的网络编程