*异常捕捉和上报的联俄和使用,以及代码块是否执行逻辑
public class ExceptionTest06 {
public static void main(String[] args) {
System.out.println("main begin");
try{
//try尝试执行先
m1();
System.out.println("以上m1()出现异常的话,本句不执行");
}catch(FileNotFoundException e){
//这个分支中可以使用e引用,e引用保存的内存地址是那个new出来异常对象的内存地址。
//catch是捕捉异常之后走的分支。
//在catch分支中处理异常
System.out.println("文件不存在,可能路径错误,也可能被删除了");
e.printStackTrace();//当出现异常时候,此句为啥是在最后才执行喃?java.io.FileNotFoundException: D:Java学习笔记1Java学习笔记-03-常用类.java (系统找不到指定的文件。)
}
//try...catch把异常抓住以后,这里ide代码会继续执行。
System.out.println("main over");
}
private static void m1() throws FileNotFoundException{
System.out.println("m1 begin");
m2();
//以上m2()出现异常,下句over语句不执行
System.out.println("m1 over");
}
private static void m2() throws FileNotFoundException{
System.out.println("m2 begin");
m3();
//以上m3()出现异常,下句over语句不执行
System.out.println("m2 over");
}
private static void m3() throws FileNotFoundException {
//调用SUN jdk中某个类的构造方法
//这个类还没有接触过,后期IO流的时候就知道了。
//我们只是借助这个类学习一下异常处理机制。
//创建一个输入流对象,该流指向一个文件。
new FileInputStream("D:Java学习笔记Java学习笔记-03-常用类.java");
//以上语句出现异常,下句语句不执行、
System.out.println("如果以上代码出现异常,这里代码不会执行");
/*
编译报错的原因是什么?
这里调用了一个构造方法:FileInputStream(Stream name);
这个构造方法的声明位置上有:throws FileNotFoundException,
FileNotFoundException的父类是IOException,IOException的父类是Exception,属于编译时异常
最终得知:FileNotFoundException是编译时异常。
错误原因?编译时异常要求程序员编写程序阶段必须对它进行处理,不处理编译器就报错。
*/
/*
我们采用第一种处理方式:
在方法声明的位置上使用throws继续上抛,在方法声明上面加入throws FileNotFoundException
一个方法体当中的代码出现异常之后,如果上报的话,此方法结束。
只能上抛FileNotFoundException类或者其父类及以上的类。不能是其他异常类。
一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会抛给JVM,JVM只能终止。
异常处理机制的作用就是增强程序的健壮性。怎么能做到,异常发生了也不影响程序的执行。
所以,一般main方法中的异常建议使用try...catch进行捕捉。main就不要继续上抛了。
第二种方式:
使用try...catch语句进行捕捉。
这个异常就不会上报,自己把这个事儿处理了。
异常上抛到此为止,不再上抛了。
注意:
只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。
另外需要注意:try语句块中的某一行出翔异常,该行后面的代码不会执行。
try...catch捕捉异常之后,后续代码可以执行。
*/
}
}
*try...catch深入
public class ExceptionTest07 {
/*
深入try...catch
1.catch后面的小括号中的类型可以是i具体的异常类型,也可以是该异常类型的父类型。
2.catch可以写多个。建议catch的时候,精确的一个一个处理,这样有利于程序的调试。
3.catch写多个的时候,从上到下的Exception的范围必须遵守从小到大。
4.在以后的开发中,处理编译时异常,应该上报还是捕捉呢?怎么选择?
如果希望调用者知道异常并处理,选择throws上报。
其他情况选择捕捉的方式。
*/
public static void main(String[] args) {
/*
try{
FileInputStream fis =new FileInputStream("D:Java学习笔记Java学习笔记-01-面向对象.java1");
}catch(FileNotFoundException e){
System.out.println("找不到该文件");
e.printStackTrace();//java.io.FileNotFoundException: D:J1ava学习笔记Java学习笔记-01-面向对象.java (系统找不到指定的路径。)
}
*/
/*
try{
FileInputStream fis =new FileInputStream("D:Java学习笔记Java学习笔记-01-面向对象.java1");
}catch(IOException e){ //多态。IOException e = new FileNotFoundException();
System.out.println("找不到该文件");
e.printStackTrace();//java.io.FileNotFoundException: D:J1ava学习笔记Java学习笔记-01-面向对象.java (系统找不到指定的路径。)
}
*/
/*
try{
FileInputStream fis =new FileInputStream("D:Java学习笔记Java学习笔记-01-面向对象.java1");
}catch(Exception e){ //多态。Exception e = new FileNotFoundException();
System.out.println("找不到该文件");
e.printStackTrace();//java.io.FileNotFoundException: D:J1ava学习笔记Java学习笔记-01-面向对象.java (系统找不到指定的路径。)
}
*/
/*
try{
//创建输入流
FileInputStream fis =new FileInputStream("D:Java学习笔记Java学习笔记-01-面向对象.java1");
//读文件,如果上面创建输入流已经出现异常了,此句代码不执行了。
fis.read();
}catch(FileNotFoundException e){
System.out.println("找不到该文件");
e.printStackTrace();//java.io.FileNotFoundException: D:J1ava学习笔记Java学习笔记-01-面向对象.java (系统找不到指定的路径。)
}catch(IOException e){
System.out.println("读文件报错了!");
e.printStackTrace();
}
*/
//JDK8及以上版本的新特性
try{
//创建输入流
FileInputStream fis =new FileInputStream("D:Java学习笔记Java学习笔记-01-面向对象.java1");
//进行数学运算
System.out.println(100/0);//这个异常是运行时异常,编写程序时可以处理,也可以不处理。
}catch(FileNotFoundException | ArithmeticException|NullPointerException e){
System.out.println("文件不存在?数学异常?空指针异常?都有可能!");
e.printStackTrace();//java.io.FileNotFoundException: D:J1ava学习笔记Java学习笔记-01-面向对象.java (系统找不到指定的路径。)
}
}
}
*异常对象的常用方法
/*
异常对象有两个非常重要的常用方法:
1.获取异常简单的描述信息:
String msg = exception.getMessage();
2.打印异常追踪的堆栈信息:
exception.printStackTrace();
*/
public class ExceptionTest08 {
public static void main(String[] args) {
//这里只是为了测试getMessage()方法和printStackTrace()方法。
//这里只是new了异常对象,但是没有将异常对象输出。JVM会认为这是一个普通的java对象。
NullPointerException e = new NullPointerException("空指针异常");
//获取异常简单描述信息:这个信息实际上就是构造方法上面String参数。
String msg = e.getMessage();
System.out.println(msg);//空指针异常
//打印异常堆栈信息
//java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。
e.printStackTrace();
System.out.println("hello world!");
}
}
异常对象的常用方法练习2
/*
异常对象的两个方法:
String msg = e.getMessage();
e.printStackTrace();//一般使用这个
我们以后查看异常的追踪信息,我们应该怎么看,可以快速的调式程序呢?
异常信息追踪信息,从上往下一行一行看。
但是需要注意的时:SUN写的代码就不用看了(看包名就知道是否是SUN公司的)。主要的问题时出现在自己编写的代码上。
*/
public class ExceptionTest09 {
public static void main(String[] args) {
try {
m1();
} catch (FileNotFoundException e) {
//获取异常简单描述信息
System.out.println(e.getMessage());
//打印异常堆栈追踪信息!!!
//在实际的开发中,建议使用这个,养成良好习惯。
//这行代码要写上,不然出问题你也不知道。
e.printStackTrace();
/*
java.io.FileNotFoundException: D:Java学习笔记Java学习笔记-01-面向对象.java1 (系统找不到指定的文件。)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at com.kuangyixi.javase.exception.ExceptionTest09.m3(ExceptionTest09.java:52) at com.kuangyixi.javase.exception.ExceptionTest09.m2(ExceptionTest09.java:49) at com.kuangyixi.javase.exception.ExceptionTest09.m1(ExceptionTest09.java:46) at com.kuangyixi.javase.exception.ExceptionTest09.main(ExceptionTest09.java:18) 因为52行出问题导致了49行
49行出问题导致了46行
46行出问题导致了18行
应该先查看52行的代码,52行是代码错误的根源。
*/
}
System.out.println("捕捉之后继续执行");
}
private static void m1() throws FileNotFoundException {
m2();
}
private static void m2() throws FileNotFoundException {
m3();
}
private static void m3() throws FileNotFoundException {
new FileInputStream("D:Java学习笔记Java学习笔记-01-面向对象.java1");
}
}
*try...catch和finally
/*
关于try...catch中的finally字句:
1.在finally字句中的代码时最后执行,并且时一定会执行的,即使try语句块中的代码出现了异常。
finally字句必须和try一起出现,不能单独编写。
2.finally语句通常使用在哪些情况下呢?
通常在finally语句块中完成资源的释放/关闭。
因为finally中的代码比较有保障。
即使try语句块中的代码出现异常,finally中代码也会正常执行。
*/
public class ExceptionTest10 {
public static void main(String[] args) {
FileInputStream fis=null;//声明位置放在try外面,这样在finally中才能用。
try {
//创建输入流对象
fis = new FileInputStream("D:Java学习笔记Java学习笔记-01-面向对象.java");
//开始读文件...
String s = null;
//这里一定会出翔空指针异常!
s.toString();
System.out.println("hello world!!!");
//流使用完需要关闭,因为流是占用资源的。
//即使以上程序出现异常,流也必须要关闭
//放在这里有可能流关闭不了
//fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e){
e.printStackTrace();
} finally {
System.out.println("hello haoke!");
//流的关闭放在这里比较保险
//finally中的代码时一定会执行的。
//即使try中出现了异常!
if (fis != null) {
try {
//close()方法有异常,采用捕捉的方式。
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("hello kitty!!");
}
}
*try和finally,没有catch
/*
finally语句
放在finally语句块中的代码是一定会执行的。
*/
public class ExceptionTest11 {
public static void main(String[] args) {
/*
try和finally,没有catch可以吗?可以。
try不能单独使用。
try finally可以联合使用。
以下代码的执行顺序:
先执行try...
再执行finally...
最后执行return(return语句只要执行方法必然结束。)
*/
try{
System.out.println("try....");
return;
}finally{
System.out.println("finally...");
}
//这里不能写语句,因为这个代码是无法执行到的;
//System.out.println("hello world!!");
}
}
*finally语句不执行的情况
/*
finally语句不执行的情况
*/
public class ExceptionTest12 {
public static void main(String[] args) {
try{
System.out.println("try....");
//退出JVM
System.exit(0);
//退出JVM之后,finally语句中的代码就不执行了。
}finally{
System.out.println("finally...");
}
}
}
*finally经典面试题
/*
finally面试题
*/
public class ExceptionTest13 {
public static void main(String[] args) {
int result = m();
System.out.println(result);//100
}
/*
java语法规则(有一些规则是不能破坏的,一旦这么说了,就必须这么做!):
java中有一条这样的规则:
方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!)
java中还有一条语法规则:
return语句一旦执行,整个方法必须结束(亘古不变的语法!)
*/
public static int m(){
int i = 100;
try{
//return i;这行代码出现后JVM先保存i的值为100,待finally执行完毕后再返回100结束方法;
//return语句还必须保证是最后执行的。一旦执行,整个方法结束。
//这个return一定是最后执行的。
return i;
}finally {
i++;
}
}
}
/*
反编译之后的效果
public static int m()
{
int i = 100;
int j = i;
i++;
return j;
Exception exception;
exception;
i++;
throw exception;
}
*/
*final finally finalize有什么区别?
/*
final finally finalize有什么区别?
final 关键字
final修饰的变量不能重新赋值,
final修饰的方法不能被覆盖,
final修饰的类不能被继承.
finally 关键字
和try联合使用。
finally语句块中的代码是必须执行的。
finalize 标识符
finalize()是Object类中的一个方法名。
这个方法是JVM的垃圾回收器GC负责调用。
*/
public class ExceptionTest14 {
public static void main(String[] args) {
//final是一个关键字。表示最终的。不变的。
//final修饰的变量不能重新赋值,修饰的方法不能被覆盖,修饰的类不能被继承。
//public static final 表示常量;
final int i = 100;
//i = 200;//不行
//finally也是一个关键字,和try联合使用,使用在异常处理机制中
//在finally语句块中的代码时一定会执行的。
try{
}finally{
System.out.println("finally...");
}
//finalize()是Object类中的一个方法。作为方法名出现。
//所以finalize是标识符。
//finalize()方法是JVM的GC垃圾回收器负责调用。
Object obj;
}
}
*自定义异常类
/*
1.SUN提供的JDK内置的异常肯定是不够用的。在实际的开发中,有很多业务
这些业务出现异常之后,JDK中都是没有的。和业务挂钩的。那么异常类我们可以自己定义。
2.java中怎么自定义异常
第一步:编写一个类继承Exception或者RuntimeException
第二步:提供两个构造方法,一个无参数的,一个带有String参数的。
*/
//public class MyException extends RuntimeException{//运行时异常
public class MyException extends Exception{//编译时异常
public MyException(){
super();
}
public MyException(String s){
super(s);
}
}
*使用自定义异常类
public class ExceptionTest15 {
public static void main(String[] args) {
//创建异常对象(只new了异常对象,并没有手动抛出)
MyException e =new MyException("用户名不能为空");
//打印异常堆栈信息
e.printStackTrace();
//获取异常简单描述信息
String msg =e.getMessage();
System.out.println(msg);
}
}
*自定义异常在实际开发中的作用
//测试改良之后的MyStack
//注意:以下案例,是异常最重要的案例。必须掌握。自定义异常在实际开发中的应用。
public class ExceptionTest16 {
public static void main(String[] args) {
//创建栈对象
MyStack stack = new MyStack();
//压栈
try {
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
//这里栈满了
stack.push(new Object());
} catch (MyStackOperationException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
//弹栈
try {
stack.pop(new Object());
stack.pop(new Object());
stack.pop(new Object());
stack.pop(new Object());
stack.pop(new Object());
stack.pop(new Object());
stack.pop(new Object());
stack.pop(new Object());
stack.pop(new Object());
stack.pop(new Object());
//这里栈已空了
stack.pop(new Object());
} catch (MyStackOperationException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
/*
栈操作异常:自定义异常
*/
public class MyStackOperationException extends Exception{ //编译时异常!
public MyStackOperationException() {
super();
}
public MyStackOperationException(String s) {
super(s);
}
}
public class MyStack {
// 1/这个栈可以存储java中的任何引用类型的数据
// 2/在栈中提供push方法模拟压栈。(栈满了,要有提示信息。)
// 3/在栈中提供pop方法模拟弹栈。(栈空了,也要有提示信息。)
// 4/编写测试程序,new栈对象,调用push pop方法来模拟压栈弹栈动作。
private Object[] elements;
//set和get也许用不上,但是必须协商,这是规矩。你使用IDEA生成就行了。
//封装:第一步:属性私有化,第二部:对外提供set和get方法。
//Alt+insert
private int index;
public void push(Object obj) throws MyStackOperationException {
if (this.index>=elements.length-1) {
/*
改良之前
System.out.println("栈已满,压栈失败");
return;
*/
//改良之后
//创建异常对象
MyStackOperationException e = new MyStackOperationException("栈已满,压栈失败");
//手动将异常跑出去
throw e;//这里捕捉没有意义,自己new一个异常,自己捉,没有意义。栈已满这个信息需要传递出去。
//可以合并以上两行代码:throw new MyStackOperationException("栈已满,压栈失败");
}
this.index++;
this.elements[index]=obj;
//所有System.out.println("引用")方法执行时,如果输出的是引用时,会自动调用引用的toString方法。
System.out.println("压栈"+obj+"元素成功,栈帧指向"+index);
}
//弹栈就是往外取出元素,每取出一个,栈帧减1;
public void pop(Object obj) throws MyStackOperationException {
if (this.index<=-1){
/*
改良之前
System.out.println("栈空了,弹栈失败");
return;
*/
//改良之后
throw new MyStackOperationException("栈空了,弹栈失败");
}
System.out.print("弹栈"+elements[index]+"元素成功。");
this.index--;
System.out.println("栈帧指向"+index);
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public Object[] getElements() {
return elements;
}
public void setElements(Object[] elements) {
this.elements = elements;
}
public MyStack() {
//一维数组动态初始化
//默认初始化容量为10
this.elements=new Object[10];
this.index = -1;
}
}
*关于异常的方法覆盖
/*
重写之后的方法不能比重谢之前的方法抛出更多(更宽泛)的异常,可以更少。只是针对编译时异常,所有运行时异常除外。
这是之前讲解方法覆盖的时候遗留的问题
*/
public class ExceptionTest17 {
public static void main(String[] args) {
}
public void doSome() { }
public void doOther() throws Exception{ }
}
class Cat extends ExceptionTest17{
//方法覆盖成功
//public void doSome() throws RuntimeException{ }
//方法覆盖失败
//public void doSome() throws Exception{ }
//方法覆盖成功
//public void doOther() throws Exception{ }
//方法覆盖成功
//public void doOther() { }
//方法覆盖成功
//public void doOther() throws NullPointerException{ }
}
/*
总结异常中的关键字:
异常捕捉:
try
catch
finally
throws 在方法声明位置上使用,表示上报异常信息给调用者
throw 手动抛出异常!
*/
异常作业练习:
编写程序模拟用户注册:
1.程序开始执行时,提示用户输入“用户名”和“密码”信息。
2.输入信息之后,后台java程序模拟用户注册。
3.注册时用户要求长度在6-14]之间,小于或者大于都表示异常。
注意:
完成注册的方法放到一个单独的类中。
异常类自定义即可。
class UserService{
public void register(String username,String password){
//这个方法中完成注册!
}
}
编写main方法,在main方法中接收用户输入的信息,在main方法中调用UserService的register方法完成注册。