浅谈 Android Exception & Throwable 类

一、Throwable 类

概述:java将所有的错误封装为一个对象,其根本父类为Throwable, Throwable有两个子类:Error和Exception。

Error: 这种异常被设计成不被捕获,因为这种异常产生于JVM自身。
Runtime Exception: 运行时异常往往与环境有关,编译时无法检查,并且可能发生的情况太广泛,所以系统会去处理,程序不需要捕获。 

注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

二、异常 类

Exception是Throwable的一个主要子类。Exception下面还有子类,其中一部分子类分别对应于Java程序运行时常常遇到的各种异常的处理,其中包括隐式异常。比如,程序中除数为0引起的错误、数组下标越界错误等,这类异常也称为运行时异常RumtimeException,因为它们虽然是由程序本身引起的异常,但不是程序主动抛出的,而是在程序运行中产生的。Exception子类下面的另一部分子类对应于Java程序中的非运行时异常的处理,这些异常也称为显式异常非RumtimeException。它们都是在程序中用语句抛出、并且也是用语句进行捕获的,比如,文件没找到引起的异常、类没找到引起的异常等。

  •  异常分类
Exception 这种异常分两大类运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。

       运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

      运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
       非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

问题:内存溢出不能捕获的原因:
首先,你得明白,内存溢出不是异常,是Error,Java中的异常分为受查异常和不受查异常。不受查异常时无法获取之后用程序处理的。OutOfMemory是不受查异常,它是由于java虚拟机运行时无法再分配到内存引起的,这时java虚拟机已经无法正常工作了,只能停止,所以就不能再捕获

三、异常的捕获方法——你是否面试躺枪过,老铁?

try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1、在finally语句块中发生了异常。
2、在前面的代码中用了System.exit()退出程序。
3、程序所在的线程死亡。
4、关闭CPU。

面试题: try、catch、finally语句块的执行顺序

1、当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;

2、当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;

3、当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;

try语句中的代码越少越好。在多个catch语句中,需要将父异常放在最后面,否则将不会执行具体异常。异常1、异常2都是平级关系的异常,若含有字符关系将报错。

  • 异常处理的方式 一  捕获异常
try{}...catch(异常类名 变量名){}

try{}...catch(异常类名 变量名){}...catch(异常类名 变量名){}

try{}...catch(异常类名 变量名){}...catch(异常类名 变量名){}...finally{}

try{}...finally{}
  •  异常处理的方式 二  声明异常

      声明异常:在方法的大括号前用throws 异常类名

      当在写方法时,如果觉得会出现异常,且自己没有解决或者想让调用者自己解决时,可以在方法的大括号前用throws异常类         名,将可能会发生的异常声明出去。

public static int method(int a, int b) throws Exception {
	int c = a / b;
	return c;
}
  •   异常处理的方式 三  抛出异常

       抛出异常:是在方法中用 throw 异常对象 

public static int method(int a, int b) {
	if (b==0) {
		throw new ArithmeticException();
	}
	int c = a / b;
	return c;
}

 

  •   异常处理的方式 三   自定义异常

       1、如何自定义一个异常类?

       让自定义类继承RuntimeException 或者继承Exception

       注意:编译时期异常(非RuntimeException),如果在方法内部用throw抛出了一个异常对象那么在方法声明上必须用throws关键        字声明异常,运行时期异常就不需要这样。

public class MyException extends Exception {
	public MyException(String message) {
		super(message);
	}
}

 更多异常处理面试题 可参阅:https://blog.youkuaiyun.com/lgm_hakka/article/details/47438897

 四、常见异常和解决方法

runtimeException子类:

1 .java.lang.NullPointerException

2. java.lang.IllegalArgumentException: n must be positive

3.集合变化 java.util.ConcurrentModificationException, Caused by: java.lang.ArrayIndexOutOfBoundsException

4.动画里面的算术 java.lang.NumberFormatException: Invalid double: "" java.lang.ArithmeticException: divide by zero

5.java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

奇葩的问题:

1.图片。类,找不到,混淆问题:android.content.res.Resources$NotFoundException: File res/drawable-xhdpi-v4/abnormal_purple_beg.png from drawable resource ID #0x7f020008

2.没有方法,可能是API低版本的不支持,java.lang.NoSuchMethodError

3.java.util.concurrent.TimeoutException:

4.java.lang.SecurityException: Not allowed to start activity Intent

5.Caused by: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died 

6.java.lang.RuntimeException: Could not read input channel file descriptors from parcel.

IOException

1.数据库的IO异常   android.database.sqlite.SQLiteDiskIOException:

2.文件没有找到

Error:

Error:内存溢出:java.lang.OutOfMemoryError: Failed to allocate a 1868981625 byte allocation with 991781 free bytes and 509MB until OOM

五、奔溃日志上报  

全局捕获异常并上报:比如第三方的友盟、蒲公英等,腾讯Bugly、听云

UncaughtExceptionHandler:在程序异常的时候,默认是把进程杀死,有们自己先上报然后手动再杀死进程!

在Android开发中,常常会出现uncheched Exception 导致程序的crash,为了提供良好的用户体验,并对出错的信息进行收集,以便对程序进行改进,提高程序的健壮性。

因此,常使用Thread.UncaughtExceptionHandler来进行处理。

/**
 * 奔溃日志收集类
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {

    //系统默认的UncaughtException处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler实例
    private static CrashHandler instance = new CrashHandler();
    //程序的Context对象
    private Context mContext;
    private static final String TAG=CrashHandler.class.getSimpleName();
    /** 崩溃应用包名KEY */
    private final String KEY_PKG = "key_pkg";
    /** APP版本号KEY */
    private final String KEY_VER = "key_ver";
    /** APP渠道号KEY */
    private final String KEY_CXHZCHANNEL = "key_cxhzchannel";
    /** 厂商 */
    private final String KEY_BRAND = "key_brand";
    /** 机型 */
    private final String KEY_MODEL = "key_model";
    /** 系统版本KEY */
    private final String KEY_OS_VER = "key_os_ver";
    /** 崩溃时间KEY */
    private final String KEY_TIME = "key_time";
    //用来存储设备信息和异常信息 系统崩溃日志
    private Map<String, String> infos;
    //用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter;

    /** 保证只有一个CrashHandler实例 */
    private CrashHandler() {
        infos = new HashMap<String, String>();
        formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.CHINA);
    }

    /** 获取CrashHandler实例 ,单例模式 */
    public static CrashHandler getInstance() {
        return instance;
    }

    /**
     * 初始化
     * @param context
     */
    public void init(final Context context) {
        mContext = context;
        //获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
        new Thread(new Runnable() {
            @Override
            public void run() {
                String pkg = CXPackageManagerUtil.getCurProcessName(context);
                for(int i=0;i<3;i++){
                    if(pkg.contains(context.getPackageName())){
                        try {
                            Thread.sleep(1000);
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                        pkg = CXPackageManagerUtil.getCurProcessName(context);
                    }else{
                        break;
                    }
                }
                infos.put(KEY_PKG, pkg);
                if(pkg.contains(context.getPackageName())){
                    infos.put(KEY_VER, String.valueOf(CXPackageManagerUtil.getPackageVersionCode(context)));
                    infos.put(KEY_CXHZCHANNEL, CXPackageManagerUtil.getChannel(context));
                }else{
                    infos.put(KEY_VER, String.valueOf(CXPackageManagerUtil.getVersionCode(mContext, mContext.getPackageName())));
                    infos.put(KEY_CXHZCHANNEL, "");
                }
                infos.put(KEY_BRAND, Build.BRAND);
                infos.put(KEY_MODEL, Build.MODEL);
                infos.put(KEY_OS_VER, Build.VERSION.RELEASE);
                CXLog.i(TAG, infos.toString());
            }
        }).start();
    }

    /**
     * 初始化数据
     * @param pkg
     */
    public void init(String pkg){
        if(pkg != null){
            infos.put(KEY_PKG, pkg);
            infos.put(KEY_VER, String.valueOf(CXPackageManagerUtil.getVersionCode(mContext, mContext.getPackageName())));
            infos.put(KEY_CXHZCHANNEL, CXPackageManagerUtil.getChannel(mContext));
            infos.put(KEY_BRAND, Build.BRAND);
            infos.put(KEY_MODEL, Build.MODEL);
            infos.put(KEY_OS_VER, Build.VERSION.RELEASE);
            CXLog.i(TAG, infos.toString());
        }
    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        boolean handled = false;
        if(ex != null){
            // 上传奔溃日志
            postCrash(getCrashInfo(ex));
            // 保存奔溃日志
            CXLog.e(TAG, infos.toString());
            CXLog.e(TAG, getCrashInfo(ex));
//            writeCrashInfo(ex);
            //使用Toast来显示异常信息
            if(infos.get(KEY_PKG).equals(mContext.getPackageName())){
                new Thread() {
                    @Override
                    public void run() {
                        Looper.prepare();
                        Toast.makeText(mContext, "程序发生异常,正在退出……", Toast.LENGTH_LONG).show();
                        Looper.loop();
                    }
                }.start();
            }
            handled = true;
        }
        return handled;
    }

博文撰写主要参阅:

https://blog.youkuaiyun.com/whb20081815/article/details/76269590

https://blog.youkuaiyun.com/lgm_hakka/article/details/47438897

尊重原创,感谢以上我参阅的两篇博文,此篇博文只为巩固基础知识,特此记录下,如果你觉得本篇博客对你有用,就留个言或者顶一下吧(^-^)!

&lt;think&gt;嗯,用户现在想了解Java中Throwable的子结构以及异常的层次关系。他们之前已经问过似的问题,现在需要更详细的解释。我需要结合之前的回答和提供的引用资料来组织内容。 首先,用户的问题明确提到想知道Throwable的两个直接子,以及整个继承体系和异常分。根据引用[1]和[4],Throwable的直接子是Error和Exception。这点需要明确作为开头。 接下来,要详细展开Error和Exception的区别。Error是系统级错误,比如OutOfMemoryError,这些通常程序无法处理,也不需要捕获。而Exception分为RuntimeException和其他检查异常,比如IOException。这里需要引用资料中的例子,比如引用[4]提到的RuntimeException包括NullPointerException,而IOException下的子。 用户可能还想知道自定义异常的情况,根据引用[2],自定义异常需要继承Exception或其子。这部分可以作为补充内容,说明实际开发中的应用。 另外,用户之前的回答里提到过Error和Exception的区别,这里需要保持一致性,但可能要更详细。比如,在继承体系图中,应该用树状结构展示Throwable的子,然后分别列举Error和Exception下的常见子。 还要注意异常处理中的规则,比如方法覆盖时不能抛出更高层级的异常,这点在引用[3]中提到了,可能需要结合例子说明,比如子方法抛出IOException时,父方法不能抛出更通用的Exception。 用户可能对检查型异常和非检查型异常的区别不太清楚,需要明确说明RuntimeException是未检查的,而其他Exception是必须处理的。引用[4]中的内容可以支持这一点,比如IOException需要强制捕获。 最后,总结部分要简明扼要,强调Throwable的结构和分,以及实际编程中的注意事项。可能还需要回答相关问题,比如自定义异常如何创建,或者Error和Exception的应用场景差异。 需要确保回答结构清晰,分点说明,使用代码示例和表格对比,帮助用户更好地理解。同时,参考之前的回答格式,保持一致性,比如使用标题、代码块和表格来组织内容。避免使用复杂术语,保持解释通俗易懂。&lt;/think&gt;--- ### Java Throwable继承体系及异常分详解 --- #### 一、Throwable的直接子 **Throwable的两个直接子**: 1. **`Error`**:表示系统级错误(JVM或硬件层面的严重问题),程序**无需且无法处理**[^1][^4]。 2. **`Exception`**:表示程序可处理的异常,需通过`try-catch`或`throws`显式处理[^4]。 --- #### 二、Throwable继承体系结构 ```mermaid %% Java异常继承关系示意图 %% graph TD Throwable --&gt; Error Throwable --&gt; Exception Error --&gt; VirtualMachineError Error --&gt; AWTError VirtualMachineError --&gt; OutOfMemoryError VirtualMachineError --&gt; StackOverflowError Exception --&gt; RuntimeException Exception --&gt; IOException RuntimeException --&gt; NullPointerException RuntimeException --&gt; IndexOutOfBoundsException IOException --&gt; FileNotFoundException IOException --&gt; EOFException ``` --- #### 三、异常分与核心特性 ##### 1. **`Error`:系统级错误** - **特点**:由JVM抛出,程序无需捕获,通常无法恢复[^1][^4]。 - **典型子**: - `OutOfMemoryError`:堆内存耗尽 - `StackOverflowError`:栈溢出 - `AWTError`:GUI组件致命错误 ##### 2. **`Exception`:程序级异常** - **分**: - **检查型异常(Checked Exception)**: - 继承自`Exception`但**非**`RuntimeException`(如`IOException`) - **必须处理**:不处理则编译报错 ```java try { FileInputStream fis = new FileInputStream(&quot;test.txt&quot;); // 可能抛出FileNotFoundException } catch (FileNotFoundException e) { System.out.println(&quot;文件未找到&quot;); } ``` - **非检查型异常(Unchecked Exception)**: - 继承自`RuntimeException`(如`NullPointerException`) - **无需强制处理**:由编程错误导致(如空指针、数组越界) ```java String str = null; System.out.println(str.length()); // 运行时抛出NullPointerException ``` ##### 3. **自定义异常** - **规则**:必须继承`Exception`或其子[^2] - **示例**:实现除法运算的自定义异常 ```java class DivideByZeroException extends Exception { public DivideByZeroException(String message) { super(message); // 调用父构造方法 } } public class Calculator { public int divide(int a, int b) throws DivideByZeroException { if (b == 0) { throw new DivideByZeroException(&quot;除数不能为0&quot;); } return a / b; } } ``` --- #### 四、关键规则与编程实践 1. **方法覆盖规则**: - 子方法抛出的异常范围**不能超过父方法**(例如父抛`IOException`,子只能抛`IOException`或其子)[^3] ```java // 父方法 public void readFile() throws IOException { ... } // 子方法(合法) @Override public void readFile() throws FileNotFoundException { ... } // 子方法(非法,编译错误) @Override public void readFile() throws Exception { ... } ``` 2. **异常处理优先级**: ```java try { // 可能抛出多种异常 } catch (FileNotFoundException e) { // 先捕获具体异常 // 处理文件未找到 } catch (IOException e) { // 再捕获通用异常 // 处理IO错误 } catch (Exception e) { // 最后捕获最通用异常 // 兜底处理 } ``` --- ### 总结 | 型 | 特点 | 典型场景 | 处理要求 | |-------------------|--------------------------------|---------------------------|----------------| | **`Error`** | JVM级错误,不可恢复 | 内存溢出、栈溢出 | 无需处理 | | **检查型异常** | 程序可预见的异常 | 文件操作、网络请求 | 必须显式处理 | | **非检查型异常** | 代码逻辑错误导致 | 空指针、数组越界 | 无需强制处理 | --- #### 相关问题 1. **如何选择自定义异常的父(继承`Exception`还是`RuntimeException`)?** &gt; 若需要强制调用方处理异常,继承`Exception`;若为程序逻辑错误,继承`RuntimeException`[^2][^4]。 2. **为什么`Error`不需要捕获?** &gt; `Error`表示系统级不可恢复错误(如内存耗尽),程序无法有效处理,通常直接终止运行[^1][^4]。 3. **检查型异常与非检查型异常在编译时有何区别?** &gt; 检查型异常不处理会导致编译错误,非检查型异常不会影响编译(仅在运行时暴露)[^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值