java常用类解析二
来源:互联网 发布:贵州广电网络通讯录 编辑:程序博客网 时间:2024/05/16 11:22
IO系统File类及文件搜索工具类
1、先看一个File类的简单的例子
- <span style="font-size:16px;">package test;
- import java.io.File;
- import java.io.FilenameFilter;
- import java.util.Arrays;
- import java.util.Scanner;
- import java.util.regex.Pattern;
- /**
- * File代表文件和目录,静态域有:与系统有关的路径分隔符、与系统有关的默认名称分隔符。
- * 主要操作有:创建文件或目录、删除文件、给文件设定属性、返回指定目录下的文件列表、
- * 返回过滤后的文件列表、 检测文件是否存在、是否隐藏、是否是目录还是文件、
- * 返回文件名称和路径
- *
- * @author Touch
- *
- */
- public class FileDemo {
- /*
- * 查找指定路径下的匹配regex的文件
- */
- public String[] find(String path, final String regex) {
- File file = new File(path);
- //匿名内部类
- return file.list(new FilenameFilter() {
- private Pattern pattern = Pattern.compile(regex);
- @Override
- public boolean accept(File dir, String name) {
- // TODO Auto-generated method stub
- return pattern.matcher(name).matches();
- }
- });
- }
- public static void main(String[] args) {
- String path = null;
- String key = null;
- String regex = null;
- int choice = 1;
- Scanner scanner = new Scanner(System.in);
- System.out.println("please input the file path:");
- path = scanner.next();
- System.out.println("please input key:");
- key = scanner.next();
- System.out.println("choise:\n0:匹配以" + key + "为后缀的文件\n1:匹配包含" + key
- + "的文件");
- if ((choice = scanner.nextInt()) == 0)
- regex = ".*\\." + key;
- else
- regex = ".*" + key + ".*";
- String[] list;
- list = new FileDemo().find(path, regex);
- System.out.println(Arrays.deepToString(list));
- //返回指定路径下的目录列表
- File[] fileList = new File(path).listFiles();
- for (File file : fileList) {
- if (file.isDirectory()) {
- list = new FileDemo().find(file.getPath(), regex);
- System.out.println(Arrays.deepToString(list));
- }
- }
- }
- }
- </span>
2、看完这个例子,是不是可以写个工具类呢,用于搜索指定路径下的所有文件或者目录,当然也可以输入正则表达式,这样就可以筛选出我们想要的文件(如有时候我们只需要.java文件或者.txt文件)
- <span style="font-size:16px;">package mine.util;
- import java.io.File;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * FileDirectory类用于查找指定根目录下的所有文件和目录 可以通过正则表达式对要查找的 文件及目录进行筛选
- *
- * @author Touch
- */
- public final class SearchFile {
- // 存放文件
- private List<File> fileList = new ArrayList<File>();
- // 存放目录
- private List<File> directoryList = new ArrayList<File>();
- // 存放文件和目录
- private List<File> list = new ArrayList<File>();
- private File file;// 目录
- private String regex;// 正则表达式
- public SearchFile(String path) {
- file = new File(path);
- this.regex = ".*";
- }
- public SearchFile(File file) {
- this.file = file;
- this.regex = ".*";
- }
- public SearchFile(String path, String regex) {
- file = new File(path);
- this.regex = regex;
- }
- public SearchFile(File file, String regex) {
- this.file = file;
- this.regex = regex;
- }
- // 返回当前目录下的所有文件及子目录
- public List<File> files() {
- File[] files = file.listFiles();
- List<File> list = new ArrayList<File>();
- for (File f : files)
- if (f.getName().matches(regex))
- list.add(f);
- return list;
- }
- // 返回该根目录下的所有文件
- public List<File> allFiles() {
- if (list.isEmpty())
- search(file);
- return fileList;
- }
- // 返回该根目录下的所有子目录
- public List<File> allDirectory() {
- if (list.isEmpty())
- search(file);
- return directoryList;
- }
- // 返回该根目录下的所有文件及子目录
- public List<File> allFilesAndDirectory() {
- if (list.isEmpty())
- search(file);
- return list;
- }
- // 递归搜索当前目录下的所有文件及目录
- private void search(File file) {
- File[] files = file.listFiles();
- if (files == null || files.length == 0)
- return;
- for (File f : files) {
- if (f.getName().matches(regex))
- list.add(f);
- if (f.isFile() && f.getName().matches(regex))
- fileList.add(f);
- else {
- if (f.getName().matches(regex))
- directoryList.add(f);
- search(f);
- }
- }
- }
- }
- </span>
3、测试
- <span style="font-size:16px;">package mine.util;
- import java.io.File;
- import java.util.List;
- public class TestSearchFile {
- public static void main(String[] args) {
- System.out.println("-------- 指定目录中所有文件及子目录-------");
- List<File> list = (List<File>) new SearchFile(
- "G:/java/workspace/test/file").files();
- for (File file : list)
- System.out.println(file.getName());
- System.out.println("--------指定目录中以txt为后缀的文件------");
- list = (List<File>) new SearchFile("G:/java/workspace/test/file",
- ".*\\.txt").files();
- for (File file : list)
- System.out.println(file.getName());
- System.out.println("--------以该目录为根目录的所有文件及子目录--");
- list = (List<File>) new SearchFile("G:/java/workspace/test")
- .allFilesAndDirectory();
- for (File file : list)
- System.out.println(file.getName());
- }
- }
- </span>
4、结果:
-------- 指定目录中所有文件及子目录-------
aa.data
bb.dat
object
test.txt
test1.txt
test2.txt
test3.txt
test4.txt
test5
--------指定目录中以txt为后缀的文件------
test.txt
test1.txt
test2.txt
test3.txt
test4.txt
--------以该目录为根目录的所有文件及子目录--
.classpath
.project
.settings
org.eclipse.jdt.core.prefs
bin
http
PassWord.class
Test.class
mine
util
SearchFile.class
TestSearchFile.class
test
A.class
ArraysDemo.class
B.class
ByteArrayInputStreamDemo.class
DataInputStreamAndByteArrayInputStreamDemo.class
DataInputStreamDemo.class
DeepCloneDemo.class
FileDemo$1.class
FileDemo.class
FileInputStreamDemo.class
IO系统文件读写工具类
几个文件读写的工具类:文本文件读写、二进制文件读写、对象读写。其中对象读写工具类有错误,在试图进行多个对象读取时,读第二个对象就抛出异常,这是为什么?此外怎样把一个存放对象的文件中所有的对象读出来?
这个问题已经解决,非常感谢Aguo的文章:自定义ObjectOutputStream,解决追加写入后,读取错误的问题 。在这篇文章中我找到了答案,同时对作者的源代码添加了一些注解。解决方案请看文章最后。
1、文本文件读写工具类
- <span style="font-size:16px;">package mine.util;
- import java.io.BufferedReader;
- import java.io.BufferedWriter;
- import java.io.FileReader;
- import java.io.FileWriter;
- import java.io.IOException;
- /**
- * 此工具类用于文本文件的读写
- *
- * @author Touch
- */
- public class TextFile {
- // 读取指定路径文本文件
- public static String read(String filePath) {
- StringBuilder str = new StringBuilder();
- BufferedReader in = null;
- try {
- in = new BufferedReader(new FileReader(filePath));
- String s;
- try {
- while ((s = in.readLine()) != null)
- str.append(s + '\n');
- } finally {
- in.close();
- }
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return str.toString();
- }
- // 写入指定的文本文件,append为true表示追加,false表示重头开始写,
- //text是要写入的文本字符串,text为null时直接返回
- public static void write(String filePath, boolean append, String text) {
- if (text == null)
- return;
- try {
- BufferedWriter out = new BufferedWriter(new FileWriter(filePath,
- append));
- try {
- out.write(text);
- } finally {
- out.close();
- }
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- </span>
- <span style="font-size:16px;">package mine.util;
- public class TestTextFile {
- public static void main(String[] args) {
- TextFile.write("file/textfile.txt", false,
- "这是一个文本文件的读写测试\nTouch\n刘海房\n男\n");
- TextFile.write("file/textfile.txt", true, "武汉工业学院\n软件工程");
- System.out.println(TextFile.read("file/textfile.txt"));
- }
- }
- </span>
2、二进制文件读写工具类
- <span style="font-size:16px;">package mine.util;
- import java.io.BufferedInputStream;
- import java.io.BufferedOutputStream;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- /**
- * 此工具类用于二进制文件的读写
- *
- * @author Touch
- */
- public class BinaryFile {
- // 把二进制文件读入字节数组,如果没有内容,字节数组为null
- public static byte[] read(String filePath) {
- byte[] data = null;
- try {
- BufferedInputStream in = new BufferedInputStream(
- new FileInputStream(filePath));
- try {
- data = new byte[in.available()];
- in.read(data);
- } finally {
- in.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return data;
- }
- // 把字节数组为写入二进制文件,数组为null时直接返回
- public static void write(String filePath, byte[] data) {
- if (data == null)
- return;
- try {
- BufferedOutputStream out = new BufferedOutputStream(
- new FileOutputStream(filePath));
- try {
- out.write(data);
- } finally {
- out.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- </span>
- <span style="font-size:16px;">package mine.util;
- import java.util.Arrays;
- public class TestBinaryFile {
- public static void main(String[] args) {
- BinaryFile.write("file/binaryfile.dat", new byte[] { 1, 2, 3, 4, 5, 6,
- 7, 8, 9, 10, 'a', 'b', 'c' });
- byte[] data = BinaryFile.read("file/binaryfile.dat");
- System.out.println(Arrays.toString(data));
- }
- }
- </span>
3、对象读写工具类(有问题,在读取多个对象时有问题,怎样把一个对象文件中的所有对象读出来?)
- <span style="font-size:16px;">package mine.util;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- /**
- * 此类用于对象的读写
- *
- * @author Touch
- */
- public class ObjectFile {
- // 把一个对象写入文件,isAppend为true表示追加方式写,false表示重新写
- public static void write(String filePath, Object o, boolean isAppend) {
- if (o == null)
- return;
- try {
- ObjectOutputStream out = new ObjectOutputStream(
- new FileOutputStream(filePath, isAppend));
- try {
- out.writeObject(o);
- } finally {
- out.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- // 把一个对象数组写入文件,isAppend为true表示追加方式写,false表示重新写
- public static void write(String filePath, Object[] objects, boolean isAppend) {
- if (objects == null)
- return;
- try {
- ObjectOutputStream out = new ObjectOutputStream(
- new FileOutputStream(filePath, isAppend));
- try {
- for (Object o : objects)
- out.writeObject(o);
- } finally {
- out.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- // 读取对象,返回一个对象
- public static Object read(String filePath) {
- Object o = null;
- try {
- ObjectInputStream in = new ObjectInputStream(new FileInputStream(
- filePath));
- try {
- o = in.readObject();
- } finally {
- in.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return o;
- }
- // 读取对象,返回一个对象数组,count表示要读的对象的个数
- public static Object[] read(String filePath, int count) {
- Object[] objects = new Object[count];
- try {
- ObjectInputStream in = new ObjectInputStream(new FileInputStream(
- filePath));
- try {
- for (int i = 0; i < count; i++) {
- //第二次调用in.readObject()就抛出异常,这是为什么?
- objects[i] = in.readObject();
- }
- } finally {
- in.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return objects;
- }
- }
- </span>
- <span style="font-size:16px;">package mine.util;
- import java.io.Serializable;
- public class TestObjectFile {
- public static void main(String[] args) {
- ObjectFile.write("file/object1", new Person(), false);
- ObjectFile.write("file/object1", new Person(), true);
- ObjectFile.write("file/object1", new Person[] { new Person("Touch", 1),
- new Person("Rainbow", 0), new Person() }, true);
- for (Object o : ObjectFile.read("file/object1", 5))
- ((Person) o).display();
- }
- }
- class Person implements Serializable {
- private String name = "刘海房";
- private int sex = 0;
- Person(String name, int sex) {
- this.name = name;
- this.sex = sex;
- }
- Person() {
- }
- void display() {
- System.out.println("my name is :" + name);
- String s = (sex == 0) ? "男" : "女";
- System.out.println("性别:" + s);
- }
- }</span>
4、对象读写工具类(解决了3中的问题,能够写入及读取多个对象)
3中到底问题出在哪呢?先来看一段ObjectOutputStream构造方法的源代码,此源代码来自jdk1.6版。
- <span style="font-size:16px;"> public ObjectOutputStream(OutputStream out) throws IOException {
- verifySubclass();
- bout = new BlockDataOutputStream(out);
- handles = new HandleTable(10, (float) 3.00);
- subs = new ReplaceTable(10, (float) 3.00);
- enableOverride = false;
- </span><span style="font-size:16px;"><span style="color:#ff0000;">writeStreamHeader();
- </span> bout.setBlockDataMode(true);
- if (extendedDebugInfo) {
- debugInfoStack = new DebugTraceInfoStack();
- } else {
- debugInfoStack = null;
- }
- }</span>
这段代码中我们只需要关注writeStreamHeader();方法,这个方法在源代码中的解释是
- <span style="font-size:16px;"> /**
- * The writeStreamHeader method is provided so subclasses can append or
- * prepend their own header to the stream. It writes the magic number and
- * version to the stream.
- *
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */</span>
也就是说我们打开(new)一个ObjectOutputStream的时候,这个ObjectOutputStream流中就已经被写入了一些信息,这些信息会写入到我们的文件中。在第一次写入文件时,这些头部信息时需要的,因为ObjectInputStream读的时候会帮我们过滤掉。但是当我们追加写入一个文件时,这些头部信息也会写入文件中,读取的时候只会把文件第一次出现的头部信息过滤掉,并不会把文件中间的头部信息也过滤掉,这就是问题的根源所在。
怎么解决呢?正如lichong_87提到的
一、可以在每次写入的时候把文件中所有对象读出来,然后重新写入,这种方法效率比较低。
二、如果不是第一次写入文件,在写入时去掉头部信息,怎么去掉呢?头部信息是在writeStreamHeader();方法中写入的,所以我们可以通过继承ObjectOutputStream来覆盖这个方法,如果不是第一次写入文件,这个方法什么也不做。
下面是第二种解决方案的源代码及测试
- <span style="font-size:16px;">package mine.util.io;
- import java.io.File;
- import java.io.IOException;
- import java.io.ObjectOutputStream;
- import java.io.OutputStream;
- /**
- * 此类继承ObjectOutputStream,重写writeStreamHeader()方法,以实现追加写入时去掉头部信息
- */
- public class MyObjectOutputStream extends ObjectOutputStream {
- private static File f;
- // writeStreamHeader()方法是在ObjectOutputStream的构造方法里调用的
- // 由于覆盖后的writeStreamHeader()方法用到了f。如果直接用此构造方法创建
- // 一个MyObjectOutputStream对象,那么writeStreamHeader()中的f是空指针
- // 因为f还没有初始化。所以这里采用单态模式
- private MyObjectOutputStream(OutputStream out, File f) throws IOException,
- SecurityException {
- super(out);
- }
- // 返回一个MyObjectOutputStream对象,这里保证了new MyObjectOutputStream(out, f)
- // 之前f已经指向一个File对象
- public static MyObjectOutputStream newInstance(File file, OutputStream out)
- throws IOException {
- f = file;// 本方法最重要的地方:构建文件对象,两个引用指向同一个文件对象
- return new MyObjectOutputStream(out, f);
- }
- @Override
- protected void writeStreamHeader() throws IOException {
- // 文件不存在或文件为空,此时是第一次写入文件,所以要把头部信息写入。
- if (!f.exists() || (f.exists() && f.length() == 0)) {
- super.writeStreamHeader();
- } else {
- // 不需要做任何事情
- }
- }
- }
- </span>
- <span style="font-size:16px;">package mine.util.io;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- /**
- * 此类用于对象的读写
- *
- * @author Touch
- */
- public class ObjectFile {
- // 把一个对象写入文件,isAppend为true表示追加方式写,false表示重新写
- public static void write(String filePath, Object o, boolean isAppend) {
- if (o == null)
- return;
- try {
- File f = new File(filePath);
- MyObjectOutputStream out = MyObjectOutputStream.newInstance(f,
- new FileOutputStream(f, isAppend));
- try {
- out.writeObject(o);
- } finally {
- out.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- // 把一个对象数组写入文件,isAppend为true表示追加方式写,false表示重新写
- public static void write(String filePath, Object[] objects, boolean isAppend) {
- if (objects == null)
- return;
- try {
- File f = new File(filePath);
- MyObjectOutputStream out = MyObjectOutputStream.newInstance(f,
- new FileOutputStream(f, isAppend));
- try {
- for (Object o : objects)
- out.writeObject(o);
- } finally {
- out.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- // 读取对象,返回一个对象
- public static Object read(String filePath) {
- Object o = null;
- try {
- ObjectInputStream in = new ObjectInputStream(new FileInputStream(
- filePath));
- try {
- o = in.readObject();
- } finally {
- in.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return o;
- }
- // 读取对象,返回一个对象数组,count表示要读的对象的个数
- public static Object[] read(String filePath, int count) {
- Object[] objects = new Object[count];
- try {
- ObjectInputStream in = new ObjectInputStream(new FileInputStream(
- filePath));
- try {
- for (int i = 0; i < count; i++) {
- objects[i] = in.readObject();
- }
- } finally {
- in.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return objects;
- }
- }
- </span>
- <span style="font-size:16px;">package mine.util.io;
- import java.io.Serializable;
- public class TestObjectFile {
- public static void main(String[] args) {
- ObjectFile.write("file/t.dat", new Person(), false);
- ObjectFile.write("file/t.dat", new Person(), true);
- ObjectFile.write("file/t.dat", new Person[] { new Person("Touch", 1),
- new Person("Rainbow", 0), new Person() }, true);
- for (Object o : ObjectFile.read("file/t.dat", 5))
- ((Person) o).display();
- }
- }
- class Person implements Serializable {
- private static final long serialVersionUID = 1L;
- private String name = "刘海房";
- private int sex = 0;
- Person(String name, int sex) {
- this.name = name;
- this.sex = sex;
- }
- Person() {
- }
- void display() {
- System.out.println("my name is :" + name);
- String s = (sex == 0) ? "男" : "女";
- System.out.println("性别:" + s);
- }
- }</span>
运行结果:
my name is :刘海房
性别:男
my name is :刘海房
性别:男
my name is :Touch
性别:女
my name is :Rainbow
性别:男
my name is :刘海房
性别:男
java异常机制、异常栈、异常处理方式、异常链、异常丢失
1、java标准异常概述
Throwable表示任何可以作为异常被抛出的类,有两个子类Error和Exception。从这两个类的源代码中可以看出,这两个类并没有添加新的方法,Throwable提供了所以方法的实现。Error表示编译时和系统错误。Exception是可以被抛出的异常类。RuntimeException继承自Exception(如NullPointerException),表示运行时异常,JVM会自动抛出.
2、自定义异常类
自定义异常类方法: 通过继承Throwable或Exception。异常类的所有实现都是基类Throwable实现的,所以构造自定义异常类完全可以参考Exception和Error类。我们只要添加上自定义异常类的构造方法就可以了
- <span style="font-size:16px;">package demo.others;
- /**
- * 自定义异常类方法
- * 1、通过继承Throwable
- * 2、通过继承Exception
- *
- * @author Touch
- */
- public class MyExceptionDemo extends Exception {
- private static final long serialVersionUID = 1L;
- public MyExceptionDemo() {
- super();
- }
- public MyExceptionDemo(String message) {
- super(message);
- }
- public MyExceptionDemo(String message, Throwable cause) {
- super(message, cause);
- }
- public MyExceptionDemo(Throwable cause) {
- super(cause);
- }
- }
- </span>
3、异常栈及异常处理方式
可以通过try、catch来捕获异常。捕获到的异常。下面的示例演示了几种常用异常处理方式
- <span style="font-size:16px;">package demo.others;
- import mine.util.exception.MyException;
- public class ExceptionDemo1 {
- public void f() throws MyException {
- throw new MyException("自定义异常");
- }
- public void g() throws MyException {
- f();
- }
- public void h() throws MyException {
- try {
- g();
- } catch (MyException e) {
- //1、通过获取栈轨迹中的元素数组来显示异常抛出的轨迹
- for (StackTraceElement ste : e.getStackTrace())
- System.out.println(ste.getMethodName());
- //2、直接将异常栈信息输出至标准错误流或标准输出流
- e.printStackTrace();//输出到标准错误流
- e.printStackTrace(System.err);
- e.printStackTrace(System.out);
- //3、将异常信息输出到文件中
- //e.printStackTrace(new PrintStream("file/exception.txt"));
- //4、重新抛出异常,如果直接抛出那么栈路径是完整的,如果用fillInStackTrace()
- //那么将会从这个方法(当前是h()方法)作为异常发生的原点。
- //throw e;
- throw (MyException)e.fillInStackTrace();
- }
- }
- public static void main(String[] args) {
- try {
- new ExceptionDemo1().h();
- } catch (MyException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- </span>
运行结果:
f
g
h
main
mine.util.exception.MyException: 自定义异常
at demo.others.ExceptionDemo1.f(ExceptionDemo1.java:7)
at demo.others.ExceptionDemo1.g(ExceptionDemo1.java:11)
at demo.others.ExceptionDemo1.h(ExceptionDemo1.java:16)
at demo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)
mine.util.exception.MyException: 自定义异常
at demo.others.ExceptionDemo1.f(ExceptionDemo1.java:7)
at demo.others.ExceptionDemo1.g(ExceptionDemo1.java:11)
at demo.others.ExceptionDemo1.h(ExceptionDemo1.java:16)
at demo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)
mine.util.exception.MyException: 自定义异常
at demo.others.ExceptionDemo1.f(ExceptionDemo1.java:7)
at demo.others.ExceptionDemo1.g(ExceptionDemo1.java:11)
at demo.others.ExceptionDemo1.h(ExceptionDemo1.java:16)
at demo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)
mine.util.exception.MyException: 自定义异常
at demo.others.ExceptionDemo1.h(ExceptionDemo1.java:30)
at demo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)
分析上面的程序,首先main函数被调用,然后是调用h函数,再g函数、f函数,f函数抛出异常,并在h函数捕获,这时将依次从栈顶到栈底输出异常栈路径。
4、异常链
有时候我们会捕获一个异常后在抛出另一个异常,如下代码所示:
- <span style="font-size:16px;">package demo.others;
- import java.io.IOException;
- import mine.util.exception.MyException;
- public class ExceptionDemo2 {
- public void f() throws MyException {
- throw new MyException("自定义异常");
- }
- public void g() throws Exception {
- try {
- f();
- } catch (MyException e) {
- e.printStackTrace();
- throw new Exception("重新抛出的异常1");
- }
- }
- public void h() throws IOException {
- try {
- g();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- throw new IOException("重新抛出异常2");
- }
- }
- public static void main(String[] args) {
- try {
- new ExceptionDemo2().h();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- </span>
运行结果:
mine.util.exception.MyException: 自定义异常
at demo.others.ExceptionDemo2.f(ExceptionDemo2.java:9)
at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:14)
at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:32)
java.lang.Exception: 重新抛出的异常1
at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:17)
at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:32)
java.io.IOException: 重新抛出异常2
at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:27)
at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:32)
从结果中我们可以看出,异常栈变小了。也就是说丢失了最原始的异常信息。怎样保存最原始的异常信息呢?Throwable类中有个Throwable cause属性,表示原始异常。通过接收cause参数的构造器可以把原始异常传递给新异常,或者通过initCause()方法。如下示例:
- <span style="font-size:16px;">package demo.others;
- import java.io.IOException;
- import mine.util.exception.MyException;
- public class ExceptionDemo2 {
- public void f() throws MyException {
- throw new MyException("自定义异常");
- }
- public void g() throws Exception {
- try {
- f();
- } catch (MyException e) {
- e.printStackTrace();
- throw new Exception("重新抛出的异常1",e);
- }
- }
- public void h() throws IOException {
- try {
- g();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- IOException io=new IOException("重新抛出异常2");
- io.initCause(e);
- throw io;
- }
- }
- public static void main(String[] args) {
- try {
- new ExceptionDemo2().h();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- </span>
结果:
mine.util.exception.MyException: 自定义异常
at demo.others.ExceptionDemo2.f(ExceptionDemo2.java:9)
at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:14)
at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:34)
java.lang.Exception: 重新抛出的异常1
at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:17)
at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:34)
Caused by: mine.util.exception.MyException: 自定义异常
at demo.others.ExceptionDemo2.f(ExceptionDemo2.java:9)
at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:14)
... 2 more
java.io.IOException: 重新抛出异常2
at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:27)
at demo.others.ExceptionDemo2.main(ExceptionDemo2.java:34)
Caused by: java.lang.Exception: 重新抛出的异常1
at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:17)
at demo.others.ExceptionDemo2.h(ExceptionDemo2.java:23)
... 1 more
Caused by: mine.util.exception.MyException: 自定义异常
at demo.others.ExceptionDemo2.f(ExceptionDemo2.java:9)
at demo.others.ExceptionDemo2.g(ExceptionDemo2.java:14)
... 2 more
从结果中看出当获取到“重新抛出异常2的时候,同时可以输出原始异常“重新抛出的异常1“和原始异常”自定义异常,这就是异常链。
5、finally的使用
finally子句总是执行的,通常用来做一些清理工作,如关闭文件,关闭连接等
下面举几个finally的例子:
- <span style="font-size:16px;">// 读取指定路径文本文件
- public static String read(String filePath) {
- StringBuilder str = new StringBuilder();
- BufferedReader in = null;
- try {
- in = new BufferedReader(new FileReader(filePath));
- String s;
- try {
- while ((s = in.readLine()) != null)
- str.append(s + '\n');
- } finally {
- in.close();
- }
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return str.toString();
- }</span>
分析:如果调用in = new BufferedReader(new FileReader(filePath));时发生异常,这时是一个文件路径不存在的异常,也就是说并没有打开文件,这时将会直接跳到catch块,而不会执行try...finally块(并不是finally子句)里面的语句in.close();此时不需要关闭文件。
再看一个例子,会导致异常的丢失
- <span style="font-size:16px;">package demo.others;
- import mine.util.exception.MyException;
- public class ExceptionDemo3 {
- public void f() throws MyException {
- throw new MyException("异常1");
- }
- public void g() throws MyException {
- throw new MyException("异常2");
- }
- public static void main(String[] args) {
- try {
- ExceptionDemo3 ex = new ExceptionDemo3();
- try {
- ex.f();
- } finally {
- ex.g();//此时捕获g方法抛出的异常,f方法抛出的异常丢失了
- }
- } catch (MyException e) {
- System.out.println(e);
- }
- }
- }
- </span>
结果:mine.util.exception.MyException: 异常2
此时异常1就丢失了
或者这样写:
- <span style="font-size:16px;">package demo.others;
- import mine.util.exception.MyException;
- public class ExceptionDemo3 {
- public void g() throws MyException {
- throw new MyException("异常2");
- }
- public static void main(String[] args) {
- ExceptionDemo3 ex = new ExceptionDemo3();
- try {
- ex.g();
- } finally {
- //直接return会丢失所以抛出的异常
- return;
- }
- }
- }
- </span>
6、异常的限制
(1)当覆盖方法时,只能抛出在基类方法的异常说明里列出的那些异常,有些基类的方法声明抛出异常其实并没有抛出异常,这是因为可能在其子类的覆盖方法中会抛出异常
(2)构造器可以抛出任何异常而不必理会基类构造器所抛出的异常,派生类构造器异常说明必须包含基类构造器异常说明,因为构造派生类对象时会调用基类构造器。此外,派生类构造器不能捕获基类构造器抛出的异常。
- java常用类解析二
- java常用类解析二:IO系统输入输出类
- java常用类解析二:IO系统输入输出类
- java常用类解析
- Java常用类二
- JAVA常用类解析-String
- java常用类解析一
- java常用类解析三
- Java笔记二:Java常用系统类
- Java常用类库(二)
- Java常用类库(二)
- JAVA常用类库(二)
- 第八天-Java常用类之二
- Java 常用类库(二)
- Java常用类库(二)
- java常用类解析八:java字符串与格式化输出
- java常用类解析八:java字符串与格式化输出
- html常用解析工具(java)
- NO.3 C++中的signed和unsigned
- 一个应届毕业生程序员的独白
- uva 699 The Falling Leaves(建树)
- shared pool 共享池
- 数据挖掘 工作
- java常用类解析二
- intel dpdk rte_eal_cpu_init() 函数介绍
- vimrc文件详解
- java常用类解析三
- 基本实体--弱化到下游实体上 ;;; 上游的关系变成map set可以控制值,变成List 也可以,可控性更高
- android 五大布局
- poj 2728 Desert King(最优比率生成树)
- Linux man
- 【内网不正常引起死机解决办法】