1. 异常的概念
1.1什么是异常
异常指的是程序运行时出现的非正常情况.
1.2异常的层次
Java的异常类是处理运行时的特殊类,每一种异常对应一种特定的运行错误.所有Java异常类都是系统类库中Exception类的子类,其继承结构如图:
Throwable类为该处理层次的最高层,其中定义了一个异常描述串,以及获取该描述的getMessage()方法.
对于严重的:Error类时JVM系统内部错误,程序中不能对其编程处理.
//对Error一般不编写针对性的代码对其进行处理
对于不严重的:Exception类时程序代码中要处理的异常,这类异常发生可能与程序运行时的数据有关.
//可进行相应处理
1.3系统定义的异常
Exception类有若干子类,每个子类代表一种特定的运行错误,这些子类有的是系统事先定义好并包含在Java类库中的,成为系统定义的运行异常,如下表:
2. 异常的处理
2.1 try...catch..finally结构
①try
{语句块;}
②catch (异常类名 参变量名) //注意如果没有catch就代表异常没被处理过,如果该异常是检测时异常,则必须声明.
{语句块;}
③finally //定义一定执行的代码:通常用于关闭资源
{语句块;}
1,2 1,3, 1,2,3 三种方式都是合法语句.
(1)一个try可以引导多个catch块。但是不要定义多余的catch块,多个catch块的异常出现继承关系,父类异常catch块放在最后面。
(2)异常发生后,try块中的剩余语句将不再执行。
(3)catch块中的代码要执行的条件是,首先在try块中发生了异常,其次异常的类型与catch要捕捉的一致。 建议声明更为具体的异常,这样处理的可以更具体。
(4)可以无finally部分,但如果存在,则无论异常发生否,finally部分的语句均要执行。即便是try或catch块中含有退出方法的语句return,也不能阻止finally代码块的执行;除非执行System.exit(0)等导致程序停止运行的语句。
3. 自定义异常(编译时检测的异常 和 不被检测的异常[RuntimeException以及子类])
3.1 自定义异常类设计
创建用户自定义异常一般是通过继承Exception来实现。在自定义异常中,一般包括异常标识,构造方法和toString()方法.
3.2 抛出异常
通过throw语句抛出,异常的本质是对象,因为throw关键词后跟的是new运算符来创建的一个异常对象.
3.3 方法的异常声明
如果一个方法有异常抛出,有两种选择:
[1]在方法内对异常进行捕获处理
[2]在方法内不处理异常,而是交给外部调用程序,这是要在方法头使用throws子句列车该方法可能产生的异常.
3.4 继承Exception原因:
异常体系有一个特点:因为异常类和异常对象都可被抛出.他们都具备可抛性,这是Throwable体系中独有特点.只有这个体系中的类和对象才可以被throws和throw操作.
3.5 异常在子类覆盖中的体现:
1.子类覆盖父类时,如果父类的方法抛出的异常,那么子类只能抛出父类异常或该异常的子类.
2.如果父类方法抛出多个异常,那么子类在覆盖方法时,只能抛出父类异常的子集.
3.如果父类或借口的方法中没有异常抛出,那么子类在覆盖方法时,也不可能抛出异常.如果子类方法发生异常,就必须进行try处理,绝对不能抛.
4. 线程的概念
4.1多进程与多线程
进程:一个正在执行的程序.每个进程执行都有一个执行顺序,该顺序是一个执行路径,或叫一个控制单元.
多进程的缺点:
进程切换开销大;
进程间的通信很不方便。
多线程: 指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务,线程切换的开销小 。
4.2线程的状态
Java语言使用Thread类及其子类的对象来表示线程,新建的线程在它的一个完整的生命周期通常要经历5种状态.
4.3线程调度与优先级
Java采用抢占式调度策略,下面几种情况下,当前线程会放弃CPU:
(1)当前时间片用完;
(2)线程在执行时调用了yield() 或sleep() 方法主动放弃;
(3)进行I/O 访问,等待用户输入,导致线程阻塞;或者为等候一个条件变量,线程调用wait()方法;
(4)有高优先级的线程参与调度。
线程的优先级用数字来表示,范围从1~10。主线程的默认优先级为5
Thread.MIN_PRIORITY=1
Thread.NORM_PRIORITY=5
Thread.MAX_PRIORITY=10
5. 多线程编程方法
5.1 Thread类简介
Thread类综合了Java程序中一个线程需要拥有的属性和方法,其构造方法如下:
public Thread (ThreadGroup group,Runnable target,String name);
public Thread();
public Thread(Runnable target);
public Thread(Runnable target,String name);
public Thread(String name);
public Thread(ThreadGroup group,Runnable target);
public Thread(ThreadGroup group,String name);
Thread类的主要方法以及功能如表
5.2 继承Thread类实现多线程
需要重写run方法实现线程的任务.需要注意的是,程序中不要直接调用此方法,而是调用线程对象的start()方法启动线程,让其进入可调度状态,线程获得调度自动执行run()方法.
5.3 实现Runnable接口编写多线程
通过 Thread 类的构造函数public Thread(Runnable target)可以将一个Runnable 接口对象传递给线程,线程在调度时将自动调用Runnable 接口对象的run方法。
6. 线程资源的同步处理
6.1 临界资源问题
Java对于多线程的安全问题提供了专业的解决方式: 就是同步代码块
synchronized(对象)
{ 需要被同步的代码 }
对象如同锁,持有锁的线程可以再同步中执行.
没有持有锁的线程及时获取cpu的执行权,也进不去,因为没有获取锁.
同步的前提:
1.必须要有两个或者以上的线程
2.必须要多个线程使用同一个锁
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源.
同步函数用的是哪个锁:
函数需要被对象调用,那么函数都有一个所属对象的引用.就是this.
所以同步函数使用的锁是this锁.静态函数使用的是该方法所在类的字节码文件对象.
6.2 wait()和notify()
6.3 避免死锁
多个线程相互等待对方释放持有的锁,并且在得到对方锁之前不释放自己的锁.(自己能写一个死锁代码)
7. 线程间通信
思考1: wait(),notify(),nofifyAll()用来操作线程为什么定义在Object类中?
1.这些方法存在同步中,用来操作同步中的线程,必须要标志它们所操作线程的只有锁.
2.使用这些方法必须标识出所属同步的锁,只有同一个锁上被等待线程,可以被同一个锁上notify唤醒.不可以对不同锁中的线程进行唤醒.
3.锁可以是任意对象,所以任意对象调用的方法一定定义Object类中.
思考2:wait(),sleep()有什么区别?
wait():释放资源,释放锁
sleep():释放资源,不释放锁
生产消费者问题
生产消费者问题--JDK1.5提供的多线程升级解决方法
将同步synchronized替换成Lock操作.
将Object中的wait,notify,notifyAll方法替换成Condition对象.
该对象可以Lock锁进行获取.
停止线程
1.定义循环结束标记
因为线程运行代码一般都是循环,只是控制了循环即可.
2.使用interrupt(中断)方法: 该方法是结束线程的冻结状态,使线程回到运行状态中来.这样就可以操作标记结束.
(注)stop方法已过时不在再使用.
setDeamon()方法
标记为后台线程, 当所有前台前程结束后会自动结束.
join()方法
等待该线程终止, 其实是抢夺CPU执行权.
当A线程执行到B线程的.join()方法, A就会等待. 等B线程都执行完, A才会执行. join可以用来临时加入线程执行.
toString()
返回该线程的字符串表示形式,包括线程名称,优先级和线程组
yield()
暂停当前执行的线程对象,并执行其他线程