您的位置:首页 > 职场人生

黑马程序员--Java学习15--异常与多线程

2014-05-29 18:19 459 查看
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流!
----------------------

一、异常

1、异常概念

异常是对问题的描述,将问题进行对象的封装。

异常的体系如下:

<span style="font-size:14px;"><span style="font-size:14px;">Throwable
|--Error
|--Exception
|--RuntimeExceptio
</span></span>


这只是一部分,也是我们要重点学习的。

异常体系的特点:异常体系中的所有类以及建立的对象都是具备可抛性,也就是说可以被throw和throws关键字所操作。也只有异常体系具备这个特点

2、throw和throws的用法

throw 定义在函数内,用于抛出异常对象。

throws定义在函数上,用于抛出异常类,可以抛出多个,用逗号隔开

当函数内容有throw抛出异常对象,并未进行try 处理。必须在函数上进行声明。否则编译失败

注意:RuntimeException除外,也就是说,函数内如果抛出RuntimeException异常,函数上不用声明

如果,函数声明了异常,调用者需要进行处理,处理方法可以throws 也可以 try catch.

异常有两种:

编译时被检测异常

该异常在编译时,如果没有处理(没有抛也没有try),编译失败

该异常被标识,代表这可以被处理。

运行时异常(编译时不检测)

在编译时,不需要处理,编译器不检查。

该异常的发生,建议不处理,让程序停止,需要对代码进行修正(RuntimeException类及其子类)

一个简单的除法运算的例子来看看throw和throws是怎么用的:

<span style="font-size:14px;"><span style="font-size:14px;">class Test {
public static void main(String[] args) {

int c=div(3,6);
System.out.println(c);
}
//定义一个除法运算
public static int div(int a,int b) {
return a/b;
}
}</span></span>


运行结果是:0;

但是当我们这样做呢:double c=div(3,0);

结果如下:



ArithmeticException:当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例

对于这样的代码,我们知道他会出现异常,那么如何解决呢,我们来看看将异常抛出:

<span style="font-size:14px;">class Test {
public static void main(String[] args) {

int c=div(3,0);
System.out.println(c);
}
//定义一个除法运算
public static int div(int a,int b) throws ArithmeticException{

if (b==0) {
throw new ArithmeticException("除零了");//此处抛出了异常
}
return a/b;
}
}</span>


这时的结果是:



3、异常处理的语句 try catch

try-catch 语句是对异常进行处理的语句。其格式有三种:

<span style="font-size:14px;">第一种
try {

}
catch () {
}
第二种
try {

}
finally {
}
第三种
try {

}
catch () {
}
finally{
}</span>


try中放的是要执行的代码,catch是对捕捉到的遗产进行的处理

<span style="font-size:14px;"><pre class="java" name="code">class Test {
public static void main(String[] args) {

try {
int c=div(3,0);
System.out.println(c);
}
catch (ArithmeticException e) {
System.out.println("分母为零,无法进行运算");
}
finally {
System.out.println("finally中的代码是一定运行的");
}
}
//定义一个除法运算
public static int div(int a,int b) throws ArithmeticException{

if (b==0) {
throw new ArithmeticException();//此处抛出了异常
}
return a/b;
}
}</span>



运行结果是:



但是要注意的是:注意:1、finally中定义的通常是 关闭资源代码,因为资源必须释放。2、finally 只有一种情况不会执行,当执行到System.out.exit(0);finally不会再执行

4、自定义异常

自定义异常: 定义类继承Exception或者RuntimeException 1、为了让该自定义类具备可抛性 2、让该类具备操作异常的共性方法

当要定义自定义异常的信息时,可以使用父类已经定义好的功能将异常信息传递给构造函数

<span style="font-size:14px;">/*
有一个圆形和长方形。
都可以获取面积。对于面积如果出现非法的数值,视为是获取面积出现问题。
问题通过异常来表示。
现有对这个程序进行基本设计。

*/

class NoValueException extends Exception
{
NoValueException(String message)
{
super(message);
}
}
interface Shape
{
void getArea();
}
class Rec implements Shape
{
private int len,wid;
Rec(int len,int wid)throws NoValueException
{
if (len<=0||wid<=0)
{
throw new NoValueException("数据传入有误!1");
}
else
{
this.len = len;
this.wid = wid;
}
}
public void getArea()
{
System.out.println(len*wid);
}
}

class Circle implements Shape
{
private int radius;
public static final double PI = 3.14;
Circle(int radius)throws NoValueException
{
if (radius<=0)
{
throw new NoValueException("数据传入有误!2");
}
this.radius = radius;
}
public void getArea()
{
System.out.println(radius*radius*PI);
}

}
class ExceptionTest1
{
public static void main(String[] args)
{

try
{
Circle c =new Circle(-3);
c.getArea();
Rec r = new Rec(-3,1);
r.getArea();

}
catch (NoValueException e)
{
System.out.println(e.toString());
}

}
}</span>


5、异常在子父类覆盖中的体现

异常在子父类覆盖中需要注意的:

1、子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。

2、如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。原则是父类能处理。

3、如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。

如果子类方法发生了异常,就必须要进行try处理,绝对不能抛出。

<span style="font-size:14px;">class AException extends Exception
{
}
class BException extends AException
{
}
class CException extends Exception
{
}

class Fu
{
void show()throws AException
{

}
}
class Zi extends Fu
{
void show()//要么不抛,要么throws AException,要么throws BException,不能throws CException。
{
//如果子类内部真的发生了CException,只能内部处理,不能抛。
}
}
class Test
{
void function(Fu f)
{
try
{
f.show();
}
catch (AException e)
{

}

}
}

class  CatchDemo2
{
public static void main(String[] args)
{
Test t = new Test();
t.function(new Zi());
}
}</span>


二、多线程

1、多线程的概念

以往开发的程序大多是单线程的,即一个程序只有一条从头至尾的执行线索。然而现实世界中的很多过程都具有多条线索同时动作的特性:例如,我们可以一边看电视,一边活动胳膊,如果不容许这样做,我们会感觉很难受。再如一个网络服务器可能需要同时处理多个客户机的请求等。

Java的一大特性点就是内置对多线程的支持。多线程是指同时存在几个执行体,按几条不同的执行线索共同工作的情况,它使得编程人员可以很方便地开发出具有多线程功能,能同时处理多个任务的功能强大的应用程序。虽然执行线程给人一种几个事件同时发生的感觉,但这只是一种错觉,因为计算机在任何给定的时刻只能执行那些线程中的一个。为了建立这些线程正在同步执行的感觉,Java快速地把控制从一个线程切换到另一个线程。

2、线程创建的两种方式:继承Thread类和使用Runnable接口

创建线程的第一种方式:继承Thread类

步骤:

1、定义类继承Thread类

2、复写Thread类中的run方法

目的:将自定义的代码存储在run方法,让线程运行

3、调用线程的start方法,该方法有两个作用:1、启动线程 2、调用方法

/*
需求:简单的卖票程序。
多个窗口同时买票。
*/
class Ticket extends Thread
{
private static  int ticket=100;
public void run()
{
while(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"...ticket"+ticket--);
}
}
}
class  TicketDemo
{
public static void main(String[] args)
{

Ticket t2= new Ticket();
Ticket t3= new Ticket();
Ticket t4= new Ticket();

t2.start();
t3.start();
t4.start();
}
}


运行结果是:



可以看出不同的线程在同时运行着。

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

步骤:

1、定义实现Runnable接口

2、覆盖Runnable接口中的run方法。

3、通过Thread类建立线程对象。

4、将Thread接口的子类对象作为实际参数传递给Thread类的构造函数。

5、调用Thread类的start方法开启线程并调用Runable接口子类的run方法。

class Ticket implements Runnable//extends Thread
{
private /*static */ int ticket=100;
public void run()
{
while(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"...ticket"+ticket--);
}
}
}
class  TicketDemo
{
public static void main(String[] args)
{
Ticket t= new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);

t1.start();
t2.start();
t3.start();
t4.start();
}
}

运行结果也是几个线程同时运行,顺序不定。

那么实现方式和继承方式有什么不同呢?

实现方式:避免了单继承的局限性,在定义线程时,

建议使用实现方式

两种方式的区别:

继承Thread:线程大妈存放在Thread子类run方法中。

实现Runnable:代码存放在接口的子类的run方法。

3、多线程中的安全问题

问题的原因:当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分还没有执行完,另一个线程参与进来,导致共享数据的错误解决办法:对多条操作共享数据的语句只能让一个线程都执行完,在执行过程,其他线程不可以参与执行

Java对于多线程问题提供了专业的解决办法

就是同步代码

synchronized(对象) {
需要同步的代码(谁需要操作共享数据谁就是同步代码)
}


同步的前提:

1、必须要有两个或者两个以上的线程

2、必须是多个线程使用同一个锁

必须保证同步中只能有一个线程运行

好处:解决了多线程的安全问题。

弊端:多个线程需要先判断锁,较为消耗资源

class Ticket implements Runnable{
private /*static */ int ticket=1000;
Object obj=new Object();
public void run() {
synchronized(obj) {
while(ticket>0) {

try {
Thread.sleep(10);}
catch(Exception e) {
}
System.out.println(Thread.currentThread().getName()+"...ticket.."+ticket--);
}
}
}
}

class  TicketDemo3 {
public static void main(String[] args) {
Ticket t= new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}


4、懒汉式

在单例设计模式中我提到了,饿汉式和懒汉式,那么多线程中的懒汉式为:

/*
单利设计模式:

饿汉式
class Single {
private static final Single s = new Single();
private Single(){}
public static Single getInstance() {
return s;
}
}

*/

//懒汉式
//效率较低
class Single {
private static Single s=null;
private Single(){}
public static synchronized Single getInstance() {
if (s==null) {
s=new Single();
}
return s;
}
}
//提高效率
class Single {
private static Single s=null;
private Single(){}
public static  Single getInstance() {
if(s==null){//双重判断
synchronized(Single.class)//锁为:该类所属的字节码文件对象
if (s==null) {
s=new Single();
}
}
}
return s;
}
}


这种方式提高了效率,保证了对象的唯一。

---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流!
----------------------




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: