day19字符流的缓冲区。自己MyBufferReader的readLine方法。装饰设计模式。字节流。自定义一个缓冲区模拟BufferedInputStream。读取键盘

/*
字符流的缓冲区。自己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"));//改变输出的目的。
	}
}

你可能感兴趣的:(java基础日记)