字符流的缓冲区:
对应类:
字符写入流缓冲区:BufferWriter:Writer的子类
字符读取流缓冲区:BufferedReader:Reader的子类
缓冲区要结合流才可以使用,是在流的基础上对流的功能进行了增强。所以在建立缓冲区之前,必须要先有流对象。
*/
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args)throws IOException
{
//创建一个流对象:
FileWriter fw = new FileWriter("buf.txt");
//为了提高流的效率,加入了缓冲技术。
//将需要被提高效率的流对象作为参数传递给缓冲区的构造函数。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1;x<10;x++)
{
bufw.write("abcd"+x);
//缓冲区的特有换行方法,不分系统:newLine()
bufw.newLine();
//只要用到缓冲区,就要记得刷新。写一次刷新一次,因为缓冲区的数据先写入内存,如果写完一段再刷新,遇到断电或者机器重启等的情况,数据就会写不进去。
bufw.flush();
}
//其实关闭缓冲区就是关闭缓冲区中的流对象。只需关闭缓冲区,不必再关闭流对象。
bufw.close();
}
}
字符流读取缓冲区:
*/
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args)throws IOException
{
//创建一个读取流对象:
FileReader fr = new FileReader("buf.txt");
//为了提高效率,加入缓冲技术,将字符流对象作为参数传递给缓冲区对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
//缓冲区的特有读取方法,读一行:readLine(),当返回null时,表示读到文件末尾。
String line = null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
}
通过缓冲区复制一个.java文件。
readLine方法返回的时候,只返回回车符之前的数据内容。并不返回回车符。
*/
import java.io.*;
class CopyTextBuf
{
public static void main(String[] args)throws IOException
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new FileReader("CopyTextBuf.java"));
bufw = new BufferedWriter(new FileWriter("复制.txt"));
//该变量line是两个流之间的中转站:
String line=null;
while((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new IOException("读写失败");
}
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch (IOException e)
{
throw new IOException("读取关闭失败");
}
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw new IOException("写入关闭失败");
}
}
}
}
import java.lang.*;
class MyBufferedReader
{
private FileReader r;
MyBufferedReader(FileReader r)
{
this.r=r;
}
//定义一个一次读取一行数据的方法:
public String myReadLine()throws IOException
{
//定义一个临时容器,原BufferedReader封装的是字符数组。
//为了演示方便,定义一个StringBuilder容器,因为最终还是要将数据变成字符串。
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();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr = new FileReader("buf.txt");
MyBufferedReader mybuf = new MyBufferedReader(fr);
String line=null;
//注意line=mybuf.myReadLine(),丢失line的话返回的都是null
while((line=mybuf.myReadLine())!=null)
{
System.out.println(line);
}
mybuf.myClose();
}
}
装饰设计模式:
当想要对已有的对象进行功能增强时,可以定义类,将已有的对象传入,基于已有的功能,提供加强功能,那么自定的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
*/
class Person
{
public void chifan()
{
System.out.println("吃饭");
}
}
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p=p;
}
public void superchifan()
{
System.out.println("开胃酒");
p.chifan();
System.out.println("甜点");
System.out.println("来一根");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
// p.chifan();
SuperPerson sp = new SuperPerson(p);
sp.superchifan();
}
}
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。
找到其参数的共同类型。通过多态的形式。可以提高扩展性。
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。
装饰模式比继承要灵活。避免了继承体系臃肿。
而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。
class MyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(Reader r)
{
this.r = r;
}
//可以一次读一行数据的方法。
public String myReadLine()throws IOException
{
//定义一个临时容器。原BufferReader封装的是字符数组。
//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
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;
}
//覆盖Reader类中的抽象方法。
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len) ;
}
public void close()throws IOException
{
r.close();
}
public void myClose()throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo1
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("buf.txt");
MyBufferedReader myBuf = new MyBufferedReader(fr);
String line = null;
while((line=myBuf.myReadLine())!=null)
{
System.out.println(line);
}
myBuf.myClose();
}
}
是BufferedReader的子类:
带行号的装饰类:
特有方法:setLineNumber()和getLineNumber():分别用于设置和获取当前行号,默认从0开始
*/
import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr = new FileReader("LineNumberReaderDemo.java");
LineNumberReader lnr = new LineNumberReader(fr);
String line=null;
while((line=lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
}
class MyLineNumberReader extends MyBufferedReader
{
private int lineNumber;
MyLineNumberReader(Reader r)
{
super(r);
}
public String myReadLine()throws IOException
{
lineNumber++;
return super.myReadLine();
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
}
class MyLineNumberReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr = new FileReader("MyLineNumberReaderDemo.java");
MyLineNumberReader mylnr = new MyLineNumberReader(fr);
mylnr.setLineNumber(100);
String line = null;
while((line=mylnr.myReadLine())!=null)
{
System.out.println(mylnr.getLineNumber()+":"+line);
}
mylnr.myClose();
}
}
字节流:
InputStream:读
OutputStream:写
*/
import java.io.*;
class FileStream
{
public static void main(String[] args)throws IOException
{
// writeFile();
// readFile_1();
readFile_2();
}
//字节写入流:FileOutputStream
public static void writeFile()throws IOException
{
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("abcdefg".getBytes());
fos.close();
}
//字节读取流:FileInputStream
//读一个操作一个:
public static void readFile_1()throws IOException
{
FileInputStream fis = new FileInputStream("FileStream.java");
int ch=0;
while((ch=fis.read())!=-1)
{
System.out.print((char)ch);
}
fis.close();
}
//用数组作中转站:
public static void readFile_2()throws IOException
{
FileInputStream fis = new FileInputStream("FileStream.java");
// int num = fis.available();
// System.out.println(num);
//定义一个刚刚好的缓冲区,不用再循环了。这种情况容易内存溢出。
byte[] buf = new byte[fis.available()];
fis.read(buf);
System.out.println(new String(buf));
/*
byte[] buf = new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1)
{
System.out.print(new String(buf,0,len));
}
*/
fis.close();
}
}
复制一个图片
思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。
*/
import java.io.*;
class CopyPic2
{
public static void main(String[] args)
{
FileOutputStream fos = null;
FileInputStream fis = null;
try
{
//创建目的地:
fos = new FileOutputStream("c:\\99.png");
//用字节读取流与已有的文件相关联:
fis = new FileInputStream("c:\\JIUJIU.png");
//定义数组为中转站:
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("写入关闭失败");
}
}
}
}
演示mp3的复制。通过缓冲区。
BufferedOutStream
BufferedInputStream
*/
import java.io.*;
class CopyMp3
{
public static void main(String[] args)throws IOException
{
long start = System.currentTimeMillis();
copy_1();
long end = System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
public static void copy_1()throws IOException
{
//与已有文件相关联,并加入缓冲技术:
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
//创建目的地。加入缓冲技术:
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\000.mp3"));
//缓冲区中其实已经定义了数组为中转站。所以这里不需要再定义中转站。
int by=0;
while((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
}
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024*4];
private int pos=0;
private int count=0;
MyBufferedInputStream(InputStream in)
{
this.in=in;
}
//一次读取一个字节。从缓冲区中(字节数组)获取。
public int myRead()throws IOException
{
//通过in对象读取硬盘上的数据。并存储到buf中。
if(count==0)
{
count=in.read(buf);
if(count<0)
return -1;
pos=0;
byte b = buf[pos];
count--;
pos++;
return b&255;
}
else if(count>0)
{
byte b = buf[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}
class MyBufferedInputStreamDemo
{
public static void main(String[] args)throws IOException
{
long start = System.currentTimeMillis();
copy_1();
long end = System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
public static void copy_1()throws IOException
{
//与已有文件相关联,并加入缓冲技术:
MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\0.mp3"));
//创建目的地。加入缓冲技术:
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\0099.mp3"));
//缓冲区中其实已经定义了数组为中转站。所以这里不需要再定义中转站。
int by=0;
//这次没有写进去,因此拷贝的文件会比源文件小一个字节。
System.out.println("第一个字节是:"+bufis.myRead());
//因此复制的文件大小为0字节的原因是读取到的第一个字节是-1,循环没有进行。
while((by=bufis.myRead())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.myClose();
}
}
读取键盘写入:
System.out:对应的是标准的输出设备,控制台。
System.in:对应的是标准的输入设备,键盘。
需求:通过键盘录入数据,当录入一行数据偶,就将改行数据打印,如果录入over,那么停止录入。
*/
import java.io.*;
class ReadIn
{
public static void main(String[] args)throws IOException
{
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
//int ch = 0;
//Ctrl+C其实就是在加了一个结束标记:返回-1;
//读取一个字节,打印一个字节:
/*
while((ch=in.read())!=-1)
{
System.out.println(ch);
}
*/
//敲入回车符,再打印:
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 = new StringBuilder();
//清空缓冲区,达到只打印每次录入的内容。
sb.delete(0,sb.length());
}
else
sb.append((char)ch);
}
}
}
通过刚才键盘录入一行数据并且打印其大写。发现其实就是读一行数据的原理。
也就是readLine方法。
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法。
而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转换成字符流再使用字符流缓冲区中的readLine方法呢?
InputStreamReader:本身是一个字符流,构造函数需要接收一个字节流。
*/
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
//获取键盘录入:
InputStream in = System.in;
//将字节流对象转化成字符流对象,使用转换流。InputStreamReader
InputStreamReader isr = new InputStreamReader(in);
//加入缓冲技术。提高效率,使用BufferedReader
BufferedReader bufr = new BufferedReader(isr);
String line=null;
while((line=bufr.readLine())!=null)
{
//定义结束标记:录入over时结束
if("over".equals(line))
break;
System.out.println(line.toUpperCase());
}
bufr.close();
}
}
OutputStreamWriter
*/
import java.io.*;
class OutputStreamWriterDemo
{
public static void main(String[] args)throws IOException
{
// InputStream in = System.in;
// InputStreamReader isr = new InputStreamReader(in);
// BufferedReader bufr = new BufferedReader(isr);
// OutputStream out = System.out;
// OutputStreamWriter osw = new OutputStreamWriter(out);
// BufferedWriter bufw = new BufferedWriter(osw);
//键盘录入:一次读取一行数据。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
}
}
1,几种操作:
1,源:键盘录入。
目的;控制台。
2,需求:把键盘录入的数据存储到一个文件中。
源:键盘。
目的:文件。
3,需求:将一个文件中的数据打印在控制台上。‘
源:文件。
目的:控制台。
2,流操作的基本规律:通过两个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer
2,操作的数据是否是纯文本。
是:字符流
不是:字节流
3,当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台。
3,将一个文本文件中的数据存储到另一个文件中。复制文件。书写思考步骤:
源:InputStream Reader
是不是操作纯文本文件:是,选择Reader,这样体系就明确了。
接下来明确使用体系中的哪个对象,明确设备,设备是硬盘上的一个文件,Reader中可以操作文件的对象是FileReader
FileReader fr = new FileReader("a.txt");
提高效率:
BufferedReader bufr = new BufferedReader(fr);
目的:OutputStream Writer
是不是纯文本:是选择Writer。
设备:硬盘上的一个文件。Writer中可以操作文件的对象是FileWriter
FileWriter fw = new FileWriter("b.txt");
提高效率:
BufferedWriter bufw = new BufferedWriter(fw);
4,将一个图片文件数据存储到另一个文件中。
*/
import java.io.*;
class TransStreamDemo2
{
public static void main(String[] args)throws IOException
{
//键盘录入:一次读取一行数据。
BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("TransStreamDemo2.java")));
//
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
}
}
这个需求中有源和目的都存在。
那么分别分析
源:InputStream Reader
是不是纯文本?是!Reader
设备:键盘。对应的对象是System.in.
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader,那么就将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
**************
扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
但是FileWriter是使用的默认编码表。GBK.
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。
而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,
需要用到转换流。
练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
System.setOut(new PrintStream("zzz.txt"));
import java.io.*;
import java.util.*;
import java.text.*;
class ExceptionInfo
{
public static void main(String[] args)throws IOException
{
try
{
int[] arr = new int[2];
System.out.println(arr[3]);
}
catch (Exception e)
{
try
{
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d);
PrintStream ps = new PrintStream("exeception.log");
ps.println(s);
System.setOut(ps);
}
catch (IOException ex)
{
throw new RuntimeException("日志文件创建失败");
}
e.printStackTrace(System.out);
}
}
}
建立日志信息工具:log4j :
import java.io.*;
class SystemInfo
{
public static void main(String[] args) throws IOException
{
Properties prop = System.getProperties();
//System.out.println(prop);
prop.list(new PrintStream("sysinfo.txt"));
}
}