异常的基本概念
1) 异常事件改变程序流程;
2)
当一个异常事件发生时,一个异常被抛出;
3) 响应处理异常的代码被称为exception
handler;
4) exception handler捕获异常;
5)
异常处理能让你集中精力在一个地方解决问题,然后将处理错误的代码分开来放在另一个地方。
捕获异常
1) 设置一个try/catch的代码块;
2)
如果try块内的任何代码抛出了由catch子句指定的异常,则
a.
程序跳过try块中的其他代码;
b.
程序执行catch从句中的处理器代码。
3)
如try块内没有抛出异常,直接跳过catch从句内容。
4)
如try块内抛出的异常没有在catch从句中指定,
则该方法会立即退出。
处理异常
1.如何控制try的范围:根据操作的连动性和相关性,如果前面的程序代码块抛出的错误影响了后面程序代码的运行,那么这个我们就说这两个程序代码存在关联,应该放在同一个try中。
对已经查出来的例外,有throw(消极)和try
catch(积极)两种处理方法。
对于try
catch放在能够很好地处理例外的位置(即放在具备对例外进行处理的能力的位置)。如果没有处理能力就继续上抛。
当我们自己定义一个例外类的时候必须使其继承excepiton或者RuntimeException。
3)
对子类方法抛出的异常不能超出父类方法throws指令的范围。如父类方法不抛出任何异常,在子类方法中必须捕捉每一个“已检查异常”。
捕捉多个异常
1)
每个异常类型使用一个catch从句;
2)
如前面catch从句捕获异常,将直接跳过后面catch从句内容;
3)
建议按异常类型的子类->超类的顺序排列catch从句的先后顺序。
finally 声明
无论是否捕获异常,都会执行finally从句中的代码;
例子:
finally{
con.close();}
异常调用栈
1.
哪个方法调用的代码发生异常,返回到调用方法的地方;
2.
main方法调用的代码发生异常,返回到虚拟机。
异常层次
答:1) 起源于Error的类代表不常用的环境(通常是硬件层面);
2)
应用程序不能够从Error中恢复正常;
3)
所有的Java异常都起源于Exception;
4)
RuntimeExcepiton也称为未检查异常;
5) 未检查异常无须捕获;
6)
其它异常,也称为检查异常,必须处理
Object
↑
Throwable
↑
|ˉˉˉˉˉˉˉˉ|
Error Exception
|
↑
| |ˉˉˉˉˉˉˉ|
RuntimeException
一些未检查的异常
答:1) java.lang.ArithmeticException 如:除0;
2) java.lang.NullPointerException
如:没初始化一个References便使用;
3)
java.lang.ArrayIndexoutofBoundsException
如:调用一个有十个元素的Array的第十一个元素的内容;
4) java.lang.NumberFORMatException
如:Integer.parseInt("a");
写你自己的异常
答:1)
要做的仅仅是从Exception继承或者是从Exception的一个子类衍生出自己需要的类就可;
2)
习惯为每一个异常类提供一个默认的构造器以及一个包含详细信息的构造器。
抛出你自己的异常
答:1)
在方法的定义中增加一个throws修饰符,以通知调用者可能会抛出一个异常;
2)
在方法中构造一个该类的实例,然后抛出该实例。
九·AWT(Abstract
Window Toolkit)
事件模型
十·The AWT
Component Library
十一·JFC(Java
Foundation
Classes)
线程原理
进程是数据独占的
线程是数据共享的(所以需要处理数据并发)
并发原理:宏观并行,微观串行
OS将一段时间分为多个时间片,每个时间片CPU只能运行一个任务。
线程实现的两种形式
继承java.lang.Thread:
class MyThread extends Thread{
public void run(){
需要进行执行的代码,如循环。
}
}
启动线程
public class TestThread{
public static void main(){
Thread t1=new
Mythread();
T1.start();
}
}
实现java.lang.Runnable接口:
Class MyThread implements Runnable{
Public void run(){
}
}
这种实现可以再继承其他类。
启动线程时不同前者
public static void main(){
Runnable myThread =
new MyThread();
Thread t = new
Thread(myThread);
t.start();
}
线程的状态
下面为线程中的7中非常重要的状态:(有的书上也只有认为前五种状态:而将“锁池”和“等待池”都看成是“阻塞”状态的特殊情况:这种认识也是正确的,但是将“锁池”和“等待池”单独分离出来有利于对程序的理解)
Blocked
sleepover /
\ sleep()
join
over /
\ jion()
start() / Cpu时间片
\
New
---------------->Runnable------------>Running
----------------->Dead
<------------
/
\
时间片结束 /synchro\ o.wait()
/ nized
\
Lockpool <-----------wait
pool
o.notify
Thread的方法
public static void sleep(long millis)
throws InterruptedException
括号中以毫秒为单位,
使线程停止一段时间,间隔期满后,线程不一定立即恢复执行。
当main()运行完毕,即使在结束时时间片还没有用完,CPU也放弃此时间片,继续运行其他程序。
Try{Thread.sleep(1000);}
Catch(InterruptedException e){e.printStackTrace(e);}
Public final void join() throws InterruptedException
表示其他运行线程放弃执行权,进入阻塞状态,直到调用线程结束。
实际上是把并发的线程变为串行运行。
线程的优先级:1-10,越大优先级越高,优先级越高被OS选中的可能性就越大。(不建议使用,因为不同操作系统的优先级并不相同,使得程序不具备跨平台性,这种优先级只是粗略地划分)。
注:程序的跨平台性:除了能够运行,还必须保证运行的结果。
Public static void field()
使当前线程马上交出执行权,回到可运行状态,等待OS的再次调用。
Public final Boolean isActive()
验证当前线程是否是活动的,不管它是否正在运行。
共享数据的并发处理
两个线程修改共享资源时会出现数据的不一致,为避免这种现象采用对访问的线程做限制的方法。利用每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。
1.Synchronized修饰代码块
public void push(char c){
synchronized(this){
...
}
}
对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块
2.Synchronized修饰方法
public synchronized void push(char c)
{
...
}
对当前对象的加锁,只有拿到锁标记的对象才能执行该方法
注:方法的Synchronized特性本身不会被继承,只能覆盖。
线程因为未拿到锁标记而发生阻塞进入锁池(lock
pool)。每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。由系统决定哪个线程拿到锁标记并运行。
使用互斥锁的注意事项
锁标记如果过多,就会出现线程等待其他线程释放锁标记,而又都不释放自己的锁标记供其他线程运行的状况。就是死锁。
死锁的两种处理方法
统一排列锁顺序(解决不同方法中对多个共享资源的访问)
对象1的方法
synchronized(a)
synchronized(b)
对象2的方法
synchronized(a)
synchronized(b)
2.线程间通信(也就是线程间的相互协调)
线程间通信使用的空间称之为对象的等待池(wait
pool),该队列也是属于对象的空间的。
进入等待池
使用Object类中wait()的方法,在运行状态中,线程调用wait(),此时表示线程将释放自己所有的锁标记和CPU的占用,同时进入这个对象的等待池。等待池的状态也是阻塞状态,只不过线程释放自己的锁标记。
退出等待池进入锁池
notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。
notifyAll():
将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行
注意:只能对加锁的资源进行wait()和notify()。
1) wait():交出锁和CPU的占用;
2)
notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。
3) notifyAll():
将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行
注:在java.io包中Vector 和 HashTable
之所以是线程安全的,是因为每个方法都有synchronized修饰。