字节流的缓冲区 缓冲区的出现提高了对流的操作效率。 原理:其实就是将数组进行封装。 对应的对象: BufferedWriter: 特有方法:newLine():跨平台的换行符。 BufferedReader: 特有方法:readLine():一次读取一行,到行标记时,将行标记之前的字符数据作为字符串返回。当读到末尾时,返回full。 在使用缓冲区对象时,要明确,缓冲的存在是为了增强流的功能而存在,所以在建立缓冲区对象时,要先有流对象的存在。 其实缓冲区内部就是在使用流对象的方法,只不过加入了数组对数据进行了临时存储,为了提高操作数据的效率。 代码上的体现: 写入缓冲区对象。 //建立缓冲区对象必须把流对象作为参数传递给缓冲区的构造函数。 BufferedWriter bw = new BufferedWriter(newFileWriter(“buf.txt”)); //将数据写入到了缓冲区 bw.writer(“itheima”); //对缓冲区的数据进行刷新,将数据刷到目的地 bw.flush(); //关闭缓冲区,其实是关闭了被包装在内部的流对象。 bw.close();
例子1:利用字节流的缓冲区将数据写入到一个文本文件中。
package cn.itheima.day03; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; /** * 缓冲区的出现时为了提高流的操作效率而出现的。 * 所以,在创建缓冲区是必须先有流对象。 * @author wl-pc */ public class BufferedWriterDemo { public static void main(String[] args) { FileWriter fw=null; BufferedWriter bw=null; try { //1.创建一个字符写入流对象 fw = new FileWriter("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\buf.txt"); //为了提高字符的写入流效率,加入了缓冲技术,只要将需要提 //高效率的流对象作为参数传递给缓冲区的构造函数即可 bw = new BufferedWriter(fw); for(int x = 0; x < 5; x++ ){ bw.write("itheima"+x+",helloworld!"); bw.newLine(); //跨平台的换行 //记住:只要用到缓冲区就需要刷新 bw.flush(); } } catch (IOException e) { System.out.println(e.toString()); } finally{ if(bw!=null){ try { //关闭缓冲区的流对象其实关闭的是缓冲区中的写入流对象 //所以在这里只需要关闭缓冲区流即可 fw.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
例子2:利用字符读取流的缓冲区读取一个.java的文件显示到控制台上。
package cn.itheima.day03; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; /** * 字符读取流的缓冲区 * 该缓冲区提供了一次读取一行的方法,方便了对文本数据的获取。 * readLine()方法,当返回空时,表示读到了文件的末尾 * @author wl-pc */ public class BufferedReaderDemo { public static void main(String[] args) { FileReader fr=null; BufferedReader br=null; try { //创建一个读取流对象,和文件相关联 fr = new FileReader("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\BufferedWriterDemo.java"); //为了提高效率,加入了缓冲技术,将字符读取流对象传递给缓冲对象的构造函数。 br = new BufferedReader(fr); String line=null; try { while((line = br.readLine())!=null){ //readLine一行一行的读取数据 System.out.println(line); } } catch (IOException e) { System.out.println(e.toString()); } } catch (FileNotFoundException e) { System.out.println(e.toString()); }finally{ if(br!=null){ try { br.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
例子3:通过缓冲区的方式,对文本文件进行拷贝。
package cn.itheima.day03; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /** * 通过缓冲区复制一个*.java的文件 * @author wl-pc * readLine()方法返回的时候,只返回回车符之前的数据内容,并不返回回车符。 */ public class CopyTextByBuf { public static void main(String[] args) { BufferedReader bufr = null; BufferedWriter bufw = null; try { bufr=new BufferedReader(new FileReader("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\BufferedWriterDemo.java")); bufw=new BufferedWriter(new FileWriter("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\BufferedWriter_copy.txt")); String line = null; //临时数据的中转站 while((line = bufr.readLine())!=null){ bufw.write(line); bufw.newLine(); //跨平台换行 } } catch (FileNotFoundException e) { System.out.println(e.toString()); } catch (IOException e) { System.out.println(e.toString()); }finally{ if(bufr!=null){ try { bufr.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } if(bufw!=null){ try { bufw.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
readLine()方法的原理: 其实缓冲区中的readLine()方法,用的还是与缓冲区关联的流对象的read()方法。只不过,每一次读到一个字符,先不进行具体的操作,先进行临时存储。当读到回车标记时,将临时容器中存储的数据一次性的返回。 既然明确了原理,我们也可以实现一个类似的功能方法。
例子4:自定义一个类中包含一个功能和readLine()一致的方法来模拟一下BufferedReader
package cn.itheima.day03; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; /** * 明白了BufferedReader类中特有的方法readLine()的原理后, * 可以自定义一个类中包含一个功能和readLine()一致的方法 * 来模拟一下BufferedReader */ class MyBufferedReader{ private FileReader fr; public MyBufferedReader(FileReader fr) { this.fr = fr; } //可以一次读一行数据的方法 public String myReaderLine() throws IOException{ //定义一个临时容器,原BufferedReader封装的是字符数组, //为了演示方便,定义了一个StringBuilder容器,因为最终还是要将数据变成字符串 StringBuilder sb = new StringBuilder(); int ch = 0; while((ch=fr.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{ fr.close(); } } //主函数入口点 public class MyBufferedReaderDemo { public static void main(String[] args) { FileReader fileReader=null; MyBufferedReader mbr=null; try { fileReader = new FileReader("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\buf.txt"); mbr = new MyBufferedReader(fileReader); String line = null; try { while((line=mbr.myReaderLine())!=null){ System.out.println(line); } } catch (IOException e) { System.out.println(e.toString()); } } catch (FileNotFoundException e) { System.out.println(e.toString()); } finally{ if(mbr!=null){ try { mbr.myClose(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
由例子4可以看出:readLine()方法的出现实际上是在增强read()方法的功能,增强的方式是把被增强的对象传给增强类。这也是一种设计模式:装饰设计模式。 装饰设计模式:当想要对已有的对象进行功能增强时,可以定义类,将已有的对象传入,基于已有的功能,并提供加强功能,那么自定义的这个类就叫装饰类。即:对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
例子5:定义一个装饰类的实现
package cn.itheima.day03; 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("来一根烟"); } } public class PersonDemo { public static void main(String[] args) { Person p = new Person(); SuperPerson sp = new SuperPerson(p); sp.superChifan(); } }
装饰和继承都能实现一样的特点:进行功能的扩展增强,但是装饰比继承灵活,装饰的特点:装饰类和被装饰类都必须所属同一个接口或者父类。 下面举一个例子: Writer |--MediaWriter |--TextWriter (注意:MediaWriter和TextWriter两个类在JDK中并不存在,为了是更形象举例说明而“创建”的,在这里不要误解) 需求:想要对数据的操作提高效率,就用到了缓冲技术。 通过所学习的继承特性。可以建立子类复写父类中的write方法。即可 Witer(注:不要误解,以下的两个对象在JDK中是不存在的,只为举例说明) |-- MediaWriter |--BufferedMediaWriter |-- TextWriter |--BufferedTextWriter 当Writer中子类对象过多,那么为了提高每一个对象效率,每一个对象都有一个自己的子类Buffered。 虽然可以实现,但是继承体系变的很臃肿。 那么是否可以对其进行一下优化呢? 其实子类都是在使用缓冲技术。 可不可以对缓冲技术进行描述,将需要增强的对象传递给缓冲区即可。 classBufferdWriter{ BufferedWriter(MediaWritermw){ } BufferedWriter(TextWritermw){ } } 但是发现该方法缺点:该类虽然完成了对已有两个对象的增强。 但是当有新的对象出现时,还要继续在该类中添加构造函数。这样不利于扩展和维护。 还有一个方法就是:将对这些对象父类型进行操作即可。这就是多态,提高了程序的扩展性。同时BufferedWriter中一样具体write方法,只不过是增强后的write。所以BuferedWriter也应该是Writer中的一个子类。 classBufferedWriter extends Writer{ private Writer w; BufferedWriter(Writer w){ this.w = w; } } 即体系变成如下格式:(变化:从继承结构变为组合结构) Writer |--MediaWriter |--TextWriter |--BufferedWriter 这样就会发现装饰设计模式,优化增强功能的部分。比继承要灵活很多。而且降低了类与类之间的关系。 但是要注意:装饰类因为增强了已有对象,具备的功能和已有的功能是相同的,只不过提供了更强的功能,所以,装饰类可被装饰类通常是都属于一个体系中的。
例子6:利用装饰类定义一个功能readLine()一致的方法来模拟一下BufferedReader
package cn.itheima.day03; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.Reader; /** * 明白了BufferedReader类中特有的方法readLine()的原理后, * 可以自定义一个类中包含一个功能和readLine()一致的方法 * 来模拟一下BufferedReader */ class MyBufferedReader2 extends Reader{ private Reader fr; public MyBufferedReader2(Reader fr) { this.fr = fr; } //可以一次读一行数据的方法 public String myReaderLine() throws IOException{ //定义一个临时容器,原BufferedReader封装的是字符数组, //为了演示方便,定义了一个StringBuilder容器,因为最终还是要将数据变成字符串 StringBuilder sb = new StringBuilder(); int ch = 0; while((ch=fr.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{ fr.close(); } @Override public void close() throws IOException { fr.close(); } @Override public int read(char[] cbuf, int off, int len) throws IOException { return fr.read(cbuf,off,len); } } //主函数入口点 public class MyBufferedReaderDemo2 { public static void main(String[] args) { FileReader fileReader=null; MyBufferedReader2 mbr=null; try { fileReader = new FileReader("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\buf.txt"); mbr = new MyBufferedReader2(fileReader); String line = null; try { while((line=mbr.myReaderLine())!=null){ System.out.println(line); } } catch (IOException e) { System.out.println(e.toString()); } } catch (FileNotFoundException e) { System.out.println(e.toString()); } finally{ if(mbr!=null){ try { mbr.myClose(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
例子7:利用LineNumberReader的 getLineNumber()方法可以的带行号的输出文本内容。
package cn.itheima.day03; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; public class LineNumberReaderDemo { public static void main(String[] args) { FileReader fr=null; LineNumberReader inr=null; try { fr = new FileReader("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\PersonDemo.java"); inr = new LineNumberReader(fr); String line = null; try { while((line=inr.readLine())!=null){ //LineNumberReader getLineNumber()方法可以的带行号的输出 System.out.println(inr.getLineNumber()+"::"+line); } } catch (IOException e) { System.out.println(e.toString()); } } catch (FileNotFoundException e) { System.out.println(e.toString()); } finally{ if(inr!=null){ try { inr.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
例子8:利用自定义的MyLineNumberReader的 getLineNumber()方法可以的带行号的输出
package cn.itheima.day03;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
//直接继承父类,可以复用myReaderLine()方法和myClose()方法。
class MyLineNumberReader extends MyBufferedReader{
private int lineNumber;
public MyLineNumberReader(FileReader fr) {
super(fr);
}
public int getLineNumber() {
return lineNumber;
}
public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
}
public String myReaderLine() throws IOException{
lineNumber++;
return super.myReaderLine();
}
}
public class MyLineNumberReaderDemo {
public static void main(String[] args) {
MyLineNumberReader mylnr=null;
try {
FileReader fr = new FileReader("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\BufferedWriterDemo.java");
mylnr = new MyLineNumberReader(fr);
} catch (FileNotFoundException e) {
System.out.println(e.toString());
}
String line = null;
try {
while((line=mylnr.myReaderLine())!=null){
System.out.println(mylnr.getLineNumber()+"::"+line);
}
} catch (IOException e) {
System.out.println(e.toString());
}finally{
if(mylnr!=null){
try {
mylnr.myClose();
} catch (IOException e) {
System.out.println("close:"+e.toString());
}
}
}
}
}
目前学习的流对象: 字符流: FileReader FileWriter BufferedReader BufferedWriter 那么,我们还要学习字节流 字节流: FileInputStream FileOutputStream BufferedInputStream BufferedOutputStream 字节流: 抽象基类: InputStream OutputStream 字节流可以操作任何数据。 注意:字符流使用的数组是字符数组。char[] chs 字节流使用的数组是字节数组。byte [] bt 主要代码模式: FileOutputStreamfos = new FileOutputStream(“a.txt”); fos.write(“abcdef”); //直接将数据写入到了目的地 fos.close(); //只关闭资源 FileInputStreamfis = new FileInputStream("a.txt"); //fis.available();//获取关联的文件的字节数。 //如果文件体积不是很大。 //可以这样操作。 byte[]buf = new byte[fis.available()];//创建一个刚刚好的缓冲区。 //但是这有一个弊端,就是文件过大,大小超出jvm的内容空间时,会内存溢出。 fis.read(buf); System.out.println(newString(buf));
例子9:利用字节流读取或写入文本内容到控制台或者文本文件中
package cn.itheima.day03; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class FileStream { //读文件(一个一个的读,读一个存一个) public static void readFile_1(){ FileInputStream fis=null; try { fis = new FileInputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\fos.txt"); int ch = 0; try { while((ch=fis.read())!=-1){ System.out.print((char)ch); } } catch (IOException e) { System.out.println(e.toString()); } } catch (FileNotFoundException e) { System.out.println(e.toString()); }finally{ if(fis!=null){ try { fis.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } //读方法2(读完之后,一起存起来) public static void readFile_2(){ FileInputStream fis = null; try { fis = new FileInputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\fos.txt"); byte[] buf = new byte[1024]; int len = 0; while((len=fis.read(buf))!=-1){ System.out.println(new String(buf,0,len)); } } catch (FileNotFoundException e) { System.out.println(e.toString()); } catch (IOException e) { System.out.println(e.toString()); }finally{ if(fis!=null){ try { fis.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } //读文件 public static void readFile_3(){ FileInputStream fis = null; try { fis = new FileInputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\fos.txt"); int num = fis.available(); //获得内容的长度大小 byte[] buf = new byte[num]; //可以定义一个刚刚好的byte数组(即:刚刚好的缓冲区),它的长度就是num int len =fis.read(buf); //在这里不用循环了 System.out.println(new String(buf,0,len)); } catch (FileNotFoundException e) { System.out.println(e.toString()); } catch (IOException e) { System.err.println(e.toString()); }finally{ if(fis!=null){ try { fis.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } //写文件 public static void writeFile(){ FileOutputStream fos=null; try { fos = new FileOutputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\fos.txt"); fos.write("itheima,helloworld!".getBytes()); //字节流不需要刷新的,但需要关闭资源 } catch (FileNotFoundException e) { System.out.println(e.toString()); } catch (IOException e) { System.out.println(e.toString()); }finally{ if(fos!=null){ try { fos.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } public static void main(String[] args) { //writeFile(); //readFile_1(); //readFile_2(); readFile_3(); }
例子10:想要操作图片数据,copy一个图片到指定位置
package cn.itheima.day03; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * 演示:复制一张图片 * 思路: * 1.用字节读取流对象和图片关联 * 2.用字节写入流对象创建一个图片文件,用于存储获取到的图片数据 * 3.通过循环读写,完成数据的存储 * 4.关闭资源 * @author wl-pc */ public class CopyPic { public static void main(String[] args) { FileOutputStream fos =null; FileInputStream fis = null; try { fos = new FileOutputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\2.jpg"); fis = new FileInputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\1.jpg"); byte[] buf = new byte[1024*4]; int len = 0; while((len=fis.read(buf))!=-1){ fos.write(buf,0,len); } } catch (FileNotFoundException e) { System.out.println(e.toString()); } catch (IOException e) { System.out.println(e.toString()); }finally{ if(fos!=null){ try { fos.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } if(fis!=null){ try { fis.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
例子11:演示:mp3复制,通过缓冲区
package cn.itheima.day03; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * 演示:mp3复制,通过缓冲区 * BufferedOuputStream缓冲输出流 * BufferedInputStream缓冲输入流 * @author wl-pc */ public class CopyMP3 { //通过字节流的缓冲区完成复制 public static void copy_1(){ BufferedInputStream bufis=null; BufferedOutputStream bufos=null; try { bufis = new BufferedInputStream(new FileInputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\0.mp3")); bufos = new BufferedOutputStream(new FileOutputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\1.mp3")); int by = 0; while((by=bufis.read())!=-1){ bufos.write(by); } } catch (FileNotFoundException e) { System.out.println(e.toString()); } catch (IOException e) { System.out.println(e.toString()); }finally{ if(bufis!=null){ try { bufis.close(); } catch (IOException e) { System.out.println(e.toString()); } } if(bufos!=null){ try { bufos.close(); } catch (IOException e) { System.out.println(e.toString()); } } } } //利用自己定义的缓冲区拷贝mp3文件 public static void copy_2(){ MyBufferedInputStream bufis=null; BufferedOutputStream bufos=null; try { bufis=new MyBufferedInputStream(new FileInputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\0.mp3")); bufos=new BufferedOutputStream(new FileOutputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\2.mp3")); int by = 0; while((by=bufis.myRead())!=-1){ bufos.write(by); } } catch (FileNotFoundException e) { System.out.println(e.toString()); } catch (IOException e) { System.out.println(e.toString()); }finally{ if(bufis!=null){ try { bufis.myClose(); } catch (IOException e) { System.out.println(e.toString()); } } if(bufos!=null){ try { bufos.close(); } catch (IOException e) { System.out.println(e.toString()); } } } } public static void main(String[] args) { long start = System.currentTimeMillis(); //copy_1(); copy_2(); long end = System.currentTimeMillis(); System.out.println((end-start)+"毫秒"); } }
自定义的读取mp3的缓冲区代码:
package cn.itheima.day03; import java.io.IOException; import java.io.InputStream; class MyBufferedInputStream { private InputStream in; private byte[] buf = new byte[1024]; private int pos = 0,count = 0; MyBufferedInputStream(InputStream in) { this.in = in; } //一次读取一个字节,从缓冲区中(字节数组中读取)获取 public int myRead() throws IOException{ if(count==0){ //通过in对象读取硬盘上的数据存储到buf中 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&255; } return -1; } public void myClose() throws IOException{ in.close(); } }
注意:字节流的read()方法读取一个字节。为什么返回的不是byte类型的,而是int 类型的呢? 因为read方法读到末尾时返回的是-1.而在所操作的数据中的很容易出现连续多个1的情况,而连续读到8个1,就是-1.导致读取会提前停止。 所以将读到的一个字节给提升为一个int类型的数值,但是只保留原字节,并在剩余二进制位补0. 具体操作是:byte&255 or byte&0xff 对于write方法,可以一次写入一个字节,但接收的是一个int类型数值。只写入该int类型的数值的最低一个字节(8位)。即对于write()方法实际上进行了强制转化的动作。 简单说:read方法对读到的数据进行提升。write对操作的数据进行转换。
例子12:读取键盘录入
package cn.itheima.day03; import java.io.IOException; import java.io.InputStream; /** * 演示:读取键盘录入 * System.out对应的是标准的输出设备,控制台 * System.in对应的是标准的输入设备,键盘 * @author wl-pc * 需求:通过键盘录入数据,当录入一行的数据后,就 * 将数据进行打印,如果录入的数据是over,那么停止录入 */ public class ReadIn { public static void main(String[] args) throws IOException { /*System.out.println('\r'+0); System.out.println('\n'+0); InputStream in = System.in; int by = in.read(); //read()方法是一个堵塞时方法,没有读到数据,它就会等 System.out.println(by);*/ 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); } } } }
通过刚才的键盘录入数据并打印大写字母,发现其实就是读一行数据的原理。也就是readLine()的方法。 考虑一个问题:能不能直接使用readLine()方法来完成键盘录入的一行数据的读取呢?这样高效些。 readLine()方法是字符流BufferesReader类的方法而键盘录入的方法read()是字节流InputStream类中的方法 考虑:能不能将字节流转成字节流,再使用字符流缓冲区的readLine()方法呢? 字符流中有一个这样的转换流InputStreamReader()方法,可以将字节流转换成字符流 转换流: 特点: 1,是字节流和字符流之间的桥梁。 2,该流对象中可以对读取到的字节数据进行指定编码表的编码转换。 什么时候使用呢? 1,当字节和字符之间有转换动作时。 2,流操作的数据需要进行编码表的指定时。 具体的对象体现:这两个流对象是字符流体系中的成员。 1,InputStreamReader:字节到字符的桥梁。 2,OutputStreamWriter:字符到字节的桥梁。 这两个流对象是字符流体系中的成员。那么它们有转换作用,而本身又是字符流。所以在构造的时候,需要传入字节流对象进来。 构造函数: InputStreamReader(InputStream):通过该构造函数初始化,使用的是本系统默认的编码表GBK。 InputStreamReader(InputStream,StringcharSet):通过该构造函数初始化,可以指定编码表。 OutputStreamWriter(OutputStream):通过该构造函数初始化,使用的是本系统默认的编码表GBK。 OutputStreamWriter(OutputStream,StringcharSet):通过该构造函数初始化,可以指定编码表。 操作文件的字符流对象是转换流的子类。 Reader |--InputStreamReader |--FileReader Writer |--OutputStreamWriter |--FileWriter 转换流中的read()方法,已经融入了编码表中。在底层调用字节流的read方法时将获取的一个或者多个字节数据进行临时存储,并去查指定的编码表,如果编码表没有指定,查的是默认码表。那么转流的read方法就可以返回一个字符比如中文。转换流已经完成了编码转换的动作,对于直接操作的文本文件的FileReaer而言,就不用在重新定义了,只要继承该转换流,获取其方法,就可以直接操作文本文件中的字符数据了。 注意: 在使用FileReader操作文本数据时,该对象使用的是默认的编码表。如果要使用指定编码表时,必须使用转换流。 FileReaderfr = new FileReader(“a.txt”); //操作a.txt中的数据,使用的是本系统默认的编码方式GBK。 //操作a.txt中的数据使用的也是本系统默认的编码方式GBK。 InputStreamReaderisr = new InputStreamReader(new FileInputStream(“a.txt“)); 这两句的代码意义相同。 但是:如果a.txt中的文件中的字符数据是通过utf-8的形式编码。那么在读取时,就必须指定编码表。那么转换流必须使用。 InputStreamReader isr = new InputStreamReader(newFileInputStream(“a.txt”),”UTF-8”);
例子13:将字节流转成字符流,再使用字符流缓冲区的readLine()方法读取一行信息显示出来。
package cn.itheima.day03; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; /** * 通过刚才的键盘录入数据并打印大写字母,发现其实就是读一行数据的原理。 * 也就是readLine()的方法 * 考虑一个问题:能不能直接使用readLine()方法来完成键盘录入的一行数据的读取呢?这样高效些。 * readLine()方法是字符流BufferesReader类的方法 * 而键盘录入的方法read()是字节流InputStream类中的方法 * 考虑:能不能将字节流转成字节流,再使用字符流缓冲区的readLine()方法呢? * 字符流中有一个这样的转换流InputStreamReader()方法,可以将字节流转换成字符流 * @author wl-pc */ public class TransStreamDemo { public static void main(String[] args) { //为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader类中的方法 BufferedReader bufr=null; try { //1.获取键盘录入对象 //InputStream in = System.in; //2.将字节流对象转换成字符流对象,使用转换流InputStreamReader()方法 //InputStreamReader isr = new InputStreamReader(in); //得到字符流in //bufr = new BufferedReader(isr); //键盘录入最常见写法:以上三句话变为一句话(如下:) bufr = new BufferedReader(new InputStreamReader(System.in)); //2.将字符流对象转换成字节流对象,使用转换流OutputStreamWriter()方法 //OutputStream out = System.out; //得到输出流 //OutputStreamWriter osw = new OutputStreamWriter(out); //装饰类,用来装饰转化流OutputStreamWriter类的。 //可以调用bufferedWriter中的newLine()方法,夸平台换行 //BufferedWriter bufw = new BufferedWriter(osw); //以上三句话变为一句话(如下:) 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(); } } catch (IOException e) { System.out.println(e.toString()); } finally{ if(bufr!=null){ try { bufr.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
例子14:关于流操作的基本规律,演示例子
package cn.itheima.day03; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; /** * 1. * 源:键盘录入 * 目的:控制台 * @author wl-pc * 2.改变需求:想把键盘录入的数据存储到一个文件中 * 源:键盘 * 目的:文件 * * 3.改变需求:想要将一个文件数据打印到控制上 * 源:文件 * 目的:控制台 * * ######################流操作的基本规律:########################## * 最痛苦的就是流对象有很多,不知道该用哪一个? * 通过三个明确来完成: * 1.明确源和目的 * 源:输入流. InputStream Reader * 目的:输出流. OutputStream Writer * 2.明确操作的数据是否是存文本 * 是纯文本:用字符流 * 不是纯文本:用字节流 * 3.当体系明确后,再明确要使用哪个具体的对象 * 通过设备来进行区分: * 源设备:内存、硬盘、键盘 * 目的设备:内存、硬盘(文件)、控制台 * ----------------------3个需求之一---------------------------------- * 1.将一个文本文件中的数据存储到另一个文件中 ,也就是复制文件 * 源:因为是源,所以使用读取流:InputStream Reader * a.是不是操作文本文件? * 是:这就可以选择Reader * 不是:可以选择InputStream * 这样下来,所属体系就明确了。 * * b.接下来就要明确要使用体系中的哪个对象? * 明确设备:硬盘上的一个文件 * Reader体系中可以操作文件的对象是FileReader * c.是否需要提高效率? * 需要:加入Reader体系的缓冲区:BufferedReader * * 接下来就是: 1) FileReader fr = new FileReader("a.txt"); * 2) BufferedReader br = new BufferedReader(fr); * * * 目的:文件 OutputStream Writer * a.目的是否是纯文本的? * 是:Writer * 不是:OutputStream * b.明确设备:硬盘上的一个文件 * Writer体系中可以操作一个文件的对象是FileWriter * * c.是否需要提高效率? * 需要:加入Writer体系的缓冲区:BufferedWriter * * 接下来就是: 1) FileWriter fw = new FileWriter("b.txt"); * 2) BufferedWriter bw = new BufferedWriter(fw); * * 练习: 将一个图片文件中的数据存储到另一个图片中 ,即为copy图片,要按照以上的格式去写 * 源(是图片):因为是源, 所以使用读取流:InputStream Reader * a.是不是操作的纯文本文件? * 不是:这就可以选择InputStream * 是:这就可以选择 Reader * b.接下来就要明确要使用体系中的哪个对象? * 明确设备:硬盘上的一个图片 * InputStream体系中可以操作图片的对象时FileInputStream * c.是否需要提高效率? * 需要:加入InputStream体系的缓冲区:BufferedInputStream * * 接下来就是:FileInputStream fis = new FileInputStream("1.jpg"); * BufferedInputStream bis = new BufferedInputStream(fis); * * 目的:文件 OutputStream Writer * a.是否是纯文本的文件? * 不是:OutputStream * 是:writer * b.明确设备:硬盘上的一个图片 * OutputStream体系中可以操作一个文件的对象是FileOutputStream * c.是否需要提高效率? * 需要:加入OutputStream体系的缓冲区:BufferedOutputStream * * 接下来就是: FileOutputStream fos = new FileOutputStream("2.jpg"); * BufferedOutputStream bos = new BufferedOutputStream(fos); * * ################################################################# */ public class TransStreamDemo2 { public static void main(String[] args) { //为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader类中的方法 BufferedReader bufr=null; try { /*需求2的实现 * bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\out.txt"))); */ //需求3的实现 bufr=new BufferedReader(new InputStreamReader(new FileInputStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\CopyPic.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(); } } catch (IOException e) { System.out.println(e.toString()); } finally{ if(bufr!=null){ try { bufr.close(); } catch (IOException e) { System.out.println("close:"+e.toString()); } } } } }
例子15:将系统出现的异常信息存储到文件中
package cn.itheima.day03; import java.io.FileNotFoundException; import java.io.PrintStream; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; /** * 将系统出现的异常信息存储到文件中 * @author wl-pc */ public class ExceptionInfo { public static void main(String[] args) { try { int [] arr = new int[2]; System.out.println(arr[3]); } catch (Exception e) { try { PrintStream ps= new PrintStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\exceptioninfo.txt"); Date d = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String s = sdf.format(d); ps.println(s.toString()); e.printStackTrace(ps); } catch (FileNotFoundException e1) { e1.printStackTrace(); } } } }
例子16:打印系统信息到硬盘上的一个文件中
package cn.itheima.day03;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.Properties;
public class SystemInfo {
public static void main(String[] args) {
Properties properties = System.getProperties();
//System.out.println(properties);
//properties.list(System.out);
try {
properties.list(new PrintStream("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day03\\sysinfo.txt"));
} catch (FileNotFoundException e) {
System.out.println(e.toString());
}
}
}