字符流、缓冲区、转换流
-
字符流
-
字符流介绍
-
我们使用IO流读写数据的时候,有时会出现部分的字节合并在一起,它们表示的是应字符数据。
/*
* 演示使用字节流读取汉字
*/
public class Demo {
public static void main(String[] args) throws IOException {
// 创建文件字节输入流
FileInputStream fis = new FileInputStream("e:/11.txt");
System.out.println(fis.read());
System.out.println(fis.read());
System.out.println(fis.read());
System.out.println(fis.read());
System.out.println(fis.read());
System.out.println(fis.read());
fis.close();
}
}
文件中的数据:
读取的结果:
我们使用字节流读取文本文件中的字符内容的时候,是按照文件中的一个一个字节在读取数据。而拿到读取到的每个字节数据并不知道这些数字背后对应的字符是什么?
相当于我们如果使用字节流操作某些字符数据,就可能导致操作的数据不利于程序对这些结果的处理。这时Java又给出另外一组IO流:
字符流:它们专门来读写字符数据。
数据在任何的存储设备上,肯定是二进制形式保存,那么sun公司给出的字符流底层肯定还是在读取这些二进制数据,只是它们内部会将这些二进制给我们转成对应的字符内容。
其实字符流它的本质依然是在操作字节数据,只是字符流在内部会自动的将字节转成字符,最后使用字符流的程序得到是字符而不是字节。
字符流 = 字节流 + 转换的编码表。
-
编码表介绍
计算机是老美他们发明的。而计算机的底层是以二进制的形式保存数据。
老美他们就想办法需要将自己的文字(英文字母、数字、标点符号等)输入到计算机中,可以让计算机处理生活中的文字。
于是 老美他们发明了一个表格:表格中有生活中的文字,还有数字,以及所有的符号,将这些所有的数据与每个数字进行一一对应,然后将数字转成二进制,最后将二进制保存在计算机中。
表格:
生活中的文字 十进制值 二进制值
0 49 0011 0001
1 50 0011 0010
A 65 0100 0001
B 66 0100 0010
a 97 0110 0001
b 98 0110 0010
上面的这个表格它就是全球通用的ASCII(国际信息交易码)编码表。
编码表:将生活中的文字与计算机可以识别的文字进行一一对应的数字的编号(编码)。
ASCII 编码表:它规定所有的字符全部使用一个字节表示,并且要求这个字节的最高位必须是0开始,
例如: A 0100 0001
欧洲的编码表:
拉丁文编码表: ISO-8859-1 它也规定使用一个字节表示数据,但是最高位可以是零也可以是1,一次这个编码表中有负数表示生活中的数据。
中国的编码表:
GB2312 : 它可以识别大约六千到七千的中国文字。
升级:GBK,它大约识别两万多。目前主流的汉字的编码表。
GB18030 : 它识别就更多了。
上述的编码统一采用的是2个字节表示汉字。
国际计算机协会就站出来指定了一个全球通用的编码表:
unicode:它兼容全球大部分国家的通用的字符。这个编码表它也采用2个字节表示一个字符。JDK目前内置的这个编码表。
JDK中的char类占用2个字节的内存。
升级UTF-8 编码表: 它是目前全球主流的通用的编码表。
UTF-8的特点:
1个字节可以表示的数据,就采用1个字节
2个字节可以表示的数据,就采用2个字节
3个字节可以表示的数据,就采用3个字节
必须记住的编码表名称:
ASCII:任何编码表都兼容
ISO-8859-1 : 拉丁文编码表
可以识别中文的编码表:
GB2312、GBK、unicode、utf-8.
-
字符流底层
-
字符输出流
Writer:它是字符输出流的超类(根类、基类)。它中肯定定义的如何写出字符数据。
方法简单介绍:
关闭流对象。
前三个write和字节处流write方法基本对应的。
write(String str) : 将整个字符串写到底层
write(String str , int off , int len) : 将字符串从off位置开始共计写出len个
flush刷新,是将缓冲区中的数据写刷新到底层。
-
文件字符输出流
用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。
FileWriter:它是专门用来写将字符写文件中的文件字符输出流。FileWriter它在输出字符数据的时候,使用的操作系统默认的编码表(中文简体版操作系统默认的编码表为GBK)。
在我们创建FileWriter对象的时候,底层会使用操作系统默认的编码表(本地默认编码表),同时还会在FileWriter的内部创建一个字节数组(字节缓冲区)。
使用操作系统默认的编码表的目的是在调用FileWriter中的write方法的时候,根据编码表将需要写出的字符数据转成编码值。查询到每个编码值之后,将这个编码值转成二进制(字节),将这个字节存储在FileWriter内部的字节缓冲区(字节数组)中。
当我们使用了字符输出流输出数据之后,一定要记得关流,或者一定要记得调用flush方法。
因为字符输出流,会将输出的字符数据转成字节数据最终存储在自己内容的字节缓冲区中。如果不关流,或不调用flush,那么及时程序运行结束,需要输出的数据依然在输出流的缓冲区中,根本无法写到底层文件中。
// 最基本的输出
public static void demo1() throws IOException {
// 创建输出流对象
FileWriter fw = new FileWriter("e:/fw.txt");
// 写字符数据
fw.write("演示字符串输出流");
// 调用刷新功能
fw.flush();
// 关流
fw.close();
}
面试题:
字符流中的close和flush有什么区分?
flush仅仅是刷新输出流的缓冲区,将缓冲区中的数据刷出到底层,保证可以输出。但是流不会被关闭,也就是刷新之后,依然可以调用流的write方法继续写数据。
close它是在关闭流之前,刷新缓冲区。然后将流关闭。流一旦被关闭,就无法在继续使用写数据。
上面的异常发生的原因是:在流关闭之后,还在调用流的输出功能写数据。
一定要注意:字符输出流,有字节缓冲区,一定在写数据之后记得刷新缓冲区。
-
字符输入流
Reader:它是字符输入流的超类(根类、基类)。它中定义了如何读取字符的方法。
上面的read方法只要读取到文件末尾统一返回-1.
read() : 它是读取单个字符数据,并返回这个字符数据的编码值。
read( char[] cbuf ) : 它一次可以读取读个字符数据,将读取到的字符数据存储在字符数组中,返回读取的字符个数。
-
文件字符输入流
用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的
FileReader:它是用于读取文件中的字符数据(其实就是用来读取记事本、写字板)。在读取的时候它的底层使用的操作系统默认的编码表和默认大小的字节缓冲区。
字符输入流的编码表:
字符输入流的底层,其实它不能直接读取字符数据,依然读取的字节数据。只是将读取到的字节数据根据编码表找到对应的编码值。
字符输入流底层字节缓冲区:
由于字符输入流不能直接读取字符,而先要从文件中将字节读取到FileReader内部,这时FileReader会将读取到的那些字节数据先临时存放在自己内部的字节缓冲区中,然后在调用read方法读取的时候,FileReader的内部会到字节缓冲区中获取字节数据,然后通过编码表转成编码值。
/*
* 演示字符输入流
*/
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
demo3();
}
// 一次读取多个字符数据
public static void demo3() throws IOException{
// 创建输入流对象
FileReader fr = new FileReader("e:/fw.txt");
// 需要字符数组
char[] buf = new char[1024];
// 定义变量,记录读取的字符个数
int len = 0;
while( ( len = fr.read(buf) ) != -1 ){
System.out.println( new String( buf , 0 , len ));
}
// 关流
fr.close();
}
// 使用循环读取
public static void demo2() throws IOException{
// 创建输入流对象
FileReader fr = new FileReader("e:/fw.txt");
// 定义变量,记录读取的字符数据
int ch = 0;
// 循环读取
while( ( ch = fr.read() ) != -1 ){
System.out.println((char)ch);
}
// 关流
fr.close();
}
// 演示一次读取一个字符数据
public static void demo1() throws IOException {
// 创建输入流对象
FileReader fr = new FileReader("e:/fw.txt");
// 读取数据
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
// 关闭流对象
fr.close();
}
}
-
字符流读取非字符数据的问题分析
非字符数据:图片、音频、视频、压缩文件、word等,它们都是非字符数据的文件。
字符数据:记事本、写字板。
/*
* 演示使用字符流复制图片
*/
public class Test {
public static void main(String[] args) throws IOException {
// 定义输入流读取数据
FileReader fr = new FileReader("e:/1.docx");
// 定义输出流写数据
FileWriter fw = new FileWriter("c:/1.docx");
// 定义数组
char[] buf = new char[1024];
int len = 0 ;
while( ( len = fr.read(buf) ) != -1 ){
fw.write(buf, 0, len);
}
fw.flush();
// 关流
fr.close();
fw.close();
}
}
上面程序复制非字符文件的时候出现错误情况。
使用字符流读写非字符数据:
在读写的时候如果读取的字节数据在编码表中没有对应的字符数据,这时就会使用编码表中统一的未知字符代替,在写出的时候,这个未知字符就没有办法还原成原来的字节数据,写出之后导致文件复制的数据发生错误。
后期一定要记住:
不要使用字符流读写出记事本、写字板之外其他任何数据文件。
如果在开发中不确定数据是字节还是字符,统一使用字节流操作。
-
转换流(重点)
-
转换流介绍
-
在读写数据的时候,可以使用字节流读写,有时如果是字符文件的还可以使用字符流读写。但在某些情况下,我们不能直接使用字符流读写数据。
如果磁盘上存储的文件不是本地(操作系统)默认的编码表时,这时要么直接使用字节流操作,如果要使用字符流的话,肯定不能使用FileReader或FileWriter完成。
FileReader和FileWriter它们使用的都是操作系统默认的编码表。这时我们就需要先使用字节流读取这些数据,然后在使用转换流将字节转成字符,在转的时候可以人为的指定编码表。
转换流:它的主要功能是将字节转成字符,或者是将字符转成字节,在转换的过程中,可以使用我们指定的编码表。
-
字节转换字符的输入转流(重点)
需求:在磁盘上有个记事本文件,但是这文件中的数据使用的UTF-8的编码表保存。
使用FileReader在读取UTF-8 编码表保存的记事本数据。结果:
记事本中明显保存的汉字"你好",结果使用FileReader读取之后得到的"浣犲ソ",得到的是错误数据。
原因是在读取数据的时候使用的编码表不统一导致。我们只要读取数据的时候和保存数据使用相同的编码表,那么就不会出现错误数据。
FileReader它使用的本地默认(GBK)编码表,也就是说FileReader目前不能读取我们指定的记事本中的数据。
在FileReader的api中提示:要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
InputStreamReader:它是将字节流转成字符流,同时我们在使用InputStreamReader的时候可以指定需要的编码表。InputStreamReader的底层在将字节转成字符的时候,会根据我们指定的编码表进行转换。如果在使用InputStreamReader的时候没有指定编码表,那么InputStreamReader它依然会使用平台默认的编码表。
// 演示使用InputStreamReader读取UTF-8格式的数据
public static void demo2() throws IOException {
// 创建 字节转字符的转换流对象
InputStreamReader isr = new InputStreamReader( new FileInputStream ("e:/1.txt") , "utf-8");
// 读取数据
int ch = 0;
while( ( ch = isr.read() ) != -1 ){
System.out.println((char)ch);
}
// 关流
isr.close();
}
-
字符转字节的输出转换流(重点)
需求:将字符数据以UTF-8形式写到文件中。
上述的需求不能使用FileWriter完成,因为FileWriter中使用的本地默认的GBK编码表。
在FileWriter的API中告诉我们:
要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
现在我们需要创建OuputStreamWriter对象。
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
OutputStreamWriter 它是将字符转成字节的输出的转换流,在将字符转成字节的时候,可以根据指定的编码表进行转换,如果创建OutputStreamWriter的时候没有指定编码表,那么就采用系统默认的编码表,此时的OutputStreamWriter对象和FileWriter的功能一致。
/*
* 演示字符转字节的输出转换流
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
// ctrl + shift + x 将选中的字母全部转成大写
// ctrl + shift + y 将选中的字母全部转成小写
// 创建字符转字节的输出转换流对象
OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream( "e:/ows.txt" ) , "UTF-8");
// 写数据
osw.write("你好");
// 关流 或刷新
osw.flush();
osw.close();
}
}
-
转流的使用细节
-
如果我们在创建转流对象的时候,没有指定编码表,其实就和FileReader或FileWriter没有任何区别。
-
转换流的构造方法中需要的都是字节流。构造方法中的字节流的主要功能是和底层进行字节交互。转换流它的主要功能是完成转换
转换流它的主要功能是进行字符和字节之间的转换的。至于怎么和底层交互那不属于转换流的功能。
底层的数据永远都是字节数据。
InputStreamReader它的主要功能将底层读取的到字节数据根据编码表转成字符数据。它并不负责从底层读取字节数据,因此在InputStreamReader的时候需要给其指定一个可以从底层读取字节的字节输入流。
OutputStreamWriter它的主要功能是将程序中的字符数据根据编码表转成字节数据,它也不会负责将换后的字节写到底层,因此在创建OutputStreamWriter的时候需要给其指定一个可以给底层写字节的输出流对象。
-
-
InputStreamReader它是将字节转换成字符。它的内部也会自己的字节缓冲区,目的是存储底层字节流读取的字节数据
OutputStreamWriter它是将字符转成字节的,它内部也有字节缓冲区,目的是存放转后的字节数据,在调用flush或close方法的时候,会将字节缓冲区中的数据交给底层的字节输出流,最后写到底层文件中。
4、在转换流中只有字符转字节的输出转换流和字节转字符的输入转换流。而没有字符转字节的输入转换流,也没有字节转字符的输出转换流。
转换流的使用场景:
1、读写数据的时候,编码表和外界保存的数据的编码表不一致的时候。
2、有时有些数据只能以字节的方式读取到内存中,但是这些字节表示的却是字符数据,这时就需要使用输入的转换流进行转换。
-
编码解码乱码
编码解码乱码:它们都是进行字节和字符相互转换过程中出现的一些问题。
-
编码(理解)
编码:将字符数据根据编码表转成字节数据的这个过程。
简单理解:字符------编码表-------字节
大白话理解:将人能看懂转成计算机可以识别的。
使用代码测试
字符:String进行表示。
字节:byte数组表示
编码:将String转成字byte数组,它们这个过程就可以理解成编码。
对字符串进行编码:
需要使用String类中的getBytes方法
/*
* 编码演示:
* 对中文"你好"进行编码,GBK编码表得到的数据
* 你 -60 11000100
* -29 11100011
*
* 好 -70 10111010
* -61 11000011
*
* GBK编码表中中文的二进制规则:
* 一个汉字两个字节:每个字节数据格式:
* 第一个字节: 1xxx xxxx
* 第二个字节: 1xxx xxxx 或 0xxx xxxx
*
* ASCII 编码表的规则:使用一个字节表示字符数据,要求0xxx xxxx
* 而我们GBK编码表兼容ASCII编码表
* 1010 1010 0101 0010 0011 1111 0101 0010 10100101001
* 当上面的二进制使用的GBK编码表读取二进制数位的时候:
* 由于第一个二进制是1,因此获取二进制的时候,会认为当前需要读取16个二进制数位(2个字节),
* 然后到GBK编码表中查询对应的中文
*
* 联通的GBK编码形式:
* -63....11000001
-86....10101010
-51....11001101
-88....10101000
utf-8的编码规则:
1个字节可以表示的字符就使用1个字节表示: 0xxx xxxx
2个字节可以表示的字符就使用2个字节表示: 110x xxxx 10xx xxxx
3个字节可以表示的字符就使用3个字节表示: 1110 xxxx 10xx xxxx 10xx xxxx
*/
public class EncodeDemo {
public static void main(String[] args) {
//准备需要被编码的字符数据
String s = "联通";
// 编码
byte[] bs = s.getBytes();
// 打印编码后的结果数据
for (byte b : bs) {
System.out.println(b + "...." + Integer.toBinaryString(b));
}
}
}
-
解码(理解)
解码:将字节数据根本编码表转成字符数据过程。
简单理解:字节-----编码表-----字符
大白话:将计算机可以识别的转成人可以看懂的。
代码体现:将byte数组转成String。
/*
* 解码演示
*/
public class DecodeDemo {
public static void main(String[] args) {
// 定义字节数组
//byte[] buf = {97,98,99};
byte[] buf = {-28,-67,-96,-27,-91 ,-67};
// 使用构造方法转换
String s = new String(buf);
System.out.println(s);
}
}
-
乱码(重点)
乱码:编码和解码的时候使用编码表不一致,导致出现的字符乱码。
/*
* 乱码演示
*/
public class DecodeDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
// 定义字节数组
//byte[] buf = {97,98,99};
byte[] buf = {-24,-127,-108,-23,-128 ,-102};
// 使用构造方法转换 使用iso-8859-1解码 ,出现了乱码
String s = new String(buf , "iso-8859-1" );
System.out.println(s);
// 上面的s 字符串中的数据已经乱码数据,需要从新编码
byte[] bs = s.getBytes( "iso-8859-1" );
// 在对编码后的数组进行解码
String s2 = new String( bs , "utf-8" );
System.out.println(s2);
// 上面解决乱码的代码后期开发中经常需要合并在一起
String s3 = new String( s.getBytes( "iso-8859-1" ) , "utf-8" );
System.out.println(s3);
}
}
-
编码解码相关的类
// 定义字节数组
//byte[] buf = {97,98,99};
byte[] buf = {-24,-127,-108,-23,-128 ,-102};
// 使用构造方法转换 使用gkb解码 ,出现了乱码
String s = new String(buf , "iso-8859-1" );
System.out.println(s);
// 演示使用JDK中提供的可以完成编码和解码的类
String ss = URLEncoder.encode(s, "iso-8859-1");
// 解码
String ss2 = URLDecoder.decode(ss, "utf-8");
System.out.println(ss2);
System.out.println(ss);
URLEncoder:它是完成编码的,其中有一个静态的encode方法
URLDecoder:它是完成解码,其中有一个静态的方法进行解码:
-
字符流缓冲区
-
字符缓冲区介绍
-
缓冲区:它主要是用来临时存储数据,缓冲区本身就是容器。
关于IO流的缓冲区:
不管是输入流,还是输出流,它们都有自己的缓冲区。
输入流:它的目的是一次可以从底层读取更多的数据存储在缓冲区内部的数组中,当程序中调用read方法的时候,这时read方法不在会从底层获取数据,而是从输入流的缓冲区中读取数据。
输出流:当前我们调用write方法在给出写数据的时候,这时如果使用的输出的缓冲区,那么数据就不会直接写到底层,而是将需要写出的数据保存在输出的缓冲区中,当缓冲区写满,或调用的flush方法,或关流,这时才会缓冲区中的数据一次性全部写出。
流的缓冲区:存在的目的是为了提高程序读写数据的效率。
-
字符缓冲区演示
-
BufferedReader演示
-
BufferedReader:它是字符输入流的缓冲区,可以提高读取字符数据的效率。
BufferedReader:它是本质仅仅是提供内部的缓冲区数组,而这个流不能从底层读取字符数据。因此在使用BufferedReader的时候,需要给其传递一个可以从底层读取数据的字符流对象。
/*
* 演示字符输入流缓冲区
*/
public class BufferedRaederDemo {
public static void main(String[] args) throws IOException {
demo2();
}
// 演示使用readLine方法 读取一行代码的模版程序
public static void demo2() throws IOException {
// 创建字符输入流缓冲区对象
BufferedReader bufr = new BufferedReader( new FileReader("e:/123.txt"));
/*
* 由于readLine方法读取到文件的末尾返回的null,因此模版代码中需要判断读取的数据是否为null
* 如果是null,说明一定读取到文件的末尾,如果不是,说明文件中的数据还没有读取结束
*/
// 由于readLine读取的一行数据,所以定义字符串来接收这个行数据
String line = null;
while( ( line = bufr.readLine() ) != null ){
System.out.println(line);
}
// 关闭流对象
bufr.close();
}
// 演示BufferedReader基本使用
public static void demo1() throws IOException {
// 创建字符输入流缓冲区对象
BufferedReader bufr = new BufferedReader( new FileReader("e:/123.txt"));
// 使用BufferedReader类中的特有的方法readLine读取文本中的一行数据
System.out.println(bufr.readLine());
// 关闭流对象
bufr.close();
}
}
-
BufferedWriter演示
BufferedWriter:它是字符输出流缓冲区,它可以提供写出字符的效率。
BufferedWriter它是提供在写出字符数据的时候,可以提供缓冲区数组,将写出的字符数据先临时存储在缓冲区中,而缓冲区写满,或者调用flush,或者关流,才能够将缓冲区中的数据写到底层。其实BufferedWriter它根本不能给底层写数据,因此在创建这个对象的时候需要给其传递一个可以给底层写字符数据的字符输出流。
/*
* 演示BufferedWriter
*/
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
// 创建字符输出流缓冲区对象
BufferedWriter bufw = new BufferedWriter( new FileWriter("e:/bufw.txt") );
// 写数据
bufw.write("你好");
// 使用BufferedWriter中的特有的换行方法
bufw.newLine();
bufw.write("大家好!!!");
// 刷新 或关流
bufw.flush();
bufw.close();
}
}
-
键盘录入
前面我们学习Scanner类完成键盘的录入。
Scanner类是JDK1.5时期提供的键盘录入的类。
演示JDK1.5之前的键盘录入:
/*
* 演示键盘录入
*/
public class KeyInfo {
public static void main(String[] args) throws IOException {
demo2();
}
// 演示使用BufferedReader中的readLine方法完成键盘按行录入
public static void demo2() throws IOException {
// 创建字符输入流缓冲区对象
BufferedReader bufr = new BufferedReader( new InputStreamReader( System.in ) );
System.out.println("请输入数据:");
String line = bufr.readLine();
System.out.println(line);
}
// 演示 JDK5之前的键盘录入
public static void demo1() throws IOException {
// 需要使用System类获取系统的输入流
InputStream in = System.in;
System.out.println("请输入数据:");
int ch = in.read();
System.out.println(ch);
}
}
-
演示Scanner的时候
Scanner它是JDK5时期出现的类:它可以读取键盘录入的数据,也可以读取文件中的数据,也可以读取网络中的数据。
/*
* 演示使用Scanner类读取文件中的数据
*/
public class ScannerDemo {
public static void main(String[] args) throws IOException {
// 创建Scanner的对象
Scanner sc = new Scanner( new File("e:/1.txt") ,"utf-8" );
String line = sc.nextLine();
System.out.println(line);
}
}
-
流总结
-
io技术简单总结
-
IO技术中:
File类:它的功能是操作文件或文件,但不能读写文件中的数据。
IO流对象:
字节流:
字节输入流:
InputStream:
FileInputStream:它是负责以字节的方式读取文件中的数据
输入流有2个模版代码:
一次读取一个字节:
一次读取多个字节:
字节输出流:
OutputStream:
FileOutputStream:它是负责将程序中的数据以字节的方式写文件中
字符流:
字符输入流:
Reader:
InputStreamReader:它是字节转字符的输入转换流,可以指定编码表
FileReader:它是使用系统默认的编码表和缓冲区大小读取字符数据
模版代码:
一次读取一个字符:
一次读取多个字符:
BufferedReader:它是字符输入流的缓冲区,其中提供了readLine方法可以一次读取一行数据
模版代码:
BufferedReader bufr = new BufferedReader( new FileReader("文件") );
String line = null;
while( ( line = bufr.readLine() ) != null ){
}
bufr.close();
字符输出流:
Writer:
OutputStreamWriter:它是字符转字节的输出转换流,可以指定编码表
FileWriter:它是使用系统默认的编码表和缓冲区,将字符转成字节写出去。
BufferedWriter:它是字符输出流的缓冲区
-
IO流的使用规律
File类的使用:如果我们仅仅是操作文件和文件夹,根本不需要操作文件中的数据,这时肯定使用file类。
IO流的使用:
1、分析读写的数据自身的特点。
分析读写的数据到底是字节还是字符数据。通过分析我们可以确定到底需要使用的字符还字节流对象。
如果在自己无法确定具操作的数据是什么类型,这时直接使用字节流操作。
2、确定操作数据的方向。
确定到底我们是要读取数据,还是要写出数据。
读取数据:输入流
写出数据:输出流
3、确定需要转换吗?
如果读写的数据使用的编码表和系统默认的不一致,这时肯定需要使用转换流。
4、确定是否需要提供效率?
确定是否需要使用流的缓冲区。
-
功能流介绍
功能流:前面学习的字节流或者字符流,它们都是最基本的流对象,可以操作几乎所有的文件。功能流,它主要是来完成特定的功能。学习功能流,只需要知道每个流能够完成具体什么功能即可。
-
Properties介绍
Properties : 它是本身不是流中的类,它是Map集合的子类。
Properties 功能:它可以直接和流进行结合,可以将Map中的数据直接保存到文件中,或者将文件中的数据读取回来之后直接放到Map集合中。
-
保存数据
// 保存数据
public static void demo1() throws IOException {
// 创建对象
Properties prop = new Properties();
// 给集合中保存数据
prop.put("zhangsan", "shanghai");
prop.put("lisi", "beijing");
prop.put("wangwu", "guangzhou");
/*
* 将集合中的数据保存到文件中
* store(OutputStream out, String comments)
* OutputStream out : 它是用于保存集合中数据的输出流对象
* String comments : 关于保存的数据的说明信息
*/
FileOutputStream out = new FileOutputStream("e:/prop.txt");
prop.store(out, "buzhidaoxiesha");
}
说明:
使用Properties保存的数据文件,其中以#开始的信息称为文件中的注释信息,不属于被保存数据中的有效数据。在读取的时候会被忽略。
配置文件:使用Properties类保存之后生成的文件,通常被称为配置文件。有些时候,我们某个程序或软件在运行的时候需要依赖其他的文件中的参数才能正常的运行。这类文件的扩展名通常.properties.
如果在使用Properties类和IO一起保存的数据中有中文的话,会使用JDK中的native2ascii 命令将中文使用JDK内置的unicode编码转成unicode码。
演示使用JDK中的命令对中文进行unicode编码:
-
读取数据
// 读取数据
public static void demo2() throws IOException {
// 创建对象
Properties prop = new Properties();
// 使用load方法读取数据
prop.load( new FileInputStream("e:/prop.txt"));
// 上面的代码执行完之后,已经将文件中的数据直接读取回来保存到了集合中
// 遍历集合
Set<Object> set = prop.keySet();
for (Iterator it = set.iterator(); it.hasNext();) {
Object key = it.next();
Object value = prop.get(key);
System.out.println(key + "...." + value);
}
}
-
SequenceInputStream介绍
SequenceInputStream :它被称为序列流。它的主要功能是将多个输入流合并成一个输入流。
需求:将3个记事本文件中的数据合并到第四个文件中。
/*
* SequenceInputStream 演示
*/
public class SequenceInputStreamDemo {
public static void main(String[] args) throws IOException {
// 定义一个集合,将与每个文件关联的输入流保存到集合中
ArrayList<FileInputStream> list = new ArrayList<>();
// 使用循环提供多个输入流,
for( int i = 1 ; i <= 3 ; i++ ){
// 创建输入流对象
FileInputStream fis = new FileInputStream("e:/"+i+".txt");
// 将流添加到集合中
list.add(fis);
}
// 使用Collections中的方法通过集合获取Enumeration对象
Enumeration<FileInputStream> en = Collections.enumeration(list);
// 创建SequenceInputStream对象
SequenceInputStream sis = new SequenceInputStream( en );
// 定义输出流
FileOutputStream fos = new FileOutputStream("e:/4.txt");
// 模版代码读写数据
byte[] buf = new byte[1024];
int len = 0;
while( ( len = sis.read(buf) ) != -1 ){
fos.write(buf, 0, len);
}
// 关流
sis.close();
fos.close();
}
}
-
序列化和反序列化流
-
打印流
-
图形化界面
-
图形界面介绍
-
-
布局管理器
-
简单窗体实现
-
事件监听
-
各种监听演示
-
窗口监听
-
-
动作监听
-
鼠标监听
-
键盘监听