A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小石姐姐 于 2018-1-5 09:29 编辑

javaEE第二阶段 - 异常、多线程笔记节选


Exception 异常
异常
* 概述
    * 写代码时出现的编译或运行时的异常
    * 异常包含了错误的类型、原因及位置
* 体系结构
    * Throwable(最顶层)
        * Error:出现的不能够处理的严重问题
        * Exception:可以处理的问题 -- 编译时异常
            * RuntimeException -- 运行时异常
* 常见异常:
    * `NullPointerException`
    * `ArrayIndexOutOfBoundException`
    * 任何数字/10都会抛出异常

异常处理
* jvm处理的方式
    * 如果异常我们没有处理,jvm会帮我们处理,他会把异常类型,原因和位置显示在命令行
* 手动异常处理方式
    * **try...catch语句**
        * try{
        * 有可能出现问题的代码;
        }catch(异常对象){
            处理异常;
        }
```   
    try...catch语句的执行顺序
    首先执行try语句
            异常下面的代码不执行,直接跳入catch语句中,catch语句结束后,try...catch结束
```
* 异常的处理方式: 在方法声明上抛出

* 声明方法会抛出异常
    * 使用`throws`关键字(记住这个带s)
    * 定义位置: 方法形参后, 如果抛出多个异常, 可以用`,`逗号分隔
    * 作用: 不捕获异常, 当发生异常时向方法外界抛出, 直到最顶层的方法, 最终终止程序
    * 使用场景: 一般在该方法不能处理异常, 或希望外界处理异常时使用


* 注意:
    * 定义在方法上的抛出异常属于**编译时异常**, 编写代码调用该方法时IDE会提示去处理异常

* 抛出异常的定义格式
* 多异常处理
    * 多异常的捕获:
        * 可以写多个catch代码块来分别处理同一个try代码块中的多个异常
        * 可以使用多个try...catch语句
        * 使用一个try和多个catch
            * 多个catch之间的顺序,可以有子父类关系,如果被捕获的异常类没有继承关系, 则catch的顺序可以随意定义
            * 平级之间没有顺序
            * 如果有子父类,父类异常必须放在后面
* 多个catch代码块的执行逻辑
    * try代码块中抛出的异常, 如果catch中有对应的具体异常, 则执行对应的catch代码块. 其他catch代码块不会执行(类似于if...else if, switch...case)
    * try代码块中抛出的异常, 如果catch中没有对应的具体异常, 则所有catch代码块都不会执行. 相当于异常没有捕获, 仍然会导致程序终止

Throwable常用方法
* Throwable
    * String getMessage() --- 原因 - 异常的信息. 没有原因返回null
    * String toString() --- 类型+原因 - 异常的类型和原因
    * void printStackTrace() --- 类型+原因和位置 - 使用标准错误输出流打印异常信息
    *
##### finally
* finally - 组合try...语句
    * 格式
```
try{
         有可能出现问题的代码;
        }catch(异常对象){
            处理异常;
        }finally{   
            释放资源;
            清理垃圾
}
```
异常的分类
* 运行时期的异常
    * RuntimeException的子类就是运行时期的异常,在编译时起,可选择处理
* 编译时期的异常
    * 是Exception的子类,非RuntimeException子类
    * 编译时必须处理
        * 抛出
        * try...catch

```
throws:处理异常的方式,把异常抛出
throw:制造异常
```
**注意** 如果抛出编译时期异常,必须抛出异常

如何定义一个异常类
* 自定义异常
    * 自定义编译时异常类:
        * 定义一个类, 继承`Exception`或其子类(非RuntimeException), 重写构造方法
    * 自定义运行时异常类:
        * 定义一个类, 继承`RuntimeException`或其子类, 重写构造方法
* 使用自定义异常的方式:
    * 使用编译时异常:
        * 在方法声明上, 使用`throws`抛出`Exception`或其子类的类型
    * 使用运行时异常:
        * 在方法内部的代码中, 使用`throw`(注意没有s)抛出`RuntimeException`或其子类的**对象**
* throws和throw的区别:
    * `throws`: 用于抛出编译时异常, 在方法声明上使用
    * `throw`: 用于主动抛出运行时异常, 在方法中使用

多线程

线程与进程
* 进程 - Process
    * 当前正在运行的程序,一个应用程序在内存中的执行区域
* 线程 - Thread
    * 线程依赖于进程
    * 是进程中的一个执行控制单元,执行路径

* 一个进程可以有多个线程,也可以有多个线程
##### 多线程和多线程特点
* 单线程
    * 安全性高,效率低。
* 多线程
    * 安全性低,效率高。   

多线程的实现方式

* Thread
    * 方法
        * String getName() - 返回该线程的明知
        * void SetName(String name) - 改变线程的明知

```
CPU执行程序是随机的
```
    * 第一种方法
        * 是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。
    * 第二种方法
        * 是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动
   
创建线程的第一种方式: 继承Thread类

* `java.lang.Thread`类: 实现了`Runnable`接口
    * 构造方法
        * `Thread Thread()`: 创建Thead对象
        * `Thread Thread(Runnable r)`: 通过Runnable对象创建Thread对象
        * `Thread Thread(Runnable r, String threadName)`: 通过Runnable对象创建Thread对象并指定线程名
    * 成员方法
        * `void start()`: 启动线程, 即让线程开始执行`run()`方法中的代码
        * `String getName()`: 获取线程的名称
        * `void setName(String name)`: 设置线程名称
    * 静态方法
        * `static Thread currentThread()`: 返回对当前正在执行的线程对象的引用
        * `static void sleep(long millis)`: 让所在线程睡眠指定的毫秒

main-主方法是单线程
* `main()`方法
    * 也是执行在一个线程中的, 该线程的名称为`main`, 一般称为主线程, 主线程和其他线程没什么区别
    * 所以main()方法是单线程的, 方法中的代码是一行一行向下执行的


创建线程的第二种方式: 实现Runnable接口

* `java.lang.Runnable`接口: 可将其对象看作是子线程要运行的任务
    * 抽象方法:
        * `void run()`
* `Runnable`接口出现的原因
    * 因为Java只支持单继承, 如果继承了`Thread`类, 就无法继承其他类, 局限性太大, 所以使用实现`Runnable`接口的方式, 既能继承其他类, 还能实现多个接口, 不会影响类的扩展性

多线程中的线程安全问题: 使用同步代码块解决共享资源的问题

* 同步:
    * 表示多个线程在执行同步的代码时, 这些线程只能按顺序一个一个去执行
    * 优缺点
        * 同步安全性高, 效率低
        * 不同步安全性低, 效率高
    * 使用`synchronized`关键字:
        * 同步代码块: 使用`synchronized`修饰的代码块
            * 作用:
                * 被同步代码块包裹的代码相当于在一个房间内, 锁对象相当于房间的锁
                * 一个线程进入同步代码块, 会把门锁上, 同步代码块外面的线程看到同步代码块内部有线程, 只能在外面等待
                * 直到同步代码块内部的线程执行完毕从代码块中出来, 释放了锁, 外面等待的线程才能随机进去一个
            * 应用位置:
                * 哪些代码属于共享操作, 需要避免安全问题, 就将这些代码放入同步代码块
            * 术语
                * 线程进入同步代码块, 叫`获取锁`
                * 线程出同步代码块, 叫`释放锁`
        * 同步方法: 使用`synchronized`修饰的方法(下面讲)
* 锁对象
    * 锁是一个**任意类型**的对象
        * Object对象可以, String对象可以, Student对象等也可以...
    * 锁对象**必须是被所有线程共享的**, 也就是说多个线程都共用一个锁
        * 如使用同一个Runnable对象中的成员变量
        * 或使用某个类的静态变量
        * 或使用某个类的字节码对象
* 同步必须配合锁对象来使用

```java
// 同步代码块格式

synchronized (锁对象) {
    // 要同步执行的代码
}
```
* **出现多线程的安全问题, 检查一下2个方面**
    1. 对于操作了了共享数据的代码是否是同步的?
    2. 同步使用的是否是同一个锁对象?
多线程中的线程安全问题: 使用同步方法解决
* 同步方法:
    * `synchronized`修饰的方法
    * 作用:
        * 该方法在同一时间只能被一个线程执行, 多个线程需要按顺序一个一个调用方法
    * 同步方法的锁对象:
        * 同步方法也有锁对象
        * 静态同步方法:
            * 是方法所在类的`Class`对象, 也就是该类的字节码对象
        * 非静态同步方法:
            * 是`this`, 也就是该方法所在类的对象

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马