Java笔记(16)异常与IO流

Java笔记(16)异常与IO

1.异常

在程序运行时,可能会发生一些不被期望的事件,这些事件可能会导致程序无法正常运行,这就是异常。而对异常究竟该怎么处理呢,是提供错误给用户,还是让程序立马终止,还是针对异常进行处理,而在java中,针对异常的处理java就提供了异常处理机制;
Java的异常也是以对象的形式存在,当java程序中产生了异常,就会产生一个对应的异常对象来封装异常,在Java的异常中,Throwable是java异常类的顶层父类;

Java异常分类:

Throwable(顶层父类,所有异常的父类)
	---Error类,它及它的子类一般代表了JVM本身的错误,错误不能被程序员通过代码处理;
	---Exception类,它及它的子类是我们进行异常处理的核心;
		|---RuntimeException	属于运行期异常,多半属于代码问题,如除0错误或数组索引越界等,需要修正我们的代码;
		|---剩下的非RuntimeException异常都属于我们要重点处理的异常,是编译器异常;

异常的处理方式:
针对异常的处理,通常有两种方式,一种是由JVM默认处理,我们自己不处理,这种异常处理方式会把异常的名称,原因,位置等信息输出在控制台,但程序就会终止无法继续运行了;
第二种处理是我们自己处理,如try…catch…finally语句,根据异常进行对应的处理,使得后面的代码可以继续运行,或者throws,把自己处理不了的,在方法上声明,告诉调用者,这里有问题,让问题由调用者处理。

throw和throws的区别:
throw在方法体中,后面跟的是一个实例异常对象,且只能有一个,它表示当前地方一定会产生一个异常对象;
throws在方法声明上,可以跟多个异常类名,表示调用当前方法可能会产生这个异常;

finally关键字:
在异常处理语句try…cath中,可以看到后面还有一个finally语句,而finally是一个关键字,它表示它里面包含的代码一定会执行,常用于释放资源;

自定义异常:
继承自Exception或者RuntimeException,只需要提供无参构造和一个带参构造即可;可以参考IOException类的写法;

异常的注意实现:
A:父的方法有异常抛出,子的重写方法在抛出异常的时候必须要小于等于父的异常
B:父的方法没有异常抛出,子的重写方法不能有异常抛出
C:父的方法抛出多个异常,子的重写方法必须比父少或者小

2.IO

IO是指对于数据进行输入输出的操作,I是input指输入,O是output指输出;
在Java中,java也提供了针对数据输入输出操作的IO流体系,java将数据分为了字符流和字节流,并依据它们的特征提供了不同的类来进行输入输出操作;

在Java的IO体系中,我们重点要学习掌握的类有以下几个:

	 1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。

     2. InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。

     3. OutputStream(二进制格式操作):抽象类。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。

     4.Reader(文件格式操作):抽象类,基于字符的输入操作。

     5. Writer(文件格式操作):抽象类,基于字符的输出操作。
(1)File类

在Java的IO操作中,我们大部分针对的都是文件进行操作,Java也提供了一个File类,专门用于我们对于文件进行一些操作;

构造方法:

通过给定的父抽象路径名和子路径名字符串创建一个新的File实例。
File(File parent, String child);
通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例。
File(String pathname) 
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
File(String parent, String child) 
通过将给定的 file: URI 转换成一个抽象路径名来创建一个新的 File 实例。
File(URI uri) 

File类主要方法:

1. boolean createNewFile() 当指定路径名称的文件不存在时创建该文件
2. boolean mkdir() 创建此抽象路径名指定的目录。
3. boolean mkdirs() 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
4. boolean delete()  删除此抽象路径名表示的文件或目录。
5. boolean renameTo(File dest) 重新命名此抽象路径名表示的文件。
6. boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。
7. boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件。
8. boolean isAbsolute()  测试此抽象路径名是否为绝对路径名。
9. boolean isHidden() 测试此抽象路径名指定的文件是否是一个隐藏文件。
10.boolean exists() 测试此抽象路径名表示的文件或目录是否存在。 
11.String getName() 返回由此抽象路径名表示的文件或目录的名称。
12.String getAbsolutePath()  返回此抽象路径名的绝对路径名字符串。
13.String getPath() 将此抽象路径名转换为一个路径名字符串。
14.long length() 返回由此抽象路径名表示的文件的长度。
15.String[] list()  返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。
16.File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。 
17.File[] listFiles(FilenameFilter filter) 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 
注意的是FilenameFileter是一个接口,里面只有一个accept方法,重写后实现过滤功能即可;

案例演示:

	public static void main(String[] args) throws IOException {
		// 获取路径的实例文件对象
		File file = new File("c:/demo/");
		// 遍历其直属文件和目录,这里写个方法直接调用
		getListFiles(file);
		// 删除里面的test目录及文件
		fileDelete(file, "test");
		getListFiles(file);
	}
	
	private static void getListFiles(File file) {
		System.out.println("-----文件遍历列表开始-----");
		// 获取文件下的所有文件对象
		File[] listfile = file.listFiles();
		// 因为只遍历该文件下的直属目录文件,只遍历一层,这里就不添加空指针判断
		// 注意的是,如果该文件下有无权限操作的文件或目录,在listFiles这个方法时会返回null,所以在遍历所有文件或对其操作时会发生空指针异常
		for (File f : listfile) {
			System.out.println(f.getName());
		}
		System.out.println("-----文件遍历列表完成-----");
	}

	/**
	 * 这是一个用来删除文件的方法,删除的对象可以是目录也可以是文件。方法使用队列实现,并非递归。
	 * 
	 * @param file     传递一个文件对象
	 * @param filename 传递要删除的文件名
	 */
	private static void fileDelete(File file, String filename) {
		// LinkedList模拟队列,存储目录,实现遍历删除其下所有文件
		LinkedList<File> queue = new LinkedList<File>();
		// LinkedList模拟栈,存储目录,实现删除遍历完成后的所有目录
		LinkedList<File> stack = new LinkedList<File>();

		if (file.exists()) {
			// 获取该文件下的所有文件目录及文件
			File[] files = file.listFiles();

			if (files == null) {
				// 如果该文件是无权操作的文件,即files=null,就跳出当前方法
				return;
			}

			for (File f : files) {
				// 遍历该文件目录数组下的所有文件
				// 如果是目录,就将其存放到队列和栈里
				if (f.isDirectory()) {
					// 如果是目录的话,判断其名称是不是和要删除的文件名称相同
					if (f.getName().equals(filename)) {
						// 名称相同就将存进队列
						queue.add(f);
						// 并且也存入栈
						stack.add(f);
					}
				} else {
					// 首先将文件的后缀名与前缀名分割,再判断其前缀是否和指定的文件名相同
					String[] s = f.getName().split("\\.");
					if (s[0].equals(filename)) {
						// 是的话删除
						f.delete();
					}
				}
			}
			// 循环完毕后已将传递的文件对象下的第一层目录中满足条件的目录放入了队列
			// 下面我们开始循环队列
			File folders;
			while (!queue.isEmpty()) {
				// 获取第一个元素并删除
				folders = queue.removeFirst();
				// files前面已使用完毕,这里就不再重新定义
				files = folders.listFiles();
				for (File f : files) {
					// 判断是目录还是文件
					if (f.isDirectory()) {
						// 是目录就存入队列和栈
						queue.add(f);
						stack.add(f);
					} else {
						// 不是就删除
						f.delete();
					}
				}
			}
			// 遍历删除完队列里的所有文件后就可以依次删除栈中的目录了
			while (!stack.isEmpty()) {
				//从栈里删除目录,这里使用栈也是因为当目录内有目录或文件时该目录无法删除,所以用栈的特性从最里面的目录一个一个开始删除,保证让所有目录都可以删除
				folders = stack.removeLast();
				folders.delete();
			}
			System.out.println("你要删除的" + filename + "文件已删除完毕");
		}
	}

//程序运行结果:
-----文件遍历列表开始-----
test
test.txt
-----文件遍历列表完成-----
你要删除的test文件已删除完毕
-----文件遍历列表开始-----
-----文件遍历列表完成-----
(2)InputStream流

该抽象类是所有字节输入流的超类,提供了字节输入流的基本方法。
这里,我们重点学习其下的子类,FileInputStream和BufferedInputStream;

1.FileInputStream类

FileInputStream类是InputStream的实现类,它是对于文件操作的字节输入流,从文件系统中获得字节,哪些文件可用取决于主机环境;
在对于非字符的数据诸如图像视频等,建议使用该流进行读取,而对于文本等字符流建议使用FileReader进行读取操作;

构造方法:

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

常用成员方法:

 int read() 
          从此输入流中读取一个数据字节。 
 int read(byte[] b) 
          从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 
 int read(byte[] b, int off, int len) 
          从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 
 void close() 
          关闭此文件输入流并释放与此流有关的所有系统资源。 
2.BufferedInputStream类

该类是FilterInputStream的子类,FilterInputStream是FileInputStream的子类,它是一个包装器子类,在其内部内置了一个缓冲区,在每次读取的时候先将数据读到缓冲区,避免每次读取操作时直接在磁盘上的读写操作,从而提高了效率,因此也称作高效字节输入流;

构造方法:

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

其大部分常用成员方法基本和FileInputStream一样,不做罗列;

(3)OutputStream流

此抽象类是表示输出字节流的所有类的超类,提供了字节输出操作的基本方法;
这里,我们重点学习其下的子类,FileOutputStream和BufferedOutputStream;

1.FileOutputStream

文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。

构造方法:

FileOutputStream(File file) 
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
FileOutputStream(File file, boolean append) 
          这里的append参数表示是否追加,true为追加写入。
FileOutputStream(String name) 
          创建一个向具有指定名称的文件中写入数据的输出文件流。 
FileOutputStream(String name, boolean append) 
          创建一个向具有指定 name 的文件中写入数据的输出文件流。 

成员方法:

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

该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
此类也被称为高效字节输出流,注意的是,虽然调用close方法会在close前先刷新缓冲区,但close后该流将无法继续使用,在流继续使用时建议一边写一边刷新,保证缓冲区的数据及时完整的刷新到目的地;

构造方法:

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

成员方法与OutputStream一致,不列举,详细请参考API;

(4)Reader

用于读取字符流的抽象类。
在使用字节流读取和输出中文时可能会出现一些小问题,例如编码异常导致无法正常显示和操作,因此java提供了字符流来专门操作字符类的输入输出操作,它的本质是将字节流和一个编码表结合组成了字符流;

我们重点学习其下子类:InputStreamReader、FileReader和BufferedReader;

1.InputStreamReader

该类是字符转换流,用于将一个字节流按指定编码转换为字符流读取;

构造方法:

InputStreamReader(InputStream in) 
          创建一个使用默认字符集的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName) 
          创建使用指定字符集的 InputStreamReader。

成员方法:

 void close() 
          关闭该流并释放与之关联的所有资源。 
 String getEncoding() 
          返回此流使用的字符编码的名称。 
 int read() 
          读取单个字符。 
 int read(char[] cbuf, int offset, int length) 
          将字符读入数组中的某一部分。 
 boolean ready() 
          判断此流是否已经准备好用于读取。 

2.FileReader

用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。如果要指定字符编码,请使用InputStreamReader类;

FileReader 用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream。

构造方法:

FileReader(File file) 
      在给定从中读取数据的 File 的情况下创建一个新 FileReader。
FileReader(String fileName) 
      在给定从中读取数据的文件名的情况下创建一个新 FileReader。

成员方法:
其方法全部来源于其父类InputStreamReader和Reader;

3.BufferedReader

高效字符缓冲流。

构造方法:

BufferedReader(Reader in) 
          创建一个使用默认大小输入缓冲区的缓冲字符输入流。 
BufferedReader(Reader in, int sz) 
          创建一个使用指定大小输入缓冲区的缓冲字符输入流。 

主要成员方法:

 void close() 
          关闭该流并释放与之关联的所有资源。 
 void flush()
 		  刷新该流的缓冲。
 int read() 
          读取单个字符。 
 int read(char[] cbuf, int off, int len) 
          将字符读入数组的某一部分。 
 String readLine() 
          读取一个文本行。 
 boolean ready() 
          判断此流是否已准备好被读取。 
(5)Writer

写入字符流的抽象类。

其下重点子类:OutputStreamWriter、FileWriter、BufferedWriter。

1.OutputStreamWriter

字符输出转换流,可将一个字节输出流按指定编码转换为一个字符输出流;

构造方法:

OutputStreamWriter(OutputStream out) 
          创建使用默认字符编码的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) 
          创建使用指定字符集的 OutputStreamWriter。

成员方法:

 void close() 
          关闭此流,但要先刷新它。 
 void flush() 
          刷新该流的缓冲。 
 String getEncoding() 
          返回此流使用的字符编码的名称。 
 void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分。 
 void write(int c) 
          写入单个字符。 
 void write(String str, int off, int len) 
          写入字符串的某一部分。 
2.FileWriter

用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。

构造方法

FileWriter(File file) 
          根据给定的 File 对象构造一个 FileWriter 对象。 
FileWriter(File file, boolean append) 
          根据给定的 File 对象构造一个 FileWriter 对象。 
FileWriter(String fileName) 
          根据给定的文件名构造一个 FileWriter 对象。 
FileWriter(String fileName, boolean append) 
          根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 

成员方法全部来源于其父类;

3.BufferedWriter

高效字符缓冲输出流;

构造方法:

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

成员方法

 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.IO流案例

1.使用字节流复制文件
	public static void main(String[] args) throws IOException {
		// 创建原位置和目标位置路径字符串
		String source = "C:\\Users\\Administrator\\Desktop\\精选后图片\\测试\\1.jpg";
		String target = "C:\\Users\\Administrator\\Desktop\\精选后图片\\1.jpg";
		// 写一个方法用于字节流文件复制操作
		fileCopyByte(source, target);
	}

	/**
	 * 字节流复制文件方法,可复制任意类型文件
	 * 
	 * @param source 源位置
	 * @param target 目标位置
	 * @throws IOException 抛出IO异常
	 */
	private static void fileCopyByte(String source, String target) throws IOException {
		//获取复制文件前的当前毫秒时间
		long start = System.currentTimeMillis();

		// 创建字节流读取对象,为了高效使用BufferedInputStream
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
		// 创建字节流输出对象
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target));
		// 读取该文件数据到流中,一次读取1024个字节,也可以一次读一个字节,但会比较慢
		byte[] bys = new byte[1024];
		int len = 0;
		// read方法返回的是读取的字节数,如果到末尾没有字节返回-1
		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}

		// 释放资源
		bis.close();
		bos.close();

		long end = System.currentTimeMillis();
		
		System.out.println("复制完成,共耗时" + (end - start) + "毫秒");
	}
	//字节流复制文件可以使用普通流和高效流两种,建议高效流;
	读取写入也可以采用一个字节或字节数组方式,为了效率,建议使用字节数组+高效流方式。
2.使用字符流复制文本文件
	public static void main(String[] args) throws IOException {
		// 创建字符流源位置和目标位置字符串
		String source = "C:\\Users\\Administrator\\Desktop\\精选后图片\\测试\\1.txt";
		String target = "C:\\Users\\Administrator\\Desktop\\精选后图片\\2.txt";

		// 创建并调用文件复制方法
		CopyFile3(source, target);
	}
	方法一:
	/**
	 * 该方法使用InputStreamReader和OutputStreamReader实现
	 * 
	 * @param source 源位置
	 * @param target 目标位置
	 * @throws IOException IO异常
	 */
	private static void CopyFile1(String source, String target) throws IOException {
		long start = System.currentTimeMillis();
		// 创建字符转换流对象,指定按GBK格式读取,如果使用默认编码会导致解码异常,因为默认编码是UTF-8,原文件是GBK格式
		InputStreamReader isr = new InputStreamReader(new FileInputStream(source), "GBK");
		// 创建字符流输出对象,按UTF-8格式输出
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(target), "UTF-8");

		char[] chs = new char[1024];
		int len = 0;
		while ((len = isr.read(chs)) != -1) {
			String s = new String(chs, 0, len);
			System.out.println(s);
			osw.write(chs, 0, len);
		}

		// 释放资源
		isr.close();
		osw.close();

		long end = System.currentTimeMillis();

		System.out.println("复制完成,共耗时" + (end - start) + "毫秒");

		/*
		 * 字符流复制原理: 读取时:将源文件对象先转为字节流,然后再调用字符转换流将字节流按指定编码解码为字符;
		 * 输出时:查找编码表将字符转换为字节,字节写入缓冲区中,将字节通过字节流对象写到指定文件
		 * 
		 * 所以用字符流复制文件时,在读取时一定要按照正确的编码读取,否则会导致读取到和输出后的字符都是乱码;
		 */
	}
	方法二:
	/**
	 * 该方法使用FileReader和FileWriter实现
	 * 
	 * @param source 源位置
	 * @param target 目标位置
	 * @throws IOException IO异常
	 */
	private static void CopyFile2(String source, String target) throws IOException {
		long start = System.currentTimeMillis();
		
		FileReader fr = new FileReader(source);
		
		FileWriter fw = new FileWriter(target);

		char[] chs = new char[1024];
		int len = 0;
		while ((len = fr.read(chs)) != -1) {
			String s = new String(chs, 0, len);
			System.out.println(s);
			fw.write(chs, 0, len);
		}

		// 释放资源
		fr.close();
		fw.close();

		long end = System.currentTimeMillis();

		System.out.println("复制完成,共耗时" + (end - start) + "毫秒");

		// 使用此方法由于默认编码和文件编码不一致时易导致乱码问题
	}
	方法三:
	/**
	 * 该方法使用BufferedReader和FileWriter实现
	 * 
	 * @param source 源位置
	 * @param target 目标位置
	 * @throws IOException IO异常
	 */
	private static void CopyFile3(String source, String target) throws IOException {
		long start = System.currentTimeMillis();
		// 创建高效字符输入流对象
		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(source),"GBK"));
		// 创建高效字符输出流对象
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(target),"UTF-8"));

		char[] chs = new char[1024];
		int len = 0;
		while ((len = br.read(chs)) != -1) {
			String s = new String(chs, 0, len);
			System.out.println(s);
			bw.write(chs, 0, len);
		}

		// 释放资源
		br.close();
		bw.close();

		long end = System.currentTimeMillis();

		System.out.println("复制完成,共耗时" + (end - start) + "毫秒");
	}
	//建议使用高效字符流
3.将集合中的数据写到文件中
	public static void main(String[] args) throws IOException {
		// 创建ArrayList集合
		ArrayList<String> list = new ArrayList<String>();
		// 为集合添加数据
		list.add("data1");
		list.add("data2");
		list.add("data3");

		// 创建文件对象
		File file = new File("d:/test");
		// 如果目录不存在就创建它
		if (!file.exists()) {
			file.mkdirs();
		}

		// 创建字符输出流对象,写入目的地是刚才创建的文件夹下的a.txt文件里
		BufferedWriter bw = new BufferedWriter(new FileWriter(new File(file.getPath() + "\\a.txt")));

		// 遍历集合并将数据写入到文件中去
		for (String s : list) {
			// 一次写一行
			bw.write(s);
			bw.newLine();
			bw.flush();
		}
		bw.close();

		// 再从目的地读取数据到控制台,看是否正确存储
		BufferedReader br = new BufferedReader(new FileReader(new File(file.getPath() + "\\a.txt")));
		// 这里采用一次读一行的犯法
		String line = null;
		while ((line = br.readLine()) != null) {
			System.out.println(line);
		}
		br.close();
	}
4.自定义类模拟BufferedReader的readLine功能
public class MyBufferedReader {
	// 定义字段Reader
	private Reader r;

	// 创建带参构造,使外界传递一个Reader子类对象
	public MyBufferedReader(Reader r) {
		this.r = r;
	}

	// 写一个readLine方法模拟
	/*
	 * 思路: 1.方法返回值是一个字符串 2.传递的Reader对象不一定有readLine方法,所以只能依赖于read方法实现
	 * 3.是一个个读还是一个数组的读,应该是一个个读,因为数组无法确定长度 4.遇到换行符应该结束读取并将数据返回
	 * 5.数据是用字符串还是数组还是缓冲区存储,考虑到多次拼接使用字符串缓冲区
	 */
	public String readLine() throws IOException {
		// 创立字符串缓冲区,存储读取的数据,考虑效率我们使用StringBuilder
		StringBuilder sb = new StringBuilder();

		// 使用传递的Reader对象的read方法
		int ch = 0;
		while ((ch = r.read()) != -1) {
			// \r\n代表换行,由于是一个个读,所以\r时继续,读到\n时停止并将数据返回
			if (ch == '\r') {
				continue;
			}
			if (ch == '\n') {
				return sb.toString();
			} else {
				sb.append((char) ch);
			}
		}
		// 由于最后一行可能没有换行符,这里做一下判断,防止数据丢失
		if (sb.length() > 0) {
			return sb.toString();
		}

		// 没有数据时返回null
		return null;
	}

	public void close() throws IOException {
		r.close();
	}
}
//测试类
public class Test {
	public static void main(String[] args) throws IOException {
		//创建我们的模拟类对象
		MyBufferedReader mb = new MyBufferedReader(new FileReader("1.txt"));
		String line = null;
		while ((line = mb.readLine()) != null) {
			System.out.println(line);
		}	
		mb.close();
	}
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值