/* 字符流的缓冲区。自己MyBufferReader的readLine方法。装饰设计模式。字节流。自定义一个缓冲区模拟BufferedInputStream。读取键盘 录入。转换流。流操作的基本规律 */ /* 字符流的缓冲区 缓冲区的出现提高了对流数据的操作效率 对应类 BufferedWriter BufferedReader 缓冲区要结合流才可以使用 在流的基础上对流的功能进行了增强 很多软件都有自己的缓冲区。比如迅雷。 二个构造方法必须有参数,必须有流才能使用。 BufferedWriter缓冲区中提供了一个跨平台的换行符。newLine(); /r/n不跨平台。 */ import java.io.*; class BufferWriter { public static void main(String[] args) throws IOException { bufWriter(); /* //创建一个字符写入流对象 FileWriter fw = new FileWriter("buf.txt"); //为了提高字符写入流的效率,加入了缓冲技术。 //缓冲原理:对象里面封装了数组 //只要将需要被提高效率的流对象作为参数传递给 //缓冲区的构造方法就可。 BufferedWriter bufw =new BufferedWriter(fw); bufw.write("abcde"); //记住,只要用到BufferedWriter缓冲区,就要记得刷新 bufw.flush(); //其实关闭缓冲区,就是在关闭缓冲区中的流对象 bufw.close(); */ } public static void bufWriter() throws IOException { FileWriter fw = new FileWriter("demo.txt"); BufferedWriter bufw = new BufferedWriter(fw); for (int x= 0;x<5 ;x++) { bufw.write("abcd"+x); bufw.newLine();//跨平台的换行。 bufw.flush(); } bufw.close(); } } /* 字符读取流缓冲区:BufferReader 该缓冲区提供了一个一次读一行的方法:readLine(), 方便于对文本数据的获取,当读到null时,表示读到文件的末尾。 readLine()方法返回的时候只返回回车符之前的数据内容,并不返回回车符。 所以我们自己写的时候要加newLine(); 原理:无论是读一行,获取读取多个字符,其实最终都是在硬盘上一个一个 读取,所以最终还是使用read()一次读一个方法。 */ import java.io.*; class BufferReader { public static void main(String[] args) throws IOException { //创建一个读取流对象和文件相关联 FileReader fr = new FileReader("demo.txt"); //为了提高效率。加入缓冲技术,将字符读取流对象 //作为参数传递给缓冲对象的构造函数 BufferedReader bufr = new BufferedReader(fr); String s1 = null; while((s1=bufr.readLine())!=null) { System.out.println("s1="+s1); } bufr.close(); } } /*练习 通过缓冲区复制一个.java文件 */ import java.io.*; class BufferCopyJava { public static void main(String[] args) { BufferedReader bufr = null; BufferedWriter bufw = null; try { bufr = new BufferedReader(new FileReader("BufferWriter.java")); bufw = new BufferedWriter(new FileWriter("CopyBufferJava.txt")); String line = null; while ((line = bufr.readLine())!=null) { bufw.write(line); bufw.newLine(); bufw.flush(); } } catch (IOException e) { throw new RuntimeException("读写失败"); } finally { try {if(bufr!=null) bufr.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } try {if(bufw!=null) bufw.close(); } catch (IOException e) { throw new RuntimeException("写入关闭失败"); } } } } /* 定义一个自己MyBufferReader的readLine方法 原理:无论是读一行,获取读取多个字符,其实最终都是在硬盘上一个一个 读取,所以最终还是使用read()一次读一个方法。 */ import java.io.*; class MyBufferReader { private FileReader r; MyBufferReader(FileReader r) { this.r = r; } public String myReadLine() throws IOException { //定义一个临时容器,源程序中封装的是字符数组。 //这里为了演示方便,定义一个StringBuilder容器,因为最终还要是将数据变成字符串 StringBuilder sb = new StringBuilder(); int buf = 0; while((buf=r.read())!= -1) { if (buf=='\r') { continue; } if (buf=='\n') { return sb.toString(); //sb.delete(0,sb.length());??? //是因为return后,sb会自己清空。生命周期。 //还是每次都新一个new StringBuilder?? } else sb.append((char)buf); } if (sb.length()!=0) { return sb.toString(); } return null; } public void myClose()throws IOException { r.close(); } } class MyBuffer { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("demo.txt"); MyBufferReader mbr = new MyBufferReader(fr); String s = null; while ((s = mbr.myReadLine())!=null) { //return s; System.out.println(s); } mbr.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("开胃酒"); //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(); SupperPerson sp = new SupperPerson(p); sp.superChifan(); } } /* MyReader//专门用于读取数据的类 |--MyTextReader |--MyBufferTextReader |--MyMediaReader |--MyBufferMediaReader |--MyDateReader |--MyBufferDateReader class MyBufferReader { MyBufferReader(MyTextReader text){} MyBufferReader(MyMediaReader media){} }//这个类扩展类很差,可以找其参数的共同类型, //通过多态的形式,可以提高扩展性 class MyBufferReader extends MyReader { private MyReader r; MyBufferReader(MyReader r){} //从继承变结合结构 } 后来的体系 MYReader//专门用于读取数据的类 |--MyTextReader |--MyMediaReader |--MyDateReader |--MyBufferReader 装饰模式比继承更灵活,避免了继承体系的臃肿 而且降低了类之间的关系。 装饰类因为增强已有对象,具备的功能和已有对象是相同的, 只不过提供了更强的功能, 所以装饰类和被装饰类都属于一个体系中。 建设继承要写,但不要以继承为主,继承太多了会臃肿。 */ /* 自己定义一个模拟BufferedReader的方法readLine() 通过read()方法 */ import java.io.*; class MyBufferedReader extends Reader//继承 { private Reader r;//修改了FileReader,更具扩展性 MyBufferedReader( Reader r) { this.r = r; } public String myReadLine()throws IOException { int buf = 0; StringBuilder sb = new StringBuilder(); while ((buf = r.read())!=-1) { if(buf == '\r') { continue; } if(buf=='\n') { return sb.toString(); } else sb.append((char)buf); } 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 MyBufferedReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("demo.txt"); MyBufferedReader mybuf = new MyBufferedReader(fr); String line = null; while ((line=mybuf.myReadLine())!=null) { System.out.println(line); } mybuf.myClose(); } } //练习。自定义一个带行号的readLine();LineNumberReader import java.io.*; class MyLineNumberReader { private Reader r ; private int lineNumber ; MyLineNumberReader(Reader r) { this.r = r; } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } public int getLineNumber() { return lineNumber; } public String myReaderLine()throws IOException { lineNumber++; StringBuilder sb = new StringBuilder(); int buf =0; while ((buf = r.read())!=-1) { if(buf=='\r') { continue; } if (buf=='\n') { return sb.toString(); } else sb.append(buf); } if(sb.length()!=0) return sb.toString(); return null; } public void myColse()throws IOException { r.close(); } } class MyLineNumberReaderDemo { public static void main(String[] args)throws IOException { FileReader fr = new FileReader("MyBuffer.java"); MyLineNumberReader mylnr = new MyLineNumberReader(fr); String line = null; while ((line = mylnr.myReaderLine())!=null) { System.out.println(mylnr.getlineNumber()+":::"+line); } mylnr.close(); } } /* class LineNumberReaderDemo { public static void main(String[] args) throws IOException {//第一次,这是java提供的。 FileReader fr = new FileReader("MyBuffer.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 BufferedWriter 字节流 InputStream OutputStream 需求:想要操作图片数据。这时就要用到字节流。 字节流不需要缓冲。不需要刷新。 但需要关闭资源。 */ import java.io.*; class FileStream { public static void main(String[] args) throws IOException { stream_3(); } public static void stream_1()throws IOException { FileOutputStream fo = new FileOutputStream("fos.txt"); fo.write("abcdef".getBytes()); FileInputStream fi = new FileInputStream("fos.txt"); int num = 0; while ((num = fi.read())!=-1) { System.out.println((char)num); } fi.close(); fo.close(); } public static void stream_2()throws IOException { FileInputStream fi = new FileInputStream("fos.txt"); byte[] buf = new byte[1024]; int len = 0; while ((len = fi.read(buf))!=-1) { System.out.println(new String(buf,0,len)); } fi.close(); } public static void stream_3()throws IOException { FileInputStream fi = new FileInputStream("fos.txt"); byte buf[] = new byte[fi.available()]; //字节流特有的,返回字符个数。换行符(\r\n)等于二个字节。 //定义一个刚刚好的缓冲区,不用循环了。 fi.read(buf); System.out.println(new String(buf)); fi.close(); } } /* 复制一个图片 思路: 1,用字节读取流对象和图片关联 2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。 3,通过循环读写,完成数据的存储 4,关闭资源。 最后问:用字符流可以复制吗?可以,但是有可能打不开。因为要去表里面查 字符,有可能这个字符没有,就用相似的表示了。所以不要用字符流拷贝媒体文件。 */ import java.io.*; class CopyPic { public static void main(String[] args) { FileInputStream fi = null; FileOutputStream fo = null; try { fi = new FileInputStream("c:\1.jpg"); fo = new FileOutputStream("c:\2.jpg"); byte[] buf = new byte[1024]; int len = 0; while ((len = fi.read(buf))!=-1) { fo.write(buf,0,len); } } catch (IOException e) { new RuntimeException("复制失败"); } finally { try { if (fi != null) { fi.close(); } } catch (IOException e) { new RuntimeException("1111复制失败"); } try { if (fo != null) { fo.close(); } } catch (IOException e) { new RuntimeException("222复制失败"); } } } } /* 演示MP3的复制,通过缓冲区 */ import java.io.*; class CopyMp3 { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); copy1(); long end = System.currentTimeMillis(); System.out.println("end-start:"+ (end-start)+"毫秒"); } public static void copy1()throws IOException { BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\0.mp3")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\1.mp3")); int len = 0; while ((len = bufis.read())!=-1)//缓冲区里取。 { bufos.write(len);//字节流可以不用刷新。 } bufis.close(); bufos.close(); } } import java.io.*; /* 自定义一个缓冲区模拟BufferedInputStream myRead方法原理和具体实现。 myRead函数主要是把数据抓到缓冲区里,并从缓冲区里一次读一个字节 那么原理是定义一个数组,把流里面的数据抓到数据里面,但是数组长度固定,定义的数组可能装不下; 此时就要用循环读取,但是最后一次读的数组可能就不是数组的长度,此时要把数组里面元素的有效位数找到,通过read(byte[] b)方法。 用一个count变量记住一次读的字节的数量,每读myRead一次时,就把缓冲区里的字节赋值给一个变量,怎么赋值?这根把数组元素的值赋给变量一样, 并且每次赋值时,数组指针会随myRead读取次数向后移1位,故会有pos++,并且把count--(为了后面的count==0),并把变量处理后返回出去;当count为0时候, 此时又会调用一次read(byte[] b) 方法,如此循环,直到count小于(或等于(最后一次))自己定义的数组长度,当读到最后一次字节再往下读的时,此时count为0, 又会执行read(byte[] b) 方法,但此时的count返回-1,就不要在继续读取下去了。 需要注意的是:每读取一次myRead方法,就爱需要读取下一个字节,就需要pos++; int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 */ class MyBufferedInputStream { private FileInputStream r; private int pos = 0,count = 0; byte[] buf = new byte[1024]; MyBufferedInputStream (FileInputStream r) { this.r = r; } public int myRead()throw IOException//返回int是为了避免出现byte为-1情况。 { //一次性全部读到缓冲区,不要再到硬盘里读,所以速度比较快。 if (count ==0) { count = r.read(buf); if (count < 0) { return -1; } pos = 0; byte b = buf[pos]; pos++; count--; return b&255; } if (count > 0) { byte b = buf[pos]; pos++; count--; return b&255; /*1111-1111———>提升了一个int类型,那不还是-1吗?是-1的原因是因为在前面补1的原因造成的。 那么我只要在前面补0,可以保留原字节数据不变,又可以避免-1出现。怎么补0呢?就是与上255. b&255;也可以写成b&0xff。 */ } return -1; } public void myClose()throw IOException { r.close(); } } class MyBufferedInputStreamDemo { public static void main(String[] args)throw IOException { long start = System.currentTimeMillis(); copy2();//这是一个比较快的复制文件的方法。自定义的读取流缓冲。 long end = System.currentTimeMillis(); System.out.println((end-start)+"毫秒"); } public static int copy2()throw IOException { FileInputStream fis = new FileInputStream("c:\0.mp3"); MyBufferedInputStream mybuf = new MyBufferedInputStream(fis); BufferedOutputStream bufout = new BufferedOutputStream(new FileOutputStream("c:\3.mp3")); int num = 0; while ((num = mybuf.myRead())!=-1) { bufout.write(num);//上面提升了4倍大小,byte->int...其实write在自动做一个强转动作。 } //把最低的八位写出去,其他的全部砍掉。 mybuf.myClose(); bufout.close(); } private static void copy3() throws IOException{//15最快啊。这是最快的复制文件的方法 BufferedInputStream bufin = new BufferedInputStream( new FileInputStream("1.mp3")); BufferedOutputStream bufout = new BufferedOutputStream( new FileOutputStream("2copy.mp3")); int i = 0; byte[] buf = new byte[1024]; while((i=bufin.read(buf))!=-1){//读一片,写一片。 bufout.write(buf,0,i); } bufout.close(); bufin.close(); } } /* 读取键盘录入 System.out:对应的是标准输出设备,控制台 System.in:对应的标准输入设备:键盘 需求: 通过键盘录入数据 当录入一行数据后,就将该行数据进行打印 如果录入的数据是over,那么停止录入 */ import java.io.*; class SystemOut { public static void main(String[] args) throws IOException { InputStream in = System.in;//键盘录入 StringBuilder sb = new StringBuilder(); while (true) { int num = in.read(); if (num == '\r') { continue; } if (num == '\n') { String s = sb.toString(); if (s.equals("over")) { break; } System.out.println(s.toUpperCase()); sb.delete(0,sb.length());//这里不是返回,所以要删除。 } else//为什么要写上这个over才会停止?break跳出当前循环 sb.append((char)num); } //int by = in.read();//阻塞式方法 //int by1 = in.read();//阻塞式方法 //System.out.println(by); //System.out.println(by1); //System.out.println('\r'+0);//验证'\r'的int /* int num = 0; while ((num = in.read())!=-1) { System.out.println(num);//这也是可以的,一个一个字节,还不能判断-1; } */ } } /*转换流 本来就有字节流,字符流可以更方便的操作字符数据,所以把转换流放到字符流里。 */ import java.io.*; class InputStreamReaderDemo { public static void main(String[] args) throws IOException { //获取键盘录入。 //InputStream in = System.in; //将字节流转换成字符流对象。 //InputStreamReader isr = new InputStreamReader(in); //缓冲 //BufferedReader bufr = new BufferedReader(isr); BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); OutputStream out = System.out; //输出 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(); bufw.flush();//字符输出流要刷新 } } } /* 流操作的基本规律 最痛苦的就是流对象有很多,不知道该用哪一个 通过三个明确来完成 1.明确源和目的 源:输入流。InputStream Reader 目的:输出流。OutputStream Writer. 2.操作的数据是否是纯文本。 是;字符流 不是:字节流。 3.当体系明确后,再明确要使用哪个具体的对象 通过设备来进行区分 源设备:内存,硬盘,键盘 目的设备:内存,硬盘,控制台 需求: 1.将一个文本文件中的数据存储到另一个文件中。复制文件。 源:输入流InputStream Reader,是一个文本文件,用Reader. 设备是硬盘,FileReader fr = new FileReader("a.txt"); 需要高效:BufferedReader bufr = new BufferedReader(fr); 目的:输出流。OutputStream Writer. 是一个文本,Writer 设备是硬盘,FileWriter fw = new FileWriter("b.txt"); 需要高效:BufferedWriter bufw = new BufferedWriter(); 2:将键盘录入的数据保存到一个文件中。 源:输入流InputStream Reader,是否纯文本?是!Reader. 设备是键盘,InputStream fis = System.in; 不是选择Reader吗?System.in对应的不是字节流吗? 为了操作键盘的文本数据方便,转成字符流操作字符串最方便。 所以明确了Reader,那就将System.in转成字符流 用到Reader体系中的转换流。InputStreamReader InputStreamReader isr = new InputStreamReader(System.in); 提高效率:BufferedReader bufr = new BufferedReader(isr); 目的:输出流。OutputStream Writer.是一个文本,Writer. 设备:硬盘。FileWriter fw = new FileWriter("c.txt"); 高效:BufferedWriter bufw = new BufferedWriter(fw); --------------------------- 扩展一下,想要把录入的数据按照指定的编码表(UTF-8), 将数据存入到文件中。 目的:输出流。OutputStream Writer.是一个文本,Writer. 设备:硬盘。FileWriter 。 但是!!!FileWriter 是使用的默认编码表。GBK 在存储时,要加入指定的编码表UTF- 8,指定编码表只有转换流可以。 所以指定的对象是OutputStreamWriter!!! 而转换流要接收一个字节输出流,而且是可以操作文件的字节输出流 FileOutputStream!!! FileOutputStream fos = new FileOutputStream("d.txt"); OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8"); 高效: BufferWriter bufw = new BufferedWriter(osw); 所以记住,转换流什么时候使用。 1.字符和字节之间的桥梁,通常涉及到编码表时,就要使用。 2.当源和目的是字节流时,而操作的是字符。! */ /* System.setIn();改变输入 System.setOut();改变输出。如:System.setOut(new PrintStream("zz.txt")) */ import java.io.*; import java.text.*; import java.util.Date; //建立java异常信息。。。其实网络上有专门的工具。log4j class ExceptionInfo { public static void main(String[] args) throws IOException { int[] arr = new int[2]; Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String s = sdf.format(date); try { System.out.println(arr[3]);//这里会出错。 } catch (Exception e) { //e.printStackTrace(); try { PrintStream ps = new PrintStream("b.txt"); //ps.write(date.toString().getBytes()); //ps.println(date.toString()); ps.println(s); System.setOut(ps); //System.out.println(s.toString()); } catch (IOException ex) { throw new RuntimeException("日志文件创建失败"); } e.printStackTrace(System.out); //e.printStackTrace(new PrintStream("exceptioninfo.txt")); } } } import java.io.FileNotFoundException; import java.io.PrintStream; import java.util.Properties; public class SystemInfo { /** * @param args * @throws FileNotFoundException */ public static void main(String[] args) throws FileNotFoundException { // TODO Auto-generated method stub Properties prop = System.getProperties(); //System.out.println(prop);//打印在控制台上 //prop.list(System.out);//自己的方法打印在控制台上 prop.list(new PrintStream("systemInfo.txt"));//改变输出的目的。 } }
/* 字符流的缓冲区。自己MyBufferReader的readLine方法。装饰设计模式。字