Keywords: 文件、字节流、字符流、编码与解码、装饰设计模式
文件:
File
字节流:
InputStream
OutputStream
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
字符流:
Reader
Writer
FileReader
FileWriter
InputStreamReader
OutputStreamWriter
BufferedReader
BufferedWriter
IO(Input Output)流概述
IO流用来处理设备之间的数据传输。
输入流和输出流是相对于内存设备而言的:
将外设中的数据读取到内存中:输入。
将内存的数写入到外设中:输出。
文件File类:操作文件的属性信息
数据最终持久化到硬盘上上,体现就是文件。
找文件api思路:文件有名称、大小、创建时间。既然有这么多信息,最好将其封装成对象后,操作更容易。找对象,搜索file,找到File
类。java.io
代表的是文件或者目录(文件夹)。
java.io.File常用方法
查api
(io.xenaliu.io.day1.file
)(io.xenaliu.io.day1.digui
)
-
将文件/文件夹封装成
File
文件File f = new File(String pathName); File f = new File(File parent, String child); File f = new File(String parent, String child);
-
获取文件的信息:名称、大小、时间
String filename = file.getName(); long size = file.length(); long time = file.lastModified();
-
对文件进行操作:创建、删除
boolean b = file.createNewFile(); boolean b = file.delete();//不去回收站,慎用。
-
对目录进行操作:创建、删除
boolean b = dir.mkdir();//创建目录 boolean b = dir.mkdirs();//创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。 boolean b = dir.delete();//删除目录时,如果目录中有内容,无法直接删除
-
判断文件和目录
boolean b = file.exists();//判断文件/目录是否存在 //判断是否是文件或目录,先判断存在 f.isFile();//判断是否为文件 f.isDirectory();//判断是否为目录
-
获取给定目录内部的内容
//方法一: String[] names = dir.list();//返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。 for (String name : names) { System.out.println(name); } //方法二: File[] files = dir.listFiles();//返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录。 for (File file : files) { System.out.println(file.lastModified()); }
-
过滤器:获取给定目录下指定类型的文件(
io.xenaliu.io.day1.filter
)File[] files = dir.listFiles(new FileFilterByDir());
一个栗子:
(
/io/src/io/xenaliu/io/day1/file/FileTest.java
)
需求:列出当前目录下的子目录中的所有内容
方法:递归
字节流
IO程序的书写:
- 导入IO包中的类
- 进行IO异常处理
- 再
finally
中对流进行关闭
字节输出流
需求:数据写入到文件中
输入输出操作:File
对象操作文件的属性等信息,但不能操作文件中的数据。要操作文件中的数据,需要对数据进行输入输出流操作(往文件中写入数据),通过api查找output
,找到java.io.OutputStream
OutputStream
:输出字节流的超类
- 操作的数据都是字节
- 定义了输出字节流的基本共性功能
- 输出流中定义都是写
write
方法操作字节数组或单个字节 - 子类有规律:所有名称后缀都是父类名,前缀名就是这个流对象的功能
FileOutputStream
常用方法
- 将数据写到文件中
(/io/src/io/xenaliu/io/day2/bytestream/FileOutputStreamDemo.java
)
//创建临时目录
File dir = new File("tempfile");
if (!dir.exists()) {
dir.mkdir();
}
//创建存储数据的文件
File file = new File("tempfile\\file.txt");
//创建一个用户操作文件的字节输出流对象。一创建就必须明确数据存储目的地。
//输出流目的是文件,会自动创建,如果文件存在,则覆盖
FileOutputStream fos = new FileOutputStream(file);
byte[] data = "abcdef".getBytes();
//调用父类中的write方法
fos.write(data);
//关闭流资源
fos.close();
- 将数据续写到已有文件中
(/io/src/io/xenaliu/io/day2/bytestream/FileOutputStreamDemo2.java
)
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
//FileOutputStream(File file, boolean append)续写
File file = new File("tempfile\\file.txt");
FileOutputStream fos = new FileOutputStream(file, true);
String str = LINE_SEPARATOR+"xenaliu";
fos.write(str.getBytes());
fos.close();
}
注意:自己写代码时一定要记得解决异常
try/catch
。
(/io/src/io/xenaliu/io/day2/bytestream/FileOutputStreamDemo3.java
)
public static void main(String[] args) {
File file = new File("k:\\file.txt");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write("abdcf".getBytes());
fos.close();
}catch (IOException e) {
System.out.println(e.toString()+"-------");
}finally {
if (fos!=null) {
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
}
}
字节输入流
需求:读取文件中的数据,显示在屏幕上
通过api查找input
,找到java.io.InputStream
InputStream
:输出字节流的超类
- 操作的数据都是字节
- 定义了输出字节流的基本共性功能
- 输入流中定义都是读
read
方法操作字节数组或单个字节 - 子类有规律:所有名称后缀都是父类名,前缀名就是这个流对象的功能。如:
InputStream
的子类FileInputStream
;Reader
的子类FileReader
。
FileInputStream
常用方法
- 读取文件中的数据
(/io/src/io/xenaliu/io/day2/bytestream/read/FileInputStreamDemo.java
)
(/io/src/io/xenaliu/io/day2/bytestream/read/FileInputStreamDemo2.java
)
File file = new File("tempfile\\\\file.txt");
//创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联
FileInputStream fis = new FileInputStream(file);
//方法一:
//读取数据,使用read(),一次读一个字节
int ch = 0;
while ((ch=fis.read())!=-1) {
System.out.println("ch="+(char)ch);
}
//方法二:
int len = 0;//读入缓冲区的总字节数,如果没有更多的数据,因为文件的结尾已经到达, -1 。
//创建一个字节数组,作为读取数据的缓冲区
byte[] buffer = new byte[1024];//长度可以定义为1024的整数倍
//从该输入流读取最多b.length字节的数据到字节数组
while ((len=fis.read(buffer))!=-1) {
System.out.println(new String(buffer,0,len));
}
//关闭资源
fis.close();
字节流缓冲区对象
其实自定义数组就可以解决缓冲区问题并提高效率,为什么还要使用流中的缓冲区对象呢?因为缓冲区对象中除了封装数组以外,还提供了更多的操作缓冲区数据的方法。
BufferedInputStream
类:
BufferedInputStream bufis = new BufferedInputStream(InputStream in);//构造函数
bufis.read();
BufferedOutputStream
类:
BufferedOutputStream bufos= new BufferedOutputStream(OutputStream out);//构造函数
bufos.write();
bufos.flush();
练习
练习:复制文件:读取一个已有的数据,并将这些读取的数据写入另一个文件中
(/io/src/io/xenaliu/io/day2/bytestream/test/CopyFileTest.java
)
(/io/src/io/xenaliu/io/day2/bytestream/test/CopyFileByBufferTest.java
)
练习:将学生对象(姓名,语文分数,数学分数,英语分数,总分)按照总分从高到低排序,并将姓名从高到低写入文件中
(/io/src/io/xenaliu/io/day2/test/Test2.java
)
建立一个清单列表:
获取指定目录下所有的.java文件(包含子目录中的),并将这些java文件的绝对路径写入到一个文件中,建立一个java文件清单列表
(/io/src/io/xenaliu/io/day2/test/Test3.java
)
编码表
生活中的文字和计算机二进制的对应关系表
文字--->二进制(数字):编码
二进制(数字)--->文字:解码
常见码表:
- ASKII:英文表,用一个字节中的7位表示,对应的字节都是正数。0-xxxxxx
- ISO8859-1:拉丁码表latin,用一个字节中的8位表示,对应的字节都是负数。1-xxxxxxx
- GB2312:简体中文码表,六七千个中文和符号。用两个字节表示。两个字节都是开头为1,两个字节都是负数
GBK:目前最常用的中文码表,2w+的中文和符号,用两个字节表示,一部分文字第一个字节开头是1,第二个字节开头是0
GB18030:目前收录量最大的中文码表 - unicode:国际标准码表。无论什么文字,都用两个字节存储。
java
中的char
类型用的就是这个码表。char c = 'a';
占两个字节。 - UTF-8:基于unicode改良,一个字节就可以存储数据,而不是必须两个字节。这个码表更加标准化,在每个字节头加入了编码信息(后期到api中查找)。
编码与解码
字符串:String
字节数组:byte[]
字符串--编码(getBytes()
)-->字节数组
字节数组--解码(new String(byte[])
)-->字符串
(/io/src/io/xenaliu/io/d20100106/encoding/EncodingDemo.java
)
注:
- 在
java
中,字符串是按照系统默认码表来解析的,简体中文版字符串默认的码表是GBK。 - 对于开发而言,常见的编码表有GBK、UTF-8、ISO8859-1
- 能识别中文的码表:GBK、UTF-8,正因为识别中文码表不唯一,涉及到了编码解码问题。
栗子:
对字符串按照字节数截取
(/io/src/io/xenaliu/io/d20100106/encoding/CutStringTest.java
)
联通乱码问题
(/io/src/io/xenaliu/io/d20100106/encoding/LianTongDemo.java
)
字符流
字符流的由来: 为了便于操作数据中的字符数据。字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字,再对这个文字进行操作。
原理: 字节流+编码表
字符流的两个基类:
Reader:read()
读取字符
Writer:write()
:写入字符
字节流操作的是字节数组,字符流操作的是字符数组。
字符流操作文件的便捷类
字符流创建文件
需求
将一些文字存储到硬盘一个文件中。
如果要操作文字数据,建议优先考虑字符流。而且要将数据从内存写到硬盘上,要使用字符流中的输出流Writer
。硬盘的数据基本体现是文件,希望找到一个可以操作文件的writer
。
通过api找到FileWriter
基本操作
- 创建流对象,建立数据存放文件
- 调用流对象的写入方法,将数据写入流
- 关闭流资源,并将流中的数据清空到文件中。
(/io/src/io/xenaliu/io/d20100103/charstream/FileWriteDemo.java
)
FileWriter fw = null;
try{
fw = new FileWriter("Test.txt");
fw.write("text");
// fw.flush();
}
catch (IOException e){
System.out.println(e.toString());
}
finally{
if(fw!=null){
try{
fw.close();
}
catch (IOException e){
System.out.println(e.toString());
}
}
}
flush()
和close()
的区别:
flush()
:将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用
close()
:关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后关闭流,流不可以继续使用
如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close()
完成刷新并关闭。
注:如果缓冲区满了,即使没有刷新也会自动写入目的地,但如果缓冲区没满,则不会自动写入,数据可能丢失,所以一定要一边写一边刷新。
字符流读取文件
需求
读取一个文本文件。将读取到的字符打印到控制台。要使用字符流中的输出流Reader
。硬盘的数据基本体现是文件,希望找到一个可以操作文件的Reader
。
通过api找到FileReader
基本操作
- 建立一个流对象,将已存在的一个文件加载进流
- 创建一个临时存放数据的数组
- 调用流对象的读取方法将流中的数据读入到数组中
(/io/src/io/xenaliu/io/d20100103/charstream/CharStreamDemo.java
)
FileReader fr = null;
try{
fr = new FileReader("c:\\test.txt");
char[] buf = new char[1024];
int len= 0;
while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
}
catch (IOException e){
System.out.println("read-Exception :"+e.toString());
}
finally{
if(fr!=null){
try{
fr.close();
}
catch (IOException e){
System.out.println("close-Exception :"+e.toString());
}
}
}
一些技巧(FileReader
为例)
了解一个子类的功能,先它所属的体系顶层。
栗子:FileReader
,它的体系顶层为Reader
Reader
:读取字符流的抽象超类
Reader
的功能:
int read();//Reads a single character
int read(char[] cbuf)//Reads characters into an array.
转换流:流中的编码与解码
需求
编码(存储): 字节--->字符:看不懂的--->看得懂的
既然识别中文的码表有两个,GBK和UTF-8,能不能将中文数据按照utf-8的方式进行文件的存储?
不能使用FileWriter
,因为FileWriter
中默认的是GBK。
通过FileWriter
的api描述,要指定编码表这些值,需要使用OutputStreamWriter
。OutputStreamWriter
是字符流通向字节流的桥梁:可使用指定的charset
将要写入流中的字符编码成字节。它的作用是:将字符串按照指定的编码表转成字节,再使用字节流将这些字节写出去。
解码(读取): 字符--->字节:看得懂的--->看不懂的
同上理。InputStreamReader
。
转换流的由来:
- 字符流与字节流之间的桥梁
- 方便了字符流与字节流之间的操作
转换流的应用: 字节流中的数据都是字符时,转成字符流操作更高效
基本操作
public static void writeCN() throws UnsupportedEncodingException, FileNotFoundException, IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("tmpfile\\u8cn.txt"),"utf-8");
osw.write("你好");
osw.close();
}
public static void readCN() throws IOException {
FileReader fr = new FileReader("tmpfile\\u8cn.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream("tmpfile\\u8cn.txt"),"utf-8");
char[] buf = new char[1024];
int len = isr.read(buf);
System.out.println(new String(buf,0,len));//你好
fr.close();
}
总结
继承关系:
OutputStreamWriter
:
|--FileWriter
InputStreamReader
:
|--FIleReader
父类和子类的功能区别:
OutputStreamWriter
和InputStreamReader
是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。
FileWriter
和FIleReader
作为子类,进作为操作字符文件的便捷类存在。当操作的字符文件内,使用的是默认编码表时可以不用父类,二直接使用子类就可以完成操作,简化了代码。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");
FileReader fr = new FileReader("a.txt");
这三句代码的功能是一样的,其中第三句最为便捷。
注意: 一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。
使用子类的条件:
- 操作的是文件
- 使用默认编码
字节--->字符:看不懂的--->看得懂的,需要读,输入流,InputStreamReader
字符--->字节:看得懂的--->看不懂的,需要写,输出流,OutputStreamWriter
练习:
复制文本文件
(/io/src/io/xenaliu/io/d20100106/charstream/copy/CopyTextFile.java
)
字符流缓冲区
字符流缓冲区对象
BufferedReader
类:
BufferedReader bufr = new BufferedReader(Reader in);//构造函数
bufr.read();
bufr.readLine();//一次读取一行
BufferedWriter
类:
BufferedWriter bufw = new BufferedWriter(Writer out);
bufw.write();
bufw.newline();
bufw.flush();//写一行刷新一次
缓冲区原理:
- 使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内
- 通过缓冲区的
read()
方法从缓冲区中获取具体的字符数据,这样就提高了效率 - 如果用
read()
方法读取字符数据并存储到另一个容器中,直到读取到了换行符时,另一个容器中临时存储的数据转成字符串返回,就形成了readLine()
功能
模拟缓冲区原理:
read()
和readLine()
方法
(/io/src/io/xenaliu/io/d20100107/buffer/MyBufferReader.java
)
装饰设计模式
一个引出栗子
TextReader
:读取文本
MediaReader
:读取媒体数据
抽取共性,形成体系
Reader
|--TextReader
|--MediaReader
需求1:提高读取文本的效率,使用缓冲技术,提供一个读取文本更高效的读取方法。覆盖
TextReader
中的方法,建立高效的Read()
方法。所以建立一个TextReader
的子类,用以高效的读取。
需求2:读取媒体数据也想高效,那就同理,也给读取媒体数据的对象派生一个高效子类
继承体系:
Reader
|--TextReader
|--BufferedTextReader
|--MediaReader
|--BufferedMediaReader
一个小问题: 如果Reader
中还有读取其他数据的子类,如果要高效,就必须给其他子类也添加一个高效子类。为了给具体读取数据的对象增加一些功能,需要通过子类来完成,但是这样做,会导致这个集成体系很臃肿。仅仅为了增加一些功能,而进行继承,是不建议的
解决方法: 这些子类无非就是需要高效,而这些高效的功能实现是一致的,就是提供了一个缓冲区而已,没有必要每一个对象都存在一个功能重复的子类。干脆单独定义一个具备这个缓冲功能的对象class BufferedReader
,哪个子类需要被缓冲,就将哪个子类传递进来。
class BufferedReader extends Reader{
private [];//提供数组
BufferedReader(Reader r){}//对Reader高效
read(){//高效的读取动作
操作的是数组
}
}
解决后的继承体系:
Reader
|--TextReader
|--MediaReader
|--BufferedReader
这种设计方式减少了继承体系的臃肿,增加了功能,比继承更为灵活,称为装饰设计模式。
装饰设计模式概述
解决问题: 给一组类增加功能,避免继承的臃肿,提高灵活性。
注意: 装饰类和被装饰类必须所属于同一个体系,通常装饰类都会提供构造函数接收被装饰类对象,装饰类通常不单独存在。
举个栗子:
房子 居住()
|--毛坯楼房 居住(){简陋}
|--毛坯平房
|--田园风光房
|--欧式风格房
class 田园风光房 extends 房子{
田园风光房(房子){}
居住(){
田园风格的居住,惬意!
}
}
class 欧式风格房 extends 房子{
欧式风格房(房子){}
居住(){
欧式设计的居住,高端大气上档次!
}
}
毛坯楼房 x = new 毛坯楼房();
x.居住();//普通
田园风光房 y = new 田园风光房(x);
y.居住();//普通惬意
欧式风格房 z = new 欧式风格房(y);
z.居住();//普通惬意高端大气上档次
标准输入输出流
System
类中的字段:in
、out
,它们各代表了系统标准的输入和输出设备,默认输入设备是键盘,输出设备是显示器
System.in
的类型是InputStream
System.out
的类型是PrintStream
是OutputStream
的子类FilterOutputStream
的子类
需求: 将键盘录入的数据存储到文件中
按字节读取:
(/io/src/io/xenaliu/io/d20100107/readkey/ReadKeyDemo.java
)
按行读取:
(/io/src/io/xenaliu/io/d20100107/readkey/ReadKeyDemo2.java
)
以后但凡提到了键盘录入,就使用以下操作,一行一行的读取,除非要对读取每一个字节操作
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
IO流对象使用规律
规律总结
字节流:
InputStream
OutputStream
FileInputStream
FileOutputStream
FilterInputStream
FilterOutputStream
|--BufferedInputStream
|--BufferedOutputStream
字符流:
Reader
Writer
InputStreamReader
OutputStreamWriter
|--FileReader
|--FileWriter
BufferedReader
BufferedWriter
问题: IO流中对象很多,解决问题(处理设备上的数据)时到底改用哪个对象?
四个明确:
要操作的数据时数据源还是数据目的?根据需求明确要读还是要写
源:InputStream
Reader
目的:OutputStream
Writer
要操作的设备上的数据是字节还是文本?
源:
字节:InputStream
文本:Reader
目的:
字节:OutputStream
文本:Writer
明确数据所在的具体设备
源设备:
硬盘:文件,File
开头
内存:数组、字符串
键盘:Syetem.in
网络:Socker
目的设备:
硬盘:文件,File
开头
内存:数组、字符串
屏幕:Syetem.out
网络:Socker
是否需要额外功能?
转换吗:转换流InputStreamReader
OutputStreamWriter
高效吗:缓冲区对象BufferedReader
BufferedWriter
有多个源吗:序列流SequenceInputStream
对象需要序列化吗:ObjectInputStream
ObjectOutputStream
需要操作基本类型数据保证字节原样性吗:DataOutputStream
DataInputStream
规律应用
需求一:将字符串写入到文件中
明确1: 有源吗?有目的吗?
源:字符串
String
,不用IO技术,直接定义String
字符串即可
目的:文件。使用IO技术,输出流OutputStream
Writer
明确2: 是文本数据吗?
是。目的:
Writer
明确3: 具体设备是?
目的设备:硬盘
Writer
体系中的File
开头的对象
具体要使用的对象是FileWriter
FileWriter fw = new FileWriter("a.txt");
fw.writer(string);
明确4: 需要额外功能吗?
需要,高效。缓冲区对象
BufferedWriter
BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));
bufw.write(string);
bufw.newLine();
bufw.flush();
需求二:复制一个文本文件,有可能在复制过程中对文本进行过滤
明确1: 有源吗?有目的吗?
源:文件。使用IO技术,输入流
InputStream
Reader
目的:文件。使用IO技术,输出流OutputStream
Writer
明确2: 是文本数据吗?
是。
源:Reader
目的:Writer
明确3: 具体设备是?
源设备:硬盘
Reader
体系中的File
开头的对象
具体要使用的对象是FileReader
目的设备:硬盘
Writer
体系中的File
开头的对象
具体要使用的对象是FileWriter
注: 如果仅做复制动作,不需要考虑数据是字节还是文本,直接使用字节流即可。
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fis = new FileOutputStream("b.txt");
但是如果在复制过程中,需要对文本中的字符数据进行操作,必须使用字符流
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
fw.writer(string);
明确4: 需要额外功能吗?
需要,高效。缓冲区对象
BufferedReader
BufferedWriter
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
String line = bufr.readLine();
line = line.replace("nba","美国职业球赛大联盟");
bufw.write(line);
需求三:读取键盘录入将数据存储到文件中
明确1: 有源吗?有目的吗?
源:键盘。使用IO技术,输入流
InputStream
Reader
目的:文件。使用IO技术,输出流OutputStream
Writer
明确2: 是文本数据吗?
是。
源:Reader
目的:Writer
明确3: 具体设备是?
源设备:键盘
System.in
目的设备:硬盘
Writer
体系中的File
开头的对象
具体要使用的对象是FileWriter
InputStream in = System.in;
FileWrite fw = new FileWrite("a.txt");
byte[] buf = new byte[1024];
int len = in.read(buf);
String str = new String(buf,0,len);
fw.write(str);
明确4: 需要额外功能吗?
需要,转换。因为明确源的体系是
Reader
,可是具体设备System.in
时字节流,需要字符流,需要转换功能,将字节流转成字符流。字节-->字符InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("a.txt");
还需要额外功能吗?
需要,高效
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));
需求四:读取文本文件打印到屏幕上
明确1: 有源吗?有目的吗?
源:文件。使用IO技术,输入流
InputStream
Reader
目的:屏幕。使用IO技术,输出流OutputStream
Writer
明确2: 是文本数据吗?
是。
源:Reader
目的:Writer
明确3: 具体设备是?
源设备:硬盘
Reader
体系中的File
开头的对象
具体要使用的对象是FileReader
目的设备:屏幕
System.out
FileReader fr = new FileReader("a.txt");
PrintStream out = System.out;
其实这样就已经完成需求了,因为PrintStream
中有很多print
方法。
fr.read();
out.println();
明确4: 需要额外功能吗?
需要,高效。缓冲区对象
BufferedReader
BufferedWriter
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new OutputStreamWriter(System.out);
需求五:读取文件中的文本数据,将数据按照UTF-8的方式存储到文件中
是。
源:Reader
目的:Writer
明确3: 具体设备是?
源设备:硬盘
Reader
体系中的File
开头的对象
具体要使用的对象是FileReader
目的设备:硬盘
Writer
体系中的File
开头的对象
具体要使用的对象是FileWriter
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
fw.writer(string);
但是不符合题目的要求,对于目的的要求必须是UTF-8的编码,使用必须使用额外功能
明确4: 需要额外功能吗?
需要,转换。
FileReader fr = new FileReader("a.txt");//默认编码
OutputStreamWriter osw = new OutputStreamWriter(new OutputStream("b.txt"),"utf-8");
还需要其他额外功能吗?
需要,缓冲区
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new OutputStream("b.txt"),"utf-8"));
IO包中的其他类
总结
- IO基础流对象: 直接调用底层资源,操作数据的对象
- 根据io流的学习规律,后面出现的流对象无非是在增加一些额外功能 :
需求:写一个数据(整数)到文件中,保证输出值的表现形式
可以通过将整数转成字符串,变成字节数组写入目的地。
简化方式,之前学习过输出语句发现要输出的内容都原样不变的体现出来。输出语句对应的对象PrintStream
,提供了很多打印的方法,打印方法的好处在于保证输出值的表现形式。
Printwriter
:字符打印流
需求:保证数据值字节原样性不变
如:写一个整数,源是四个字节,希望目的也是四个字节。
需要可以操作基本数据类型数值的对象。
DataOutputStream
源和目的都是内存的流对象:
字节流:
ByteArrayInputStream ByteArrayOutputStream
字符流
CharArrayReader
CharArrayWriter
StringReader
StringWriter
原理其实是用过流的read()
、write()
方法对数组及字符串进行操作
关闭这些流都是无效的,因为并未调用系统资源,不需要抛出IOException
需求:对文件进行读或者写的操作,想从哪里读就从哪里读,想从哪里写就从哪里写。