首页 > 代码库 > Java SE学习笔记-IO

Java SE学习笔记-IO

1、主要内容

技术分享

2、详细内容

2.1IO流概念

(1)用来处理设备(硬盘,控制台,内存)之间的数据;

(2)Java中对数据的操作都是通过流的方式;

(3)Java中用于操作流的类都是放在IO包内;

(4)按照流操作的数据类型不同,可以分为字节流和字符流;而字符流是为了方便中文的操作而来的;

(5)按照流的流向不同分为:输入流和输出流。

2.2IO流常用基类

2.2.1、字节流的抽象基类

完整图如下图所示:

技术分享

其中重点的类如下图所示:

技术分享

(1)InputStream:输入字节流,字节读取流抽象类;

public abstract class InputStream
extends Object
implements Closeable

其中包括:FileInputStream:字节读取流;BufferedInputStream:字节读取流缓冲区。

常用方法摘要:

  void close():关闭此输入流并释放与该流关联的所有系统资源。
  int available() :返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。 (特有方法!!)
  abstract  int read(): 从输入流中读取数据的下一个字节。 
  int read(byte[] b) :从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
  int read(byte[] b, int off, int len) :将输入流中最多 len 个数据字节读入 byte 数组。
  long skip(long n) :跳过和丢弃此输入流中数据的 n 个字节。

(2)OutputStream:输出字节流,字节写入流抽象类;

public abstract class OutputStream
extends Object
implements Closeable, Flushable

其中包括:FileOutputStream:字节写入流;BufferedOutputStream:字节写入流缓冲区; PrintStream:打印流;

常用方法摘要:

  void close() :关闭此输出流并释放与此流有关的所有系统资源。
  void flush():刷新此输出流并强制写出所有缓冲的输出字节。
  abstract  void write(int b) :将指定的字节写入此输出流。
  void write(byte[] b): 将 b.length 个字节从指定的 byte 数组写入此输出流。    
  void write(byte[] b, int off, int len) :将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 

2.2.2、字符流的抽象基类

完整图如下图所示:

技术分享

其中重点的类如下图所示:

技术分享

(1)Reader:输入字符流,字符读取流的抽象类;

public abstract class Reader
extends Object
implements Readable, Closeable

其中包括:FileReader:字符读取流;LineNumberReader:跟踪行号的缓冲字符读取流; BufferedReader:字符读取流缓冲区;  InputStreamReader:字节桐乡字符的转换流(涉及键盘录入时使用)。

常见方法摘要:

abstract  void close() 关闭该流并释放与之关联的所有资源。
int read() 读取单个字符。
int read(char[] cbuf)  将字符读入数组
abstract  int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。
long skip(long n)  跳过字符。 

(2)Writer:输出字符流。字节写入流的抽象类;

public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable

其中包括:FileWriiter:字符写入流;BufferedWriter:字符写入流缓冲区;OutputStreamWriter:字节通向字节的转换流(涉及键盘录入时使用);OutputStreamWriter:打印流,可以初六各种类型的数据。

常见方法摘要:

abstract  void close() 关闭此流,但要先刷新它。
abstract  void flush() 刷新该流的缓冲。
void write(int c) 写入单个字符。
void write(char[] cbuf) 写入字符数组。          
abstract  void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 
void write(String str) 写入字符串。 
void write(String str, int off, int len) 写入字符串的某一部分

2.3、字符流读取文件

2.3.1、读取文件(FileReader

public class FileReader
extends InputStreamReader

(1)构造方法:

FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。
FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 
FileReader(FileDescriptor fd) 在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。 
(2)方法摘要:和Reader基类方法一致:

abstract  void close() 关闭该流并释放与之关联的所有资源。
int read() 读取单个字符。
int read(char[] cbuf)  将字符读入数组
abstract  int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。
long skip(long n)  跳过字符。 
(3)示例代码(1)如下所示:

/**
 * 读取硬盘上的文本文件,并将数据打印在控制台
 * */
public class FileReaderDemo
{
	public static void main(String[] args)
	{
		method_1();
		// 通过字符数组进行读取
		method_2();
	}
	/**
	 * 第二种方式:通过字符数组进行读取
	 * */
	private static void method_2()
	{
		try
		{
			// 创建文件读取流对象,和指定名称的文件相关联
			FileReader fr = new FileReader("source//test.txt");
			// 定义一个字符串数组,用于存储读取到的字符
			char[] buf = new char[1024];
			int num = 0;
			while ((num = fr.read(buf)) != -1)
			{
				System.out.println(new String(buf, 0, num));
			}
			if (fr != null)
			{
				fr.close();
			}
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}
	/**
	 * 第一种方式
	 */
	private static void method_1()
	{
		try
		{
			// 创建文件读取流对象,和指定名称的文件相关联
			FileReader fr = new FileReader("source//test.txt");
			// 调用读取对象的read方法,read方法是一次读取一个字符,然后自动往下读
			int ch = 0;
			while ((ch = fr.read()) != -1)
			{
				System.out.println((char) ch);
			}
			if (fr != null)
			{
				fr.close();
			}

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

2.3.2、写出文件(FileWriiter

1构造方法:

FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 
FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。 
FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 
FileWriter(FileDescriptor fd) 构造与某个文件描述符相关联的 FileWriter 对象。
(2)方法摘要:跟Writer一样

abstract  void close() 关闭此流,但要先刷新它。
abstract  void flush() 刷新该流的缓冲。
void write(int c) 写入单个字符。
void write(char[] cbuf) 写入字符数组。          
abstract  void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 
void write(String str) 写入字符串。 
void write(String str, int off, int len) 写入字符串的某一部分。 

(3)示例代码(1)如下所示:

/**
 * 演示对已有文件的数据续写
 */
public class FileWriterDemo
{
	public static void main(String[] args)
	{
		FileWriter fw = null;
		try
		{
			// 创建FileWriiter对象,该对象一被初始化就必须明确要被操作的文件
			// 传递一个true参数,代表不覆盖已有的文件并在其后面对数据续写
			fw = new FileWriter("source//test.txt", true);
			// 调用write方法,将字符串写入六种
			fw.write("\r\nitheima");
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		finally
		{
			try
			{
				if (fw != null)
				{
					//关闭流资源,但是关闭之前会刷新内部缓冲中的数据
					fw.close();
				}
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
	}
}

示例代码(2)如下所示:

/**
 * 将C盘的一个文本文件复制到D盘 复制的原理: 将C盘下的文件数据存储到D盘的一个文件中 步骤: 1,在D盘中创建一个文件,用于存储C盘文件中的数据;
 * 2,定义读取流和C盘文件相关联; 3,通过不断的读写完成数据的存储; 4,关闭资源。
 */
public class CopyTest
{
	public static void main(String[] args)
	{
		FileWriter fw = null;
		FileReader fr = null;
		try
		{
			// 创建目的地
			fw = new FileWriter("source//test_copy.txt");
			// 与已有的文件相关联
			fr = new FileReader("source//test.txt");
			method_1(fw, fr);
			method_2(fw, fr);

		}
		catch (IOException e)
		{
			throw new RuntimeException("读写失败!");
		}
		finally
		{
			//关闭流
			try
			{
				if (fw != null)
				{
					fw.close();
				}
				if (fr != null)
				{
					fr.close();
				}
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
	}
	/**
	 * 通过数组方式
	 */
	private static void method_2(FileWriter fw, FileReader fr) throws IOException
	{
		char[] buf = new char[1024];
		int len = 0;
		while ((len = fr.read(buf)) != -1)
		{
			fw.write(buf, 0, len);
		}
	}
	/**
	 * 普通方式
	 */
	private static void method_1(FileWriter fw, FileReader fr) throws IOException
	{
		int ch = 0;
		while ((ch = fr.read()) != -1)
		{
			fw.write(ch);
		}
	}
}

2.4、字符流缓冲区读写

2.4.1、自定义缓冲区读写

为什么定义缓冲区?

由于单个字符读写需要频繁操作文件,所以效率非常低。我们可以定义缓冲区将要读取或写出的数据缓存,减少操作文件次数。

2.4.2BufferedReaderBufferedReader

(1)BufferedReader

public class BufferedReader
extends Reader

1构造方法:

BufferedReader(Reader in) :创建一个使用默认大小输入缓冲区的缓冲字符输入流。
2方法摘要:

int read() :读取单个字符。 
int read(char[] cbuf, int off, int len) :将字符读入数组的某一部分。 
String readLine() :读取一个文本行。 
3)示例代码(1)如下所示:
public class BufferedReaderDemo
{
	public static void main(String[] args) throws Exception
	{
		//创建一个读取流对象和文件相关联
		FileReader fr = new FileReader("source//test.txt");
		//为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递
		BufferedReader bufr = new BufferedReader(fr);
		String line = null;
		//readLine方法只返回回车符之前的数据内容,并不返回回车符
		while((line = bufr.readLine())!=null)
		{
			System.out.println(line);
		}
		bufr.close();
	}
}

(1)BufferedWriter

1构造方法:

BufferedWriter(Writer out) :创建一个使用默认大小输出缓冲区的缓冲字符输出流。 
BufferedWriter(Writer out, int sz) :创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 
2方法摘要:

void close() :关闭此流,但要先刷新它。 
void flush() :刷新该流的缓冲。 
void newLine() :写入一个行分隔符。 
void write(char[] cbuf, int off, int len) :写入字符数组的某一部分。 
void write(int c)  写入单个字符。 
void write(String s, int off, int len) 写入字符串的某一部分。 
3)示例代码(1)如下所示:
public class BufferedWriterDemo
{
	public static void main(String[] args) throws Exception
	{
		//创建一个字符写入流对象
		FileWriter fw = new FileWriter("source//buf.txt");
		//为了提高字符写入流的效率,加入了缓冲技术
		//将需要被提高效率的流对象作为参数传递给缓冲区
		BufferedWriter bufw = new BufferedWriter(fw);
		for(int x = 1;x<5;x++)
		{
			bufw.write("itheima" + x);
			//跨平台的换行符:newLine()
			bufw.newLine();
			bufw.flush();
		}
		bufw.close();
	}
}

示例代码(2)如下所示:

/**
 *通过缓冲区复制一个文件
 **/
public class CopyTextByBuf
{
	public static void main(String[] args)
	{
		BufferedReader bufr = null;
		BufferedWriter bufw = null;
		try
		{
			bufr= new BufferedReader(new FileReader("source//buf.txt"));
			bufw = new BufferedWriter(new FileWriter("source//buf_copy.txt"));
			String line = null;
			while((line = bufr.readLine())!=null)
			{
				bufw.write(line);
				bufw.newLine();
				bufw.flush();
			}
		}
		catch (FileNotFoundException e)
		{
			throw new RuntimeException("文件未发现!");
		}
		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("写入关闭失败!");
			}
		}
	}
}

2.5、装饰设计模式

2.5.1、格式

当我们需要对一个类的功能进行改进、增强的时候需要使用装饰设计模式。

(1)含有被装饰类的引用;

(2)通过构造函数传入被装饰类对象;

(3)和被装饰类含有同样的方法,其中调用被装饰类的方法,对其进行改进、增强

(4)和被装饰类继承同一个类或实现同一个接口,可以当做被装饰类来使用。

2.5.2BufferedReaderBufferedWriter原理

BufferedReaderBufferedWriter都是装饰类,可以装饰一个ReaderWriter,给被装饰的ReaderWriter提供缓冲的功能。

示例代码(1)如下所示:

/**
 * 可以自定义一个类中包含一个功能和readLine一致的方法, 来模拟一下BufferedReader
 * */
class MyBufferedReader extends Reader
{
	private FileReader fr;
	public MyBufferedReader(FileReader fr)
	{
		super();
		this.fr = fr;
	}
	// 模拟ReadLine方法,实现一次读取一行数据
	public String myReadLine() throws Exception
	{
		// 定义一个临时容器,使用StringBuilder容器,因为最终将数据变成字符串
		StringBuilder sb = new StringBuilder();
		int ch = 0;
		while ((ch = fr.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;
	}
	public void myClose() throws IOException
	{
		fr.close();
	}
	// 覆盖抽象类Reader的方法
	@Override
	public void close() throws IOException
	{
		fr.close();
	}
	@Override
	public int read(char[] cbuf, int off, int len) throws IOException
	{
		return fr.read(cbuf, off, len);
	}
}
public class MyBufferedReaderDemo
{
	public static void main(String[] args) throws Exception
	{
		FileReader fr = new FileReader("source//buf.txt");
		MyBufferedReader myBufr = new MyBufferedReader(fr);
		String line = null;
		while((line= myBufr.myReadLine())!=null)
		{
			System.out.println(line);
		}
		myBufr.myClose();
	}
}

示例代码(2)如下所示:

public class LineNumberReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("source//buf.txt");
		LineNumberReader lnr = new LineNumberReader(fr);
		String line = null;
		lnr.setLineNumber(100);
		while ((line = lnr.readLine()) != null)
		{
			System.out.println(lnr.getLineNumber() + ":" + line);
		}
		lnr.close();
	}
}

示例代码(3)如下所示:

/**
 * 模拟一个LineNumberReader类
 * */
class MyLineNumberReader extends MyBufferedReader
{
	private int lineNumber;
	public MyLineNumberReader(FileReader fr)
	{
		super(fr);
	}
	public String myReadLine() throws Exception
	{
		lineNumber++;
		return super.myReadLine();
	}
	public void setLineNumber(int lineNumber)
	{
		this.lineNumber = lineNumber;
	}
	public int getLineNumber()
	{
		return lineNumber;
	}
}
public class MyLineNumberReaderDemo
{
	public static void main(String[] args) throws Exception
	{
		FileReader fr = new FileReader("source//buf.txt");
		MyLineNumberReader mylnr = new MyLineNumberReader(fr);
		String line = null;
		mylnr.setLineNumber(100);
		while ((line = mylnr.myReadLine()) != null)
		{
			System.out.println(mylnr.getLineNumber() + ":" + line);
		}
		mylnr.myClose();
	}
}

2.6、字节流

(1)FileInputStream

public class FileInputStream
extends InputStream

1构造方法:

FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
2方法摘要:

int available() 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。(字节读取流特有方法!!!)
int read() 从此输入流中读取一个数据字节。 
int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 
int read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 
long skip(long n) 从输入流中跳过并丢弃 n 个字节的数据。

(2)FileOutputStream

public class FileOutputStream
extends OutputStream

1构造方法:

FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
2方法摘要:

public void flush()
void close() 关闭此文件输出流并释放与此流有关的所有系统资源。
void write(int b) 将指定字节写入此文件输出流。
void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 
void write(int b) 将指定字节写入此文件输出流。
示例代码(1)如下所示:
//演示使用FileOutputStream写入文件和FileInputStream读取文件
public class FileStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		writeFile();
		readFileByTrad();
		readFileByArray();
		readFileByArray_2();
	}

	public static void writeFile() throws IOException
	{
		FileOutputStream fos = new FileOutputStream("source//buf_copy.txt");
		fos.write("itheima".getBytes());
		fos.close();
	}

	public static void readFileByArray_2() throws IOException
	{
		FileInputStream fis = new FileInputStream("source//buf.txt");
		// 定义一个刚刚好的缓冲区,会发生内存溢出,如果数据内容太大的话
		byte[] buf = new byte[fis.available()];
		fis.read();
		System.out.println(new String(buf));
		fis.close();
	}

	public static void readFileByArray() throws IOException
	{
		FileInputStream fis = new FileInputStream("source//buf.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 readFileByTrad() throws IOException
	{
		FileInputStream fis = new FileInputStream("source//buf.txt");
		int ch = 0;
		while ((ch = fis.read()) != -1)
		{
			System.out.println((char) ch);
		}
		fis.close();
	}
}

示例代码(2)如下所示:

/**
 * 复制一张图片 解题思路:1,用字节读取流对象和图片相关联; 2,用字节写入流对象创建一个图片文件,用于存储获取到的图片对象;
 * 3,通过循环读写,完成数据的存储; 4,关闭资源。
 */
public class CopyPicture
{
	public static void main(String[] args)
	{
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try
		{
			fis = new FileInputStream("pics//1.jpg");
			fos = new FileOutputStream("pics//1_copy.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.7、字节流缓冲区读写

2.7.1、自定义缓冲区读写

原理和字符流相同,都是为了提高效率;定义数组缓冲数据,一次读取一个数组,一次写出一个数组,减少操作文件的次数。

2.7.2BufferedInputStreamBufferedOutputStream

(1)BufferedInputStream:

public class BufferedInputStream
extends FilterInputStream

1构造方法:

BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 
BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 
2方法摘要:

int available() (字节读取流特有方法!!!)返回可以从此输入流读取(或跳过)、且不受此输入流接下来的方法调用阻塞的估计字节数。 
int read() 参见 InputStream 的 read 方法的常规协定。 
int read(byte[] b, int off, int len) 从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。 
long skip(long n) 参见 InputStream 的 skip 方法的常规协定。
 (2)BufferedOutputStream

public class BufferedOutputStream
extends FilterOutputStream

1构造方法:

BufferedOutputStream(OutputStream out) :创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 
BufferedOutputStream(OutputStream out, int size) :创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
2方法摘要:

void flush() 刷新此缓冲的输出流。          
void write(byte[] b, int off, int len) :将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。 
void write(int b) 将指定的字节写入此缓冲的输出流。
示例代码(1)如下所示:
/**
 * 通过缓冲区复制一张图片
 */
public class CopyPicByBuf
{
	public static void main(String[] args) throws IOException
	{
		BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("pics//1.jpg"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("pics//1_copybuf.jpg"));
		int buf = 0;
		while ((buf = bufis.read()) != -1)
		{
			bufos.write(buf);
		}
		if (bufis != null)
		{
			bufis.close();
		}
		if (bufos != null)
		{
			bufos.close();
		}
	}
}

示例代码(2)如下所示:

/**
 * 通过缓冲区复制MP3
 **/
public class CopyMp3
{
	public static void main(String[] args) throws IOException
	{
		long start = System.currentTimeMillis();
		copyByBuf();
		copyByMyBuf();
		long end = System.currentTimeMillis();
		System.out.println((end - start) + "毫秒");
	}
	// 通过自定义的缓冲区完成复制
	public static void copyByMyBuf() throws IOException
	{
		MyBufferedInputStream mybufis = new MyBufferedInputStream(new FileInputStream("mp3//1.mp3"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("mp3//1_copybymybuf.mp3"));
		int buf = 0;
		while ((buf = mybufis.myRead()) != -1)
		{
			bufos.write(buf);
		}
		if (mybufis != null)
		{
			mybufis.myClose();
		}
		if (bufos != null)
		{
			bufos.close();
		}
	}
	// 通过字节流的缓冲区完成复制
	public static void copyByBuf() throws IOException
	{
		BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("mp3//1.mp3"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("mp3//1_copy.mp3"));
		int buf = 0;
		while ((buf = bufis.read()) != -1)
		{
			bufos.write(buf);
		}
		if (bufis != null)
		{
			bufis.close();
		}
		if (bufos != null)
		{
			bufos.close();
		}
	}
}
class MyBufferedInputStream
{
	private InputStream in;
	private byte[] buf = new byte[1024 * 4];
	private int pos = 0, count = 0;
	public MyBufferedInputStream(InputStream in)
	{
		super();
		this.in = in;
	}
	// 一次读取一个字节,从缓冲区(字节数组)中获取
	public int myRead() throws IOException
	{
		// 通过in对象读取硬盘上数据,并将其存储在buf当中
		if (count == 0)
		{
			count = in.read(buf);
			if (count < 0)
			{
				return -1;
			}
			pos = 0;// 每次读取数据的时候pos都要清零
			byte b = buf[pos];
			count--;
			pos++;
			return b & 255;
		}
		else if (count > 0)
		{
			byte b = buf[pos];
			count--;
			pos++;
			return b & 0xff;
		}
		return -1;
	}
	public void myClose() throws IOException
	{
		in.close();
	}
}

2.8、转换流

2.8.1InputStreamReader

public class InputStreamReader
extends Reader

(1)构造方法:

InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。
(2)方法摘要:

int read() 读取单个字符。
int read(char[] cbuf)  将字符读入数组
abstract  int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。
long skip(long n)  跳过字符。
2.8.2OutputStreamWriter
public class OutputStreamWriter
extends Writer

1构造方法:

OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter。<span style="font-family:宋体"></span>

(2)方法摘要:

void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 
void write(int c) 写入单个字符。 
void write(String str, int off, int len) 写入字符串的某一部分。

示例代码(1)如下所示:

/**
 * 需求:通过键盘录入数据,当录入一行数据后,就将该行数据是over时,程序结束
 * */
public class TransDemo
{
	public static void main(String[] args) throws IOException
	{
		// 获取键盘录入对象
		// 将字节流对象转成字符流对象,使用转换流InputStreamReader
		// 为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
		String line = null;
		while ((line = bufr.readLine()) != null)
		{
			if ("over".equals(line))
			{
				break;
			}
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
		bufw.close();
	}
}

2.9、标准输入输出流

2.9.1System.in

public static final InputStream in

“标准”输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。 

2.9.2System.out

public static final PrintStream out

“标准”输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。 

示例代码(1)如下所示:

/**
 * 需求:通过键盘录入数据,当录入一行数据后,就将该行数据是over时,程序结束
 * */
public class InOutDemo
{
	public static void main(String[] args) throws IOException
	{
		InputStream in = System.in;
		StringBuilder sb = new StringBuilder();
		while (true)
		{
			int ch = in.read();
			if (ch == '\r')
			{
				continue;
			}
			if (ch == '\n')
			{
				String str = sb.toString();
				if ("over".equals(str))
				{
					break;
				}
				System.out.println(str.toUpperCase());
				sb.delete(0, sb.length());
			}
			else
			{
				sb.append((char) ch);
			}
		}
	}
}

2.10File

public class File
extends Object
implements Serializable, Comparable<File>

(1)构造方法:

File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
(2)方法摘要:

1)创建:

boolean createNewFile() 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 
boolean mkdir() 创建一级文件夹
boolean mkdirs() 创建多级文件夹

2判断:

boolean canExecute() 测试应用程序是否可以执行此抽象路径名表示的文件。 
boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。 
boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。
int compareTo(File pathname) 按字母顺序比较两个抽象路径名。 
boolean isAbsolute() 测试此抽象路径名是否为绝对路径名。 
boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。 
boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件。 
boolean isHidden() 测试此抽象路径名指定的文件是否是一个隐藏文件。
boolean exists() 测试此抽象路径名表示的文件或目录是否存在。 

3获取:

String getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。 
 File getParentFile() 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null。 
 String getName() 返回由此抽象路径名表示的文件或目录的名称。
 String getPath() 将此抽象路径名转换为一个路径名字符串。 
 String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
 File getAbsoluteFile() 返回此抽象路径名的绝对路径名形式。
4删除:

boolean delete() 删除此抽象路径名表示的文件或目录。 
void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。 

5获取全部:(非常重要!!!)

 String[] list() 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。 
 String[] list(FilenameFilter filter) 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。 
 File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。 
 File[] listFiles(FileFilter filter) 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录

示例代码(1)如下所示:

/**
 * File类常用方法
 * */
public class FileDemo
{
	public static void main(String[] args) throws IOException
	{
		// 创建File对象
		File file = new File("source\\file.txt");
		File file2 = new File("source\\file2.txt");
		sop("rename : " + file2.renameTo(file));
		sop("path : " + file.getPath());
		sop("AbsolutePath : " + file.getAbsolutePath());
		sop("ParentPath : " + file.getParent());
		file.mkdir();
		sop("isDirectory : " + file.isDirectory());
		sop("isFile : " + file.isFile());
		sop("isAbsolute : " + file.isAbsolute());
		sop("isHidden : " + file.isHidden());
		sop("execute : " + file.canExecute());
		sop("exists : " + file.exists());
		sop("create : " + file.createNewFile());
		sop("delete : " + file.delete());
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

示例代码(2)如下所示:

public class FileDemo2
{
	public static void main(String[] args)
	{
		listRootDemo();
		listDemo();
		listFileDemo();
		getFileLength();
	}

	public static void getFileLength()
	{
		File dir = new File("D:\\");
		File[] files = dir.listFiles();
		for (File file : files)
		{
			System.out.println(file.getName() + " 的长度为 : " + file.length());
		}
	}

	public static void listFileDemo()
	{
		File dir = new File("F:\\2013年\\itheima\\day20");
		String[] names = dir.list(new FilenameFilter()
		{
			@Override
			public boolean accept(File dir, String name)
			{
				return name.endsWith(".java");
			}
		});
		System.out.println("该文件夹中文件的数目为  : " + names.length);
		System.out.println("该文件夹中文件的名称为  : ");
		for (String name : names)
		{
			System.out.println(name);
		}
	}

	public static void listRootDemo()
	{
		File[] files = File.listRoots();
		System.out.println("该电脑的根目录有:");
		for (File file : files)
		{
			System.out.print(file + " ");
		}
		System.out.println();
	}

	public static void listDemo()
	{
		File file = new File("D:\\");
		String[] names = file.list();
		// 调用list方法的file对象必须是封装成了一个目录,该目录必须存在
		System.out.println("D盘目录下文件夹有:");
		for (String name : names)
		{
			System.out.println(name);
		}
	}
}

2.11、递归

递归就是指函数自己调用自己。

示例代码(1)如下所示:

public class ListFile
{
	public static void main(String[] args)
	{
		File dir = new File("F:\\2013年\\itheima");
		showDir(dir, 0);
	}
	/**
	 * 显示文件目录以及目录下文件名称
	 * @param dir 需要显示的文件目录
	 * @param level 需要显示的层数
	 * */
	public static void showDir(File dir, int level)
	{
		System.out.println(getLevel(level) + " 目录名称为: " + dir.getName());
		level++;
		File[] files = dir.listFiles();
		for (int x = 0; x < files.length; x++)
		{
			if (files[x].isDirectory())
			{
				showDir(files[x], level);
			}
			else
			{
				System.out.println(getLevel(level) + files[x]);
			}
		}

	}
	//格式化显示文件目录
	public static String getLevel(int level)
	{
		StringBuilder sb = new StringBuilder();
		sb.append("|--");
		for (int x = 0; x < level; x++)
		{
			sb.insert(0, "|");
		}
		return sb.toString();
	}
}

示例代码(2)如下所示:

/**
 * 将一个指定目录下的java文件的绝对路径,存储到一个文本文件中 建立一个java文件列表文件
 *思路: 1,对指定的目录进行递归;
 * 2,获取递归过程中所有的Java文件的路径; 3,将这些路径存储到集合当中; 4,将集合中的数据写入到一个文件当中
 */
public class ListJavaFile
{
	public static void main(String[] args)
	{
		File dir = new File("f:\\2013年\\itheima");
		List<File> list = new ArrayList<File>();
		fileToList(dir, list);
		File file = new File(dir, "javalist.txt");
		writeToFile(list, file.toString());
	}
	private static void writeToFile(List<File> list, String javaListFile)
	{
		BufferedWriter bufw = null;
		try
		{
			bufw = new BufferedWriter(new FileWriter(javaListFile));
			for (File file : list)
			{
				String path = file.getAbsolutePath();
				bufw.write(path);
				bufw.newLine();
				bufw.flush();
			}
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		finally
		{
			try
			{
				if (bufw != null)
				{
					bufw.close();
				}
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭异常!");
			}
		}
	}
	private static void fileToList(File dir, List<File> list)
	{
		File[] files = dir.listFiles();
		for (File file : files)
		{
			if (file.isDirectory())
			{
				fileToList(file, list);
			}
			else
			{
				if (file.getName().endsWith(".java"))
				{
					list.add(file);
				}
			}

		}
	}
}

示例代码(3)如下所示:

/**
 * 删除一个带内容的目录;思路:在Windows中,删除目录是从里面往外面删除的,使用递归。
 */
public class RemoveDirDemo
{
	public static void main(String[] args)
	{
		File dir = new File("F:\\itheima");
		removeDir(dir);
	}
	private static void removeDir(File dir)
	{
		// 获取该目录下的所有文件夹以及文件
		File[] files = dir.listFiles();
		for (int i = 0; i < files.length; i++)
		{
			// 如果文件不是隐藏而且是文件夹则递归调用
			if (!files[i].isHidden() && files[i].isDirectory())
			{
				removeDir(files[i]);
			}
			else
			// 打印出删除的文件名称
			{
				System.out.println(files[i].toString() + "==file==" + files[i].delete());
			}
		}
		System.out.println(dir + "--dir--" + dir.delete());
	}
}

2.12IO包中的其它类

2.12.1、序列流

public class SequenceInputStream
extends InputStream

表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 

(1)构造方法:

SequenceInputStream(Enumeration<? extends InputStream> e) 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。 
SequenceInputStream(InputStream s1, InputStream s2) 通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),以提供从此 SequenceInputStream 读取的字节。 

(2)方法摘要:

int available() 返回不受阻塞地从当前底层输入流读取(或跳过)的字节数的估计值,方法是通过下一次调用当前底层输入流的方法。 
void close() 关闭此输入流并释放与此流关联的所有系统资源。 
int read() 从此输入流中读取下一个数据字节。 
int read(byte[] b, int off, int len) 将最多 len 个数据字节从此输入流读入 byte 数组。 
/**
 * 将音乐文件分为三份之后再合并起来
 */
public class SpiltAndMergeFile
{
	public static void main(String[] args) throws IOException
	{
		spileFile();
		mergeFile();
	}

	private static void spileFile() throws IOException
	{
		FileInputStream fis = new FileInputStream("mp3\\1.mp3");
		FileOutputStream fos = null;
		byte[] buf = new byte[1024*1024];
		int len = 0;
		int count = 1;
		while ((len = fis.read(buf)) != -1)
		{
			fos = new FileOutputStream("mp3\\" + (count++) + ".part");
			fos.write(buf, 0, len);
			fos.close();
		}
		fis.close();
	}

	private static void mergeFile() throws IOException
	{
		ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
		for (int i = 1; i <= 4; i++)
		{
			al.add(new FileInputStream("mp3" + i + ".part"));
		}
		final Iterator<FileInputStream> it = al.iterator();
		Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
		{
			@Override
			public FileInputStream nextElement()
			{
				return it.next();
			}

			@Override
			public boolean hasMoreElements()
			{
				return it.hasNext();
			}
		};
		SequenceInputStream sis = new SequenceInputStream(en);
		FileOutputStream fos = new FileOutputStream("mp3\\sm.mp3");
		byte[] buf = new byte[1024];
		int len = 0;
		while ((len = sis.read(buf)) != -1)
		{
			fos.write(buf, 0, len);
		}
		fos.close();
		sis.close();
	}

}

2.12.2、打印流

public class PrintStream
extends FilterOutputStream
implements Appendable, Closeable

PrintStream:字节打印流

(1)构造方法:

PrintStream(String fileName) 创建具有指定文件名称且不带自动行刷新的新打印流。
PrintStream(File file) 创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(OutputStream out) 创建新的打印流。 
PrintStream(OutputStream out, boolean autoFlush) 创建新的打印流。 

(2)方法摘要:

PrintWriter append(char c) 将指定字符添加到此 writer。 
void close() 关闭该流并释放与之关联的所有系统资源。 
void flush() 刷新该流的缓冲。 
void print(Object obj) 打印对象。 
void print(String s) 打印字符串。
void println() 通过写入行分隔符字符串终止当前行。

PrintWriter:字符打印流

(1)构造方法:

PrintWriter(String fileName) 创建具有指定文件名称且不带自动行刷新的新 PrintWriter。
PrintWriter(File file) 使用指定文件创建不具有自动行刷新的新 PrintWriter。
PrintWriter(Writer out) 创建不带自动行刷新的新 PrintWriter。 
PrintWriter(Writer out, boolean autoFlush) 创建新 PrintWriter。 
PrintWriter(OutputStream out) 根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。 
PrintWriter(OutputStream out, boolean autoFlush) 通过现有的 OutputStream 创建新的 PrintWriter。

(2)方法摘要:

PrintWriter append(char c) 将指定字符添加到此 writer。 
void print(各种类型的数据:) 打印各种类型的数据 
void println(各种类型的数据:):自动换行,打印各种类型的数据
void write(char[] buf) 写入字符数组。 
void write(char[] buf, int off, int len) 写入字符数组的某一部分。 
void write(int c) 写入单个字符。 
void write(String s) 写入字符串。 
void write(String s, int off, int len) 写入字符串的某一部分。 

示例代码如下所示:

/**
 *演示用打印流向文件中写入内容
 */
public class PrintStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		// 为了提高效率的利用使用缓冲区
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("source\\buf.txt")));
		String line = null;
		while ((line = bufr.readLine()) != null)
		{
			if ("over".equals(line))
			{
				break;
			}
			out.println(line.toUpperCase());
		}
		out.close();
		bufr.close();
	}
}

2.12.3、操作对象

ObjectInputStreamObjectOutputStream

被操作的对象需要实现Serializable 

示例代码如下所示:

class Person implements Serializable
{
	public static final long serialVersionUID = 43L;
	private String name;
	transient int age;
	static String country="CN";//静态是不能被序列化的
	Person(String name,int age,String country)
	{
		this.name = name;
		this.age = age;
		this.country = country;
	}
	public String toString()
	{
		return name + ":::" + age + "::" + country;
	}
}
/**
 * 演示使用操作对象读取文件和写入文件
 * */
class ObjectStreamDemo
{
	public static void main(String[] args) throws Exception
	{
		writeObj();
		readObj();
	}
	public static void writeObj() throws IOException
	{
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
		oos.writeObject(new Person("lisi", 23, "KR"));
		oos.close();
	}
	public static void readObj() throws Exception
	{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
		Person p = (Person) ois.readObject();
		System.out.println(p);
		ois.close();
	}
}
2.12.4、操作内存缓冲数组

(1)ByteArrayInputStreamByteArrayOutputStream

ByteArrayInputStream:在构造的时候需要接收数据源,而且数据源是字节数组;
ByteArrayOutputStream:在构造的时候,不用定义数据的目的,因为该对象中内部已经封装了可变长度的字节数组,这就是数据目的地。

因为这两个流对象都要操作数组,并没有使用系统资源;所以不用close关闭。

/**
 * 演示操作内存缓冲数组
 * */
class ByteArrayStream
{
	public static void main(String[] args) throws Exception
	{
		// 指定数据源
		ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());
		// 明确数据目的
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		int by = 0;
		while ((by = bis.read()) != -1)
		{
			bos.write(by);
		}
		System.out.println(bos.size());
		System.out.println(bos.toString());
		bos.writeTo(new FileOutputStream("a.txt"));
	}
}

2.12.5、管道流

PipedInputStreamPipedOutputStream

输入输出可以直接进行连接,通过结合线程使用

示例代码如下所示:

class PipedStreamDemo
{
	public static void main(String[] args) throws Exception
	{
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);
		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(r).start();
		new Thread(w).start();
	}
}

class Read implements Runnable
{
	private PipedInputStream in;

	Read(PipedInputStream in)
	{
		this.in = in;
	}

	public void run()
	{
		try
		{
			byte[] buf = new byte[1024];
			System.out.println("读取前没有数据,就阻塞");
			int len = in.read(buf);
			System.out.println("读到数据,阻塞结束");
			String s = new String(buf, 0, len);
			System.out.println(s);
			in.close();
		}
		catch (IOException e)
		{
			throw new RuntimeException("管道流读取流失败!");
		}
	}
}

class Write implements Runnable
{
	private PipedOutputStream out;

	Write(PipedOutputStream out)
	{
		this.out = out;
	}

	public void run()
	{
		try
		{
			System.out.println("开始写入数据,等待六秒!");
			Thread.sleep(6000);
			out.write("piped is coming!".getBytes());
			out.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("管道输出流失败!");
		}
	}
}
运行结果如下所示:
技术分享

2.12.6、操作基本数据类型

DataInputStreamDataOutputStream

示例代码如下所示:

/**
 * 演示操作基本数据类型
 * */
class DataStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		// 以默认编码写入文件
		writeData();
		// 以默认编码读取文件
		readData();
		// 以UTF-8编码写入文件
		writeUTFDemo();
		// 以UTF-8编码读取文件
		readUTFDemo();
	}
	public static void readUTFDemo() throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
		String s = dis.readUTF();
		System.out.println(s);
		dis.close();
	}
	public static void writeUTFDemo() throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt"));
		dos.writeUTF("你好");
		dos.writeUTF("黑马");
		dos.close();
	}
	public static void readData() throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
		int num = dis.readInt();
		boolean b = dis.readBoolean();
		double d = dis.readDouble();
		System.out.println("num=" + num);
		System.out.println("b=" + b);
		System.out.println("d=" + d);
		dis.close();
	}
	public static void writeData() throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
		dos.writeInt(234);
		dos.writeBoolean(true);
		dos.writeDouble(892.23);
		dos.close();
	}
}

2.12.7、随机访问文件

public class RandomAccessFile
extends Object
implements DataOutput, DataInput, Closeable
 

自身具备读写方法,通过skipByte(int x)seek(int x)来达到随机访问文件;该类不是IO体系子类,而是直接继承Object,但它是IO包中的成员,因为它具备读写方法;该类内部封装了数组,而且通过指针对数组的元素进行操作,可以通过getFilePoint获取指针位置,同时可以通过seek改变指针位置。该类完成读写的原理是内部封装了字节输入输出流

通过该类的构造看出,该类只能操作文件,而且操作的文件只能有固定模式:"r":只读、"rw":读写

(1)构造方法:

RandomAccessFile(File file, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 
RandomAccessFile(String name, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 
(2)方法摘要:

void write(byte[] b) 将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。 
void write(byte[] b, int off, int len) 将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。 
void write(int b) 向此文件写入指定的字节。 
int read() 从此文件中读取一个数据字节。 
int read(byte[] b) 将最多 b.length 个数据字节从此文件读入 byte 数组。 
int read(byte[] b, int off, int len) 将最多 len 个数据字节从此文件读入 byte 数组。
String readLine() 从此文件读取文本的下一行。 
long getFilePointer() 返回此文件中的当前偏移量。 
long length() 返回此文件的长度。 
void seek(long pos) 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。

示例代码如下所示:

class RandomAccessFileDemo
{
	public static void main(String[] args) throws Exception
	{
		writeFile();
		readFile();
	}
	public static void readFile() throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("run.txt", "r");
		// 调整对象中的指针
		raf.seek(8);
		// 跳过指定的字节数
		raf.skipBytes(8);
		byte[] buf = new byte[4];
		raf.read(buf);
		String name = new String(buf);
		int age = raf.readInt();
		System.out.println("name=" + name);
		System.out.println("age=" + age);
		raf.close();
	}
	public static void writeFile() throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("run.txt", "rw");
		//向此文件中写入指定的字节
		raf.write("李四".getBytes());
		raf.writeInt(97);
		raf.write("王五".getBytes());
		raf.write(99);
		//关闭流
		raf.close();
	}
}

综合代码题:

/**
 *有五个学生,每个学生有3门课的成绩, 从键盘输入以上数据(包括姓名,三门课成绩), 输入的格式:如:zhagnsan,30,40,60计算出总成绩,
 * 并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。 解题思路: 1,描述学生对象; 2,定义一个可以操作学生对象的工具类;
 * 步骤:1,通过获取键盘录入的一行数据,并将改行的信息取出封装学生对象; 2,使用TreeSet来对学生的信息进行存储和排序;
 * 3,将集合中的信息写入到一个文件中。
 */
public class StudentInfoTest
{
	public static void main(String[] args)
	{
		Comparator<Student> cmp = Collections.reverseOrder();
		Set<Student> stus = StudentInfoTool.getStudents(cmp);
		StudentInfoTool.write2File(stus);
	}
}

class StudentInfoTool
{
	public static Set<Student> getStudents()
	{
		return getStudents(null);
	}
	public static Set<Student> getStudents(Comparator<Student> cmp)
	{
		//使用缓冲流提高效率
		BufferedReader bufr = null;
		//将学生信息存放在Set集合当中
		Set<Student> stus = null;
		try
		{
			bufr = new BufferedReader(new InputStreamReader(System.in));
			String line = null;
			if (cmp == null)
			{
				stus = new TreeSet<Student>();
			}
			else
			{
				stus = new TreeSet<Student>(cmp);
			}
			while ((line = bufr.readLine()) != null)
			{
				if ("over".equals(line))
				{
					break;
				}
				String[] info = line.split(",");
				//将获取到的信息变成Student对象
				Student stu = new Student(info[0], Integer.parseInt(info[1]), Integer.parseInt(info[2]), Integer
						.parseInt(info[3]));
				//向集合中添加对象
				stus.add(stu);
			}
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		finally
		{
			//关闭读取流
			try
			{
				if (bufr != null)
				{
					bufr.close();
				}
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭流失败!");
			}
		}
		return stus;
	}

	public static void write2File(Set<Student> stus)
	{
		//使用缓冲流提高效率
		BufferedWriter bufw = null;
		try
		{
			bufw = new BufferedWriter(new FileWriter("source\\stus.txt"));
			//使用增强for循环向文件中写入信息
			for (Student stu : stus)
			{
				bufw.write(stu.getName() + "\t");
				bufw.write(stu.getSum() + " ");
				bufw.newLine();
				bufw.flush();
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("写入文件失败!");
		}
		finally
		{
			//关闭写入流
			try
			{
				if (bufw != null)
				{
					bufw.close();
				}

			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭流失败!");
			}
		}
	}
}
class Student implements Comparable<Student>
{
	private String name;
	private int math;
	private int chinese;
	private int english;
	private int sum;
	public Student(String name, int math, int chinese, int english)
	{
		super();
		this.name = name;
		this.math = math;
		this.chinese = chinese;
		this.english = english;
		sum = this.chinese + this.math + this.english;
	}
	@Override
	public int compareTo(Student stu)
	{
		int num = new Integer(this.sum).compareTo(stu.sum);
		if (num == 0)
		{
			return this.name.compareTo(stu.name);
		}
		return num;
	}
	@Override
	public boolean equals(Object obj)
	{
		// 判断是否是学生类型
		if (!(obj instanceof Student))
		{
			throw new ClassCastException("类型不匹配!");
		}
		// 强制类型转换
		Student stu = (Student) obj;
		return this.name.equals(stu.name) && this.sum == stu.sum;
	}
	//get和set方法
	.......
	@Override
	public String toString()
	{
		return "Student [chinese=" + chinese + ", english=" + english + ", math=" + math + ", name=" + name + ", sum="
				+ sum + "]";
	}
}

2.13IO流转换流的字符编码

2.13.1、编码表

(1)字符流的出现为了方便操作字符,更重要的是加入了编码转换

(2)通过子类转换流来完成

InputStreamReander

OutputStreamWriter

(3)在两个子类对象进行构造的时候可以加入编码表

技术分享

2.13.2、编码和解码

编码:字符串变成字节数组。String --> byte[]:str.getBytes(charsetName)

解码:字节数组变成字符串。byte[] --> String:new String(byte [],charsetName)

示例代码(1)如下所示:

public static void write() throws IOException
{
	OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");
	osw1.write("你好");
	osw1.close();

	OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"UTF-8");
	osw2.write("你好");
	osw2.close();
}
public static void read() throws IOException
{
	InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");
	byte[] buf = new byte[1024];
	int len = isr.read(buf);
	sop(new String(buf,0,len));
}

示例代码(2)如下所示:

public static void main(String[] args)
{
	//编码解码1:默认编码
	String str1 = "你好";
	byte[] buf1 = str1.getBytes();//默认解码:Unicode,四个字节
	//编码解码2:指定编码
	String str2 = "你好";
	byte[] buf2 = str2.getBytes("UTF-8");//指定解码:UTF-8,六个字节
	//编码解码3:编码正确解码错误
	String str3 = "你好";
	byte[] buf3 = str3.getBytes("GBK");//指定编码:GBK,四个字节
	String str3 = new String(buf3,"ISO8859-1");//错误解码
	//编码解码4:错误编码正确解码
	String str4 = "你好";
	byte[] buf4 = str4.getBytes("ISO8859-1");//错误编码
	String str4 = new String(buf4,"GBK");//正确解码,读不出来
	//编码解码5:编码对了,但是解码错误了,怎么办呢?
	//此时可以将错误的解码再错编回去,载用正确编码解码
	String str5 = "你好";
	byte[] buf5 = str5.getBytes("GBK");//正确编码
	String str6 = new String(buf5,"ISO8859-1");//错误解码,读不出来
	byte[] buf6 = str6.getBytes("ISO8859-1");//再错误编码
	String str7 = new String(buf6,"GBK");//再正确解码,这样就可以读出来了
}

总结:流操作的规律:

(1)第一步:先明确源和目的

源:

         文本:用Reader

         字节:用InputStream

目的:

       文本:用Writer

      字节:用OutputStream

(2)第二步:明确是不是纯文本

          是:用字符流;

        不是:用字节流

(3)第三步:明确流体系后,通过设备来明确具体使用哪个流对象

源设备:

          键盘:System.in

         硬盘:文件流File

        内存:数组流ArrayStream

目的设备:

       键盘:System.out

      硬盘:文件流File

     内存:数组流ArrayStream


Java SE学习笔记-IO