关闭

黑马程序员-异常,包,多线程

119人阅读 评论(0) 收藏 举报

异常

(1)异常:就是程序在运行时出现不正常情况。
(2)异常由来:问题也是现实生活中具体的事物,也可以通过Java的类的形式进行描述,并封装成对象。其实就是java对不正常情况进行描述后的对象体现。
(3)对于问题的划分:两种:一种是严重的问题,一种非严重的问题。
     对于严重的,java通过Error类进行描述。
                          对于Error一般不编写针对性的代码对其进行处理。

     对于非严重的,java通过Exception类进行描述。
                          对于Eception可以使用针对性的处理方式进行处理。

     无论Error或者Exception都具有一些共性内容。
     比如:不正常情况的信息,引发原因等。

异常体系 

   Throwable

                |---Error  //通常出现重大问题如:运行的类不存在或者内存溢出等。

                |---Exception//运行时出现的一起情况

                            |---RuntimeException   //特殊异常类,抛时不需要声明

 

(4)异常的处理
java 提供了特有的语句进行处理。


try{
     需要被检测的代码;
}
catch(异常类 变量){
     处理异常的代码;(处理方式)
}
finally{
     一定会执行的语句;
}

注:(1)finally中定义的通常是关闭资源代码。因为资源必须释放。

       (2)finally只有一种情况不会执行。当执行到System.exit(0);finally不会执行。

       (3)catch里有return,也会执行finally.

 

(5)对捕获到的异常对象进行常见方法操作:
        String getMessage();    //获取异常的信息。返回字符串。

        toString();                   //获取异常类名和异常信息,返回字符串。

        printStackTrace();                 //获取异常类名和异常信息,以及异常出现在程序中的位置.返回值void.

                                                        //其实JVM默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。

        printStackTrace(PrintStream s)//通常用该方法将异常内容保存在日志文件中,以便查阅。

 

 

 

(6)对多异常的处理
1)声明异常时,建议声明更为具体的异常.这样处理的可以更具体.
2)对方声明几个异常,就对应有几个catch快。不要定义多余的catch块。
  如果多个catch跨种的异常出现继承关系,父类异常catch块放在最下面。

建立在进行catch处理时,catch中一定要定义具体处理方式。
不要简单定义一句 e.printStackTrace(),
也不要简单的就书写一条输出语句。

(7)自定义异常:必须是自定义类继承Exception。


因为项目中会出现特有的问题。
而这些问题并未被java所描述并封装对象。
所以对于这些特有的问题可以按照Java的对问题封装的思想。
将特有的问题。进行自定义的异常封装。


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

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

{
    MyException(String message){
    super(message);
}
}
自定义异常:按照java的面向对象思想,将程序中出现的特有问题进行封装。

 

为什么要继承Exception?
异常体系有一个特点:因为异常类和异常对象都被抛出。
他们都具备可抛性。这个可抛性是Throwable这个体系中都有特点。
只有这个体系中的类和对象才可以被throws和throw操作

 

一般情况下,函数内出现异常,函数上需要声明。

:
class FuShuException extends Exception{       //自己定义的异常信息
FuShuException(String msg)
{
     super(msg);
}
}
class Demo{
int div(int a,int b)throws FuShuException{
if(b<0)
   throw new FuShuException(“错误,数字小于0”);            //手动通过throw关键字抛出一个自定义异常对象。
return a/b;
}
}
main{
try{                                    
  int x=d.div(4,0);
  System.out.println("x="x);
 }
catch(FuShuException e){
System.out.println(e.toString());
}
}

发现打印的结果中只有异常的名称,却没有异常的信息。
因为自定义的异常并未定义信息。
因为父类中已经把异常信息的操作都完成了。
所以子类只要在够着是,将异常信息传递给父类通过super语句。
那么就可以直接通过getMessage方法获取自定义的异常信息了。

 

(8)throws和throw的区别


throws使用在函数上){之间。throw使用在函数内。
throws后面跟的异常类。可以跟多个。用逗号隔开。throw后跟的是异常对象。

 

:当throw抛出异常对象,那么就必须要给对应的处理动作。
要么在内部try catch处理,要么在函数上声明调用者处理。

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

 RuntimeException和它的子类除外。

 

(9)对于异常分两种:
1)编译时被检测的异常。
2)编译时不被检测的异常(运行时异常。RuntimeException以及其子类)

(10)Exception中有一个特殊的子类异常RuntimeException运行时异常。
(1)如果在函数内容抛出该异常,函数上可以不用声明,便一样通过。
(2)如果在函数上声明了该异常。调用者可以不用进行处理。
之所以不用再函数声明,是因为不需要让调用者处理。
当该异常发生,希望程序停止,因为在运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修正。

自定义异常是:如果该异常的发生,无法在继续进行运算,就让自定义异常继承RuntimeException.

 

public void method(){           //因为问题在catch里所以不用再写throws Exception()
try{
throw new Exception();
}
catch(Exception e){
try{
throw e
}
}

记住:catch是用于处理异常。如果没有catch就代表异常没有被处理过,如果该异常是检测时异常。那么必须声明。

(11)异常在子父类覆盖重的体现:
1)子类在覆盖父类是,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2)如果父类方法抛出多个异常,那么子类在覆盖该方法是,只能抛出父类异常的自己。
3)如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法是,也不可以抛出异常。
   如果子类方法发生了异常,就必须要进行try处理。绝对不能抛。

RuntimeException은throws할필요없다

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


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

异常有两种
     编译时被检测异常
          该异常在编译时,如果没有处理(没有抛也没有try),编译失败。
     该异常被标识,代表着可以被处理。
     运行时异常(编译时不检测)
          在编译时,不需要处理,编译起不检查。
          该异常的发生,建议不处理,让程序停止,需要对代码进行修正。

异常的好处
  1)将问题进行封装成对象。
  2)将正常流程的代码和问题处理代码相分离,方便与阅读。

异常的处理原则
  1)处理方式有两种:try或者throws.
  2)调用到抛出异常的功能时,抛出几个,就处理几个。
     一个try对应多个catch
  3)多个catch,父类的catch放到最下面。
  4)catch内,需要定义针对性的处理方式。不要简单的定义printStackTrace,输出语句。
     也不要不写
     当捕获到的异常,本功能处理不了时,可以继续在抛出。
     try{
     throw new AException();
     }
     catch(AException e){
     throw e;
     }
     如果该异常处理不了,但并不属于该功能出现的异常。
     可以将异常转换后,在抛出和该功能相关的异常。
     或者异常可以处理,当需要将异常产生的本功能相关的问题提供出去。
     当调用者知道,并处理,也可以将扑火异常处理后,转换信息的异常。
     try{
          throw new AException();
     }
     catch(AException e){
          throw new AException();
     }

异常的注意事项
    在子父类覆盖时:
    1)子类抛出的异常必须是父类的异常的子类或者子集。
    2)如果子类或者接口没有异常抛出是,子类覆盖处向异常只能try不能抛。

包(package)
이 package에서 다른 package로 넘어갈때 꼭 包名.类名
꼭 public class,그리고 함수앞에도 public void show() {}    다들 꼭 public있어야된다

总结:
1) 包于包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰。
2) 不同包中的子类还可以访问父类中被protected权限修饰的成员。

 

作用

       (1)为避免多个类重名的情况,如果出现两个相同名字的类,可通过包将两者区分,从而避免冲突。

        (2)对类文件进行分类管理,可以将相关的一些类放在同一个包中。

        (3)给类提供多层命名空间,如a包中的Demo.class文件,如果要创建Demo对象,就要在使用时加上a.如:a.Demo demo=new a.Demo();

        (4)包的出现可以将java的类文件和源文件相分离。

规则      

        (1)包必须写在程序的第一行。因为要先有包,才知道类文件的存放地方。

        (2)类的全称:包名.类名。

        (3)编译定义了包的程序文件时,在编译时要指定包的存储目录。

       如:javac –d c:\mypack类名.java

 

------------------------------------------------------------------------------------

                       public      protected     default     private
同一个类中     OK              OK              OK            OK
同一个包中     OK              OK              OK       
子类                OK              OK     
不同包中         OK

------------------------------------------------------------------------------------

 

包导入

包名太长的时候
import pack.haha.hehe.heihei.*    //导入的是类

为了简化类名的书写,使用一个关键字,import
import导入的时包中的类。
建议,不要写通配符*,需要用到包中的哪个类,就导入哪个类。

jar包 

       创建jar

                jar  -cvf  mypack.jar packa packb

        查看jar

                jar  -tvf  mypack.jar  [>定向文件]

        解压缩

                jar  -xvf  mypack.jar

        自定义jar包的清单文件

                jarcvfm  mypack.jar mf.txt  packa packb


 

多进程
进程:是一个正在执行中的程序。
          每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
           线程在控制着进程的执行。
一个进程中至少有一个线程。

Java VM 启动的时候会有一个进程java.exe
该进程中至少有一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。

扩展:其实更细节说明jvm.jvm启动不知一个线程,还有负责垃圾回收机制的线程。

类Thread

如果自定义的代码中,自定义一个线程呢?
通过对api的查找,java已经提供了对县城这类食物的描述,就thread类。
创建线程的第一种方法:继承Thread类。
                 步骤:1)定义类继承Thread
                            2)腹泻Thread类中的run()方法。
                                 目的:将自定义的代码存储在run方法中,让线程运行。
                            3)调用线程的start方法,
                                  该方法两个作用:启动线程,调用run方法。

发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个线程在运行。
cpu在做着快速的切换,已达到看上去是同时运行的效果。
我们可以形象把多线程的运行形容为在乎想抢夺cpu的执行权。

这就是多县曾的一个特性,随机性。谁抢谁执行,至于执行多长,cpu说的算。

为什么要覆盖run方法呢?
thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。

.start():开启线程并执行该线程的run方法。
.run():仅仅是对象调用方法,而线程创建了,并没有运行。

原来线程都有自己的默认名称。
Thread-编号 该编号从0开始。
自定义线程名称:Test(String name){
               super(name)
               }
    this.getName();
static Thread currentThread():获取当前线程对象。
getName():获取线程名称。
设置线程名称:setName或者构造函数。

接口Runnable
第二种方式:

步骤:1)定义类实现Runnable接口
           2)覆盖Runnable接口中的run方法。
                 将线程要运行的代码存放在该run方法中。
           3)通过Thread类建立线程对象。
           4)将Runnable接口的子类对象作为实际阐述传递给Thread类的构造函数。
 为什么要将Runnable接口的子类对象传递给Thread的构造函数?
       因为,自定义的run方法所属的对象是Runnable接口的子类对象。
       所以要让线程去指定对象的run方法。就必须明确该run方法所属的对象。
     5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

(面试)实现方式和继承方式有什么区别呢
       实现方式好处:避免了单继承的具象性。
       在定义线程时,建立使用实现方式。
       继承Thread:线程代码存放Thread子类run方法中。
       实现Runnable,线程代码存放在接口的子类的run方法。
多线程的运行出现安全问题原因:
       当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分还没有执 行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
       对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以 产于执行。

Java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块。
synchronized(对象){          
需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
object obj=new object();
synchronized(obj){          
需要被同步的代码
}

同步的前提:
1)必须要有两个或者两个以上的线程。
2)必须是多个线程使用同一个锁。

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

好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源

Ticket t=new Ticket();
Thread t1=new Thread(t)
Thread t2=new Thread(t)
Thread t3=new Thread(t)

同步函数用得是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用。就是this.
所以同步函数使用的锁事this

通过该程序进行验证。

同步函数被静态修饰后,使用的锁不是this。因为静态方法中也不可以定义this.
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class 该对象的类型是class
静态的同步方法,使用的锁是该方法所在累的字节码文件对象。类名.class

synchronized(Ticket.class)
public static synchronized void show(){}
------
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;
}
}
main(){
Single s1=Single.getInstance();
}
延迟加载的单例延迟实力-懒汉式方式

死锁:同步中嵌套同步

线程间的通讯:其实就是多个线程在操作同一个资源。但是操作的动作不同。

wait(),notify(),notifyAll():都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步具有锁。

为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识他们所操作线程只有的锁,
只有同一个锁上的被等待线程,可以被同一个锁上Notify唤醒。
不可以对不同锁中的线程进行唤醒,
也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。

if=数据错乱 notify=停止

Q:对于多个生产者和消费者。为什么要定义while判断标记?
A:原因:让被唤醒的线程在一次额判断标记。

Q:为什么定义notifyAll?
A:因为需要唤醒对方线程。
因为只用notify。容易出现唤醒本方线程的情况。导致程序中的所有线程都等待。

await()=wait()  signal()=notify()
private Lock lock=new ReentrantLock();
private Condition con=lock.newDondition();
看ProducerConsumerDemo2.java文件
它体现了只唤醒本类的

JDK1.5中提供了多线程升级解决方案。将同步Synchronized替换成现实Lock操作。将Object中wait,notify notifyAll,替换成了Condition对象。该对象可以Lock锁,进行获取。
该实力中,实现了本方只唤醒对方操作。

------------------------------
停止线程
stop方法已经过时。
Q:如何停止线程?
A:只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。

特殊情况:当线程出狱了冻结状态。
就不会读取到标记,那么线程就不会结束。

join方法

        当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。

setPriority()方法用来设置优先级

        MAX_PRIORITY 最高优先级10

        MIN_PRIORITY   最低优先级1

        NORM_PRIORITY 分配给线程的默认优先级

yield()方法可以暂停当前线程,让其他线程执行。


 

 

 

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:816次
    • 积分:103
    • 等级:
    • 排名:千里之外
    • 原创:10篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章存档