Java基础--------异常


                                                     异常


异常的概述(个人理解)我们在编写程序的时候在很多时候,可能会有好多问题存在,为了将来方便的表示这些问题的原因,类型,位置,Java就提供了异常对象供我们使用。

    在别的地方看到的例子,觉得描述的挺好,就拿来帮助理解:流水线上的产品,前面一直都很顺利,到了某个环节发生了问题,产品就做不下去了,要人为的去检查哪儿出了错误,对于不同的错误需要用不同的解决方案,不同的解决方案在异常中就可以体现。


其实异常就是程序中出现了不正常的情况。这个问题按照面向对象思想进行描述,并封装成了对象。  

    因为问题的产生有产生的原因、有问题的名称、有问题的描述等多个属性信息存在。当出现多属性信息最方便的方式就是将这些信息进行封装。异常就是java按照面向对象的思想将问题进行对象封装。这样就方便于操作问题以及处理问题。

    

   出现的问题有很多种,比如脚标越界,空指针等都是。就这些问题进行分类,而且这些问题都有共性内容

比如:每一个问题都有名称,同时还有问题描述的信息,问题出现的位置,所以可以不断的向上抽取,形成了异常体系。


-------java.lang.Throwable

    Throwable:可抛出的。 

        |--Error: (严重)错误,一般情况下,不编写针对性的代码进行处理,通常是jvm发生的,需要对程序进行修正。

        |--Exception:(不严重)异常,可以有针对性的处理方式 :

                                           |--- 编译期间:非RuntimeException的,这个是需要我们处理的。

                                           |--- 运行期间:RuntimeException,这个是不需要我们处理的,需要修改代码。


   无论是错误还是异常,它们都有具体的子类体现每一个问题,它们的子类都有一个共性,就是都以父类名作为子类的后缀名。


public class ExceptionDemo {
	public static void main(String[] args) {
		// 运行时期
		int a = 10;
		int b = 0;
		System.out.println(a / b);

		// 编译时期
		// FileInputStream fis = new FileInputStream("a.txt");
	}
}


如果在编写程序的过程中出现了问题,我们自己没有处理,jvm会采用自动的处理方式:它会把异常的类型,原因,位置直接显示在控制台,后面的代码是不能执行的。


那么,如果程序有异常,怎么解决呢?

A:编写处理代码  

          基本格式:

                  try{

                         可能发生问题的代码。

                       }catch(异常类名  变量名){

                          异常处理代码;

                       }finally{

                          释放资源的代码;

                       }

             注意:一旦有一次发生,就会立马执行catch里面的代码。

          变形格式:
              try...catch
              try...catch...catch
              try...catch...catch...finally
              try...finally

          JDK7针对多个catch进行了优化:
               catch(异常1 | 异常2 | 异常3 ... 变量){}
           注意:这些异常必须是平级关系。
       和try...catch...catch的不同点是:JDK7的这种方案是必须平级关系,不能有子父关系。
       而try...catch...catch父亲放最后是可以的。


B:抛出处理

           用throws关键字在方法上声明(抛出)。
下面是代码中遇到的各种异常情况:

1)一个代码中,有多个问题,怎么解决呢?

/*
 * 一个代码中,有多个问题,怎么解决呢?
 * A:一个个用异常处理方案解决。
 * B:针对所有问题,写一个try...catch代码。
 * 		try{}catch(){}catch(){}...
 * 注意:在异常处理中,一旦try里面有问题了。就直接跳到catch里面执行。
 */
public class ExceptionDemo2 {
	public static void main(String[] args) {
		// method1();  // 针对每一个异常写一个try...catch代码。
		
                  method2();      //一个个用异常处理方案解决。
	}

	public static void method2() {
		try {
			//错误类型: ArithmeticException
			int a = 10;
			// int b = 0;
			int b = 2;
			System.out.println(a / b);

			// 错误类型:ArrayIndexOutOfBoundsException
			int[] arr = { 1, 2, 3 };
			System.out.println(arr[3]);

			// 继续操作
			System.out.println("over");  
		} catch (ArithmeticException ae) {
			System.out.println("除数不能为0");
		} catch (ArrayIndexOutOfBoundsException ae) {
			System.out.println("数组越界异常");
		}
	}

	// 针对每一个异常写一个try...catch代码。
	public static void method1() {
		// ArithmeticException
		int a = 10;
		int b = 0;
		try {
			System.out.println(a / b);
			System.out.println("hello");
		} catch (ArithmeticException ae) {
			System.out.println("除数不能为0");
		}

		// ArrayIndexOutOfBoundsException
		int[] arr = { 1, 2, 3 };
		try {
			System.out.println(arr[3]);
		} catch (ArrayIndexOutOfBoundsException ae) {
			System.out.println("数组越界异常");
		}
		// 继续操作
		System.out.println("over");
	}
}


A:如果把method2()先注释掉,编译后打出的结果为:


                               
为什么没有打出Hello? 

   这是因为运行到System.out.println(a / b);时,程序出现了错误,然后就会直接跳到catch里面的代码执行,看看是否是ArithmeticException这个问题,判断之后发现是,就执行了System.out.println("除数不能为0");


B:如果把method1()先注释掉,编译后打出的结果为:

                               

  为什么只打出了这两个呢? 接下来一块分析一下,程序走到System.out.println(arr[3]);时,发现索引越界了,于是直接直接跳到catch里面的代码执行,发现第一个不符合,就跳到下一个catch接着判断,符合,于是就执行了第二个catch里面的代码,System.out.println("数组越界异常");

2)针对多个异常,写一个try的代码,catch里面会不会有顺序问题呢?下面咱们用一个代码来测试一下

public static void main(String[] args) {
			method1();
			// method2();
			// method3();
		}

		public static void method3() {
			try {
				// ArithmeticException
				int a = 10;
				// int b = 0;
				int b = 2;
				System.out.println(a / b);

				// ArrayIndexOutOfBoundsException
				int[] arr = { 1, 2, 3 };
				// System.out.println(arr[3]);
				System.out.println(arr[2]);

				// 这里又出现了一个问题,这里很明显是一个空指针问题。
				// 假设我们忘了,也就是说我们不知道这是一个什么异常。
				// 怎么处理呢?
				String s = null;
				System.out.println(s.length());

				// 继续操作
				System.out.println("over");
			} catch (Exception e) {
				System.out.println("空指针问题");
			}
			// catch (ArrayIndexOutOfBoundsException ae) {
			// System.out.println("数组越界异常");
			// } catch (ArithmeticException ae) {
			// System.out.println("除数不能为0");
			// }
		}

		public static void method2() {
			try {
				// ArithmeticException
				int a = 10;
				// int b = 0;
				int b = 2;
				System.out.println(a / b);

				// ArrayIndexOutOfBoundsException
				int[] arr = { 1, 2, 3 };
				// System.out.println(arr[3]);
				System.out.println(arr[2]);

				// 这里又出现了一个问题,这里很明显是一个空指针问题。
				// 假设我们忘了,也就是说我们不知道这是一个什么异常。
				// 怎么处理呢?
				String s = null;
				System.out.println(s.length());

				// 继续操作
				System.out.println("over");
			} catch (Exception e) {
				System.out.println("空指针问题");
			}
		}

		public static void method1() {
			try {  
				    //第一部分
				// ArithmeticException
				int a = 10;
				 int b = 0;
				 //int b = 2;
				System.out.println(a / b);
				
	           //第二部分
	   // ArrayIndexOutOfBoundsException
				int[] arr = { 1, 2, 3 }; 
				System.out.println(arr[3]); 
				//System.out.println(arr[2]);
				
	                             //第三部分
	             // 这里又出现了一个问题,这里很明显是一个空指针问题
				// 假设我们忘了,也就是说我们不知道这是一个什么异常。// 怎么处理呢?
				//String s = null;
				//System.out.println(s.length());
				// 继续操作
		    	System.out.println("over");
			}
			catch (ArrayIndexOutOfBoundsException ae) {
				System.out.println("数组越界异常");
			} catch (ArithmeticException ae) {
					System.out.println("除数不能为0");
					//
			} catch (Exception e) {
						//System.out.println("空指针问题");
			        }
		}

 
在上面程序中,A:如果我们把method2();method3();都先注释掉,打印的结果会是什么呢? 

                      

为什么程序中System.out.println(arr[3]);肯定会出错,而没有打印“数组越界异常”呢? 这是因为程序执行到System.out.println(a / b);时,发现除数为0,就直接跳到下面catch里面找,知道最后一个catch里面符合,于是就打印出"除数不能为0",程序结束,而没有再看其他的代码。

B:如果把程序的第三部分放开,第一、二部分分别把错误的注释掉,运行会出错,但是如果我们不知道这个是什么错,可以直接抛Exception,编译之后发现程序没有出错,如果上面两部分错误部分没有注释掉,也可以把上面两个catch全删了,编译之后也不会出错,为什么呢?因为通过查API,我们发现Exception下面有好多子类,每个子类又有好多子类,Exception,程序会在Exception里面找,找到符合的错误,就会往下执行,这也体现了面向对象的多态。

 其实好多时候,如果你知道程序是什么错误,就不要懒省事抛Exception,因为这样使效率变低,不可取。

C:如果把method1();method2();都先注释掉,结果会是什么呢?

程序会直接出错,而把划红线的两个catch注释掉之后,就没问题了,这是为什么呢?因为Exception是下面两个的父类。

综上ABC三种情况,可以得出以下结论(很重要哦~~

针对多个异常,写一个try的代码,catch里面会不会有顺序问题呢? 

      如果异常是平级关系,没有顺序问题。  

      如果异常存在着子父关系,父一定要放在最后。

                                        

                                      异常处理中JDK7的新特性

JDK7新特性:多个catch用一个catch替代。 不是说多个catch的内容,用一个Exception处理。  

 格式:     catch(异常1 | 异常2 | 异常3 ... 变量名){}

 下面把上面的代码改进一下,大家就理解啦~~

/*
 * JDK7新特性:多个catch用一个catch替代。 不是说多个catch的内容,用一个Exception处理。
 * 格式:
 * 		catch(异常1 | 异常2 | 异常3 ... 变量名){}
 */
public class ExceptionDemo4 {
	public static void main(String[] args) {
		// method();
		method2();
	}

	private static void method2() {
		try {
			// ArithmeticException
			int a = 10;
			// int b = 0;
			int b = 2;
			System.out.println(a / b);

			// ArrayIndexOutOfBoundsException
			int[] arr = { 1, 2, 3 };
			// System.out.println(arr[3]);
			System.out.println(arr[2]);

			// 这里又出现了一个问题,这里很明显是一个空指针问题。
			// 假设我们忘了,也就是说我们不知道这是一个什么异常。
			// 怎么处理呢?
			String s = null;
			System.out.println(s.length());

			// 继续操作
			System.out.println("over");
		} catch (ArrayIndexOutOfBoundsException | ArithmeticException
				| NullPointerException ae) {
			System.out.println("这里出问题了");
		}
	}

	private static void method() {
		try {
			// ArithmeticException
			int a = 10;
			// int b = 0;
			int b = 2;
			System.out.println(a / b);

			// ArrayIndexOutOfBoundsException
			int[] arr = { 1, 2, 3 };
			// System.out.println(arr[3]);
			System.out.println(arr[2]);

			// 这里又出现了一个问题,这里很明显是一个空指针问题。
			// 假设我们忘了,也就是说我们不知道这是一个什么异常。
			// 怎么处理呢?
			String s = null;
			System.out.println(s.length());

			// 继续操作
			System.out.println("over");
		} catch (ArrayIndexOutOfBoundsException ae) {
			System.out.println("数组越界异常");
		} catch (ArithmeticException ae) {
			System.out.println("除数不能为0");
		} catch (NullPointerException ne) {
			System.out.println("空指针问题");
		}
	}
}
把method();注释掉,重写一个method2();我们发现没有报错,这就是JDK7的新特性,使程序变得更简明。

                                     

                                   Throwable的几个重要方法


Throwable中的方法:
     public String getMessage():返回的是异常的消息字符串。
  public String toString():返回异常的简单描述信息。全路径类名 : 消息字符串
     public void printStackTrace():把错误信息显示在控制台。(最重要


附一个代码帮助理解:

public class ExceptionDemo {
	public static void main(String[] args) {
		int a = 10;
		int b = 0;
		try {
			System.out.println(a / b); // 一旦发生问题,在这里其实就产生了一个ArithmeticException对象。
										// 然后,拿着这个对象,去和catch里面的类型进行匹配。如果有,就赋值。
						// new ArithmeticException()
		} catch (ArithmeticException ae) { // ArithmeticException ae = new <span style="font-family: SimSun;">ArithmeticException();</span>
											
			// System.out.println("除数不能为零");
			// 通过分析,那么,ae是一个对象了
			// / by zero
			// System.out.println(ae.getMessage());

			// java.lang.ArithmeticException: / by zero
			// System.out.println(ae.toString());

			// 实际开发中,获取到异常后,<span style="font-family: SimSun;">会</span><span style="font-family: SimSun;">自动判断是某种异常,然后一个错误信息处理界面。</span>
			ae.printStackTrace();
		}

		System.out.println("over");
	}
}
</pre><p><span style="font-size:14px">运行后结果为:</span></p><p></p><p><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-size:14px"><img src="https://img-blog.csdn.net/20150826174717329?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /> </span></span></span></span></span></p><p><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-size:14px">程序中的<span style="font-family:SimSun; font-size:14px; line-height:25.98958396911621px">错误信息会显示在控制台上,然后继续执行以下面的代码。</span></span></span></span></span></span></p><p><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-size:14px"><span style="font-family:SimSun; font-size:14px; line-height:25.98958396911621px"></span></span></span></span></span></span></p><p><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-size:14px">                                         </span><strong><span style="font-size:18px">   </span></strong></span></span></span></span></p><p><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><strong><span style="font-size:18px">                                  </span><span style="font-size:24px">       finally的使用</span></strong></span></span></span></span></p><p><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><strong><span style="font-size:18px"> </span><span style="font-size:14px">1)基本格式:      </span></strong><span style="font-size:14px">try{  <span style="white-space:pre"> </span><span style="white-space:pre"></span>可能有问题的代码          }catch(异常类名 变量名){<strong><span style="color:#ff0000">           <span style="white-space:pre"> </span>处理方案。变量名.printStackTrace();</span></strong>         }finally{  <span style="white-space:pre"> <span style="white-space:pre"></span></span>释放资源。(数据库,IO)        }</span></span></span></span></span></p><p><span style="font-family:SimSun; line-height:25.98958396911621px; background-color:rgb(255,255,255)"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><span style="font-family:SimSun; line-height:25.98958396911621px"><strong><span style="font-size:14px">2)特点:finally:里面代码永远会执行。</span></strong></span></span></span></span></p><p><span style="font-family:SimSun; font-size:14px; line-height:25.98958396911621px; background-color:rgb(255,255,255)"><span style="font-family:SimSun; font-size:14.44444465637207px; line-height:25.98958396911621px"><span style="font-family:SimSun; font-size:14.44444465637207px; line-height:25.98958396911621px"><span style="font-family:SimSun; font-size:14.44444465637207px; line-height:25.98958396911621px"></span></span></span></span></p><pre name="code" class="java" style="font-weight: bold;">public class FinallyDemo {
	public static void main(String[] args) {
		int a = 10;
		int b = 0;
		try {
			System.out.println(a / b);
		} catch (ArithmeticException ae) {
			ae.printStackTrace();
		} finally {
			System.out.println("over");
		}
	}
}


上面的代码运行后会打印出over,这时有人会问,就算不写finally,over也会打印出来啊,finally有什么用啊? 有时候下面的代码可能和try catch里面的有关,可能是一个整体,加上finally,程序执行完try catch之后,就会直接执行finally里面的,增强程序的可读写。

在这儿说一下相关的面试题:


1、finally里面的代码真的永远会执行吗? 

 会永远执行。但是有一个特殊情况:在代码执行到finally之前,jvm就退出了。

2、假如在catch里面有return语句,请问finally里面的代码还会执行吗?如果执行,是在return前,还是return后?

回答之前,先看一个代码:

public class FinallyTest {
	public static void main(String[] args) {
		int result = method3();
		System.out.println(result);
	}

	private static int method3() {
		int a = 10;
		try {
			System.out.println(a / 0);
			System.out.println("a1:" + a);
		} catch (ArithmeticException ae) {
			System.out.println("a2:" + a); // a2:10
			a = 20;
			return a; //这个时候,在内存中就会有一个路径产生,该路径返回值是20.
					  //但是,它看到了finally代码块,所以,继续执行finally里面的内容		
					  //当finally里面结束后,会回到以前的执行路径。
		} finally {
			System.out.println("a3:" + a); // a3:20
			a = 30;
			//return a; 
		}
		return a;
	}


运行后结果为:

                  

从结果中我们可以看出:finally里面的代码会执行,而且是在return前执行。但是为什么呢?

在内存中就会有一个路径产生,该路径返回值是20.但是,它看到了finally代码块,所以,继续执行finally里面的内容,当finally里面结束后,会回到以前的执行路径。


但是看下面这种情况:把finally里的return语句放开,运行后结果为:

                        

当程序执行到finally里面是,它发现在里面也有一个return语句,这时候内存中会重新产生一个路径覆盖原来的那个,所以打出30.

                                         

                                 抛出异常

把异常抛出,方法调用中常见。
  怎么抛出呢?
    格式:
在方法名称后面跟一个关键字:throws 异常类名
  异常的分类:
  A:Exception下非RuntimeException 编译时异常
   B:RuntimeException 运行时异常 
  异常处理两种方式:
   A:try...catch...finally
  B:throws 


  请问我们选择谁?
   如果能够处理,尽量选择A。否则选择B。


  运行时期异常和编译时期异常的区别?

    A:运行时期异常 
  是不需要try...catch或者throws的。
   B:编译时期异常 
  编译时期异常是需要进行处理的。

                                

                                   自定义异常 

概述:java虽然已经考虑到了很多种异常情况,但是,有些需求java程序是考虑不到的。比如说:要求学生的年龄不能为负数;那么,针对这种情况,我们就要编写自定义异常类进行处理。


如何编写一个自定义异常类呢?
  就是自定义一个类,去继承Exception或者RuntimeException。

  开发中:一般继承自RuntimeException。  


两道面试题:

(1)Exception和RuntimeException的区别?
   A:Exception 编译时期异常,必须处理的。
       如果在方法上,throws了该类型的异常,将来调用者必须处理。
       如果在方法内部,throw了该类型的异常,必须在方法上throws该异常。
   B:RuntimeException 运行时期异常,是不需要处理的。要改代码的。
       如果在方法上,throws了该类型的异常,不需要处理。
       如果在方法内部,throw了该类型的异常,方法上可以throws该异常,也可以不throws该异常。
(2)throw和throws的用法和区别?
  A:throw
      用法:用在方法内部,后面跟的是异常对象名称。
      区别:用throw抛出了编译时期异常,方法上面必须用throws抛出。
         用throw抛出了运行时期异常,方法上面可以不用throws抛出。
 B:throws
     用法:用在方法声明上,后面跟的是异常类名。
     区别:用throws在方法上声明了异常,内部可以没有throw

简单总结为两句话:

      throw在方法内部运行 抛出的是一个异常的对象 只能抛出1个
      throws在方法的声明处 抛出的是一个异常的类 可以多个

   

   到这儿,异常的所有问题都总结完啦~~~   有写的不对的地方,欢迎大家给我指正! 谢谢!!!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值