------- android培训、java培训、期待与您交流! ----------
异常:
定义:在错误发生的时候减少损失,提高容错性
1、异常的分类
Throwable有两个子类:Error和Exception。
一个Error对象表示一个程序错误,指的是底层的、低级的、不可恢复的严重错误。此时程序一定会退出,因为已经失去了运行所必须的物理环境。
对于Error错误我们无法进行处理,因为我们是通过程序来应对错误,可是程序已经退出了。
我们可以处理的Throwable类中只有Exception类的对象(例外/异常)。
Throwable
|
------------------
| |
Error Exception
|
--------------
| |
RuntimeException 非Runtime异常
未检查异常 已检查异常
由于程序员疏忽造成的 不可避免的
可避免的 必须处理
可处理,可不处理
2、异常的产生和传递
throw new一个异常对象; ---表示抛出一个异常
throw new NullPointerException();
相当于return,函数返回上一级
传递:
沿着方法调用链反向传递!
当一个方法中出现异常,而没有作处理,则以异常对象为返回值返回调用处(逐级传递)
异常返回给虚拟机时,虚拟机终止退出,程序结束
3、异常的处理
(1)声明抛出
是方法声明的第五部分 throws+异常名字(多个异常用“,”分隔)
出现异常,不处理,抛给上一级处理
并且子类抛出异常的范围不能比父类抛出异常的范围更宽。
(2)捕获异常
try - catch
try - catch - finally
try - finally //不捕获异常,当异常发生,返回上一级之前,要运行finally中的代码
以上语句可以嵌套
返回类型 方法名(参数){
try{
可能出错语句
正常语句
}catch(异常类 e){ //某种异常的引用
对异常的处理
}
正常语句
}
捕获多个异常:
程序任何时刻只发生一个异常
可对产生的每个异常分别捕捉,也可由同一异常进行处理,前提是这个共用的异常应该是所有这些该被捕获的异常的父类,但是,对于非受查异常不成立
当try后面有多个语句块时,注意catch异常的顺序,子类必须放在父类的前面
finally关键字
无论异常是否发生,一定会执行的代码,可放在finally块内。
要点:没有异常产生时:正常执行try{}catch(){} —> 进入finally语句块 —> 方法中剩余代码
有异常产生时(捕找到) —> 进入catch处理 —> 进入finally语句块 —> 方法中剩余代码
有异常产生时(没捕找到)—> 进入finally语句块 —> 离开方法
一般写一些释放资源的代码
在try - catch 块中遇到System.exit(0);则不会执行finally中的代码
Throwable有一个message属性。在使用catch的时候可以调用:
Catch(IOException e){System.out.println(e.getMessage())}; //打印出来的是创建(throw new)异常对象的时候,给定的参数
Catch(IOException e){e.printStackTrace()}; //打印堆栈追踪信息
以上两条语句都是可以打印出错的过程信息。告诉我们出错类型所历经的过程,在调试的中非常有用。
开发中的两个道理:
①如何控制try的范围:根据操作的连动性和相关性,如果前面的程序代码块抛出的错误影响了后面程序代码的运行,那么这个我们就说这两个程序代码存在关联,应该放在同一个try中。
②对已经查出来的异常,有throw(消极)和try catch(积极)两种处理方法。
对于throws把异常抛到try catch能够很好地处理异常的位置(即放在具备对异常进行处理的能力的位置 )。如果没有处理能力就继续上抛。
4、自定义异常
(1)继承Exception类
(2)构造方法:
不带参数的构造方法
带参数的构造方法:参数指出错误性质,super(message);把参数传递给父类构造异常
public class MyException extends Exception {
public MyException() {
}
public MyException(String message) {
super(message);
}
}
多线程
C++的多进程是OS系统并发的一个任务
Java中没有多进程,一个JVM就是一个进程
线程是在进程中并发的一个顺序的执行流程
多进程:划分时间片,宏观上并行,微观上串行
多线程:cpu在进程内部再划分时间片
CPU ,代码 ,数据
进程:进程间数据独立
线程:数据空间共享,堆空间的共享(堆空间中存放的是对象),栈空间是独立的
所以线程间切换容易,称为轻量级进程
一个线程对象代表了一个线程,并非就是一个线程
线程是操作系统中负责维护的资源
java.lang.Thread类的一个对象就代表一个线程
线程是底层OS维护的资源,JVM跑在OS上,在JVM中创建一个Thread对象,调用其start()方法,底层OS会申请一个线程资源,线程
对象可到底层管理一个线程
创建好线程之后,把要让线程执行的代码封装到线程对象中(覆盖run()方法)
实现线程代码的方式:
1、继承Thread 类,覆盖run()方法
去底层申请线程并运行,对线程对象调start()方法,main方法是一个主线程
宏观并行,微观串行
2、实现Runnable接口
使用多态获得Runnable对象,成为目标对象
再利用目标对象构造线程对象 Thread t = new Thread(target);
多线程的状态转换图
■ 阻塞状态
▲ 初始状态 ▲阻塞状态 ▲终止状态
\ / ^ 1 ^
\ / \ 2sleep /
\start / \ 3join /stop
\ / \ /
V V \ /
▲ 可运行状态 _ _ _ OS选中 _ _ _\ ▲运行状态
(只缺CPU) \ CPU到期或调用yield
下面为线程中的7中非常重要的状态:(有的书上也只有认为前五种状态:而将“锁池”和“等待池”都看成是“阻塞”状态的
特殊情况:这种认识也是正确的,但是将“锁池”和“等待池”单独分离出来有利于对程序的理解)
1,初始状态,线程创建,线程对象调用start()方法。
2,可运行状态,也就是等待Cpu资源,等待运行的状态。
3,运行状态,获得了cpu资源,正在运行状态。
4,阻塞状态,也就是让出cpu资源,进入一种等待状态,而且不是可运行状态,有三种情况会进入阻塞状态。
1)如等待数据输入(输入设备进行处理,而CPU不处理),则放入阻塞,直到输入完毕,阻塞结束后会进入可运行状态。
2)线程休眠,线程对象调用sleep()方法,阻塞结束后会进入可运行状态。
public static void sleep(long millis)
throws InterruptedException
括号中以毫秒为单位, 使线程停止一段时间,间隔期满后,线程不一定立即恢复执行。
当main()运行完毕,即使在结束时时间片还没有用完,CPU也放弃此时间片,继续运行其他程序。
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace(e);
}
线程中有异常,只能trycatch,子类中不能抛出比父类更多的异常,父类run方法没有抛出异
常。
3)线程对象2调用线程对象1的join()方法,那么线程对象2进入阻塞状态,直到线程对象1中止。
public final void join() throws InterruptedException
表示其他运行线程放弃执行权,进入阻塞状态,直到调用线程结束。
实际上是把并发的线程变为串行运行。
t1 num
t2 char if(c=='m') -> t1.join()
//t2对t1调用join,t2进入了阻塞状态
//当条件成立时,t1加入打印数字,一直到打印完,此时t2继续运行
5,中止状态,也就是执行结束。
6,锁池状态
7,等待队列
共享数据的并发处理
数据的错误发生
多线程并发访问同一个对象(临界资源)
破坏了原子操作,就会发生数据不一致的情况
多线程同时并发访问的资源叫做临界资源。
多个线程同时访问对象并要求操作相同资源时分割了原子操作就会出现问题。(原子操作,不可再分的操作)会出现数据的不
一致或数据不完整,为避免这种现象采用对访问的线程做限制的方法。
互斥锁机制,利用每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池
。任何一个对象系统都会为其创建一个互斥锁,这个琐是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线
程。
■ Synchronized用法
1.Synchronized修饰代码块(同步代码块),
public void push(char c){
synchronized(this)//只有持有当前对象锁标记的线程才能访问这个代码块
{
...
}
}
对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块
2.Synchronized修饰方法
public synchronized void push(char c) {
...
}
在整个方法里,对当前对象的加锁,只有拿到锁标记的对象才能执行该方法。
初始状态 阻塞状态 终止状态
\ / ┍ 1 ┓
\ / \ 2sleep /
\start / \ 3join /stop
\ / \ /
┙ ┕ \ /
可运行状态 _ _ _ OS选中 _ _ _\ 运行状态
(只缺CPU) \ CPU到期或调用yield
┍ /
\ /
\ Synchronized/
\ /
\ ┕
锁池状态
锁池:一个空间,每个对象都有一个,用来存放等待锁标记的线程
当一个对象中有了锁标记,不会释放其它对象的锁标记。
当t1线程正在访问对象O的同步方法时,别的线程t2不能访问O的任何同步方法,但还是可以访问其它的非同步方法
构造方法 ×对象没有完全构造好了,没有当前对象概念
抽象方法 ×抽象方法没有代码块,没用
静态方法 √是对类对象的加锁
☆注意:构造方法不能Synchronized修饰
静态方法可以用Synchronized修饰(是对类对象加锁,类对象会在反射时讲到)
抽象方法不能用Synchronized修饰,不影响子类覆盖,子类在覆盖这个方法是可以加Synchronized,也可以不加Synchronized
,所以根据Java不允许写废代码的特点是不能写在一起。