----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
一、装饰设计模式
装饰设计模式。
解决的问题:给已有的对象提供增强额外的功能。还不用对原有对象进行修改。
比继承更为灵活。
Writer
|--TextWriter
|--MediaWriter
现有一个体系用于各种数据的写入。
但是,发现写入效率有点低。想要对其进行效率的提高。
可以使用缓冲技术来完成的。
已有对象中的写入方法,不够高效,可以通过派生子类的形式对其进行复写,定义高效的写入动作。
Writer
|--TextWriter
|--BufferTextWriter
|--MediaWriter
|--BufferMediaWriter
|--DataWriter
|--BufferDataWriter
通过继承的方式提高了效率。
但是对于扩展性是一个问题。而且所需的功能越多,子类就越多。
如何解决这个问题呢?优化!
既然都需要缓冲,对数据写入效率进行提高 。
可以转变一下思想,这样来做,将缓冲技术单独进行封装。
哪个对象需要缓冲,就把哪个对象传递给缓冲对象即可。
class Buffer{
Buffer(TextWriter w){
}
Buffer(MediaWriter w){
}
}
体系就变成了这样:
Writer
|--TextWriter
|--MediaWriter
|--BufferWriter
BufferWriter的出现,增强了Writer体系中的功能。
这种设计方式比原理更为灵活,避免了继承的臃肿。
将这样解决方式就定义了一个名称方便于后人使用:装饰设计模式。
记住:装饰类和被装饰类都所属于同一个体系。
二、行号装饰类
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("tempfile\\demo.java");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100);
while((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
三、字符流:
FileReader
FileWriter
BufferedReader
BuffereWriter
字节流。
InputStream OutputStream
FileInputStream FileOutputStream
BufferedInputStream BufferedOutputStream
转换流:
字节流--->字符流的桥梁。InputStreamReader
字符流--->字节流的桥梁。OutputStreamWriter
public static void main(String[] args) throws IOException {
需求:将一个字符串写入一个文件中。使用FileOutputStream来演示。
FileOutputStream fos = new FileOutputStream("tempfile\\fos.txt");
fos.write("abcdef".getBytes());//字节流的直接操作文件写入时,直接将数据写入到目的地。
为啥FileWriter就需要刷呢?
那是因为FileWriter底层使用了字节流在操作字符数据时,会先将这些数据进行临时存储,并查指定的编码表。按照指定的编码表中的内容写入到目的地。
"你好"FileWriter ---> 编码表 GBK--->数字字节--->目的地。
而字节流处理的数据不一定都是文字数据,所以是不需要指定查表的。
直接在操作文件数据时,就将具体的字节数据写入到目的地。
fos.flush();使用的是父类OutputStream的flush。该方法什么都没有做,只有OutputStram某一些子类定义了该方法的具体内容。
fos.close();//必须有,因为要关闭资源。
}
需求:读取一个文件。用字节流。
int ch = fis.read();
System.out.println("ch="+ch);
//一次读一个,打一个。
int ch = 0;
while((ch=fis.read())!=-1){
System.out.println((char)ch);
}
//读取并存储到数组,将数组打印。以该种方式。
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
//直接创建一个刚刚好的大小的数组缓冲区。
byte[] buf = new byte[fis.available()];//如果文件过大,内存溢出,bang 一声!慎用!//
fis.read(buf);//将数据存储到刚刚好的数组中。
System.out.println(new String(buf));
fis.read();
System.out.println(fis.available());//获取流关联的文件的字节数。
fis.close();
四、复制mp3
1、自定义数组缓冲区的方式。
public static void copy_1() throws IOException {
//1,读取流对象,和mp3关联。
FileInputStream fis = new FileInputStream("C:\\0.mp3");
//2,写入流对象,明确存储mp3数据的目的。
FileOutputStream fos = new FileOutputStream("c:\\1.mp3");
//3,定义一个字节缓冲区。
byte[] buf = new byte[1024*8];
int len = 0;
while((len=fis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
fis.close();
}
2、不建议。使用刚刚好的缓冲区。因为文件过大会溢出。
public static void copy_3() throws IOException {
FileInputStream fis = new FileInputStream("C:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\3.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();
}
3、使用字节流已有的缓冲区。
public static void copy_2() throws IOException {
FileInputStream fis = new FileInputStream("C:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int by = 0;
while((by=bufis.read())!=-1){
bufos.write(by);
}
bufos.close();
bufis.close();
}
4、读一个 写一个
public static void copy_4() throws IOException {
FileInputStream fis = new FileInputStream("C:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\4.mp3");
int by = 0;
while((by=fis.read())!=-1){
fos.write(by);
}
fos.close();
fis.close();
五、键盘录入
读取键盘录入。
public static void readKey() throws IOException{
//获取到读取键盘录入的流对象。类型是InputStream
InputStream in = System.in;
//用流读取数据。如果没有数据录入,那么控制台会一致等待输入。read方法就是一个阻塞式的方式。
int ch = in.read();
System.out.println("ch="+ch);
int ch1 = in.read();
System.out.println("ch1="+ch1);
int ch2 = in.read();
System.out.println("ch2="+ch2);
in.close();//不需要关闭。
//注意:系统中获取的流对象都是唯一的,如果将其关闭,就不能在使用了。
//想要在使用只有重新再次运行这个程序才可以。
//所以一般情况下,从System获取到的流对象,一般不需要关闭。随着程序的停止而结束。
// InputStream in2 = System.in;这里会出错 。
// int ch4 = in2.read();
// System.out.println("ch4="+ch4);
六、键盘录入练习
需求:读取键盘录入的数据,将这些数据转成大写打印在屏幕上,
如果录入的是 over, 程序结束。
思路:
1,通过键盘录入读取字节数据。
2,将这些读到的字节数据进行存储以变成字符串。
3,对这个字符串进行判断。如果不是over就将其转成大写打印。如果是over就结束。
public static void readKey2() throws IOException {
//1,获取键盘录入流对象。
InputStream in = System.in;
//2,定义一个容器用于存储读取到的字节。
StringBuilder sb = new StringBuilder();
//3,循环读取键盘。
int ch = 0;
while((ch=in.read())!=-1){
//需要对读取到的字节进行判断。
//如果是/r 或者 /n,不存储,并视为一次录入内容结束符。对之前的录入数据进行判断。
if(ch=='\r')
continue;
if(ch=='\n'){
String s = sb.toString();
if("over".equals(s)){//记住:如果要使用键盘录入,一定要自定义结束标记。
break;
}
else{
System.out.println(s.toUpperCase());
//清空缓冲区。
sb.delete(0, sb.length());
}
}
else
sb.append((char)ch);
}
}
七、转换流
既然键盘录入转成大写输出,并判断over结束的程序中
使用了到对字节数据的存储,并对回车符进行判断。
发现这个功能和readLine方法一致。
因为readLine是字符流BufferedReader对象中过的方法。
而键盘录入是字节流。
能不能将这个字节读取流转成字符流呢?因为BufferedReader只能对字符流进行装饰 。
这就用到了转换流
转换流:
字节流--->字符流的桥梁。InputStreamReader
字符流--->字节流的桥梁。OutputStreamWriter
从键盘读取数据,转成大写,显示在屏幕上。
获取键盘录入,数据源。
InputStream in = System.in;
为了处理文字数据方便。将字节数据转成字符数据.这个功能在转换流中。InputStreamReader
InputStreamReader isr = new InputStreamReader(in);
//为了提高了读取的效率。使用缓冲区。
BufferedReader bufr = new BufferedReader(isr);
//打印到显示器上。目的。
OutputStream out = System.out;
//因为要打印到显示器上的数据都是文字数据。所以必须将这些文字数据转成字节数据。
//具备这个功能的对象是OutputStreamWriter.
OutputStreamWriter osw = new OutputStreamWriter(out);
//为了提高写入的效率。
BufferedWriter bufw = new BufferedWriter(osw);
//频繁的读写操作。
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();//newLine()需要有BufferedWriter对象
bufw.flush();
}
//因为是从System获取的流可以不关闭,随着系统的结束而结束。
bufw.close();
bufr.close();
}
八、流的操作规律:
规律就是四个明确?
1,明确源和目的。
源:InputStream Reader 一定是被读取的。
目的:OutputStream Writer 一定是被写入的。
2,处理的数据是否是纯文本的数据?
是:使用字符流。Reader Writer
否:使用字节流。 InputStream OutputStream
如果是源并且是纯文本,Reader
如果是目的并且是纯文本,Writer
到这里,两个明确确定完,就可以确定出要使用哪个体系。
接下来,就应该明确具体这个体系要使用哪个具体的对象。
3,明确数据所在的设备:
源设备:
键盘(System.in)
硬盘(FileXXX)
内存(数组)
网络(Socket)
目的设备:
显示器(控制台System.out)
硬盘(FileXXX)
内存(数组)
网络(Socket)
具体使用哪个对象就可以明确了。
4,明确是否需要额外功能?
1,是否需要高效?缓冲区Buffered
2,是否需要转换?转换流
后面会学到更多。
九、需求练习
需求1:复制一个文本文件。
直接明确具体对象并创建。
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
然后是频繁的读写操作。
需要额外功能吗?
需要,高效。 使用缓冲区。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
需求3:读取键盘录入,存储到一个文件中。
明确是否是纯文本?一般键盘录入的都是文字,所以是纯文本的。
InputStream in = System.in;
FileWriter fw = new FileWriter("a.txt");
需要将键盘录入的字节转成字符。
使用转换流。而且是 将字节-->字符的转换流对象。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"));
需求4:读取一个文本文件,显示到显示器上。
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;
需要将已有的字符数据转成字节。字符-->字节的桥梁 OutputStreamWriter
FileReader fr = new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWrier(System.out));
需求5:读取一个文本文件,将文件中文本按照指定的编码表UTF-8写入到另一个文件中。
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
这样做不行,满足不了需求,为什么呢?
因为这个两个对象在操作文本数据,都是用了默认的编码表。在我的本机中默认码表是GBK.而需求中希望写入到文件的数据是按照utf-8的码表。其实这两个对象就是字节流+默认编码表。
源对象不变。
FileReader fr = new FileReader("a.txt");
需要目的为指定编码表。
这时就要用到转换流。因为转换流中可以指定具体的编码表。 需要往里传递一个字节流,而且要操作文件,FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8");
需要高效
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8"));