异常
(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包的清单文件
jar–cvfm 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()方法可以暂停当前线程,让其他线程执行。