Java系列(45)——IO 流中的字节流

字节流简介

Java系列(45)——IO 流中的字节流
Java系列(45)——IO 流中的字节流

  • 不可被实例化,使用其子类。
  • 字节输入流:InputStream:常用子类FileInputStream。
  • 字节输出流:OutputStream:常用子类FileOutputStream。

字节输出流 OutputStream 的子类 FileOutputStream

构造器
Java系列(45)——IO 流中的字节流
写入方法
Java系列(45)——IO 流中的字节流

  • 范例:使用字节流向文件中写入”weiyuxuan”。

示例代码:

public static void writeFile()
{
	//创建字节输出流对象
	OutputStream out = null;
	
	try
	{
		//第一种构造器
		out = new FileOutputStream("text.txt");
		//第二种构造器(与第一种效果相同)
		//out = new FileOutputStream(new File("text.txt"));
		//第三种构造器(追加)
		//out = new FileOutputStream("text.txt",true);
		//第四种构造器(追加,与第三种效果相同)
		//out = new FileOutputStream(new File("text.txt"),true);
		
		String str = "weiyuxuan";
		
		//把字符串变成字节数组
		byte[] bs = str.getBytes();
		
		/**
		 * 第一种写入方式
		 */			
		//for (byte b : bs)
		//{
			//单个字节写入
		//	out.write(b);
		//}													

		/**
		 * 第二种写入方式
		 */					
		//写入字节数组
		//out.write(bs);
			
		/**
		 * 第三种写入方式
		 */				
		//写入字节数组一部分写入文件
		out.write(bs,0,5);						
		
	} catch (FileNotFoundException e)
	{
		// TODO 自动生成的 catch 块
		e.printStackTrace();
	} catch (IOException e)
	{
		// TODO 自动生成的 catch 块
		e.printStackTrace();
	}finally
	{
		if(out != null)
		{
			try
			{
				out.close();
			} catch (IOException e)
			{
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
	}
}

结果图:
Java系列(45)——IO 流中的字节流

字节输入流 InputStream 的子类 FileInputStream

构造器
Java系列(45)——IO 流中的字节流
读取方法
Java系列(45)——IO 流中的字节流

  • 范例:使用字节输入流来读取文件并且输出到控制台。

示例代码:

in = new FileInputStream("text.txt");

/**
 * 第一种读取方式
 */
//从文件中读取一个字节,不建议这样输出,因为中文(两个字节)等输出不了,并且单个字节读取效率低
//int num = in.read();
//System.out.println((char)num);

/**
 * 第二种读取方式
 */
//定义一个字节数组
//byte[] bs = new byte[1024];
//向字节数组中存储数据,返回读取的长度
//int len = in.read(bs);
//查看读取到的字节长度
//System.out.println(len);
//System.out.println(new String(bs,0,len));

/**
 * 第三种读取方式
 */
//定义一个字节数组
byte[] bs = new byte[1024];
//从输入字节流的对象读取指定的字节数,从字节输入流来取4个字节,从字节数组的索引2开始放
//所以当我们在读取的时候,也应该从2开始读
int len = in.read(bs,2,6);			
//查看读取到的字节长度
System.out.println(len);
System.out.println(new String(bs,2,len));

结果图:
Java系列(45)——IO 流中的字节流

字节流做文件的拷贝

  • 范例:使用字节流来拷贝文件。
    步骤:
    1、创建输入流和输出流的对象
    2、读取数据并写入数据
    3、关闭资源

示例代码:

public static void copyFile()
{
	InputStream in = null;
	OutputStream out = null;
			
	try
	{
		//创建文件输入流的对象
		in = new FileInputStream("src/cn/wyx/io/InputStreamDemo.java");
		
		//创建文件输出流的对象
		out = new FileOutputStream("InputStreamDemo.java");
		
		byte[] bs = new byte[1024];
		
		int len = -1;
		
		while((len = in.read(bs)) != -1)
		{
			//把字节数组中的数据写入到文件中
			out.write(bs, 0, len);
		}
		
	} catch (FileNotFoundException e)
	{
		// TODO 自动生成的 catch 块
		e.printStackTrace();
	} catch (IOException e)
	{
		// TODO 自动生成的 catch 块
		e.printStackTrace();
	}finally
	{
		try
		{
			if(in != null)
			{
				in.close();
			}

			if(out != null)
			{
				out.close();
			}				
			
		} catch (IOException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}			
	}		
}

结果图:
Java系列(45)——IO 流中的字节流
Java系列(45)——IO 流中的字节流
注意:字节流可以拷贝所有文件,字符流只能拷贝文本文件!

字节流的高效缓冲区

Java系列(45)——IO 流中的字节流
Java系列(45)——IO 流中的字节流
BufferedOutputStream

构造器:
Java系列(45)——IO 流中的字节流
BufferedInputStream

构造器:
Java系列(45)——IO 流中的字节流
使用高效缓冲区与普通字节流方式拷贝文件的效率比较

示例代码:

package cn.wyx.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class BufferedCopyFileDemo2
{
	public static void main(String[] args)
	{
		long startTime1 = System.currentTimeMillis();
		copyFile1();
		long endTime1 = System.currentTimeMillis();
		
		long startTime2 = System.currentTimeMillis();
		copyFile2();
		long endTime2 = System.currentTimeMillis();
		
		long startTime3 = System.currentTimeMillis();
		copyFile3();
		long endTime3 = System.currentTimeMillis();
		
		long startTime4 = System.currentTimeMillis();
		copyFile4();
		long endTime4 = System.currentTimeMillis();
		
		System.out.println("单个字节流耗时:" + (endTime1 - startTime1));
		System.out.println("字节流数组耗时:" + (endTime2 - startTime2));
		System.out.println("单个字节高效缓冲区耗时:" + (endTime3 - startTime3));
		System.out.println("字节流数组高效缓冲区耗时:" + (endTime4 - startTime4));
	}
	
	/**
	 * 
	 * 描述:第一种方式(单个字节)
	 */
	public static void copyFile1()
	{
		InputStream in = null;
		OutputStream out = null;
				
		try
		{
			//创建文件输入流的对象
			in = new FileInputStream("C:\\Users\\NULL\\Desktop\\不再联系.mp3");
			
			//创建文件输出流的对象
			out = new FileOutputStream("C:\\Users\\NULL\\Desktop\\不再联系(副本1).mp3");
			
			//定义单个字符
			int num = -1;
			
			while((num = in.read()) != -1)
			{
				//把单个字节数据依次写入到文件中
				out.write(num);
			}
			
		} catch (FileNotFoundException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}finally
		{
			try
			{
				if(in != null)
				{
					in.close();
				}

				if(out != null)
				{
					out.close();
				}				
				
			} catch (IOException e)
			{
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}			
		}
	}
	
	/**
	 * 
	 * 描述:第二种方式(字节数组)
	 */
	public static void copyFile2()
	{
		InputStream in = null;
		OutputStream out = null;
				
		try
		{
			//创建文件输入流的对象
			in = new FileInputStream("C:\\Users\\NULL\\Desktop\\不再联系.mp3");
			
			//创建文件输出流的对象
			out = new FileOutputStream("C:\\Users\\NULL\\Desktop\\不再联系(副本2).mp3");
			
			//定义字符数组
			byte[] bs = new byte[1024];
			
			int len = -1;
			
			while((len = in.read(bs)) != -1)
			{
				//把字节数组中的数据写入到文件中
				out.write(bs, 0, len);
			}
			
		} catch (FileNotFoundException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}finally
		{
			try
			{
				if(in != null)
				{
					in.close();
				}

				if(out != null)
				{
					out.close();
				}				
				
			} catch (IOException e)
			{
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}			
		}
	}
	
	/**
	 * 
	 * 描述:第三种方式(高效缓冲区单个字节)
	 */
	public static void copyFile3()
	{
		InputStream in = null;
		OutputStream out = null;
				
		try
		{
			//创建高效缓冲区输入流的对象
			in = new BufferedInputStream(new FileInputStream("C:\\Users\\NULL\\Desktop\\不再联系.mp3"));
			
			//创建高效缓冲区输出流的对象
			out = new BufferedOutputStream(new FileOutputStream("C:\\Users\\NULL\\Desktop\\不再联系(副本3).mp3"));
				
			//定义返回的单个字符
			int num = -1;
			
			while((num = in.read()) != -1)
			{
				//把单个字节的数据依次写入到文件中
				out.write(num);
			}
			
		} catch (FileNotFoundException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}finally
		{
			try
			{
				if(in != null)
				{
					in.close();
				}

				if(out != null)
				{
					out.close();
				}				
				
			} catch (IOException e)
			{
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}			
		}
	}
	
	/**
	 * 
	 * 描述:第四种方式(高效缓冲区字节数组)
	 */
	public static void copyFile4()
	{
		InputStream in = null;
		OutputStream out = null;
				
		try
		{
			//创建高效缓冲区输入流的对象
			in = new BufferedInputStream(new FileInputStream("C:\\Users\\NULL\\Desktop\\不再联系.mp3"));
			
			//创建高效缓冲区输出流的对象
			out = new BufferedOutputStream(new FileOutputStream("C:\\Users\\NULL\\Desktop\\不再联系(副本4).mp3"));
			
			//定义字符数组
			byte[] bs = new byte[1024];
			
			int len = -1;
			
			while((len = in.read(bs)) != -1)
			{
				//把字节数组中的数据写入到文件中
				out.write(bs,0,len);
			}
			
		} catch (FileNotFoundException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}finally
		{
			try
			{
				if(in != null)
				{
					in.close();
				}

				if(out != null)
				{
					out.close();
				}				
				
			} catch (IOException e)
			{
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}			
		}
	}
}

结果图:
Java系列(45)——IO 流中的字节流

  • 复制后的文件亲测可以播放!(^ ^)

注意:字节数组具有很大优势,所以才会造成使用高效缓冲区单个字节流的复制方式耗时长于普通字节流数组复制方式耗时!

从键盘输入学生信息存储到文件中,学生按着年龄排序

示例代码:

Student 类:

package cn.wyx.io;

public class Student implements Comparable<Student>
{
	private String name;
	
	private Integer age;
		
	public String getName()
	{
		return name;
	}
	public void setName(String name)
	{
		this.name = name;
	}
	public Integer getAge()
	{
		return age;
	}
	public void setAge(Integer age)
	{
		this.age = age;
	}
	@Override
	public int compareTo(Student o)
	{
		int num = this.age - o.age;
		
		if(num == 0)
		{
			num = this.name.compareTo(o.getName());
		}		
		return num;
	}
}

ScannerTest 类:

package cn.wyx.io;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
import java.util.TreeSet;

public class ScannerTest
{

	public static void main(String[] args)
	{
		//定义学生的集合
		TreeSet<Student> ts = new TreeSet<Student>();
		
		//创建键盘输入对象
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入你需要录入的学生数量:");
		//获得输入的学生数量
		int count = sc.nextInt();
		
		for (int i = 0; i < count; i++)
		{
			sc = new Scanner(System.in);
			System.out.println("请输入学生姓名:");
			//获得输入的学生姓名
			String name = sc.nextLine();
			
			System.out.println("请输入学生年龄:");
			//获得输入的学生年龄
			int age = sc.nextInt();
			
			//创建学生对象
			Student s = new Student();
			s.setName(name);
			s.setAge(age);;
			
			//把学生加入到集合当中
			ts.add(s);
		}
				
		BufferedWriter bw = null;
		try
		{
			//定义高效缓中区的字符输出流
			bw = new BufferedWriter(new FileWriter("Student.txt"));
			
			for (Student s : ts)
			{
				bw.write(s.getName()+"---"+s.getAge());
				bw.newLine();
			}
			
			//字符流需要flush
			bw.flush();		
			
		} catch (IOException e)
		{
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}finally
		{
			try
			{
				if(bw != null)
				{
					bw.close();
				}
			} catch (IOException e)
			{
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
		System.out.println("写入文件完毕!");
	}
}

结果图:
Java系列(45)——IO 流中的字节流

装饰者设计模式

  • 装饰者模式的目的是给类的功能增强。
  • 继承也能给类的功能增强。
  • Writer 类是写文件的类,假如有三个子类 TxtWriter,MP3Writer,AVIWriter。
  • 在writer中定义了写文件的标准,三个子类分别去实现Writer中的写方法。
  • 使用继承方式来对类增强。

通过继承方式加入高效缓冲区

Writer:

  |——TxtWriter
		 |——BufferedTxtWriter
  |——MP3Writer
		 |——BufferedMP3Writer
  |——AVIWriter
		 |——BufferedAVIWriter
  |——RMWriter
		 |——BufferedRMWriter

示例代码:

package cn.wyx.extend;

public abstract class Writer
{
	/**
	 * 
	 * 描述:写文件的方法
	 */
	public abstract void writerFile();
	
}
package cn.wyx.extend;

public class TxtWriter extends Writer
{
	@Override
	public void writerFile()
	{
		System.out.println("写入文本文件");
	}
}
package cn.wyx.extend;

public class MP3Writer extends Writer
{
	@Override
	public void writerFile()
	{
		System.out.println("写入MP3文件");
	}
}
package cn.wyx.extend;

public class AVIWriter extends Writer
{
	@Override
	public void writerFile()
	{
		System.out.println("写入AVI文件");
	}
}
package cn.wyx.extend;

public class BufferedTxtWriter extends TxtWriter
{
	private TxtWriter tw;
	
	public BufferedTxtWriter(TxtWriter txtWriter)
	{
		this.tw = txtWriter;		
	}

	public void writerFile()
	{
		bufferedWriter();
		tw.writerFile();
	}
	
	public void bufferedWriter()
	{
		System.out.println("加入高效缓冲区");
	}
}
package cn.wyx.extend;

public class BufferedMP3Writer2 extends MP3Writer
{
	private MP3Writer mw;
	
	public BufferedMP3Writer2(MP3Writer mw)
	{
		this.mw = mw;		
	}
	
	public void writerFile()
	{
		bufferedWriter();
		mw.writerFile();
	}
	
	public void bufferedWriter()
	{
		System.out.println("加入高效缓冲区");
	}
}
package cn.wyx.extend;

public class Client
{

	public static void main(String[] args)
	{
		Writer writer = new BufferedTxtWriter(new TxtWriter());
		Writer writer1 = new BufferedMP3Writer2(new MP3Writer());
		
		writer.writerFile();
		writer1.writerFile();
	}
}

结果图:
Java系列(45)——IO 流中的字节流
装饰者设计模式

  • 使用继承方式的缺点:如果想继续添加写文件的子类,如果想要对它做增强,还需要创建相应的缓冲区。
  • 装饰器的设计模式可以解决我们使用高效缓冲区,但是不需要每一个写文件的类都添加高效缓冲区。
  • 如果写文件的缓冲区是同样的原理,那么我们就可以把缓冲区抽取出来作为装饰器。

角色:
1、抽象构件角色(定义写文件标准):Writer。
2、具体的构件角色(实现写文件标准):三个子类TxtWriter,MP3Writer,AVIWriter。
3、装饰角色[抽象](高效缓冲区):注意:也可定义成抽象的,如果是抽象的就必须要有具体抽象角色。
4、具体的装饰角色:来实现抽象装饰器中的增强方法。

示例代码:

package cn.tx.dect1;

/**
 * @描述:抽象构件角色
 */
public abstract class Writer
{
	public abstract void writeFile();
}
package cn.tx.dect1;

/**
 * @描述:具体构件角色
 */
public class TxtWriter extends Writer
{

	@Override
	public void writeFile()
	{
		System.out.println("写入Txt");

	}
}
package cn.tx.dect1;

public class Mp3Writer extends Writer
{

	@Override
	public void writeFile()
	{
		System.out.println("写入mp3");
	}
}
package cn.tx.dect1;

public class AVIWriter extends Writer
{
	@Override
	public void writeFile()
	{
		System.out.println("写入视频");
	}
}
package cn.tx.dect1;

/**
 * 
 * 装饰器特点: 1.继承抽象构件角色 2.有一个构造器,要以Writer类型作为属性 3.提供增强的方法
 */
public class BufferedWriter extends Writer
{

	/**
	 * 提供抽象构件角色的属性
	 */
	private Writer writer;

	/**
	 * 提供有参数的构造器
	 * 
	 * @param writer
	 */
	public BufferedWriter(Writer writer)
	{
		super();
		this.writer = writer;
	}

	@Override
	public void writeFile()
	{
		this.bufferedWrite();
		writer.writeFile();
	}

	/**
	 * 缓冲区增强的功能
	 */
	private void bufferedWrite()
	{
		System.out.println("加入缓冲区");
	}
}
package cn.tx.dect1;

public class Client
{

	public static void main(String[] args)
	{
		Writer writer = new BufferedWriter(new TxtWriter());
		Writer writer1 = new BufferedWriter(new Mp3Writer());
		Writer writer2 = new BufferedWriter(new AVIWriter());
	
		writer.writeFile(); 
		writer1.writeFile();	
		writer2.writeFile();
	}
}

结果图:
Java系列(45)——IO 流中的字节流

抽象装饰者设计及其扩展

示例代码:

package cn.tx.dect2;

/**
 * @描述:抽象构件角色
 */
public abstract class Writer
{
	public abstract void writeFile();
}
package cn.tx.dect2;

/**
 * 
 * @描述:具体构件角色
 */
public class TxtWriter extends Writer
{

	@Override
	public void writeFile()
	{
		System.out.println("写入Txt");
	}
}
package cn.tx.dect2;

public class Mp3Writer extends Writer
{
	@Override
	public void writeFile()
	{
		System.out.println("写入mp3");
	}
}
package cn.tx.dect2;

public class AVIWriter extends Writer
{

	@Override
	public void writeFile()
	{
		System.out.println("写入视频");
	}

}
package cn.tx.dect2;

/**
 * 装饰器特点: 
 * 1.继承抽象构件角色
 *  2.有一个构造器,要以Writer类型作为属性 
 *  3.提供增强的方法
 */
public abstract class BufferedWriter extends Writer
{

	/**
	 * 提供抽象构件角色的属性
	 */
	private Writer writer;

	/**
	 * 提供有参数的构造器
	 * 
	 * @param writer
	 */
	public BufferedWriter(Writer writer)
	{
		super();
		this.writer = writer;
	}

	@Override
	public void writeFile()
	{
		this.bufferedWrite();
		writer.writeFile();
		this.clean();
	}

	/**
	 * 缓冲区增强的功能
	 */
	private void bufferedWrite()
	{
		System.out.println("加入缓冲区");
	}

	/**
	 * 定义清理的抽象方法
	 *
	 */
	public abstract void clean();
}
package cn.tx.dect2;

public class BufferedWriterExtTxt extends BufferedWriter
{

	public BufferedWriterExtTxt(Writer writer)
	{
		super(writer);
	}

	@Override
	public void clean()
	{
		System.out.println("对Txt做清理工作");
	}
}
package cn.tx.dect2;

public class BufferedWriterExt extends BufferedWriter
{

	public BufferedWriterExt(Writer writer)
	{
		super(writer);
	}

	@Override
	public void clean()
	{
		System.out.println("对多媒体做清理工作");
	}
}
package cn.tx.dect2;

public class Client
{

	public static void main(String[] args)
	{
		Writer writer = new BufferedWriterExtTxt(new TxtWriter());
		Writer writer1 = new BufferedWriterExt(new Mp3Writer());
		Writer writer2 = new BufferedWriterExt(new AVIWriter());
		writer.writeFile();
		writer1.writeFile();
		writer2.writeFile();
	}
}

结果图:
Java系列(45)——IO 流中的字节流

使用 Scanner 从键盘录入数据写入文件

  • 范例:把从键盘输入的文本写入到文件中。

示例代码:

public static void main(String[] args)
{

	//创建 Scanner 对象
	Scanner sc = new Scanner(System.in);
	
	BufferedWriter bw = null;	
	try
	{
		bw = new BufferedWriter(new FileWriter("test.txt"));
		
		String line = null;
		while((line = sc.nextLine()) != null)
		{
			if("exit".equals(line))
			{
				break;
			}
			bw.write(line);
			bw.newLine();
			bw.flush(); 
		}
	} catch (IOException e)
	{
		// TODO 自动生成的 catch 块
		e.printStackTrace();
	}finally
	{
		if(bw != null)
		{
			try
			{
				bw.close();
			} catch (IOException e)
			{
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
	}
}

结果图:
Java系列(45)——IO 流中的字节流

InputStreamReader 类输入流转换应用

  • InputStreamReader是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符 。
    Java系列(45)——IO 流中的字节流
    Java系列(45)——IO 流中的字节流
  • 把从键盘输入的文本写入到文件中。

示例代码:

public static void main(String[] args)
{
	BufferedReader br = null;
	InputStreamReader ir = null;
	BufferedWriter wr = null;
	try
	{
		// 实例化字节流转换字符流的对象
		/*
		 * ir = new InputStreamReader(System.in); br = new BufferedReader(ir);
		 */
		br = new BufferedReader(new InputStreamReader(System.in));
		wr = new BufferedWriter(new FileWriter("a.txt"));
		String line = null;
		while (true)
		{
			line = br.readLine();
			if ("exit".equals(line))
			{
				break;
			}
			wr.write(line);
			wr.newLine();
			wr.flush();
		}

	} catch (IOException e)
	{
		e.printStackTrace();
	} finally
	{

		try
		{
			if (wr != null)
			{
				wr.close();
			}
			if (br != null)
			{
				br.close();
			}
		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

结果图:
Java系列(45)——IO 流中的字节流

OutputStreamReader 类输入流转换应用

  • OutputStreamWriter是字符的桥梁流以字节流:向其写入的字符编码成使用指定的字节charset 。
    Java系列(45)——IO 流中的字节流
    Java系列(45)——IO 流中的字节流
  • 从一个文件中读取内容使用标准字节流来输出到控制台(使用字符串操作)。

示例代码:

public static void main(String[] args)
{
	BufferedReader br = null;
	BufferedWriter wr = null;

	try
	{
		//创建字符流缓冲区对象
		br = new BufferedReader(new FileReader("a.txt"));
		//创建字符流缓冲区对象,内部使用字符流转换成字节流的对象OutputStreamWriter
		wr = new BufferedWriter(new OutputStreamWriter(System.out));

		String line = null;
		while ((line = br.readLine()) != null)
		{
			wr.write(line);
			wr.newLine();
			wr.flush();
		}

	} catch (Exception e)
	{
		e.printStackTrace();
	} finally
	{
		try
		{
			if (wr != null)
			{
				wr.close();
			}
			if (br != null)
			{
				br.close();
			}

		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

结果图:
Java系列(45)——IO 流中的字节流
如有错误,欢迎指正!