/*
字符流的缓冲区。自己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"));//改变输出的目的。
}
}
day19字符流的缓冲区。自己MyBufferReader的readLine方法。装饰设计模式。字节流。自定义一个缓冲区模拟BufferedInputStream。读取键盘
最新推荐文章于 2022-09-06 19:52:07 发布