-
java中的异常处理机制
-
异常在java中以类和对象的形式存在,那么异常的继承结构是怎样的?我们可以使用UML图来描述以下继承结构
画UML图的工具:Rational Rose、starUML等
Object下有Throwable(可抛出的)
Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)
Exception下有两个分支:
Exception的直接子类:编译时异常。(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常)
RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管)
-
编译时异常和运行时异常,都是发生在运行阶段,编译阶段异常是不会发生的。编译时异常因为什么而得名?
因为编译时异常必须在编译(编写 )阶段预先处理,如果不处理编译器报错,因此得名。 所有异常都是在运行时发生的。因为只有程序运行阶段才可以new对象。
因为异常的发生就是new对象
-
编译时异常和运行时异常有什么区别?
-
编译时异常一般发生的概率较高。如:
你看到外面下雨了,倾盆大雨。出门前会预料:如果不打伞,很可能会生病(生病是一种异常)。而且这个异常发生的概率很高,所以我们出门前要那一把伞。“拿一把伞”就是对“生病异常”发生之前的一种处理方式。
对于一些发生概率较高的异常,需要再运行之前对其进行预处理。
-
运行时异常一般发生的概率较低。如:
小明走在大街上,可能会被天上的飞机轮子砸到。被飞机轮子砸到也算是一种异常。但是这种异常发生概率较低。再出门值卡吗你没必要提前对这种发生概率较低的异常进行预处理。如果预处理这种异常,将“活的很累”。
假设你在出门之前,把能够发生的异常都预先处理,你这个人会更加的安全,但是你这个人会活得很累。
假设java中没有对异常进行划分,没有分为编译时异常和运行时异常,所有的异常都需要在编写程序阶段对其进行预处理,将是怎样的效果?
首先,如果这样,程序肯定是绝对的安全的。但是程序员编写程序太累,到处都是处理异常的代码。
-
-
编译时异常还有其他名字:
- 受检异常:CheckedException
- 受控异常
-
运行时异常还有其他名字:
- 未受检异常:UnCheckedException
- 非受控异常
-
再次强调:所有异常都是发生在运行阶段的。
-
java语言中对异常的处理包括两种方式:
-
第一种方式:在方法生命的位置上,使用throws关键字,抛给上一级。
谁调用我,就抛给谁。抛给上一级。
-
第二种方式:使用try…catch语句进行异常的捕捉。
这件事发生了,谁也不知道,因为我抓住了
如:
我是某集团的一个销售员,因为我的失误,导致公司损失了1000元,”损失1000元“这就可以看作是一个异常发生了。我有两种处理方式。
第一种方式:我把这件事告诉我的领导【异常上抛】
第二种方式:我自己掏腰包把这个钱补上了。【异常的捕捉】
张三 - - - -> 李四 - - - - > 王五 - - - - > CEO
思考:
异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。
-
-
注意:java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果:终止java程序的执行。
-
-
什么是UML?有什么用?
UML是一种统一建模语言。一种图标式语言(画图的)。
UML不是只有java中使用。只要是面向对象的编程语言,都有UML。一般画UML图的都是软件架构师或者是系统分析师,这些级别的人使用的。软件设计人员使用UML。
在UML图中可以描述类和类之间的关系、程序执行的流程、对象的状态等。
盖大楼和软件开发一样,一个道理。
盖大楼前,建筑师先画图纸。图纸上的一个一个符号都是标准符号,这个图纸画完,只要是搞建筑的都能看懂,因为这个图纸上标注的这些符号都是一种“标准语言”
在java软件开发中,软件分析师/设计师负责设计类,java软件开发人员必须要能看懂。
-
异常的继承结构图
-
什么是异常,java提供异常处理机制有什么用?
/* 1、什么是异常,java提供异常处理机制有什么用? 以下程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常。 java是很完善的语言,提供了异常的处理方式,以下程序执行过程中出现了不正常的情况,java把该异常信息供程序员参考。 程序员看到异常信息之后,可以对程序进行修改,让程序更加的健壮。 什么是异常:程序执行过程中的不正常情况 异常的作用:增强程序的健壮性。 2、以下程序执行控制台出现了: Exception in thread "main" java.lang.ArithmeticException: / by zero at ExceptionText01.main(ExceptionText01.java:11) 这个信息别我们成为:异常信息,这个信息是JVM打印的。 */ public class ExceptionText01 { public static void main(String[] args) { int a = 10; int b = 0; int c = a / b; //实际上JVM在执行到此处的时候,会new对象:new ArithmeticException("/ by zero"); //并且JVM将new的异常对象抛出,打印输出信息到控制台了 System.out.println(a+"/"+b+"="+c); //此处运行也会创建一个:ArithmeticException类型的异常对象 //System.out.println(100/0); //观察到异常信息之后,对程序进行修改,更加健壮。 /* int a = 10; int b = 2; if(b == 0){ System.out.println("除数不能为0"); return; } //程序执行到此处表示除数一定不是0 int c = a / b; System.out.println(a+"/"+b+"="+c);*/ } }
-
java中异常是以什么形式存在的?
/* java语言中异常是以什么形式存在的呢? 1、异常在java中以类的形式存在,每一个异常类都可以创建异常对象。 2、异常在对应的现实生活这是怎样的? 火灾(异常类): 2008年8月8日 小明家着火了(异常对象) 2008年8月9日 小刚家着火了(异常对象) 2008年9月8日 小红家着火了(异常对象) 类是: 模板 对象是:实际存在的个体 钱包丢了(异常类): 2008年1月1日,小明的钱包丢了(异常对象) 2008年1月9日,小芳的钱包丢了(异常对象) ...... */ public class ExceptionText02 { public static void main(String[] args) { //通过异常类“实例化”异常对象 NumberFormatException nfe = new NumberFormatException("数字格式化异常"); System.out.println(nfe);//java.lang.NumberFormatException: 数字格式化异常 //通过异常类“实例化”异常对象 NullPointerException npe = new NullPointerException("空指针发生了"); System.out.println(npe.toString());//java.lang.NullPointerException: 空指针发生了 } }
-
运行时异常编写程序时可以不处理
//运行时异常编写程序时可以不处理 public class ExceptionText03 { public static void main(String[] args) { /* 程序执行到此处发生了ArithmeticException异常,底层new了一个ArithmeticException异常对象,然后抛出了 由于是main方法调用了100/0,所以这个异常ArithmeticException抛给了main方法,main方法没有处理 将这个异常自动抛给了JVM。JVM最终终止程序的执行。 ArithmeticException 继承 RuntimeException ,属于运行时异常。在编写阶段不需要对这种异常进行预先的处理。 */ System.out.println(100/0); //这里的hello world 没有输出,没有执行。 System.out.println("hello world"); } }
-
编译时异常必须处理
/* 以下代码报错的原因是什么? 因为doSome()方法声明位置上使用了:throws ClassNotFoundException 而ClassNotFoundException是编译时异常。必须编写代码时处理,如不处理,编译报错 */ public class ExceptionText04 { public static void main(String[] args) { //main方法中调用doSome()方法。因为doSome()方法声明位置上有:throws ClassNotFoundException //我们在调用doSome()方法的时候必须对这种异常进行预先的处理。 // 如果不处理,编译器报错.报错信息:Unhandled exception: java.lang.ClassNotFoundException //doSome(); } /** * doSome方法在方法声明的位置上使用了:throws ClassNotFoundException * 这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常 * 叫做:类没找到异常。这个异常直接父类是:Exception,所以ClassNotFoundException属于编译时异常。 * @throws ClassNotFoundException */ public static void doSome() throws ClassNotFoundException{ System.out.println("doSome!!!!"); } }
-
异常的处理方式有两种
public class ExceptionText05 { //第一种处理方式:在方法声明的位置上继续使用:throws,来完成异常的继续上抛。抛给调用者 //上抛类似于推卸责任。(继续把异常传递给调用者) /* public static void main(String[] args) throws ClassNotFoundException { doSome(); }*/ //第二种处理方式:try..catch进行捕捉 //捕捉等于把异常拦下了,异常真正解决了!(调用者是不知道的) public static void main(String[] args) { try{ doSome(); }catch (ClassNotFoundException e){ e.printStackTrace(); } } public static void doSome() throws ClassNotFoundException{ System.out.println("doSome!!!!"); } }
注意事项
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; /* 处理异常的第一种方式:在方法声明的位置上使用throws关键字抛出,谁调用这个方法,我就抛给谁。抛给调用者来处理。 在方法声明的位置上使用throws关键字抛出,谁调用我这个方法,我就抛给谁,抛给调用者来处理. 这种处理异常的态度:上报. 处理异常的第二种方式: 使用try..catch语句对异常进行捕捉,这个异常不会上报,自己把这个事情解决了。异常抛出到此为止,不再上抛了。 注意: 只要异常没有捕捉,采用上抛的方式,此方法的后续代码不会执行 另外需要注意:try语句块中的某一行出现异常,后面的代码不会执行 try..catch捕捉异常之后,后续的代码可以执行. 在以后的开发中,处理编译时异常,应该上抛还是应该捕捉呢? 如果希望调用者来处理,选择throws上抛。 其他情况使用捕捉的方式 */ public class ExceptionText06 { /* 一般不建议在main方法上使用throws,因为这个异常如果真的发生了,一定会抛给JVM,JVM只有终止 异常处理机制的作用是增强程序的健壮性。怎么能做到,异常发生了也不会影响程序的执行。 所以一般main方法中的异常建议使用try..catch进行捕捉。main就不要继续上抛了。 */ // public static void main(String[] args) throws FileNotFoundException { public static void main(String[] args) { //100/0是算术异常,这个异常是运行时异常,你在编译阶段,可以处理,也可以不处理。编译器不管 //System.out.println(100/0);//不处理编译器也不管 //处理以下也可以 /* try{ System.out.println(100/0); }catch(ArithmeticException e){ System.out.println("算术异常了!!!"); }*/ System.out.println("main begin"); try { //try 尝试 m1(); //以上代码出现异常,直接进入catch语句进行捕捉 System.out.println("helloworld"); } catch (FileNotFoundException e) {//catch后面的好像一个形参 //这个分支中可以使用e引用,e引用保存的的内存地址是那个new出的异常对象的内存地址. //catch是捕捉异常之后走的分支。 //在catch分支中干什么?处理异常 System.out.println("文件不存在,可能路径错误,也可能该文件被删除了"); System.out.println(e.toString()); } //try..catch把异常抓住之后,这里的代码会继续执行 System.out.println("main over"); } public static void m1() throws FileNotFoundException { System.out.println("m1 begin"); m2(); //以上代码出现异常,这里也不会执行. System.out.println("m1 over"); } // private static void m2() throws ClassCastException { // 抛别的不行,抛ClassCastException说明你还是没有对FileNotFoundException进行处理 // private static void m2() throws IOException { //抛FIleNotFoundException的父对象IOException,这样是可以的,因为IOException包括FileNotFoundException // private static void m2() throws Exception { //这样也可以,因为Exception包括所有的异常. // private static void m2() throws ClassCastException,FileNotFoundException { //throws也可以写多个异常,可以使用逗号隔开 private static void m2() throws FileNotFoundException { System.out.println("m2 begin"); //编译报错的原因是:m3()方法声明位置上有:throws FileNotFoundException //我们在这里调用m3()方法没有对异常进行预处理,所以编译报错 m3(); //以上如果出现异常,下面的代码是不会执行的 System.out.println("m2 over"); } private static void m3() throws FileNotFoundException { //调用SUN jdk中的某个类的构造方法 //这个类还没接触过,后期IO流的时候就知道了。我们只是借助这个类学习一下异常处理机制。 //创建一个输入流对象,该流指向一个文件。 /* 编译报错的原因是什么? 第一:这里调用了一个构造方法:FileInputStream(String name) 第二:这个构造方法的生命位置上有:throws FileNotFoundException 第三:通过类的继承结构可以看到:FileNotFoundException父类是IOException,IOException的父类是Exception 最终得知:FileNotFoundException是编译时异常。 错误原因:编译时异常要求程序员编写程序阶段必须对他进行处理,不处理编译器就报错 */ //new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\dd.txt"); //我们采用第一种处理方式:在方法声明的位置上使用throws继续上抛。 //一个方法当中的代码出现异常之后,如果上报的话,此方法结束. new FileInputStream("D:\\360Downloads\\Softwaress\\漏洞补丁目录\\dd.txt"); System.out.println("如果new FIleInputStream出现异常,则这里的代码也不会执行"); } }
-
深入try…catch
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; /* 深入try..catch 1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型 2、catch可以写多个。建议catch的时候,精确的一个一个处理,这样有利于程序的调试。 3、catch写多个的时候,从上到小,必须遵守从小到大。 */ public class ExceptionText07 { public static void main(String[] args) { /* try { FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt"); System.out.println("以上出现异常,此处无法执行"); } catch (FileNotFoundException e) { System.out.println("文件不存在"); } System.out.println("helloworld");*/ /* try { FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt"); } catch (IOException e) {//多态:IOException e = new FileNotException(); System.out.println("文件不存在"); }*/ //不够精确 /* try { //创建输入流 FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt"); //读文件 fis.read(); } catch (Exception e) {//所有的异常都走这个分支. System.out.println("文件不存在"); }*/ /* try { //创建输入流 FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt"); //读文件 fis.read(); } catch (FileNotFoundException e) { System.out.println("文件不存在"); }catch (IOException e){ System.out.println("读文件报错了"); }*/ //编译报错:Exception 'java.io.FileNotFoundException' has already been caught // 已经被捕捉过了,因为IOException是FIleNotFoundException的父类型。 /* try { //创建输入流 FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt"); //读文件 fis.read(); }catch (IOException e){ System.out.println("读文件报错了"); } catch (FileNotFoundException e) { System.out.println("文件不存在"); }*/ /* JDK8的新特性: catch中的异常类型中间可以采用“或(|)”的方式 */ try { //创建输入流 FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt"); //进行数学运算 System.out.println(100/0);//这个异常是运行时异常,编写程序时可以处理,也可以不处理 }catch (FileNotFoundException | ArithmeticException | NullPointerException e) { System.out.println("文件不存在?算术异常?空指针异常?都有可能!"); } } }
-
异常对象的两个非常重要的方法
/* 异常对象有两个非常重要的方法: 获取异常简单的描述信息: String msg = exception.getMessage(); 打印异常追踪的堆栈信息: exception.printStackTrace(); */ public class ExceptionText08 { public static void main(String[] args) { //这里只是为了测试getMessage()方法和printStackTrace()方法 //这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。 NullPointerException npe = new NullPointerException("空指针异常对象npe"); //获取异常简单描述信息:这个信息实际上就是构造方法上的String参数。 String msg = npe.getMessage(); System.out.println(msg); //打印异常堆栈信息。java后台打印异常堆栈追踪信息处理信息的时候,采用了异步线程的方式打印的 npe.printStackTrace(); for (int i = 0; i <500 ; i++) { System.out.println(i); } System.out.println("helloworld"); } }
如何查看异常的追踪信息
import java.io.FileInputStream; import java.io.FileNotFoundException; /* 异常对象的两个方法: String msg = e.getMessage(); e.printStackTrace (一般使用这个) 我们以后查看异常的追踪信息,应该怎么看,可以快速的调试程序呢? 异常信息追踪信息,从上往下一行一行看。但是要注意的是:SUN写的代码就不用看了(看包名就知道是自己写的还是SUN写的) 主要问题出现在自己编写的代码上面。 */ public class ExceptionText09 { public static void main(String[] args) { try { m1(); } catch (FileNotFoundException e) { //获取异常的简单描述信息 System.out.println(e.getMessage()); //打印异常堆栈追踪信息!!! //在实际的开发中,建议使用这个,养成好习惯。这行代码要写上,不然出问题了都不知道 e.printStackTrace(); /* java.io.FileNotFoundException: D:\360驱动大师目录\下载保存目录\SeachDownload\eee.txt (系统找不到指定的文件。) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at java.io.FileInputStream.<init>(FileInputStream.java:93) at ExceptionText09.m3(ExceptionText09.java:47) at ExceptionText09.m2(ExceptionText09.java:43) at ExceptionText09.m1(ExceptionText09.java:39) at ExceptionText09.main(ExceptionText09.java:16) 因为47行出问题导致43行出问题,43行出问题导致39行出问题,49行出问题导致16行出问题,应该先看47行,47行是代码错误根源 */ //这里的程序不耽误执行,很健壮(服务器不会因为遇到异常而宕机) System.out.println("helloworld"); } } private static void m1() throws FileNotFoundException { m2(); } private static void m2() throws FileNotFoundException { m3(); } private static void m3() throws FileNotFoundException { new FileInputStream("D:\\360驱动大师目录\\下载保存目录\\SeachDownload\\eee.txt"); } }
-
关于try…catch的finally子句
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Parameter; /* 关于try..catch中的finally子句: 1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try..catch语句块中的代码出现了异常 finally子句必须和try一起出现,不能单独编写 2、finally语句通常使用在哪些情况下? 通常在finally语句块中完成资源的释放/关闭,因为finally中的代码有保障。 即使try语句块中的代码出现异常,finally中的代码也会执行。 */ public class ExceptionText10 { public static void main(String[] args) { FileInputStream fis = null; try { //创建输入流对象 fis = new FileInputStream("D:\\360驱动大师目录\\下载保存目录\\SeachDownload\\ee.txt"); //开始读文件.... String s = null; //这里一定会出现空指针异常 s.toString(); System.out.println("helloworld"); //流使用完需要关闭,因为流是占用资源的 //即使以上程序出现异常,流也必须关闭。放在这里可能关闭不了 fis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (NullPointerException e){ e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } finally { System.out.println("hello paoke"); //流的关闭放在这里比较保险,finally中的代码是一定会执行的。即使try中的代码出现了异常,也可以执行。 //当fis为空时不执行 if(fis != null) { try { //close()方法有异常,采用捕捉的方式 fis.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("hello kity"); } }
-
try和finally,可以没有catch。
以及try中如果有return,finally怎么执行
//finally语句:放在finally语句块中的代码是一定会执行的。 public class ExceptionText11 { public static void main(String[] args) { /* try和finally,没有catch可以吗?可以 try不能单独使用 try finally可以联合使用 以下代码的执行顺序: 先执行try.. 在执行finally... 最后执行return(return语句只要执行,方法必然结束) */ try{ System.out.println("try...."); return; }finally { //fianlly中的语句会执行,能执行到 System.out.println("finally"); } //Unreachable statement。这里不能写语句,因为这个代码是无法执行到的 //System.out.println("helloworld"); } }
使用System.exit(0);退出JVM时,finally…
//finally public class ExceptionText12 { 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 ExceptionText13 { public static void main(String[] args) { int result = m(); System.out.println(result);//100 } /* java语法规则(有一些规则是不能破坏的,一旦这么说了,必须这么做): java中有一条这样的规则: 方法体中的代码必须遵循自上而下的顺序因此逐行执行(亘古不变的 语法) java中还有一条这样的语法规则: return语句一旦执行,整个方法必须结束。(更古不变的语法) */ private static int m() { int i =100; try{ //这行代码出现在int i = 100; 的下面,所以最终结果必须返回100 //return语句还必须保证是最后执行的,一旦执行,整个方法结束。 return i; }finally { i++; } } } /* 反编译之后的结果 public static int m(){ int i = 100; int j = i; i++; return j; } */
-
final、finally、finalize有什么区别?
/*final 、finally 、finalize有什么区别? final 关键字 final修饰的类无法继承 final修饰的方法无法覆盖 final修饰的变量不能重新赋值 finally 关键字 finally与try一起联合使用 finally语句块中的代码是一定会执行的 finalize 标识符 是一个Object类中的方法名 这个方法是垃圾回收器GC负责调用的。 */ public class ExceptionText14 { public static void main(String[] args) { //final是一个关键字 final int i = 100; //finally也是一个关键字,和try联合使用,使用在异常处理机制中,在finally语句块中的代码是一定会执行的 try{ }finally{ System.out.println("finally"); } //finalize()是Object类中的一个方法,作为方法名出线。所以finalize是标识符 } }
-
如何自己定义异常?
异常类
/* 1、SUN提供的JDK内置的异常肯定是不够用的。在实际的开发中,有很多业务,这些业务出现异常之后,JDK中都是没有的 和业务挂钩的,这些异常类我们可以自己去定义 2、java中怎么自定义异常呢? 两步: 第一步:编写一个类继承Exception或RuntimeException。 第二步:提供两个构造方法,一个无参的,一个带有String参数的。 死记硬背 */ public class MyException extends Exception{//编译时异常:发生概率比较高的时候定义为编译时异常 public MyException(){ } public MyException(String s){ super(s); } } //public class MyExceptio exception RuntimeException{ //运行时异常:发生概率比较低的定义为运行时异常比较好 // //}
创建异常对象
public class ExceptionText15 { public static void main(String[] args) { //创建异常对象(只是new对象,并没有手动抛出) MyException e = new MyException("用户名不能为空"); //打印异常堆栈信息 e.printStackTrace(); //获取异常简单描述信息 String msg = e.getMessage(); System.out.println(msg); } }
-
对之前的数组模拟栈存储进行改进,使用异常进行错误的输出
改进后的栈类
public class MyStack { private Object[] elements ; private int index = -1; public MyStack() { this.elements = new Object[10]; } public MyStack(int i) { this.elements = new Object[i]; } public Object[] getElements() { return elements; } public void setElements(Object[] elements) { this.elements = elements; } //压栈 public void push(Object object) throws MyStackOperationException { if(this.index >= this.elements.length-1){ //学了异常之后对这个进行改进,不再使用输出语句,太low了 /*System.out.println("栈已满,压栈失败"); return;*/ /*//改进: //创建异常对象 MyStackOperationException e = new MyStackOperationException("栈已满,压栈失败"); //手动将异常抛出去 throw e; //这里不能使用try捕捉,自己new了一个异常,自己捕捉,没有意义。栈已满的消息需要传递出去*/ //合并 throw new MyStackOperationException("栈已满,压栈失败"); } elements[++index] = object; System.out.println("元素 "+object+" 压栈成功,栈帧此时指向"+index); } //弹栈 public void pop() throws MyStackOperationException { if(index <= -1){ /*System.out.println("栈已空,弹栈失败"); return;*/ throw new MyStackOperationException("栈已空,弹栈失败"); } System.out.println("元素 " + elements[index] +" 弹栈成功,此时栈帧指向:"+ --index); } }
自定义的内存类
/** * 栈操作异常:自定义异常 */ public class MyStackOperationException extends Exception{//编译时异常 public MyStackOperationException(){ } public MyStackOperationException(String s){ super(s); } }
测试类
//对改良之后的栈进行测试 //注意:最后这个例子,是异常最重要的案例,必须掌握,自定义异常在实际开发中的作用 public class ExceptionText16 { public static void main(String[] args) { //创建栈对象 MyStack stack = new MyStack(); //压栈、这里不能再上抛了,因为JVM发现异常之后,就会终止程序。 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) { //输出异常的简单信息 System.out.println(e.getMessage()); //打印异常信息 //e.printStackTrace(); } //弹栈 try { stack.pop(); stack.pop(); stack.pop(); stack.pop(); stack.pop(); stack.pop(); stack.pop(); stack.pop(); stack.pop(); stack.pop(); //这里栈空了 stack.pop(); } catch (MyStackOperationException e) { //输出异常的简单信息 System.out.println(e.getMessage()); //打印异常信息 //e.printStackTrace(); } } }
-
方法重写时,重写后的方法不能比之前的方法抛出更多的异常
/* 之前在讲方法覆盖时,遗留的问题: 重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,可以更少。 */ public class ExceptionText17 { public static void main(String[] args) { } } class Animal{ public void doSome(){ } public void doOther()throws Exception{ } } class Cat extends Animal{ //编译报错 //public void doSome() throws Exception{} //编译通过 //public void doOther(){} //编译通过 //public void doOther() throws Exception{} //编译通过 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方法完成注册。
用户业务类
package homework; //用户业务类:处理用户相关的业务:例如登录、注册等功能 public class UserService { /** * 用户注册 * @param username 用户名 * @param password 密码 * @throws IllegalNameException 当用户名为null,或者用户名长度<6,或者长度>14时,出现该异常 */ public void register(String username , String password) throws IllegalNameException { //引用等于null的这个判断最好放到所有条件的最前面 //if(username == null || username.length() < 6 || username.length() >14 ){} //再分享一个经验: 写成null ==username 比 username == null 要好。有时候 少一个=就成了:username = null,这种与我们想的不一样,但是这样写不会报错,采用前一种方法就比较保险 // 写成"abc".equals(username) 比 username.equals("abc") 要好 //if( null == username || username.length() < 6 || username.length() >14 ){} if( null == username || username.length() < 6 || username.length() >14 ){ throw new IllegalNameException("用户名长度必须在[6-14]之间"); } //程序能执行到这里,说明用户名没有问题 System.out.println("欢迎["+username+"]"); } }
异常类
package homework; public class IllegalNameException extends Exception{ public IllegalNameException(){ } public IllegalNameException(String s){ super(s); } }
测试类
package homework; public class Text { public static void main(String[] args) { UserService us = new UserService(); try { us.register("abcdef","123"); } catch (IllegalNameException e) { e.printStackTrace(); } } }
-
作业2
开放型题目,随意发挥: 写一个类Army,代表一支军队,这个类有一个属性Weapon数组w(用来存储该军队所拥有的所有武器), 该类还提供一个构造方法,在构造方法里通过传一个int类型的参数来限定该类所能拥有的最大武器数量, 并用这一大小来初始化数组w。 该类还提供一个方法addWeapon(Weapon wa),表示把参数wa所代表的武器加入到数组w中。 在这个类中还定义两个方法attackAll()让w数组中的所有武器攻击; 以及moveAll()让w数组中的所有可移动的武器移动。 写一个主方法去测试以上程序。 提示: Weapon是一个父类。应该有很多子武器。 这些子武器应该有一些是可移动的,有一些 是可攻击的。
可移动接口
package arrayarmy; //可移动的接口 public interface Moveable { //移动行为 void move(); }
可攻击接口
package arrayarmy; //可射击的接口 public interface Shootable { //射击行为 void shoot(); }
武器类
package arrayarmy; //所有武器的父类 public class Weapon { //武器的名字 String name ; public Weapon(String name){ this.name = name; } //重写toString方法 @Override public String toString() { return name; } }
坦克类
package arrayarmy; /** * 坦克是一个武器,可移动,可攻击 */ public class TanK extends Weapon implements Moveable,Shootable{ public TanK(String name) { super(name); } @Override public void move() { System.out.println("坦克移动"); } @Override public void shoot() { System.out.println("坦克攻击"); } }
高射炮类
package arrayarmy; //高射炮是一个武器,但是不能移动,只能射击。 public class GaoShePao extends Weapon implements Shootable{ public GaoShePao(String name) { super(name); } @Override public void shoot() { System.out.println("高射炮开炮"); } }
战斗机类
package arrayarmy; //战斗机:是武器,可移动、可攻击。 public class Fighter extends Weapon implements Shootable,Moveable{ public Fighter(String name) { super(name); } @Override public void move() { System.out.println("战斗机起飞"); } @Override public void shoot() { System.out.println("战斗机开炮"); } }
物资飞机类
package arrayarmy; //物资飞机:只能运输物资 public class WuZiFeiJi extends Weapon implements Moveable{ public WuZiFeiJi(String name) { super(name); } @Override public void move() { System.out.println("物资飞机运输物资"); } }
添加武器异常类
package arrayarmy; public class AddWeaponException extends Exception{ public AddWeaponException(){ } public AddWeaponException(String s){ super(s); } }
军队类
package arrayarmy; /* 开放型题目,随意发挥: 写一个类Army,代表一支军队,这个类有一个属性Weapon数组w(用来存储该军队所拥有的所有武器), 该类还提供一个构造方法,在构造方法里通过传一个int类型的参数来限定该类所能拥有的最大武器数量, 并用这一大小来初始化数组w。 该类还提供一个方法addWeapon(Weapon wa),表示把参数wa所代表的武器加入到数组w中。 在这个类中还定义两个方法attackAll()让w数组中的所有武器攻击; 以及moveAll()让w数组中的所有可移动的武器移动。 写一个主方法去测试以上程序。 提示: Weapon是一个父类。应该有很多子武器。 这些子武器应该有一些是可移动的,有一些 是可攻击的。 */ //军队 //类在强制类型转换过程中,如果是类转换成接口类型。那么类和接口之间不需要存在继承关系,也可以转换,java语法中允许 public class Army { //武器数组 Weapon[] weapons; /** * 创建军队的构造方法 * @param count 武器数量 */ public Army(int count){ //动态初始化数组中的每一个元素默认值都是null,武器数组是有了,但是武器数组中没有放武器 this.weapons = new Weapon[count]; } /** * 将武器加入数组weapons中 * @param weapon 要放入数组的武器 */ public void addWeapon(Weapon weapon) throws AddWeaponException { for(int i = 0 ; i < weapons.length ; i++){ if(null == weapons[i]){ weapons[i] = weapon; System.out.println(weapon+"入队"); return; } } //程序如果执行到这里,说明武器没有添加成功。 throw new AddWeaponException("武器库已满,无法放入武器"); } /** * 所有可攻击的武器攻击 */ public void attackAll(){ //遍历数组 for(int i = 0 ; i < weapons.length ; i++){ //Weapon是所有武器的父类,但是需要调用的是子类武器的方法,所以这里使用向下转型 //看weapons[i]是否具有可攻击的方法,如果有,转型----> 调用该方法 if ( weapons[i] instanceof Shootable){ Shootable shootable = (Shootable)weapons[i]; shootable.shoot(); } } } /** * 所有可移动的武器移动 */ public void moveAll(){ //遍历数组 for(int i = 0 ; i < weapons.length ; i++){ if( weapons[i] instanceof Moveable){ Moveable moveable =(Moveable)weapons[i]; moveable.move(); } } } }
测试类
package arrayarmy; public class Text { public static void main(String[] args) { //构建一个军队army Army army = new Army(4); //这个军队的武器库里只能存放四个武器 //创建武器对象 Fighter fighter = new Fighter("战斗机"); GaoShePao gaoShePao = new GaoShePao("高射炮"); TanK tanK = new TanK("坦克"); WuZiFeiJi wuZiFeiJi = new WuZiFeiJi("物资飞机"); WuZiFeiJi wuZiFeiJi2 = new WuZiFeiJi("物资飞机2"); //添加武器 try{ army.addWeapon(fighter); army.addWeapon(gaoShePao); army.addWeapon(tanK); army.addWeapon(wuZiFeiJi); army.addWeapon(wuZiFeiJi2); }catch(AddWeaponException e){ System.out.println(e.getMessage()); } //让所有可移动的移动 army.moveAll(); //让所有可攻击的攻击 army.attackAll(); } }