Java基础(IO流)

本文详细介绍了Java中的IO流,包括IO流的概述、分类、字节流与字符流的基类及其子类。重点讨论了字符流的使用场景,如FileReader和FileWriter,并对比了字符流与字节流在拷贝文件时的差异。还提到了缓冲区在字符流中的应用,如BufferedReader和BufferedWriter,并介绍了装饰模式在增强流功能中的应用。此外,还讲解了LineNumberReader的使用和自定义装饰类的实现。最后,探讨了流操作的规律和转换流在不同场景下的应用。


IO流概述

一、IO即Input、Ouput组合;

二、IO流特点:

1、IO流用来处理设备之间的数据传输

2、Java对数据的操作是通过流的方式

3、Java用于操作流的对象都在IO包中

三、IO流分类

1、按照操作数据分:字符流和字节流

       字节流:字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的(如图片,音频)

       字符流:字符流只能操作纯字符数据,比较方便(不能操作音频和图片)。

2、按照流向分为:输入流和输出流

四、字节流和字符流

1、字节流的抽象基类

        InputStram(输入流),OutputSteam(输出流)

2、字符流的抽象基类

        Reader(输入流)、Writer(输出流)

住:由这四个类派生出来的子类的名称都是以其父类名称作为后缀来命名的

如:InputStream的子类FileInputSteam

      OutputStram的子类FileOutputStram

       Reader的子类FileReader

       Writer的子类FileWriter

字符流

一、字符流是什么?

      1、字符流是可以直接读取字符的IO流

      2、字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.

二、FielReader和FileWriter

          FileReader

<span style="font-size:14px;">/*FileReader
	步骤:1、创建文件读取流对象,并指定路径下的文件名称,如果没指定路径,
	         则默认是读取该源文件所在的路径下的文件
	      2、调用读取流对象的read方法,返回读取的字符在ASCII中对应的数字,
		     如果读取到则返回-1
		  3、因为没调用一次read方法,仅仅只是读取一个字符,所以要使用循环
		  4、关闭流
*/
import java.io.*;
class  FileReaderDemo
{
	public static void main(String[] args) 
	{
		/*FileReader是IO流中的字符输入流,是从硬盘上读取数据用的*/
		//步骤1
		FileReader fr = null;
		try{
			fr = new FileReader("d:\\file.txt");  //会抛出FileNotFoundException异常
			/*因为是读取文件,所以要指定文件以及文件所在的路径,必须保证文件存在
			  如果要读取得文件不存在,则会发生FileNotFoundException文件找不到异常
			*/
			//步骤2
			int charNum = 0;
			//调用read方法,循环读取文件
			while((charNum = fr.read())!=-1){  //read会抛出IOExcepion异常
				System.out.print((char)charNum);
			}
		}
		catch(IOException e){
			System.out.println(e.toString());
		}
		finally{
			//流是必须要关闭的,所以放在finally语句块中
			try
			{
				if(fr!=null)
				fr.close();     //会抛出IOException异常
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
		}

	}
}
</span>

注:输出流中flush和close的区别:

       flush是用来刷新缓冲区的,刷完之后还可以写出

       close方法是用来关闭流的,在关闭之前会刷新一次缓冲区,刷完之后关闭,不可以再写出

FileWriter

<span style="font-size:14px;">/*FileReader
	步骤:1、创建文件写入流对象,并指定路径下的文件名称,如果该路径下没有指定文件,
	         则会在该路径下创建一个文件,如果有则会覆盖
	      2、调用输入流对象的write方法,将数据写入文件中
		  3、清空缓存或关闭流
*/
import java.io.*;
class  FileWriterDemo
{
	public static void main(String[] args) 
	{
		/*FileWriter是IO流中的字符输出流,是将读取到的数据写入指定文件中*/
		//步骤1
		FileWriter fw = null;
		try{
			fw = new FileWriter("d:\\file_Copy.txt");  //会抛出IOException异常
			//步骤2
			fw.write("abcdefg");
		}
		catch(IOException e){
			System.out.println(e.toString());
		}
		finally{
			//流是必须要关闭的,所以放在finally语句块中
			try
			{
				if(fw!=null)
					fw.close();     //会抛出IOException异常
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
		}
	}
}
</span>

三、什么情况下使用字符流

1、字符流也可以拷贝文本文件,但不推荐使用,因为读取时会把字节转换成字符,写入时会把字符转回字节

2、程序需要读取一段文本,或者需要写出一段文件的时候可以使用字符流

四、字符流是否可以拷贝非纯文本的文件

1、不可以拷贝非纯文本的文件

2、因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去

3、如果是?,直接写出,这样写出之后的文件就乱了,看不了了

五、自定义的数组拷贝

<span style="font-size:14px;">/*自定义数组拷贝  提高效率
思路:当使用FileReader的read方法时,一次只读取一个字符调用一次输出一次,
比较麻烦,所以我们能不能把读取的数据存入数组中,等达到一定数量后,在输出呢?
步骤:1、定义输入输出流
      2、定义数组,用于保存读取到的数据
	  3、将数组中的数据写入到指定文件中
*/
import java.io.*;
class FileReaderFileWriterDemo 
{
	public static void main(String[] args) 
	{
		//定义输入流
		FileReader fr = null;
		//定义输出流
		FileWriter fw = null;
		try
		{
			fr = new FileReader("d:\\file.txt");
			fw = new FileWriter("d:\\file_copy.txt");
			//定义数组存放字符
			char [] ch = new char[10];	//这里一般把数组的长度定义成1024也就是2kB
			int index = 0;
			//把读到的字符存入数组
			while((index = fr.read(ch))!=-1){
				//从字符中读取数据写入文件中
				fw.write(ch,0,index);
				fw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally{
			//分别关闭输入输出流
			try
			{
				if(fr!=null)
					fr.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
			finally{
				try
				{
					if(fw!=null)
						fw.close();
				}
				catch (IOException e)
				{
					System.out.println(e.toString());
				}
			}
		}
	}
}
</span>

六、带缓冲的字符流

1、BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率

2、BufferedWriter的write()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率

注:1、BufferedReader中有一个特有的方法   readerLine()   用于读取一行文本,还有就是用readerLine读取的数据不会自动换行,那是因为readerLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符

       2、 BufferedWriter中也有一个特有的方法   newLine()   是一个跨平台的换行符

<span style="font-size:14px;">/*带缓冲的字符流
*/
import java.io.*;
class BufferedReaderBufferedWriterDemo 
{
	public static void main(String[] args) 
	{
		BufferedReader br = null;
		BufferedWriter bw = null;
		try
		{
			br = new BufferedReader(new FileReader("d:\\file.txt"));
			bw = new BufferedWriter(new FileWriter("d:\\file_copy.txt"));
			/*缓冲区的作用是为了提高流的操作效率而出现的,所以在创建缓冲区
			之前,必须要先有流对象,所以要把流对象作为实际参数传递给缓冲区
			的构造函数*/
			String str = "";
			while((str=br.readLine())!=null){
				bw.write(str);
				bw.newLine();
				bw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally{
			//分别关闭输入输出流
			try
			{
				if(br!=null)
					br.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
			finally{
				try
				{
					if(bw!=null)
						bw.close();
				}
				catch (IOException e)
				{
					System.out.println(e.toString());
				}
			}
		}
	}
}
</span>

七、写一个自己的缓冲区

<span style="font-size:14px;">/*
明白了BufferedReader类中特有方法readLine的原理后,
可以自定义一个类中包含一个功能和readLine一致的方法。
来模拟一下BufferedReader
*/
import java.io.*;
class MyBufferedReader
{
	private FileReader fr;
	MyBufferedReader(FileReader fr){
		this.fr = fr;
	}
	public String readLine()throws IOException{
		int charNum = 0;
		String str = "";
		while((charNum = fr.read())!=-1){
			if(charNum=='\r')
				continue;
			if(charNum=='\n')
				return str;
			else
				str+=(char)charNum;
		}
		if(str.length()!=0)
			return str;
		return null;
	}
	public void close()throws IOException{
		fr.close();
	}
}
class MyBufferedReaderMyBufferedWriterDemo 
{
	public static void main(String[] args) 
	{
		MyBufferedReader br = null;
		BufferedWriter bw = null;
		try
		{
			br = new MyBufferedReader(new FileReader("d:\\file.txt"));
			bw = new BufferedWriter(new FileWriter("d:\\file_copy.txt"));
			String str = "";
			while((str=br.readLine())!=null){
				bw.write(str);
				bw.newLine();
				bw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally{
			//分别关闭输入输出流
			try
			{
				if(br!=null)
					br.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
			finally{
				try
				{
					if(bw!=null)
						bw.close();
				}
				catch (IOException e)
				{
					System.out.println(e.toString());
				}
			}
		}
	}
}
</span>

八、装饰类

1、定义:装饰类其实和继承的复写差不多,但是有一点是装饰并不是继承这个类复写该类方法,而是继承这个类的父类,把需要增强功能的类作为实际参数传递给装饰类的构造函数

如:

使用继承完成的功能增强后体系表现

 MyReader//专门用于读取数据的类。
    |--MyTextReader(需要增强的类)
        |--MyBufferTextReader(子类继承父类,增强功能)
    |--MyMediaReader(需要增强的类)
        |--MyBufferMediaReader(子类继承父类,增强功能)
    |--MyDataReader(需要增强的类)
        |--MyBufferDataReader(子类继承父类,增强功能)

发现:当超类下面有多个子类时,而子类复写父类方法后共性方法还需要增强时,可以在定义一个装饰类继承超类,把子类传入,挺高功能增强,通过多态的形式。可以提高扩展性。

如:
MyReader//专门用于读取数据的类。
    |--MyTextReader(需要增强的类)
    |--MyMediaReader(需要增强的类)
    |--MyDataReader(需要增强的类)
    |--MyBufferReader(用于增强子类)

以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。

现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。

装饰模式比继承要灵活。避免了继承体系臃肿。
而且降低了类于类之间的关系。

装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。

写一个自己的装饰类

<span style="font-size:14px;">//基础Reader类   同时必须复写抽象方法close和read(char[] cbuf, int off, int len)方法
class MyBufferedReader extends Reader
{
	
	private Reader r;
	MyBufferedReader(Reader r)
	{
		this.r = r;
	}

	//可以一次读一行数据的方法。
	public String myReadLine()throws IOException
	{
		//定义一个临时容器。原BufferReader封装的是字符数组。
		//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
		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(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();
	}
}</span>


九、.LineNumberReader

特有方法:获取行号      getLineNumber()

                  设置行号      setLineNumber()

<span style="font-size:14px;">import java.io.*;

class LineNumberReaderDemo 
{
	public static void main(String[] args)throws IOException 
	{
		FileReader fr = new FileReader("PersonDemo.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();
	}
}
</span>
运行结果:


十、写一个自己的LineNumberReader

<span style="font-size:14px;">import java.io.*;
//继承BufferedReader是为了方便调用BufferedReader的readline方法
class MyLineNumberReader extends BufferedReader
{
	private int lineNumber;
	MyLineNumberReader(Reader r)
	{
		super(r);
	}
	//没从硬盘上读取一行数据就让lineNumber+1
	public String myReadLine()throws IOException
	{

		lineNumber++;
		return super.readLine();
	}
	public void setLineNumber(int lineNumber)
	{
		this.lineNumber = lineNumber;
	}
	public int getLineNumber()
	{
		return lineNumber;
	}
}

class  MyLineNumberReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("copyTextByBuf.java");

		MyLineNumberReader mylnr = new MyLineNumberReader(fr);

		String line = null;
		mylnr.setLineNumber(100);
		while((line=mylnr.myReadLine())!=null)
		{
			System.out.println(mylnr.getLineNumber()+"::"+line);
		}

		mylnr.myClose();
	}
}
</span>

字节流

一、概述

1、字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。

2、由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。

3、读写字节流:InputStream   输入流(读)

                             OutputStream  输出流(写)

4、常用方法:

InputStream:

1、read()        读取一个字节,返回 0 255 范围内的 int 字节值

2、read(byte[] b)   从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b

3、available()       可以获取读取文件中的所有字节数

4、close()                             关闭流,释放资源  

OutputStream:

1、write(int b)      写出一个字节

2、write(byte[])     写出数组中的所有字节

3、writer(byte[],int strat,int end)   从指定位置写出数组中的字节

4、close()    关闭流,释放资源   

5、读取文件

1、创建FileINputStream对象,指定一个文件,该文件必须存在,不存在则会抛出FileNotFoundException异常
2、使用read方法从文件中读取一个字节,如果读取到文件末尾则会返回-1
3、读取完毕后,使用close方法释放资源
<span style="font-size:14px;">import java.io.*;
class  FileInputStreamDemo
{
	public static void main(String[] args) 
	{
                //创建流对象,关联file.txt文件
                FileInputStream fis = new FileInputStream("D:\\file.txt");
		int by = 0;
                //循环读取
                while((by=fis.read())!=-1){
		System.out.println(char(by));
                fis.close();
        }
}
</span>
为什么read方法返回的是int而不是byte类型呢?
原因:因为如果读取的是视频文件或音频文件或图片文件等,在读取过程中很有可能会遇到11111111,也就是byte类型的-1,那么遇到-1程序就会停止读取,会漏掉文件,为了防止这种情况出现,把byte类型提升为int类型,在这个字节前补上24个0,把遇到的-1变成255,这样可以保证将整个文件读完
6、写出文件
1、创建FileOutputStream对象,指定一个文件,如果文件不存在,会自动创建该文件,如果需要在该文件后面追加,则需要在创建FileOutputStream对象时传入true
2、使用write方法进行写入
3、使用close方法关闭资源
<span style="font-size:14px;">import java.io.*;
class  FileOutputStreamDemo
{
	public static void main(String[] args) 
	{
		//创建输出流对象,与文件关联
		FileOutputStream fos = new FileOutputStream("D:\\file.txt");
		fos.write(100);    //写出数据的时候会将前面24个0去掉,写出的是一个字节
		fos.write(65);
		fos.write(97);
		fos.write("AABBBCCCDDDEEEFFFF".getBytes());  //写出字符串时,需要转换
                fos.close();     //关闭
        }
}</span>
7、拷贝文件
1、琢个字节拷贝
<span style="font-size:14px;">import java.io.*;
class  CopyMp3
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		FileInputStream fis =null;
		FileOutputStream fos =null;
		try
		{
			fis = new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3");
			fos = new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3");
			int by = 0;
			while((by=fis.read())!=-1){
				fos.write(by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件复制失败");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件读取流关闭失败");
			}
			finally{
				try
				{
					if(fos!=null)
						fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件输出流关闭失败");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷贝使用的毫秒数:"+(start-end));
	}
}
</span>
2、自定义数组拷贝
<span style="font-size:14px;">import java.io.*;
class  BytesCopyMp3
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		FileInputStream fis =null;
		FileOutputStream fos =null;
		try
		{
			fis = new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3");
			fos = new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3");
			byte [] bys = new byte[1024];
			int by = 0;
			while((by=fis.read(bys))!=-1){
				fos.write(bys,0,by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件复制失败");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件读取流关闭失败");
			}
			finally{
				try
				{
					if(fos!=null)
						fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件输出流关闭失败");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷贝使用的毫秒数:"+(end-start));
	}
}
</span>


3、定义一个和文件字符数相同的数组拷贝import java.io.*;
<span style="font-size:14px;">import java.io.*;
class  CopyMp3_available
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		FileInputStream fis =null;
		FileOutputStream fos =null;
		try
		{
			fis = new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3");
			fos = new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3");
			//因为数组的长度和文件字节长度是一样的,所以不需要使用循环了
			byte [] bys = new byte[fis.available()];
			fis.read(bys);
			fos.write(bys);
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件复制失败");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件读取流关闭失败");
			}
			finally{
				try
				{
					if(fos!=null)
						fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件输出流关闭失败");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷贝使用的毫秒数:"+(end-start));
	}
}
</span>
4、带缓冲流拷贝
<span style="font-size:14px;">import java.io.*;
class  CopyMp3Buffered
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		FileInputStream fis =null;
		FileOutputStream fos =null;
		try
		{
			fis = new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3");
			fos = new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3");
			//创建缓冲区
			BufferedInputStream bis = new BufferedInputStream(fis);
			BufferedOutputStream bos = new BufferedOutputStream(fos);
			int by = 0;
			while((by=bis.read())!=-1){
				bos.write(by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件复制失败");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件读取流关闭失败");
			}
			finally{
				try
				{
					if(fos!=null)
						fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件输出流关闭失败");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷贝使用的毫秒数:"+(end-start));
	}
}
</span>

8、自定义字节流缓冲区

BufferedInputStream的特点:

BufferedInputStream内置了一个缓冲区(数组),从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个,存在缓冲区中,返回给程序一个,当程序再次读取时,就不用找文件了,直接从缓冲区读取,知道缓冲区中的所有的都被使用过,才重新从文件中读取8192个

BufferedOutputStream的特点:

BufferedOutputStream也内置了一个缓冲区(数组),当程序向流中写出字节时,不会直接写到文件,先写到缓冲区,知道缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件中

自定义BufferedOutputStream

/*自定义BufferedInputStream缓冲区
原理:BufferedInputStream内置了一个缓冲区(数组),从BufferedInputStream中
读取一个字节时,BufferedInputStream会一次性从文件中读取8192个,存在缓冲区
中,返回给程序一个,当程序再次读取时,就不用找文件了,直接从缓冲区读取,
直到缓冲区中的所有的都被使用过,才重新从文件中读取8192个


	思路:1、既然是缓冲区,肯定要有底层读取流,所以要有FileInputStream
	      2、缓冲区肯定要有缓存的地方,即数组   类型byte
		  3、由于已经读取了一批数据并存入了数组中,所以需要把数组中的数据读取完毕后,
		     才能从文件中再次读取一批文件存入数组,所以要先把数组中的数据写出去
		  4、要想把数组中的数据清空,只能是当调用缓冲区的read方法时,利用指针来获取

*/
import java.io.*;
class MyBufferedInputStream
{
	private byte [] bys = new byte[1024*4];   //缓存区
	private int count = 0;   //用于记录数组中数据的长度
	private int pos=0;    //指针
	private FileInputStream fis;
	public MyBufferedInputStream(FileInputStream fis){
		this.fis = fis;
	}
	public int myRead()throws IOException{
		if(count==0)	//如果count为0,从文件中读取一批数据存入数组中
		{
			count = fis.read(bys);    //给count赋值
			if((count==-1))   //如果成立表示没有文件中没有数据,直接返回-1
				return -1;
			pos = 0;		//数组中的元素被读完,把pos初始化
			byte b = bys[pos];  //用b记录bys数组中的pos位置上的元素
			count--;      //每调用一次myRead方法,条件成立,让count-1,当count等于0时,再从文件中读取一批数据
			pos++;       //每调用一次myRead方法,条件成立,让pos+1,知道把bys数组中的元素读完
			return b&255;   //因为read方法返回的是int类型,而b是byte类型,类型提升
			                //由原先的8位变成了32位,而b的二进制有可能是11111111的情况
							//也就是byte类型的-1,那么遇到-1程序就会停止读取,会漏掉文件,
							//为了防止这种情况出现,把byte类型提升为int类型,需要在这个字节
							//前补上24个0,把遇到的-1变成255,所以需要&255

		}
		else if(count>0)
		{
			byte b = bys[pos];
			count--;
			pos++;
			return b&0xff;
		}
		return -1;
	}
	public void myClose()throws IOException{
		fis.close();
	}
}
class  MyBufferedInputStreamDemo
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		MyBufferedInputStream bis =null;
		BufferedOutputStream bos =null;
		try
		{
			//创建缓冲区
			bis = new MyBufferedInputStream(new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3"));
			bos = new BufferedOutputStream(new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3"));
			int by = 0;
			while((by=bis.myRead())!=-1){
				bos.write(by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件复制失败");
		}
		finally{
			try
			{
				if(bis!=null)
					bis.myClose();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件读取流关闭失败");
			}
			finally{
				try
				{
					if(bos!=null)
						bos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件输出流关闭失败");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷贝使用的毫秒数:"+(end-start));
	}
}

流操作规律

一、键盘录入

1、标准输入输出流

        System.in:对应的标准输入设备,键盘。

        Ssytem.out:对应的是标准的输出设备,控制台。

        System.in的返回值类型是InputStream.

        System.out的返回值类型是PrintStream,它是OutputStream的子类FilterOutputStream的子类。

2、整行录入

       当使用输入流进行键盘录入时,只能一个字节一个字节进行录入。为了提高效率,可以自定义一个数组将一行字节进行存储。当一行录入完毕,再将一行数据进行显示。这种正行录入的方式,和字符流读一行数据的原理是一样的。也就是readLine方法。

      那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流InputStream的方法。

      那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?这就需要用到转换流了。

3、转换流

3.1 转换流的由来:

       a、字符流与字节流之间的桥梁

       b、方便了字符流与字节流之间的操作

转换流的应用:

      字节流中的数据都是字符时,转成字符流操作更高效。

3.2   InputStreamReader将字节流通向字符流

       a、获取键盘录入对象。

              InputStream in=System.in;

       b、将字节流对象转成字符流对象,使用转换流。

              InputStreamReade  risr = new InputStreamReader(in);

       c、为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader

              BufferedReaderbr=new BufferedReader(isr);

       //键盘录入最常见写法

              BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));

3.3   OutputStreamWriter字符流通向字节流

       字符通向字节:录入的是字符,存到硬盘上的是字节。步骤和InputStreamReader转换流一样。

示例:

/* 
需求:将键盘录入的数据,显示在控制台,当输入over时,表示结束 
源:键盘录入。 
目的:控制台。 
 
*/  
import java.io.*;  
class TransStreamDemo   
{  
    public static void main(String[] args)throws IOException  
    {  
        //获取键盘录入对象。  
        //InputStream in=System.in;  
        //将字节流对象转成字符流对象,使用转换流。  
        //InputStreamReader isr=new InputStreamReader(in);  
        //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader  
        //BufferedReader br=new BufferedReader(isr);  
  
        //键盘录入最常见写法  
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));  
  
        //字符流通向字节流  
        BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out));  
  
        String s=null;  
        while((s=in.readLine())!=null)  
        {  
            if("over".equals(s))  
                break;  
            bw.write(s.toUpperCase());//写入数据  
            bw.newLine();//换行  
            bw.flush();//刷新  
              
        }  
        bw.close();//关闭流资源  
        in.close();  
    }  
}  

二、流操作规律

1

        源:键盘录入。

        目的:控制台。

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

        源:键盘

        目的:文件。

        使用字节流通向字符流的转换流(桥梁):InputStreamReader

3、需求:想要将一个文件的数据打印在控制台上。

        源:文件

        目的:控制台

        使用字符流通向字节流的转换流(桥梁):OutputStreamWriter

4、流操作的基本规律:

        最痛苦的就是流对象有很多,不知道该用哪一个。

通过三个明确来完成:

4.1   明确源和目的。

       源:输入流。InputStream  Reader

       目的:输出流。OutputStream  Writer

4.2   操作的数据是否是纯文本。

       是:字符流

       否:字节流

4.3   当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:

       源设备:内存,硬盘,键盘

       目的设备:内存,硬盘,控制台

5、规律体现

5.1 将一个文本文件中数据存储到另一个文件中。复制文件。

        1)源:因为是源,所以使用读取流:InputStreamReader

             明确体系:是否操作文本:是,Reader

              明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader

              是否需要提高效率:是,加入Reader体系中缓冲区 BufferedReader.

              FileReader fr = new FileReader("a.txt");

              BufferedReader bufr = new BufferedReader(fr);

        2)目的:输出流:OutputStreamWriter

             明确体系:是否操作文本:是,Writer

             明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter

             是否需要提高效率:是,加入Writer体系中缓冲区 BufferedWriter

              FileWriter fw = new FileWriter("b.txt");

              BufferedWriter bufw = new BufferedWriter(fw);

练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。

        1)源:输入流,InputStreamReader

            是否是文本?否,InputStream

            源设备:硬盘上的一个文件。InputSteam体系中可以操作文件的对象是FileInputSteam

            是否需要提供效率:是,BufferedInputStream

              BufferedInputSteambis=newBufferedInputStream(newFileInputStream("c:/users/asus/desktop/1.jpg"));

        2)目的:输出流,OutputStreamWriter

             是否是文本?否,OutputStream

             源设备:硬盘上的文件,FileOutputStream

             是否需要提高效率:是,加入BufferedOutputStream

               BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("c:/users/asus/desktop/2.jpg"));

5.2 需求:将键盘录入的数据保存到一个文件中。

        1)源:InputStreamReader

             是不是纯文本?是,Reader

              设备:键盘。对应的对象是System.in。——为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。所以既然明确了Reader,那么就将System.in转换成Reader。用Reader体系中转换流,InputStreamReader

              InputStreamReaderisr = new InputStreamReader(System.in);

            需要提高效率吗?需要,BufferedReader

             BufferedReaderbufr = new BufferedReader(isr);

       2)目的:OutputStream  Writer

            是否是存文本?是!Writer

            设备:硬盘。一个文件。使用 FileWriter

             FileWriter fw = new FileWriter("c.txt");

           需要提高效率吗?需要。

            BufferedWriter bufw = new BufferedWriter(fw);

5.3   扩展:想要把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。

        目的:OutputStream  Writer

       是否是存文本?是!Writer

        设备:硬盘上的一个文件。使用 FileWriter。——但是FileWriter是使用的默认编码表:GBK。而存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter

        该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream

        OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");

        需要高效吗?需要,BufferedWriter

        BufferedWriter bufw = new BufferedWriter(osw);

记住:

       转换流什么使用?

       字符和字节之间的桥梁。通常,涉及到字符编码转换时,需要用到转换流。

练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。

        1)源:InputStreamReader

            是文本?是:Reader

            设备:硬盘。上的文件:FileReader

            是否需要提高效率?是:BufferedReader

             BufferedReader br=new BufferedReader(newFileReader("1.txt"));

       2)目的:OutputStream Writer

            是文本?是:Writer

            设备:控制台。对应对象System.out。由于System.out对应的是字节流,所以利用OutputSteamWriter转换流

            是否提高效率?是:BufferedWriter

              BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out));

示例:

  /* 
    2、需求:想把键盘录入的数据存储到一个文件中。 
    源:键盘 
    目的:文件 
    把录入的数据按照指定的编码表(UTF-8),将数据存到文件中。 
     
     
    3、需求:想要将一个文件的数据打印在控制台上。 
    源:文件 
    目的:控制台 
     
     
    */  
    import java.io.*;  
    class  TransStreamDemo2  
    {  
        public static void main(String[] args)throws IOException  
        {  
              
            //键盘录入  
            BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  
      
            //存入文件中,按照指定的编码表(UTF-8)   
            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("readin1.txt"),"UTF-8"));  
      
            String line=null;  
            while((line=br.readLine())!=null)  
            {  
                if("over".equals(line))  
                    break;  
                bw.write(line);  
                bw.newLine();  
                bw.flush();  
            }  
              
            /* 
            //录入文件数据 
            BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("TransStreamDemo2.java"))); 
     
            //显示在控制台 
            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out)); 
     
            String line=null; 
            while((line=br.readLine())!=null) 
            { 
                if("over".equals(line)) 
                    break; 
                bw.write(line); 
                bw.newLine(); 
                bw.flush(); 
            } 
            */  
        }  
    }  


小知识:

1、异常的日志信息:

        当程序在执行的时候,出现的问题是不希望直接打印给用户看的,是需要作为文件存储起来,方便程序员查看,并及时调整的。

示例:

    import java.io.*;  
    import java.text.*;  
    import java.util.*;  
    class  ExceptionInfo  
    {  
        public static void main(String[] args)   
        {  
            try  
            {  
                int[] arr =new int[2];  
                System.out.println(arr[3]);  
      
            }  
            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("info.log");//打印流对象  
                    System.setOut(ps);//修改输出流设备  
                    ps.println(s);//输出时间  
                      
                }  
                catch (IOException ex)  
                {  
                    throw new RuntimeException("文件创建失败");  
                }  
                e.printStackTrace(System.out);//将异常信息输出指定输出流  
            }  
        }  
    }  


2、系统属性信息存入文本

        获取系统信息:

                 Properties getProperties()

        将信息输出到指定输出流中

                 void list(PrintStream out)

        将输出流中数据存入指定文件中

                  new PrintStream("systeminfo.txt")

示例:

    //将系统属性信息保存到指定文本中  
    import java.util.*;    
    import java.io.*;    
      
    class SystemInfo     
    {    
       public static void main(String[] args)     
       {     
           PrintStream ps = null;    
           try    
           {    
              //获取系统信息:    
              Properties pop = System.getProperties();    
              //创建输出流对象,将输出流中数据存入指定文件中    
              ps = new PrintStream("systeminfo.txt");    
              //将属性列表输出到指定的输出流    
              pop.list(ps);    
           }    
           catch (Exception e)    
           {    
                throw new RuntimeException("获取系统信息失败。");    
           }    
        }    
    } 


3、通过System类的setInsetOut方法可以对默认设备进行改变

        System.setIn(new InputStream(“1.txt”));//将源改成文件1.txt

        System.setOut(new OutputStream(“2.txt”));//将目的改成文件2.txt

流的基本应用小结:

  • 流是用来处理数据的。
  • 处理数据时,一定要先明确数据源,与数据目的地(数据汇)。
  • 数据源可以是文件,可以是键盘。
  • 数据目的地可以是文件、显示器或者其他设备。
  • 而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理.转换处理等。

字符流继承体系简图

字节流继承体系简图




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值