---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
四个大知识点:
1.字符缓冲流以及装饰设计模式;
2.字节流和字节缓冲流;
3.转换流,包括读取转换流和写入转换流;
4.流操作规律。
三个小知识点:
1.改变标准输入输出设备;
2.将异常信息输出到日志文件中;
3.将系统信息输出到文本文件中。
一、字符缓冲流和装饰设计模式
缓冲区的出现是为了提高数据的读写效率。一个简单的小例子就是喝水,一滴一滴的喝不如用杯子接满再喝,杯子就是缓冲区。
1.BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供字符、数组和字符串的高效写入。
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args)
{
//声明文件写入流变量,缓冲区的出现是为了提高流的效率,所以在创建缓冲区
//之前必须先有流对象
FileWriter fw = null;
//声明缓冲流变量
BufferedWriter bufw = null;
//处理异常
try
{
//初始化文件写入流对象
fw = new FileWriter("buf.txt");
//初始化缓冲流对象
bufw = new BufferedWriter(fw);
//缓冲流的写方法实际上调用的还是文件写入流的写方法
//bufw.write("naxienian");
//只要用到缓冲区,就必须刷新
//bufw.flush();
for (int x=0; x<5; x++)
{
bufw.write("wqnmlgb"+x);
bufw.newLine();//换行,跨平台,仅缓冲区有此方法
bufw.flush();//防止断电,你懂的
}
}
catch (IOException e)
{
throw new RuntimeException("写入失败!");
}
finally
{
if(bufw!=null)
try
{
bufw.close();//关闭缓冲流,实际上就是关闭文件写入流
}
catch (IOException e)
{
throw new RuntimeException("流关闭失败!");
}
}
}
}
(1)所谓“防止断电”,是指bufw对象每写一行数据就必须刷新一次,把数据写入到相应的目的地中,如果不这样,那所有数据都被暂时放在缓冲区中,如果在最后关闭流(关闭流前会刷新一次,将所有数据写入目的地)前,发生某种意外(类似于电脑断电时,如果未保存工作,那么内存中的数据都会被清空),那么所有数据就会全部消失。而如果写一次数据刷新一次,写过的数据将会得到保存。
(2)关闭缓冲流,实际上就是关闭缓冲流所操作的流,所以不必再写被操作的流关闭的代码。
(3)BufferedWriter类提供了一个跨平台的换行方法newLine()。在windows中换行符为\r\n,在linux中为\n。
2.BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行(注意这里是行,BufferedWriter中是字符串)的高效读取。
/*
文件读取流缓冲区。
*/
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args)
{
BufferedReader bufr = null;
try
{
FileReader fr = new FileReader("buf.txt");
bufr = new BufferedReader(fr);
String line = null;
//readLine()方法一次读一行,读到文件末尾返回null
while((line=bufr.readLine())!=null)
System.out.println(line);
}
catch (IOException e)
{
throw new RuntimeException("读取失败!");
}
finally
{
if(bufr!=null)
try
{
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("流关闭失败!");
}
}
}
}
BufferedReader类提供了一个一次读一行的方法readLine(),方便对文本数据的获取。当返回null时,表示文件读到末尾。
3.通过缓冲区复制文本
/*
通过缓冲区复制一个.java文件。
*/
import java.io.*;
class CopyTextByBuf
{
public static void main(String[] args)
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
/*
使用匿名对象创建读取流和写入流分别与原文件和目的文件关联,
并作为参数传递给对应缓冲流的构造函数。
*/
//FileReader fr = new FileReader("BufferedWriterDemo.java");
bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java"));
//FileWriter fw = new FileWriter("BufferedWriterDemo_copy.java");
bufw = new BufferedWriter(new FileWriter("BufferedWriterDemo_copy.java"));
String line = null;
while((line=bufr.readLine())!=null)//用readLine()方法一次读取一行
{
bufw.write(line);//每读一行就写一行
bufw.newLine();//readLine()方法读取的数据不包括换行符,所以要写入换行
bufw.flush();//刷新,保存数据
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
if(bufr!=null)
try
{
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取流关闭失败");
}
if(bufw!=null)
try
{
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入流关闭失败");
}
}
}
}
/*
自己写一个缓冲区类
*/
import java.io.*;
class MyBufferedReader extends Reader//继承Reader,要实现抽象方法
{
private Reader r;
MyBufferedReader(Reader r)
{
this.r = r;
}
public String myReadLine() throws IOException//抛出异常
{
StringBuilder sb = new StringBuilder();//容器,替代数组(原缓冲类中用的就是数组)
int ch = 0;
while((ch=r.read())!=-1)
{
if((char)ch == '\r')//这里可以不用转换ch
continue;
if((char)ch == '\n')//这种写法如果文件末尾没有换行符就会导致无法输出最后一行
//return sb.toString();
break;//我自认为可以这么写
else
sb.append((char)ch);
}
//增加以下代码防止最后一行无法输出
if(sb.length()!=0)
return sb.toString();
return null;//如果什么都没有就返回null
}
public void myClose() throws IOException
{
r.close();
}
//实现父类的抽象方法
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len);
}
public void close() throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args)
{
MyBufferedReader bufr = null;
try
{
bufr = new MyBufferedReader(new FileReader("buf.txt"));
String line = null;
while((line=bufr.myReadLine())!=null)
System.out.println(line);
}
catch (IOException e)
{
throw new RuntimeException("读取失败!");
}
finally
{
if(bufr!=null)
try
{
bufr.myClose();
}
catch (IOException e)
{
throw new RuntimeException("流关闭失败!");
}
}
}
}
主要是理解readLine()方法的原理,读一行归根到底还是被缓冲的流对象一个字符一个字符的读取,每读到一个字符就放入缓冲区中(在这里使用StringBuilder,实际上应使用数组),当读到换行符的时候说明达到了一行的末尾,将之前读取到的所有字符输出就是一行数据。
5.装饰设计模式
当想要给已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能提供增强的功能。该自定义的类就是装饰类。
装饰类通常会通过构造函数接收被装饰的类。
上面的缓冲类就是对应字符流的装饰类。
小例子
/*
装饰设计模式
*/
class Person
{
public void eat()
{
System.out.println("吃饭");
}
}
class SuperPerson
{
Person p;
SuperPerson(Person p)
{
this.p = p;
}
public void superEat()
{
System.out.println("吃开胃小菜");
p.eat();
System.out.println("吃饭后甜点");
}
}
class DecorateDemo
{
public static void main(String[] args)
{
Person p = new Person();
//p.eat();
SuperPerson sp = new SuperPerson(p);
sp.superEat();
}
}
假设MyReader是一个读取数据的类,它有两个读取不同数据类型的子类MyTexReader和MyMediaReader,体系如下
MyReader
|--MyTextReader
|--MyMediaReader
为了提高效率,想加入缓冲区,但如果每个子类都创建一个装饰类,整个体系就会显示臃肿且扩展性差,如下
MyReader
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
因为所有缓冲的功能都类似,改为如下代码
class MyBufferReader
{
MyBufferReader(MyTextReader text){}
MyBufferReader(MyMediaReader media){}
}
上面代码同样扩展性很差,如果需要缓冲的类很多的话。所以找到其参数的共同类型,通过多态的形式提高扩展性。
class MyBufferReader
{
MyBufferReader(MyReader r){}
}
class MyBufferReader extends MyReader
{
MyBufferReader(MyReader r){}
}
最后整个体系如下
MyReader
|--MyTextReader
|--MyMediaReader
|-- MyBufferReader装饰设计模式比继承要灵活,避免了继承体系的臃肿,也降低了类与类之间的关系。
BufferedReader的子类,跟踪行号的字符缓冲读取流,可以设置行号和读取行号。
import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args)
{
LineNumberReader lnr = null;
try
{
lnr = new LineNumberReader(new FileReader("DecorateDemo.java"));
String line = null;
lnr.setLineNumber(100);//设置行号
while((line=lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+":"+line);//获取每行行号
}
}
catch (IOException e)
{
throw new RuntimeException("读取失败");
}
finally
{
if(lnr!=null)
try
{
lnr.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭失败");
}
}
}
}
/*
模拟一个带行号的缓冲类
*/
import java.io.*;
//继承BufferedReader
class MyLineNumberReader extends BufferedReader
{
private int lineNumber;
MyLineNumberReader(Reader r)
{
super(r);
}
public String readLine() throws IOException
{
/*
String line = super.readLine();
if(line!=null)//不用判断
lineNumber++;
return line;
*/
lineNumber++;
return super.readLine();
}
public int getLineNumber()
{
return lineNumber;
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
}
//不继承BufferedReader
/*
class MyLineNumberReader
{
private Reader r;
private int lineNumber;
MyLineNumberReader(Reader r)
{
this.r = r;
}
public String readLine() throws IOException
{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
{
lineNumber++;
return sb.toString();
}
else
sb.append((char)ch);
}
if(sb.length()!=0)
{
lineNumber++;
return sb.toString();
}
return null;
}
public int getLineNumber()
{
return lineNumber;
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
public void myClose() throws IOException
{
r.close();
}
}
*/
class MyLineNumberReaderDemo
{
public static void main(String[] args)
{
MyLineNumberReader mylnr = null;
try
{
mylnr = new MyLineNumberReader(new FileReader("LineNumberReaderDemo.java"));
String line = null;
mylnr.setLineNumber(44);
while((line=mylnr.readLine())!=null)
{
System.out.println(mylnr.getLineNumber()+":"+line);
}
}
catch (IOException e)
{
throw new RuntimeException("读取失败");
}
finally
{
if(mylnr!=null)
try
{
mylnr.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭失败");
}
}
}
}
二、字节流和字节缓冲流
1.字节流读写操作,FileInputStream和FileOutputStream
import java.io.*;
class FileStream
{
public static void main(String[] args) throws IOException
{
readFile_3();
}
public static void readFile_3() throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
//int len = fis.available();
byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区,不用再循环了
fis.read(buf);
System.out.println(new String(buf));
}
public static void readFile_2() throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1)
System.out.println(new String(buf,0,len));
fis.close();
}
public static void readFile_1() throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
int ch = 0;
while((ch=fis.read())!=-1)
System.out.println((char)ch);
fis.close();
}
public static void writeFile() throws IOException
{
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("haoren".getBytes());//不用刷新也能输出
fos.close();//流最后必须关闭
}
}
(1)字节输出流不用刷新方法也能将数据打印到目的地,但最后流还是必须关闭;
(2)字节输入流的available()方法须慎用,防止爆内存;
(3)字符流用的是字符数组,字节流用的是字节数组。
2.使用字节流拷贝图片
思路:
(1)用字节读取流和原图片文件相关联;
(2)用字节写入流创建一个图片文件,用来存储读取过来的数据;
(3)循环读取原文件数据,写入目标文件;
(4)关闭流,释放资源。
/*
复制一个图片
*/
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
fis = new FileInputStream("pic.jpg");
fos = new FileOutputStream("pic_copy.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("duxie fail");
}
finally
{
if(fis!=null)
try
{
fis.close();
}
catch (IOException e)
{
throw new RuntimeException("读取流关闭失败");
}
if(fos!=null)
try
{
fos.close();
}
catch (IOException e)
{
throw new RuntimeException("写入流关闭失败");
}
}
}
}
3.拷贝MP3文件,使用字节流的缓冲区BufferedInputStream和BufferedOutputStream
/*
复制Mp3文件,通过字节流的缓冲区
*/
import java.io.*;
class CopyMp3
{
public static void main(String[] args) throws IOException
{
long start = System.currentTimeMillis();
copy();
long end = System.currentTimeMillis();
System.out.println("拷贝费时:"+(end-start)+"毫秒");
}
public static void copy() throws IOException
{
//创建缓冲字节读取流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Adele-Rolling In The Deep.mp3"));
//创建缓冲字节写入流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("music_copy.mp3"));
/*
每读一个字节就写一个字节
*/
int by = 0;
while((by=bis.read())!=-1)
bos.write(by);
//关闭流,释放资源
bos.close();
bis.close();
}
}
/*
自定义缓冲字节流
*/
import java.io.*;
class MyBufferedInputStream
{
//创建字节数组作为缓冲区
private byte[] buf = new byte[1024];
/*
定义计数器和指针,计数器的作用是记录缓冲区中字节的数量,
指针的作用是记录当前读到的缓冲区中字节的位置。
*/
private int count,pos;
private InputStream in;
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//一次读一个字节,从缓冲区(字节数组)获取
public int myRead() throws IOException
{
/*
如果计数器等于0,就通过in对象从硬盘上读取数据到缓冲区中,
计数器记录读取到的字节数量,指针归零。并从缓冲区中读出一个字节,
同时计数器减1,指针加1,最后返回读出的字节。
如果计数器大于0,直接从缓冲区中读出一个字节,同时计数器减1,
指针加1,最后返回读出的字节。
*/
if(count==0)
{
pos = 0;
count = in.read(buf);
if(count<0)
return -1;
byte b = buf[pos];//这里b的类型应该定义为byte而不是int
count--;
pos++;
return b&255;//防止出现-1
}
else if(count>0)
{
byte b = buf[pos];
count--;
pos++;
return b&0xff;//防止出现-1
}
return -1;
}
public void myClose() throws IOException
{
in.close();
}
}
/*
读取键盘录入
*/
import java.io.*;
class ReadIn
{
public static void main(String[] args) throws IOException
{
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
while(true)
{
int ch = in.read();
if(ch==13)//即字符'\r'
continue;
if(ch==10)//即字符'\n'
{
String s = sb.toString();
if(s.equals("over"))
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());//输出一行之后就把之前的数据清空
}
else
sb.append((char)ch);
}
//不用关闭流
}
}
/*
转换流
*/
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new InputStreamReader(System.in));
bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bufr.readLine())!=null)
{
if(line.equals("over"))
break;
//System.out.println(line.toUpperCase());
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("读取异常");
}
finally
{
if(bufr!=null)
try
{
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取资源关闭失败");
}
if(bufw!=null)
try
{
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入资源关闭失败");
}
}
}
}
/*
异常的日志信息
*/
import java.io.*;
import java.text.*;
import java.util.*;
class ExceptionInfoDemo
{
public static void main(String[] args)
{
try
{
int[] arr = new int[3];
int a = arr[3];//脚标越界,发生异常
}
catch (Exception e)
{
try
{
//以特定格式获得日期时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String d = sdf.format(new Date());
PrintStream ps = new PrintStream("ex_info.log");
ps.println(d);//打印日期时间
System.setOut(ps);//改变标准输出设备
ps.close();
}
catch (IOException ex)
{
throw new RuntimeException("创建异常日志文件失败");
}
e.printStackTrace(System.out);//将异常信息打印到标准输出设备中
}
}
}
import java.io.*;
import java.util.*;
class SystemInfo
{
public static void main(String[] args)
{
Properties prop = System.getProperties();
try
{
prop.list(new PrintStream("sysinfo.txt"));
}
catch (IOException e)
{
e.printStackTrace(System.out);
}
}
}
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net