异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不一样,该部位的功能将受影响.在程序中的意思就是:java
在Java等面向对象的编程语言中,异常自己是一个类,产生异常就是建立异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。程序员
异常指的并非语法错误,语法错了,编译不经过,不会产生字节码文件,根本不能运行.
异常机制实际上是帮助咱们找到程序中的问题,异常的根类是java.lang.Throwable
,其下有两个子类:java.lang.Error
与java.lang.Exception
,日常所说的异常指java.lang.Exception
。数据库
Throwable体系:编程
Throwable中的经常使用方法:数组
public void printStackTrace()
:打印异常的详细信息。包含了异常的类型,异常的缘由,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。安全
public String getMessage()
:获取发生异常的缘由。提示给用户的时候,就提示错误缘由。网络
public String toString()
:获取异常的类型和异常描述信息(不用)。出现异常,没关系张,把异常的简单类名,拷贝到API中去查。多线程
咱们日常说的异常就是指Exception,由于这类异常一旦出现,咱们就要对代码进行更正,修复程序。并发
异常(Exception)的分类:根据在编译时期仍是运行时期去检查异常?编程语言
先运行下面的程序,程序会产生一个数组索引越界异常ArrayIndexOfBoundsException。咱们经过图解来解析下异常产生的过程。
工具类
public class ArrayTools { // 对给定的数组经过给定的角标获取元素。 public static int getElement(int[] arr, int index) { int element = arr[index]; return element; } }
测试类
public class ExceptionDemo { public static void main(String[] args) { int[] arr = { 34, 12, 67 }; intnum = ArrayTools.getElement(arr, 4) System.out.println("num=" + num); System.out.println("over"); } }
上述程序执行过程图解:
Java异常处理的五个关键字:try、catch、finally、throw、throws
在编写程序时,咱们必需要考虑程序出现问题的状况。好比,在定义方法时,方法须要接受参数。那么,当调用方法使用接受到的参数时,首先须要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时须要使用抛出异常的方式来告诉调用者。
在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。那么,抛出一个异常具体如何操做呢?
throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
使用格式:
throw new 异常类名(参数);
例如:
throw new NullPointerException("要访问的arr数组不存在"); throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
学习完抛出异常的格式后,咱们经过下面程序演示下throw的使用。
public class ThrowDemo { public static void main(String[] args) { //建立一个数组 int[] arr = {2,4,52,2}; //根据索引找对应的元素 int index = 4; int element = getElement(arr, index); System.out.println(element); System.out.println("over"); } /* * 根据 索引找到数组中对应的元素 */ public static int getElement(int[] arr,int index){ //判断 索引是否越界 if(index<0 || index>arr.length-1){ /* 判断条件若是知足,当执行完throw抛出异常对象后,方法已经没法继续运算。 这时就会结束当前方法的执行,并将异常告知给调用者。这时就须要经过异常来解决。 */ throw new ArrayIndexOutOfBoundsException("哥们,角标越界了~~~"); } int element = arr[index]; return element; } }
注意:若是产生了问题,咱们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。那么对于调用者来讲,该怎么处理呢?一种是进行捕获处理,另外一种就是继续讲问题声明出去,使用throws声明处理。
还记得咱们学习过一个类Objects吗,曾经提到过它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),那么在它的源码中,对对象为null的值进行了抛出异常操做。
public static <T> T requireNonNull(T obj)
:查看指定引用对象不是null。查看源码发现这里对为null的进行了抛出异常操做:
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
声明异常:将问题标识出来,报告给调用者。若是方法内经过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须经过throws进行声明,让调用者去处理。
关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).
声明异常格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }
声明异常的代码演示:
public class ThrowsDemo { public static void main(String[] args) throws FileNotFoundException { read("a.txt"); } // 若是定义功能时有问题发生须要报告给调用者。能够经过在方法上使用throws关键字进行声明 public static void read(String path) throws FileNotFoundException { if (!path.equals("a.txt")) {//若是不是 a.txt这个文件 // 我假设 若是不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw throw new FileNotFoundException("文件不存在"); } } }
throws用于进行异常类的声明,若该方法可能有多种异常状况产生,那么在throws后面能够写多个异常类,用逗号隔开。
public class ThrowsDemo2 { public static void main(String[] args) throws IOException { read("a.txt"); } public static void read(String path)throws FileNotFoundException, IOException { if (!path.equals("a.txt")) {//若是不是 a.txt这个文件 // 我假设 若是不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw throw new FileNotFoundException("文件不存在"); } if (!path.equals("b.txt")) { throw new IOException(); } } }
若是异常出现的话,会马上终止程序,因此咱们得处理异常:
try-catch的方式就是捕获异常。
捕获异常语法以下:
try{ 编写可能会出现异常的代码 }catch(异常类型 e){ 处理异常的代码 //记录日志/打印异常信息/继续抛出异常 }
try:该代码块中编写可能产生异常的代码。
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。
注意:try和catch都不能单独使用,必须连用。
演示以下:
public class TryCatchDemo { public static void main(String[] args) { try {// 当产生异常时,必须有处理方式。要么捕获,要么声明。 read("b.txt"); } catch (FileNotFoundException e) {// 括号中须要定义什么呢? //try中抛出的是什么异常,在括号中就定义什么异常类型 System.out.println(e); } System.out.println("over"); } /* * * 咱们 当前的这个方法中 有异常 有编译期异常 */ public static void read(String path) throws FileNotFoundException { if (!path.equals("a.txt")) {//若是不是 a.txt这个文件 // 我假设 若是不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw throw new FileNotFoundException("文件不存在"); } } }
如何获取异常信息:
Throwable类中定义了一些查看方法:
public String getMessage()
:获取异常的描述信息,缘由(提示给用户的时候,就提示错误缘由。public String toString()
:获取异常的类型和异常描述信息(不用)。public void printStackTrace()
:打印异常的跟踪栈信息并输出到控制台。 包含了异常的类型,异常的缘由,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
finally:有一些特定的代码不管异常是否发生,都须要执行。另外,由于异常会引起程序跳转,致使有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是必定会被执行的。
何时的代码必须最终执行?
当咱们在try语句块中打开了一些物理资源(磁盘文件/网络链接/数据库链接等),咱们都得在使用完以后,最终关闭打开的资源。
finally的语法:
try...catch....finally:自身须要处理异常,最终还得关闭资源。
注意:finally不能单独使用。
好比在咱们以后学习的IO流中,当打开了一个关联文件的资源,最后程序无论结果如何,都须要把这个资源关闭掉。
finally代码参考以下:
public class TryCatchDemo4 { public static void main(String[] args) { try { read("a.txt"); } catch (FileNotFoundException e) { //抓取到的是编译期异常 抛出去的是运行期 throw new RuntimeException(e); } finally { System.out.println("无论程序怎样,这里都将会被执行。"); } System.out.println("over"); } /* * * 咱们 当前的这个方法中 有异常 有编译期异常 */ public static void read(String path) throws FileNotFoundException { if (!path.equals("a.txt")) {//若是不是 a.txt这个文件 // 我假设 若是不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw throw new FileNotFoundException("文件不存在"); } } }
当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,不然finally永远会执行。
多个异常使用捕获又该如何处理呢?
通常咱们是使用一次捕获屡次处理方式,格式以下:
try{ 编写可能会出现异常的代码 }catch(异常类型A e){ 当try中出现A类型异常,就用该catch来捕获. 处理异常的代码 //记录日志/打印异常信息/继续抛出异常 }catch(异常类型B e){ 当try中出现B类型异常,就用该catch来捕获. 处理异常的代码 //记录日志/打印异常信息/继续抛出异常 }
注意:这种异常处理方式,要求多个catch中的异常不能相同,而且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
为何须要自定义异常类:
咱们说了Java中不一样的异常类,分别表示着某一种具体的异常状况,那么在开发中老是有些异常状况是SUN没有定义好的,此时咱们根据本身业务的异常状况来定义异常类。例如年龄负数问题,考试成绩负数问题等等。
在上述代码中,发现这些异常都是JDK内部定义好的,可是实际开发中也会出现不少异常,这些异常极可能在JDK中没有定义过,例如年龄负数问题,考试成绩负数问题.那么能不能本身定义异常呢?
什么是自定义异常类:
在开发中根据本身业务的异常状况来定义异常类.
自定义一个业务逻辑异常: RegisterException。一个注册异常类。
异常类如何定义:
java.lang.Exception
。java.lang.RuntimeException
。要求:咱们模拟注册操做,若是用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。
首先定义一个登录异常类RegisterException:
// 业务逻辑异常 public class RegisterException extends Exception { /** * 空参构造 */ public RegisterException() { } /** * * @param message 表示异常提示 */ public RegisterException(String message) { super(message); } }
模拟登录操做,使用数组模拟数据库中存储的数据,并提供当前注册帐号是否存在方法用于判断。
public class Demo { // 模拟数据库中已存在帐号 private static String[] names = {"bill","hill","jill"}; public static void main(String[] args) { //调用方法 try{ // 可能出现异常的代码 checkUsername("nill"); System.out.println("注册成功");//若是没有异常就是注册成功 }catch(RegisterException e){ //处理异常 e.printStackTrace(); } } //判断当前注册帐号是否存在 //由于是编译期异常,又想调用者去处理 因此声明该异常 public static boolean checkUsername(String uname) throws LoginException{ for (String name : names) { if(name.equals(uname)){//若是名字在这里面 就抛出登录异常 throw new RegisterException("亲"+name+"已经被注册了!"); } } return true; } }
咱们在以前,学习的程序在没有跳转语句的前提下,都是由上至下依次执行,那如今想要设计一个程序,边打游戏边听歌,怎么设计?
要解决上述问题,我们得使用多进程或者多线程来解决.
在操做系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感受是同时运行,那是由于分时交替运行的时间是很是短的。
而在多个 CPU 系统中,则这些能够并发执行的程序即可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每一个处理器来处理一个能够并发执行的程序,这样多个程序即可以同时执行。目前电脑市场上说的多核 CPU,即是多核处理器,核 越多,并行处理的程序越多,能大大的提升电脑运行的效率。
注意:单核处理器的计算机确定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同理,线程也是同样的,从宏观角度上理解线程是并行运行的,可是从微观角度上分析倒是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,咱们把这种状况称之为线程调度。
简而言之:一个程序运行后至少有一个进程,一个进程中能够包含多个线程
咱们能够再电脑底部任务栏,右键----->打开任务管理器,能够查看当前任务的进程:
进程
线程
线程调度:
全部线程轮流使用 CPU 的使用权,平均分配每一个线程占用 CPU 的时间。
抢占式调度
优先让优先级高的线程使用 CPU,若是线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
大部分操做系统都支持多进程并发运行,如今的操做系统几乎都支持同时运行多个程序。好比:如今咱们上课一边使用编辑器,一边使用录屏软件,同时还开着画图板,dos窗口等软件。此时,这些程序是在同时运行,”感受这些软件好像在同一时刻运行着“。
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对咱们的感受要快,看上去就是在同一时刻运行。
其实,多线程程序并不能提升程序的运行速度,但可以提升程序运行效率,让CPU的使用率更高。
Java使用java.lang.Thread
类表明线程,全部的线程对象都必须是Thread类或其子类的实例。每一个线程的做用是完成必定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来表明这段程序流。Java中经过继承Thread类来建立并启动多线程的步骤以下:
代码以下:
测试类:
public class Demo01 { public static void main(String[] args) { //建立自定义线程对象 MyThread mt = new MyThread("新的线程!"); //开启新线程 mt.start(); //在主方法中执行for循环 for (int i = 0; i < 10; i++) { System.out.println("main线程!"+i); } } }
自定义线程类:
public class MyThread extends Thread { //定义指定线程名称的构造方法 public MyThread(String name) { //调用父类的String参数的构造方法,指定线程的名称 super(name); } /** * 重写run方法,完成该线程执行的逻辑 */ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+":正在执行!"+i); } } }