黑马程序员————Java基础日常笔记---IO流一

Java IO流基础与应用
本文介绍了Java IO流的基础知识,包括流的概念、文件操作、字符流与字节流的区别,以及如何使用FileWriter、FileReader等类进行文件读写。此外,还详细解释了装饰设计模式在流操作中的应用,以及如何利用缓冲区提高读写效率。

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

黑马程序员————Java基础日常笔记---IO流一

1.1 IO的由来:

先什么是IO流呢?
java对数据的操作; 
IO流用来处理设备之间的数据传输, 并且操作流的对象都在IO包中, 
而对数据的操作: 读和写;
IO流按照操作的数据分为:字节流与字符流(InputStream,OutputStream和Reader,Writer)
        按流向分:输入流,输出流, 
字符流: 字节流+编码表;

好处:可以在内部去融合编码表,

就是你读到的字节数据,你到底是查那个GBK,还是UTF-8,,可以由你来指定,

所以来处理文字的时候,就可以变得方便,

字符流对象里面融合了编码表,只有文字识别的编码,如果处理图片的话,就用字节流,

通用字节流。

1.2 FileWriter

既然IO流式用于操作数据的,

那么数据的最常见的体现形式是:文件

那么先以操作文件为主来演示:

需求:在硬盘上,创建一个文件并写入一些文字数据

代码如下:

<span style="font-size:12px;">import java.io.*;
class FileWriterDemo 
{
	public static void main(String[] args) throws IOException
	{
		//创建一个FileWriter对象,该对象一被初始化必须要明确要操作的文件。
		//new在内存中产生一个对象,同时在指定的目录下, 会产生一个指定名称的文件。
		//而且该文件会被创建到指定的目录下, 如果该目录下已有同名的文件,将被覆盖
		//其实该步就是在明确数据要存放的目的地。
		FileWriter fw=new FileWriter("E:\\demo.txt");//写字得先有纸
		
		//调用write方法,将字符串写入到流中,
		fw.write("abcde");

		//刷新流对象中的缓冲中的数据
		//将数据刷到目的地中(指定的目录demo.txt)。
		//fw.flush();
		
		fw.write("hehe");
		//fw.flush();
		/*
		关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据
		将数据刷到目的地中,
		和flush区别:flush刷新后 流可以继续使用,close刷新后,会将流关闭。
		*/
		fw.close();//关闭此流,但要先刷新它。

		//fw.write("hehe");fw.flush();关闭了流,如果还调用write方法,这里会出现异常
	}
}</span><span style="font-size: 14px;">
</span>
运行结果:

Windows本身就具备这个动作,java也可以操作, 说明java在调用windows系统的这个写的动作,这个就是所说的流资源。

其实, java不可以往系统中写数据的, windows和UNIX系统写数据的方式不同,所以Java靠系统内部的方式来完成数据的书写,

java在调用windows系统的这个写的动作来完成数据的建立,

使用之后, 就要释放出来,那么久有个动作一定要做, close(),

在关闭之前, 需要先调用一次flush().然后把资源到底层关闭就ok;

然而在调用底层资源的时候,容易发生异常,那么该如何处理呢?

1.3 IO异常处理

思路分析: 1,有三个代码块, try中的代码块, 其他的代码块中访问不到,所以在外边建立引用, 在try中建立初始化,这样fw就作用于整个函数,

2,这个对象没有创建成功, 初始化抛出异常, 就代表着初始化失败了, 对象不存在,被catch捕获,所以 不可以调用 close(),
3, 有多个资源, 就需要分别的去关 资源

代码如下:

//IO流异常处理
import java.io.*;
class FileWriterDemo1 
{
	public static void main(String[] args) //throws IOException
	{
		FileWriter fw=null;//fw就作用于整个函数,
		try
		{
			fw=new FileWriter("E:\\demo.txt");// FileNotFoundException
			//fw=new FileWriter("demo.txt");
			fw.write("abcdefg");
			
		}
		catch (IOException e)
		{
			System.out.println("catch1"+e.toString());
		}
		finally{//有多个流,需要分别的去关资源 ,
			try
			{
				if(fw!=null)
					fw.close();
			}
			catch (IOException e)
			{
				System.out.println("catch2"+e.toString());
			}
			
		}
	}
}
结果:

但是发现,如果是写入同一个文件的话, 会将以前的数据覆盖。

1.4 文件的续写

在文件的基础上加数据,

代码如下:

//文件的续写
import java.io.*;
class FileWriterDemo2
{
	public static void main(String[] args) //throws IOException
	{
		FileWriter fw=null;//fw就作用于整个函数,
		try
		{
			fw=new FileWriter("E:\\demo.txt",true);// FileNotFoundException
			//fw=new FileWriter("demo.txt");
			fw.write("可以续写了");
			
		}
		catch (IOException e)
		{
			System.out.println("catch1"+e.toString());
		}
		finally{//有多个流,需要分别的去关资源 ,
			try
			{
				if(fw!=null)
					fw.close();
			}
			catch (IOException e)
			{
				System.out.println("catch2"+e.toString());
			}
			
		}
	}
}

运行结果:

1.5 文本文件的读取

第一种读取方式:使用read()方法读取文本文件数据

第二种读取方式:想读多个方式二:通过字符数组进行读取,定义一个字符数组,用于存储读到的字符,
读read(char[])返回的是读到字符的个数

代码如下:

import java.io.*;
class FileReaderDemo 
{
	public static void main(String[] args) 
	{
		//创建一个文件读取流对象,和指定名称的文件相关联
			//要保证该文件是已经存在的,如果不存在,会发生FIleNotFoundException
			FileReader fr=null;
		try
		{
			fr=new FileReader("demo.txt");
			//作为整数读取的字符,如果已到达流的末尾,则返回 -1
			//调用读取流对象的read方法
			//read(),一次读一个字符,而且会自动往下读。
			//int ch=fr.read();
			//想读多个方式一:
			/*
			int ch=0;
			while((ch=fr.read())!=-1)
				System.out.println("ch="+(char)ch);
			*/
			//想读多个方式二:通过字符数组进行读取
			//定义一个字符数组,用于存储读到的字符,
			//读read(char[])返回的是读到字符的个数
			//char[] buf=new char[3];
			char[] buf=new char[1024];
			int num=0;
			while((num=fr.read(buf))!=-1){
				//System.out.println("num="+num+"......"+new String(buf));
				System.out.println("num="+num+"......"+new String(buf,0,num));
			}
		}
		catch (IOException e)
		{
			System.out.println(e);
		}
		finally{
			try
			{
				if(fr!=null)
					fr.close();
			}
			catch (IOException e)
			{
				System.out.println(e);
			}
		}
	}
}
方式一结果:

方式二结果:


练习:

代码如下:

<span style="font-size: 24px;">//读取一个.java文件,并打印在工作台上,
</span><span style="font-size:12px;">import java.io.*;

class  FileReaderDemo1
{
	public static void main(String[] args) throws IOException
	{
		//创建一个文件读取对象,和指定的名称的文件相关联
		FileReader fr=new FileReader("BufCopyText.java");
		//创建一个字符数组用来存储字符
		char[] buf=new char[1024];
		//将读取的字符存的这个数组中,返回的是读取字符的个数
		int len=0;
		while((len=fr.read(buf))!=-1){
			System.out.print(new String(buf,0,len));
		}
		fr.close();
	}
}</span><span style="font-size: 24px;">
</span>
结果:


有字符流来复制文件:

代码如下:

//复制:
//原理:将C盘下的文件数据存储到D盘的一个文件夹中,
/*
步骤:
	1,在D盘创建一个文件,用于存储D盘文件中的数据
	2,定义读取流和D盘文件关联
	3,通过不断的读写完成数据存储
	4,关闭资源
*/
import java.io.*;
class FileCopy
{
	public static void main(String[] args) 
	{
		fileCopy_1();
	}
	public static void fileCopy_1(){
		//创建目的地
			FileWriter fr=null;
		//与已有的数据文件关联
			FileReader fw=null;

		try
		{
			fr=new FileWriter("CollectionDemo_copy.txt");	
			fw=new FileReader("CollectionDemo.java");
			char[] buf=new char[1024];
			int num=0;
			while((num=fw.read(buf))!=-1){
				fr.write(new String(buf,0,num));
			}
		}
		catch (IOException e)
		{
			System.out.println(e);
		}
		finally{
			try
			{
				if(fr!=null)
					fr.close();
			}
			catch (IOException e)
			{
				System.out.println(e);
			}
			try
			{
				if(fw!=null)
					fw.close();
			}
			catch (IOException e)
			{
				System.out.println(e);
			}
		}
	}
}
结果如下;

图例解释:

1.6 字符流的缓冲区

缓冲区的出现提高了对数据的读写效率

对应类:

BufferedReader

BufferedWriter

缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强。

所以在创建缓冲区之前,必须要先有流的对象。

缓冲的原理是这个流对象中封装了数组, 先把数据存起来, 然后一次性的写出去,

操作:只要将徐璈被提高效率的流对象作为参数传递给缓冲区的构造函数即可。

记住:只要用到了缓冲技术,就要记得刷新flush();。

缓冲区其实是为了提高效率存在的, 真正进行写操作的是写入字符流。

代码如下:

import java.io.*;
class BufferedWriterDemo
{
	public static void main(String[] args) throws IOException 
	{
		//创建一个字符写入流对象
		FileWriter fw=new FileWriter("buf.txt");
		//缓冲的原理是这个流对象中封装了数组, 先把数据存起来, 
		//然后一次性的写出去,
		//为了提高字符写入流效率,加入了缓冲技术
		//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
		BufferedWriter bufw=new BufferedWriter(fw);
		//bufw.write("abcde");
		//记住:只要用到缓冲区,就要记得刷新
		//bufw.flush();
		for(int i=0;i<5;i++){
			bufw.write("abcde"+i);
			bufw.newLine();
			bufw.flush();
		}
		//考虑到跨平台性,
		//bufw.newLine();
		//其实关闭缓冲区, 就是在关闭缓冲区中的流的对象
		bufw.close();
	}
}
结果:

读取字符流缓冲区:

该缓冲区中提供了一个一次读一行的方法readLine(), 方便与对文本数据的获取,当返回为null, 表示读到文件的末尾。

代码如下:

class BufferedReaderDemo 
{
	public static void main(String[] args)throws IOException 
	{
		//创建一个读取流对象和文件相关联
		FileReader fr=new FileReader("buf.txt");
		//为了提高效率,加入缓冲技术,只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可
		BufferReader bufr=new BufferedReader(fr);
		String s1=bufr.readLine();
		System.out.println("s1="+s1);
		bufr.close();
	}
}
结果:

下面通过缓冲区复制文本文件:

代码如下;

import java.io.*;
class BufCopyText
{
	public static void main(String[] args) 
	{
		BufferedWriter bufw=null;
		BufferedReader bufr=null;
		try
		{
			bufw=new BufferedWriter(new FileWriter("bufWriter_copy.txt"));
			bufr=new BufferedReader(new FileReader("BufferedWriterDemo.java"));
			String line=null;  
			while((line=bufr.readLine())!=null){
				bufw.write(line);
				bufw.newLine();//这里需要换行
				bufw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println("读写失败");
		}
		finally{
			try
			{
				if(bufw!=null)
					bufw.close();
			}
			catch (IOException e)
			{
				System.out.println("读写关闭失败");
			}
			try
			{
				if(bufr!=null)
					bufr.close();
			}
			catch (IOException e)
			{
				System.out.println("读写关闭失败");
			}
		}
	}
}
结果:

阶段总结:


 
 写入换行使用BufferedWriter类中的newLine()方法。
   
读取一行数据使用BufferedReader类中的readLine()方法。

    bufr.read():
这个read方法是从缓冲区中读取字符数据,所以覆盖了父类中的read方法。
    bufr.readLine():
另外开辟了一个缓冲区,存储的是原缓冲区一行的数据,不包含换行符。


readLine()方法的原理:

无论是读一行,或是读取多个字符,其实最终都是在硬盘上一个一个的读取,所以最终使用的还是read方法一次读一个的方法。

1.7 MyBufferedReader

明白了readLine()的原理, 可以自己创建一个类来和BufferedReader有同样的类似readLine()的方法。

这里的基础还是使用read()方法,读取每一个字符, 因此需要将他保存起来,因而用到了StringBuilder类,

代码如下:

//明白了BufferedReader类中特有的方法readLine()的原理后, 需要基于read()方法
//自己创建一个类来模拟BufferedReader
import java.io.*;
import java.util.*;
class MyBufferedReader
{
	private FileReader r;
	MyBufferedReader(FileReader r){
		this.r=r;
	}
	
	
	public String MyReadLine()throws IOException{
		StringBuilder sb=new StringBuilder();//用来接收字符
		int ch=0;
		while((ch=r.read())!=-1){
			if(ch=='\r')
				continue;
			if(ch=='\n')
				return sb.toString();
			else 
				sb.append((char)ch);
			}
		if<span style="background-color: rgb(255, 255, 0);">(sb.length()!=0</span>)//为了避免如果最后一行没有回车符, 就读不到
			return sb.toString();
		return null;
	}
	public void myClose()throws IOException{
		r.close();
	}
}
class  MyBufferedReaderDemo
{
	public static void main(String[] args)throws IOException 
	{
		FileReader fr=new FileReader("buf.txt");
		MyBufferedReader mybufr=new MyBufferedReader(fr);
		String line=null;
		while((line=mybufr.MyReadLine())!=null){
			System.out.println(line);
		}
		mybufr.myClose();
	}
}

1.8 装饰设计模式:

当想要对已有的对象进行功能增强时, 可以定义一个类, 将已有对象传入, 并基于已有对象的功能,
并提供加强功能,那么自定义的该类,就称为装饰类,
装饰类通常会通过构造方法接收被装饰的对象, 并基于被装饰的对象的功能,提供更强的功能。
例如:
/*
装饰设计模式:
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,
基于已有的功能,并提供加强功能,
那么自定义的该类称为装饰类

装饰类通常会通过构造方法接收被装饰的对象
并基于被装饰的对象的功能,提供更强的功能
*/
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("饭后来一根");
	}
}
class PersonDemo
{
	public static void main(String[] args) 
	{
		Person p=new Person();
		SuperPerson sp=new SuperPerson(p);
		//p.chifan();
		sp.superChiFan();
	}
}
运行结果:

装饰与继承间的区别:
装饰模式比继承要灵活, 避免了继承体系的臃肿, 而且降低了类与类之间的关系,
但是装饰类因为是增强已有对象, 具备的功能和已有对象的是相同的, 只不过提供了更强的功能,所以装饰类和被装饰类, 通常都是属于一个体系中,

其实上面的MyBufferReader就是一个装饰类, 

这个方式, 从继承结构变成组合结构,我中有你;

LineNumberReader实例:

代码:

//跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),
//它们可分别用于设置和获取当前行号。
import java.io.*;
class LineNumberReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个读取对象,和对应的文件名称相关联
		FileReader fr=new FileReader("NiMingInner.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();
	}
}
结果:

通过装饰设计模式的思想, 可以自己造一个:

代码:

//模拟一个带行号的缓冲区对象
import java.io.*;
class MyLineNumberReader extends MyBufferedReader
{
	private int lineNumber;//记录行号
	MyLineNumberReader(FileReader r){
		super(r);
	}
	public String myReadLine() throws IOException{
		lineNumber++;
		return super.MyReadLine();//这里直接调用其父类的方法即可
	}
	public int getLineNumber(){
		return lineNumber;
	}
	public void setLineNumber(int lineNumber){
		this.lineNumber=lineNumber;
	}
}
class  MyLineNumberReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr=new FileReader("MapDemo3.java");
		MyLineNumberReader mylnr=new MyLineNumberReader(fr);
		String line=null;
		mylnr.setLineNumber(100);
		while((line=mylnr.myReadLine())!=null){
			System.out.println(mylnr.getLineNumber()+".."+line);
		}
	}
}
结果:

1.9 字节流File读写操作

字节流:InputStream,OutputStream;
需求:想要操作图片数据,这时就要用到字节流了, 
它不仅可以操作字符,还可以操作其他媒体文件。
字符使用的是字符数组,字节流使用是字节数组,
字符流他其实一样走到字节,他需要把字节临时存起来,

中文是两个字节, 读了一个字节后不能立刻操作, 然后在读一个字节,然后去查表,

其实字符流的底层也是用的是字节流的缓冲区,

而如果直接使用字节流来操作, 没有使用具体指定的缓冲区FileoutputStream,不需要缓冲的,不管什么类型的数据都以字节来操作,所以会把字节一步一步的都写到了write中, 因此不需要刷新,

到了缓冲区的时候,才有刷新这个机制;同样是调用了系统的资源, 因此关闭资源还是需要的;

代码如下:

<span style="background-color: rgb(255, 255, 255);">//需求:
//想要操作图片数据,这时就要用到字节流,
/*
字节流:
	InputStream OutputStream

	下面第二种方式是最好的, readFile_1()
*/
import java.io.*;

class FileStream
{
	public static void main(String[] args) throws IOException
	{
		
		System.out.println("用字节流写入文件");
		writeFile();
		System.out.println("用字节流读取文件一ch=fis.read()");
		readFile();
		System.out.println("用字节流读取文件二ch=fis.read(buf)");
		readFile_1();
		System.out.println("用字节流读取文件三ch=fis.read(buf),num=fis.available()");
		readFile_2();
	}
	public static void writeFile() throws IOException{
		FileOutputStream fos=new FileOutputStream("fos.txt");
		fos.write("abced".getBytes());//将字符串变成字节数组
		fos.close();
	}

	public static void readFile() 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 readFile_1() 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_2() throws IOException{
		FileInputStream fis=new FileInputStream("fos.txt");
		int num=fis.available();//得到字节数,他作为字节数组的长度,因此不需要循环
		System.out.println("num="+num);
		byte[] buf=new byte[num];//和前面的不同, 这里需要的是字节数组
		int len=0;
		while((len=fis.read(buf))!=-1){
			System.out.println(new String(buf));
		}
		fis.close();
	}
}</span><span style="color:#ff0000;">
</span>
结果:

注意:第三种读取方式如果是操作媒体文件,数据量大的话, 就可能发生内存溢出。

需求复制图片:

代码如下:

/*
	复制一张图片
思路:
	1,用字节读取流对象和图片关联
	2,用字节写入流对象创建一个图片文件,用于存储获取到的图片数据
	3,通过循环读写,完成数据的存储
	4,关闭资源
*/
import java.io.*;
class PicCopy
{
	public static void main(String[] args) 
	{
		FileInputStream fis=null;
		FileOutputStream fos=null;
		try
		{
			fis=new FileInputStream("E:\\仓木麻衣\\仓木麻衣图片\\Kuraki_Mai.jpg");
			fos=new FileOutputStream("E:\\仓木麻衣.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("读取失败");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			try
			{
				if(fos!=null)
					fos.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("写到关闭失败");
			}
		}
	}
}

结果:

2.0 字节流缓冲区

同样是提高了字节流的读写效率。

通过字节流缓冲区对MP3的复制:

代码如下:

/*
	演示MP3的复制,通过缓冲区
	BufferedInputStream
	BufferedOutputStream  初始化的时候,需要有流进来, 
*/
import java.io.*;
class MyBufferedInputStream
{
	private InputStream in;
	private int count;//定义计数器
	private byte[] buf=new byte[1024*8];//定义字节数组
	private int pos;//定义位置即指针

	MyBufferedInputStream(InputStream in){
		this.in=in;
	}
	//一次读一个字节,从缓冲区(字节数组)获取
	public int myRead()throws IOException{
		//通过in对象读取硬盘上的数据,并存储buf中
		if(count==0)//判断个数为0
		{
			count=in.read(buf);//通过in.read(buf)返回count=1024个字节数
			if(count<0)
				return -1;
			pos=0;//每次取完了, 都需要归0
			byte b=buf[pos];//取字符数组的元素
			count--;//取了一个个数少一个
			pos++;//字符数组的位置进一位
			return b&255;//返回这个字节
		}
		else if(count>0)//第一次返回值不是-1,将值写到bufos.write(by);
							//循环第二次取的时候,这个count是1023了,
		{
			byte b=buf[pos];
			count--;
			pos++;
			return b&255;
		}
		return -1;
	}
	public void myClose()throws IOException{
		in.close();
	}
}
class Mp3Copy
{
	public static void main(String[] args) throws IOException
	{
		long start=System.currentTimeMillis();
		//copy_1();
		copy_2();
		long end=System.currentTimeMillis();
		System.out.println((end-start)+"毫秒");

	}
	public static void copy_1() throws IOException{
		BufferedInputStream bufis=
			new BufferedInputStream(new FileInputStream("F:\\应用软件\\音乐软件\\Kugou\\戚薇 - 如果爱忘了.mp3"));
		BufferedOutputStream bufos=
			new BufferedOutputStream(new FileOutputStream("E:\\戚薇 - 如果爱忘了.mp3"));
		int by=0;
		while((by=bufis.read())!=-1){
			bufos.write(by);
		}
		bufis.close();
		bufos.close();
	}

	public static void copy_2() throws IOException{
		MyBufferedInputStream bufis=
			new MyBufferedInputStream(new FileInputStream("F:\\应用软件\\音乐软件\\Kugou\\戚薇 - 如果爱忘了.mp3"));
		BufferedOutputStream bufos=
			new BufferedOutputStream(new FileOutputStream("E:\\戚薇 - 如果爱忘了.mp3"));
		int by=0;
		System.out.println("第一个字节:"+bufis.myRead());
		while((by=bufis.myRead())!=-1){
			bufos.write(by);
		}
		bufis.myClose();
		bufos.close();
	}
}
结果:

2.1 读取键盘录入

之前获取数据的形式都是指定的, 数据源变了,

键盘录入的形式来获取数据。

获取键盘录入
System.out:对应的是标准输出设备:控制台;
System.in:对应的标准输入设备:键盘

需求:

通过键盘录入数据
当录入一行数据后,就将该行数据进行打印
如果录入的数据是over,那么停止录入

思路: 
录入的时候, 先存一下,当按回车键的时候, 就打印
就需要定义一个临时缓冲区,

代码如下;

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();//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()的原理,

2.2读取转换流,

通过

涉及两个对象, 转换流,

是字节流还是字符流的成员呢?

如果是字节流的话, 不需要转换, 因为有了字符流, 才需要转换,

定义在字符流中,刚才的键盘录入一行数据并打印器其大写,发现其实就是读一行数据的原理,也就是readLine()方法,

能不能直接使用readLine()方法来完成键盘录入的一行数据的读取呢?

readLine()方法是字符流BufferedReader类中的方法,而键盘录入的read()方法是字节流InputStream的方法。

那么能不能将街字节流转成字符流在使用字符流缓冲区中的readLine()方法呢?

通过查阅Api文档发现,这一步涉及到了转换流,

可以想象如果是这个转换流是字节流的子类的话,那么就不需要转换,直接可以用了, 因为有了字符流, 才需要将字节流转换成字符流。

因此这个定义在字符流中,

类Reader

-->InputStreamReader类,是字节流通向字符流的桥梁,

本身是一个字符流, 所以在创建的时候需要接收一个指定的字节流的对象,

然后就可以用字符流的缓冲区对他进行装饰,


需求:将键盘录入的数据打印到控制台上,如果录入的数据是over,那么停止录入。

代码如下:

import java.io.*;
class TransStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		byteToString();
	}
	public static void byteToString() throws IOException{
		//获取键盘录入对象
		InputStream in=System.in;

		//将字节流对象转成字符流对象,使用转换流InputStreamReader
		//InputStreamReader 是字节流通向字符流的桥梁
		InputStreamReader isr=new InputStreamReader(in);

		//为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
		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);

		//BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out))
		
		String line=null;

		while((line=bufr.readLine())!=null){
			if("over".equals(line))
				break;
			// System.out.println(line.toUpperCase());
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}

		bufr.close();
		bufw.close();
	}
}

结果:


需求:想把键盘录入的数据存储到一个文件中,

/*
流操作规律一:
	键盘的最常见写法:
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
			是数据的源
2,需求:想把键盘录入的数据存储到一个文件中,
源:键盘
目的:文件
源没有变, 目的变了,

*/


import java.io.*;
class TransStreamDemo1
{
	public static void main(String[] args) throws IOException
	{
		byteToString();
	}
	public static void byteToString() throws IOException{
		//获取键盘录入对象
		InputStream in=System.in;

		//将字节流对象转成字符流对象,使用转换流InputStreamReader
		//InputStreamReader 是字节流通向字符流的桥梁
		InputStreamReader isr=new InputStreamReader(in);

		//为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
		BufferedReader bufr=new BufferedReader(isr);

		//BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));

		OutputStream out=new FileOutputStream("out.txt");//目的地
		OutputStreamWriter osw=new OutputStreamWriter(out);
		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;
			// System.out.println(line.toUpperCase());
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}

		bufr.close();
		bufw.close();
	}
}
结果:

代码如下:

2.3 IO流操作的规律

操作的基本规律:要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。想要知道对象的开发时用到哪些对象,只要通过四个明确即可。

1,明确源和目的
      源:InputStream, Reader
      目的:OutputStream ,Writer
2,明确数据是否是纯文本数据
     源:是纯文本:Reader
                否:InputStream
     目的:是纯文本:Writer
                否:OutputStream

3、明确具体的设备
        源设备:                                          目的设备:  
         硬盘:File                                          硬盘:File
         键盘:System.in                                 
控制台:System.out 
         内存:数组                                        
内存:数组    
         网络:Socket流                                 
 网络:Socket        

4,明确是否需要加入缓冲技术
举例代码:

/*
3,需求:想要将一个文件的数据打印到控制台上,
源:文件
目的:控制台
*/
/*
流操作的基本规律:
最痛苦的就是流对象有很多, 不大知道用哪一个
通过两个明确来完成
1,明确源和目的;
	源:输入流。InputStream Reader
	目的:输出流 OutputStream Writer
2,操作的数据是否是纯文本。
	是:字符流
	否:字节流
3,当体系明确后, 在明确要使用哪个具体的对象
	通过设备来进行区分
	原设备:内存,硬盘,键盘
	目的设备:内存,硬盘,控制台

需求一:
	将一个文本文件中数据存储到另一个文件中, 复制文件。
	源:因为是源,所以使用读取流, InputStream Reader
	是不是操作文本文件
	是!这时就可以选择Reader;
	这样体系就明确了,

	接下来明确要使用该体系中的那个对象。
	明确设备:硬盘,上一个文件。
	Reader体系中可以操作文件的对象是FileReader.
	是否需要提高效率,是!加入Reader体系汇中的缓冲区:BufferedReader
	BufferedReader bufr=new BufferedReader(new FileReader("p.txt"));

	目的:OutputStream Writer
	是不是操作文本文件
	是!Writer
	设备:硬盘,一个文件
	Writer体系中可以操作文件的对象是FileWriter ,
	是否需要提高效率,是!加入Writer体系汇中的缓冲区:BufferedWriter
	BufferedWriter bufw=new BufferedWriter(new FileWriter("p_copy.txt"));

需求一:
	将键盘录入的数据存储到另一个文件中, 
	源:因为是源,所以使用读取流, InputStream Reader
	是不是操作文本文件
	是!这时就可以选择Reader;
	这样体系就明确了,

	接下来明确要使用该体系中的那个对象。
	明确设备:键盘,System.in;
	Reader体系中可以操作文件的对象是FileReader.
	为了操作键盘的文本数据方便,转成字符流按照字符串操作是最方便的,
	既然明确了用Reader,那么就将System.in转换成Reader;
	用了Reader体系中的转换流, InputStreamReader;
	new InputStreamReader(System.in));
	是否需要提高效率,是!加入Reader体系汇中的缓冲区:BufferedReader
	BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));

	目的:OutputStream Writer
	是不是操作文本文件
	是!Writer
	设备:硬盘,一个文件
	Writer体系中可以操作文件的对象是FileWriter ,
	默认的编码表:GBK
	是否需要提高效率,是!加入Writer体系汇中的缓冲区:BufferedWriter
	BufferedWriter bufw=new BufferedWriter(new FileWriter("p_copy.txt"));

扩展:
	但是存储时,需要加入指定的编码表utf-8,
	而指定的编码表只有转换可以指定,所以要使用的对象是OutputStreamWriter.
	而该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流,FileOutStreamWriter
*/
import java.io.*;
class TransStreamDemo2
{
	public static void main(String[] args) throws IOException
	{
		byteToString();
	}
	public static void byteToString() throws IOException{
		//获取键盘录入对象
		InputStream in=System.in;

		//将字节流对象转成字符流对象,使用转换流InputStreamReader
		//InputStreamReader 是字节流通向字符流的桥梁
		InputStreamReader isr=new InputStreamReader(in);

		//为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
		BufferedReader bufr=new BufferedReader(isr);

		//BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));

		//OutputStream out=System.out;//目的地
		OutputStream out=new FileOutputStream("out_copy.txt");
		OutputStreamWriter osw=new OutputStreamWriter(out);
		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;
			// System.out.println(line.toUpperCase());
			bufw.write(line);
			bufw.newLine();
			bufw.flush();
		}

		bufr.close();
		bufw.close();
	}
}
结果:


 其中OutputStreamWriter的子类
FileWriter是用来写入字符文件的便捷类,此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。

代码如下;

<span style="font-size:12px;">//OutputStreamWriter的子类FileWriter是用来写入字符文件的便捷类,
//此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。
import java.io.*;

class TransStreamDemo4
{
	public static void main(String[] args) throws Exception
	{
		byteToString_1();
	}
	public static void byteToString_1()throws Exception{
		BufferedReader bufr=
			new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bufw=new BufferedWriter(new FileWriter("E:\\1.txt"));
		String line=null;
		while((line=bufr.readLine())!=null){
			if("over".equals(line)){
				break;
			}
			bufw.write(line);
			bufw.newLine();
			bufw.close();
		}
		bufr.close();
		bufw.close();
	}
}</span><span style="font-size: 16px;">
</span>
结果:


 任何Java识别的字符数据使用的都是Unicode码表,但是FileWriter写入本地文件使用的是本地编码,也就是GBK码表。

    而OutputStreamWriter可使用指定的编码将要写入流中的字符编码成字节。

代码如下:

import java.io.*;

class TransStreamDemo5
{
	public static void main(String[] args) throws Exception
	{
		byteToString_1();
	}
	public static void byteToString_1()throws Exception{
		BufferedReader bufr=
			new BufferedReader(new InputStreamReader(System.in));
		//下面这句代码等同于FileWriter fw = new FileWriter("1.txt");
	   //FileWriter其实就是转换流指定了本机默认码表的体现,而且这个转换流的子类对象,可以方便操作文本文件。
       //这是按照默认码表来操作文件的便捷类
       //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("1.txt"),"GBK");
       //如果操作文本文件需要明确具体的码表,FileWriter就不行了,必须用转换流。
		BufferedWriter bufw=
			new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:\\2.txt"),"UTF-8"));
		String line=null;
		while((line=bufr.readLine())!=null){
			if("over".equals(line)){
				break;
			}
			bufw.write(line);
			bufw.newLine();
			bufw.close();
		}
		bufr.close();
		bufw.close();
	}
}
结果:

什么时候使用转换流呢?
    1
、源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁,提高对文本操作的便捷。
    2
、一旦操作文本涉及到具体的指定编码表时,必须使用转换流。

2.4 异常的日志文件信息

一般运行的时候, 把异常打印在控制台上,没有什么意义,

能不能把异常信息保存在文件中呢?

管理人员定期看文件, 这样就方便了,

代码如下:

//异常的日志信息
import java.io.*;
import java.util.*;
import java.text.*;
class  ExceptionInfo
{
	public static void main(String[] args) throws Exception 
	{
		exceptionInfo();
		//system_set();
	}
	public static void exceptionInfo(){
		try
		{
			int[] arr=new int[2];
			System.out.println(arr[2]);
		}
		catch (Exception e)
		{
			try
			{
				Date d=new Date();
				SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
				String s=sdf.format(d);
				PrintStream ps=new PrintStream("E:\\a.log");
				ps.println(s);
				System.setOut(ps);
			}
			catch (Exception e1)
			{
				throw new RuntimeException("日志文件创建失败");
			}
			e.printStackTrace(System.out);
		}
	}
}
结果:

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值