一、字符流缓冲区
1、缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象。即先将流对象初始化到构造函数中。
2、缓冲技术原理:此对象中封装了数组,将数据存入,再一次性取出。
3、写入流缓冲区BufferedWriter的步骤:
1)创建一个字符写入流对象。
如:FileWriter fw=newFileWriter(“buf.txt”);
2)为了提高字符写入流效率。加入缓冲技术。只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
如: BufferedWriter bufw =new BufferedWriter(fw);
3)调用write方法写入数据到指定文件
如:bufw.write(“adfg”);
记住,只要用到缓冲区,就要记得刷新。(关闭流同样会刷新,但为了排除意外事故,保证数据存在,建议写入一次就刷新一次)
如:bufw.flush();
4)其实关闭缓冲区,就是在关闭缓冲区中的流对象。
如: bufw.close();
小知识:BufferedWriter缓冲区中提供了一个跨平台的换行符:newLine();可以在不同操作系统上调用,用作数据换行。
如:bufw.newLine();
4、读取流缓冲区BufferedReader
该缓冲区提供了一个一次读一行的方法readLine,方便于堆文本数据的获取,当返回null时表示读到文件末尾。readLine方法返回的时候,只返回回车符之前的数据内容。并不返回回车符。
readLine方法原理:
无论是读一行。或者读取多个字符。其实最终都是在在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。
步骤:
1)创建一个读取流对象和文件相关联
如: FileReader fr=newFileReader(“buf.txt”);
2)为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲区对象的构造函数。
如: BufferedReader bufr=new BufferedReader(fr);
3)调用该缓冲区提供的readLine方法一行一行读取,如果到达文件末尾,则返回null
如: String s=bufr.readLine();
4)关闭流资源
如: bufr.close();、
BufferedReader示例如下:
package IODemo;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderDemo {
/**
*读取缓冲区
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//创建一个读自符流对象
FileReader fr=new FileReader("F:\\写入缓冲区练习.txt");
//为了提高效率,创建读的缓冲区,把读字符流对象当做参数传给缓冲区的构造函数
BufferedReader br=new BufferedReader(fr);
String line =null;
while ((line=br.readLine())!=null) {
System.out.println(line);
}
br.close();
}
}
BufferedWriter示例如下:
package IODemo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterDemo {
/**
*写入缓冲区
* @param args
*/
public static void main(String[] args) {
//建立一个写入流对象
FileWriter fw=null;
BufferedWriter bw=null;
try {
fw= new FileWriter("F:\\写入缓冲区练习.txt");
//为了提高写入效率,创建写入缓冲区
bw=new BufferedWriter(fw);
for (int i = 0; i < 5; i++) {
bw.write("写入缓冲区练习"+i);
//缓冲区特有的功能newLine换行
bw.newLine();
//刷新写入流
bw.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//关闭缓冲区就是关闭写入流对象
if (bw!=null) {
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
5.LineNumberReader
在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:
setLineNumber();//设置初始行号
getLineNumber();//获取行号
示例如下:
package IODemo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class LineNumbeReaderDemo {
/**
* @param args
*/
public static void main(String[] args) {
FileReader fr=null;
LineNumberReader lnr=null;
try {
fr=new FileReader("F:\\CopyTest.java");
lnr =new LineNumberReader(fr);
String line=null;
lnr.setLineNumber(100);
while ((line=lnr.readLine())!=null) {
System.out.println(lnr.getLineNumber()+":"+line);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if (lnr!=null) {
try {
lnr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
6.装饰设计模式
(1)、简述
当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。
(2)、特点
装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
(3)、装饰和继承的区别:
1)装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。
2)装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
3)从继承结构转为组合结构。
注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。
示例:自定义MyBufferedReader的例子,代码如下
package IODemo;
import java.io.FileReader;
import java.io.IOException;
public class MyBufferReaderTest {
/**
* 自定义缓冲区
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileReader fr= new FileReader("F:\\java.txt");
MyBufferReader mbr=new MyBufferReader(fr);
String line =null;
while ((line=mbr.MyreadLine())!=null) {
System.out.println(line);
}
mbr.MyClose();
}
}
class MyBufferReader{
private FileReader r;
public MyBufferReader(FileReader r) {
this.r = r;
}
public String MyreadLine() throws IOException{
//定义一个容器存读取的数据
StringBuilder sb=new StringBuilder();
int ch=0;
while ((ch=r.read())!=-1) {
if (ch=='\r') {
continue;
}
if (ch=='\n') {
return sb.toString();
}else{
sb.append((char)ch);
}
}
if (sb.length()!=0) {
return sb.toString();
}
return null;
}
public void MyClose() throws IOException{
r.close();
}
}
二、字节流概述
1、字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。
2、由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。
3、读写字节流:InputStream 输入流(读)
OutputStream 输出流(写)
4、为何不用进行刷流动作:
因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。所以可直接将字节数据写入到指定文件中。
5、InputStream特有方法:
int available();//返回文件中的字节个数
注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用。
三、字节流缓冲区
同样是提高了字节流的读写效率。
1、读写特点:
read():会将字节byte型值提升为int型值
write():会将int型强转为byte型,即保留二进制数的最后八位。
2、原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区。
2)循环这个动作,直到最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素。
3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增。
4)取出的时候,数组中的元素在减少,取出一个,就减少一个,直到减到0即元素取完。
5)当文件中的全部数据都被读取出时,read()方法就返回-1。
练习如下:
复制图片:
package IODemo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Copypicture {
/**
* 复制图片
* @param args
*/
public static void main(String[] args) {
FileInputStream fis=null;
FileOutputStream fos=null;
try {
fis=new FileInputStream("E:\\壁纸\\63360.jpg");
fos=new FileOutputStream("F:\\copypicture.jpg");
byte[] buf=new byte[1024];
int len=0;
while ((len=fis.read(buf))!=-1) {
fos.write(buf, 0, len);
}
}catch (IOException e) {
throw new RuntimeException("复制失败");
}finally{
try {
if (fis!=null) {
fis.close();
}
} catch (IOException e) {
throw new RuntimeException("读取失败");
}
try {
if (fos!=null) {
fos.close();
}
} catch (IOException e) {
throw new RuntimeException("写入失败");
}
}
}
}
复制音乐:
package IODemo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyMp3 {
/**
* 复制一个MP3
* 用缓冲区
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
long start=System.currentTimeMillis();
CopyMp3_1();
long end=System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
//通过字节流完成复制
public static void CopyMp3_1() throws IOException{
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("F:\\三万英尺.mp3" ));
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("D:\\KuGou\\迪克牛仔 - 三万英尺.mp3"));
int ch=0;
while ((ch=bis.read())!=-1) {
bos.write(ch);
}
bos.close();
bis.close();
}
}
四、流操作规律
(1)键盘录入
1、标准输入输出流
System.in:对应的标准输入设备,键盘。
Ssytem.out:对应的是标准的输出设备,控制台。
System.in的类型是InputStream.
System.out的类型是PrintStream是OutputStream的子类FilterOutputStream的子类。
2、整行录入
当使用输入流进行键盘录入时,只能一个字节一个字节进行录入。为了提高效率,可以自定义一个数组将一行字节进行存储。当一行录入完毕,再将一行数据进行显示。这种正行录入的方式,和字符流读一行数据的原理是一样的。也就是readLine方法。
那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?这就需要用到转换流了。
3、转换流
3.1 转换流的由来:
a、字符流与字节流之间的桥梁
b、方便了字符流与字节流之间的操作
转换流的应用:
字节流中的数据都是字符时,转成字符流操作更高效。
3.2 InputStreamReader将字节流通向字符流
a、获取键盘录入对象。
InputStream in=System.in;
b、将字节流对象转成字符流对象,使用转换流。
InputStreamReaderisr=new InputStreamReader(in);
c、为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
BufferedReaderbr=new BufferedReader(isr);
//键盘录入最常见写法
BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));
3.3 OutputStreamWriter字符流通向字节流
字符通向字节:录入的是字符,存到硬盘上的是字节。步骤和InputStreamReader转换流一样。
练习如下:
package IODemo;
import java.io.IOException;
import java.io.InputStream;
public class ReadIn {
/**
* 键盘录入
* 当录入一行数据后,就打印该行数据
* 当录入的数据是Over,就停止录入
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
InputStream in=System.in;
StringBuilder sb=new StringBuilder();
while (true) {
int ch=in.read();
if (ch=='\r') {
continue;
}
if (ch=='\n') {
String s= sb.toString();
if ("over".equals(s)) {
break;
}
System.out.println(s.toUpperCase());
sb.delete(0, sb.length());
}else{
sb.append((char)ch);
}
}
}
}
(2)’流操作规律
1、
源:键盘录入。
目的:控制台。
2、需求:想把键盘录入的数据存储到一个文件中。
源:键盘
目的:文件。
使用字节流通向字符流的转换流(桥梁):InputStreamReader
3、需求:想要将一个文件的数据打印在控制台上。
源:文件
目的:控制台
使用字符流通向字节流的转换流(桥梁):OutputStreamWriter
4、流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
通过三个明确来完成:
4.1 明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer
4.2 操作的数据是否是纯文本。
是:字符流
否:字节流
4.3 当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
5、规律体现
5.1 将一个文本文件中数据存储到另一个文件中。复制文件。
1)源:因为是源,所以使用读取流:InputStream和Reader
明确体系:是否操作文本:是,Reader
明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader
是否需要提高效率:是,加入Reader体系中缓冲区 BufferedReader.
FileReader fr = new FileReader(“a.txt”);
BufferedReader bufr = new BufferedReader(fr);
2)目的:输出流:OutputStream和Writer
明确体系:是否操作文本:是,Writer
明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:是,加入Writer体系中缓冲区 BufferedWriter
FileWriter fw = new FileWriter(“b.txt”);
BufferedWriter bufw = new BufferedWriter(fw);
练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。
1)源:输入流,InputStream和Reader
是否是文本?否,InputStream
源设备:硬盘上的一个文件。InputSteam体系中可以操作文件的对象是FileInputSteam
是否需要提供效率:是,BufferedInputStream
BufferedInputSteambis=newBufferedInputStream(newFileInputStream(“c:/users/asus/desktop/1.jpg”));
2)目的:输出流,OutputStream和Writer
是否是文本?否,OutputStream
源设备:硬盘上的文件,FileOutputStream
是否需要提高效率:是,加入BufferedOutputStream
BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream(“c:/users/asus/desktop/2.jpg”));
5.2 需求:将键盘录入的数据保存到一个文件中。
1)源:InputStream和Reader
是不是纯文本?是,Reader
设备:键盘。对应的对象是System.in。——为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。所以既然明确了Reader,那么就将System.in转换成Reader。用Reader体系中转换流,InputStreamReader
InputStreamReaderisr = new InputStreamReader(System.in);
需要提高效率吗?需要,BufferedReader
BufferedReaderbufr = new BufferedReader(isr);
2)目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = newFileWriter(“c.txt”);
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
5.3 扩展:想要把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘上的一个文件。使用 FileWriter。——但是FileWriter是使用的默认编码表:GBK。而存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter。
该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream
OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream(“d.txt”),”UTF-8”);
需要高效吗?需要,BufferedWriter
BufferedWriter bufw = new BufferedWriter(osw);
记住:
转换流什么使用?
字符和字节之间的桥梁。通常,涉及到字符编码转换时,需要用到转换流。
练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
1)源:InputStreamReader
是文本?是:Reader
设备:硬盘。上的文件:FileReader
是否需要提高效率?是:BufferedReader
BufferedReader br=new BufferedReader(newFileReader(“1.txt”));
2)目的:OutputStream Writer
是文本?是:Writer
设备:控制台。对应对象System.out。由于System.out对应的是字节流,所以利用OutputSteamWriter转换流
是否提高效率?是:BufferedWriter
BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out));