内部类,异常
1内部类
在java中,一个文件可以定义多个类,文件名必须和public类型的类的类名保持一致。这两个类 是平行关系。
1.1 内部类概念:
在java中,一个类可以定义在一个类的内部 ,定义在内部的类称为内部类(inner class),定义在类的外部的类叫做外部类(outer class)。
1 public class Outer { 2 3 [修饰符] class Inner{ 4 5 } 6 }
编译后的结果
内部类根据具体的修饰符和具体位置分为很多情况。
2.1 成员内部类
一个内部类作为外部类的成员而存在,此时该内部类称为外部类的成员内部类。一般而言,内部类都用默认修饰符。
public class Outer { class Inner{ } }
2.1.1 内部类创建对象
1 public class Test01 { 2 public static void main(String[] args) { 3 4 // 【1】创建外部类对象 5 Outer outer = new Outer(); 6 // 【2】创建内部类对象 7 Inner inner = outer.new Inner(); 8 inner.showInfo(); 9 } 10 }
2.1.2 内部类可以访问外部类的私有变量
1 public class Outer { 2 3 private String name = "Outer"; 4 5 class Inner{ 6 7 public void showInfo() { 8 System.out.println(name); 9 } 10 } 11 }
特殊情况:外部类和内部类的变量同名
1 public class Outer { 2 3 private String name = "Outer"; 4 5 class Inner{ 6 7 private String name = "Inner"; 8 9 public void showInfo() { 10 // String name = "show info"; 11 12 // 访问外部类的私有成员 13 System.out.println(this.name); 14 15 // 访问外部类的私有成员 16 System.out.println(Outer.this.name); 17 18 } 19 } 20 }
3 静态内部类
如果一个内部类被static修饰,我们认为这个内部类是静态内部类。形式如下。
1 package cn.sxt01.inner02; 2 3 public class Outer { 4 5 static class Inner{ 6 } 7 }
3.1 静态内部类创建对象
1 public class Test01 { 2 public static void main(String[] args) { 3 4 Inner inner = new Outer.Inner(); 5 inner.showInfo(); 6 } 7 }
3.2静态内部类可以访问外部类中的(私有)静态成员。
1 public class Outer { 2 3 private static String name = "Outer"; 4 5 static class Inner{ 6 7 public void showInfo() { 8 System.out.println("showInfo"); 9 10 System.out.println(name); 11 } 12 } 13 }
特殊情况:静态内部类和外部类有同名变量(避免发生)
public class Outer { private static String name = "Outer"; static class Inner{ private static String name = "Outer"; public void showInfo() { System.out.println("showInfo"); System.out.println(name); System.out.println(Outer.name); } } }
4 方法内部类
4.1 如果一个内部类定义在一个方法中,称之为方法内部类。形式如下:
1 public class Outer { 2 3 4 public void print() { 5 6 class Inner{ 7 8 public void showInfo() { 9 System.out.println("show info"); 10 } 11 } 12 13 Inner inner = new Inner(); 14 inner.showInfo(); 15 } 16 }
4.2 方法的局部变量进入方法内部类时,会被final修饰,出了方法的内部类则不会被final修饰。
1 public class Outer { 2 3 public void print(int b) { 4 5 int a = 10; 6 7 class Inner{ 8 9 public void showInfo() { 10 System.out.println("show info"); 11 12 System.out.println("print()->a:"+10); 13 14 // 在方法内部类中不能修改方法的局部变量(final) 15 // a = 20; 16 // b = 20; 17 } 18 } 19 20 a = 20; 21 22 Inner inner = new Inner(); 23 inner.showInfo(); 24 } 25 }
5 匿名内部类
未来一个类如果只使用一次,我们可以把类声明为匿名类。匿名类一般和内部类一起使用。形成匿名内部类。
匿名内部类可用在:实现接口
1 package cn.sxt01.inner04; 2 3 public class Outer { 4 5 public void print() { 6 7 // 方法内部类 8 /*class Inner implements MyInterface{ 9 10 @Override 11 public void showInfo() { 12 System.out.println("Inner:showInfo"); 13 } 14 15 }*/ 16 17 /*Inner inner = new Inner(); 18 inner.showInfo();*/ 19 20 // new Inner().showInfo(); 21 22 // 匿名内部类 23 new MyInterface(){ 24 @Override 25 public void showInfo() { 26 System.out.println("Inner:showInfo"); 27 } 28 }.showInfo(); 29 30 } 31 }
2 异常处理
2.1传统异常处理的方式
1 public class Test01 { 2 public static void main(String[] args) { 3 Scanner sc = new Scanner(System.in); 4 System.out.println("请输入第一个数:"); 5 6 int num1 = 0; 7 if(sc.hasNextInt()) { 8 num1 = sc.nextInt(); 9 10 System.out.println("请输入第二个数:"); 11 int num2 = 0; 12 if(sc.hasNextInt()) { 13 num2 = sc.nextInt(); 14 15 if(0 == num2) { 16 System.out.println("除数不能为0!"); 17 }else { 18 int r = num1 / num2; 19 System.out.println("num1/num2 = "+r); 20 } 21 22 }else { 23 System.out.println("第二个数输入不是数字"); 24 } 25 26 }else { 27 System.out.println("第一个数输入不是数字!"); 28 } 29 } 30 }
缺点:
[1] 通过判断影响执行效率。
[2] 判断逻辑和业务逻辑交织在一起,可维护性很差。
2.2 异常
异常是指在程序运行过程中发生的不正常的情况,它会中断正在运行的程序。
异常处理机制
java中通过异常处理机制为程序提供异常处理的能力,保证java在运行过程中,能够保持运行而不因为异常中断程序。
2.3异常处理
涉及异常处理的关键字有try ...catch/try ...catch ...finally
2.3.1 try/catch
把可能产生异常的代码放在try代码块中,catch代码块去买个捕获并处理异常。
【1】正常执行,没有异常
【2】出现异常,异常处理,正常结束
Exception是所有异常类的直接的或间接的父类。
getMessage:返回异常的描述信息
1 package cn.sxt02.exception02; 2 import java.util.Scanner; 3 public class Test01 { 4 public static void main(String[] args) { 5 Scanner sc = new Scanner(System.in); 6 System.out.println("请输入第一个数:"); 7 8 int num1 = 0; 9 int num2 = 0; 10 11 try { 12 num1 = sc.nextInt(); 13 14 System.out.println("请输入第二个数:"); 15 num2 = sc.nextInt(); 16 17 int r = num1 / num2; 18 System.out.println("num1/num2 = " + r); 19 }catch (Exception e) { 20 System.out.println("程序出现异常"); 21 // 打印异常的信息 22 // System.out.println(e.toString()); 23 24 25 // 打印异常堆栈信息 26 e.printStackTrace(); 27 28 // 返回异常的描述信息,如果没有信息,返回null(InputMismatchException 没有描述信息) 29 System.out.println(e.getMessage()); 30 } 31 32 System.out.println("程序正常结束"); 33 } 34 }
【3】异常类型不匹配
【4】多重catch
1 public class Test03 { 2 public static void main(String[] args) { 3 Scanner sc = new Scanner(System.in); 4 System.out.println("请输入第一个数:"); 5 6 int num1 = 0; 7 int num2 = 0; 8 9 try { 10 num1 = sc.nextInt(); 11 12 System.out.println("请输入第二个数:"); 13 num2 = sc.nextInt(); 14 15 int r = num1 / num2; 16 System.out.println("num1/num2 = " + r); 17 }catch (ArithmeticException e) { 18 System.out.println("数学计算异常:"+e.getMessage()); 19 }catch(InputMismatchException e) { 20 System.out.println("输入不匹配异常:"+e.getMessage()); 21 }catch (Exception e) { 22 System.out.println("发送异常:"+e.getMessage()); 23 } 24 25 System.out.println("程序正常结束"); 26 } 27 }
2.3.2 try/catch/finally
把有可能产生异常的代码放到try代码块中,catch能够捕获并处理异常,finally块用于进行收尾工作(关闭数据库、关闭文件、释放内存等资源).
不管是否发生异常,finally都执行。
1 public static void main(String[] args) { 2 Scanner sc = new Scanner(System.in); 3 System.out.println("请输入第一个数:"); 4 5 int num1 = 0; 6 int num2 = 0; 7 8 try { 9 num1 = sc.nextInt(); 10 11 System.out.println("请输入第二个数:"); 12 num2 = sc.nextInt(); 13 14 int r = num1 / num2; 15 System.out.println("num1/num2 = " + r); 16 } catch (Exception e) { 17 System.out.println("程序出现异常"); 18 } finally { 19 System.out.println("不管是否出现异常,finally都执行"); 20 } 21 22 System.out.println("程序正常结束"); 23 }
finally总是执行,常用进行收尾工作。
特殊情况
[1] finally不执行的情况。
System.exit(0);正常退出jvm,finally不会执行。
[2]catch可以省略,变成try...finally块。
2.3.3 return
存在return的try/catch/finally执行顺序。
【1】如果不出现异常,顺序为:try->finally->return
【2】如果出现异常,顺序为:try->catch->finally->return
1 package cn.sxt02.exception03; 2 3 /** 4 * 存在return的情况 5 */ 6 public class Test02 { 7 8 public static int div(int a, int b) { 9 10 try { 11 int r = a / b; 12 return r; 13 14 } catch (Exception e) { 15 System.out.println("出现异常"); 16 17 return 0; 18 19 } finally { 20 System.out.println("我是finally"); 21 } 22 23 } 24 25 public static void main(String[] args) { 26 27 int r = Test02.div(10, 0); 28 System.out.println("r=" + r); 29 System.out.println("程序正常结束"); 30 } 31 }
2.4异常的分类
Throwable类是java中所有错误(error)或异常(exception)的父类。
Exception类表示异常类,分为两种:
RuntimeException:运行时异常。不要求程序必须做出处理。是所有运行时异常的父类。
CheckedException:检查时异常。要求程序必须做出处理,不处理无法编译。
1 public class Test01 { 2 public static void main(String[] args) { 3 // 运行时异常 4 Scanner sc = new Scanner(System.in); 5 // runtime exception 6 int r = sc.nextInt(); 7 System.out.println("r = "+ r); 8 9 // 检查时异常 10 SimpleDateFormat df = new SimpleDateFormat(); 11 try { 12 Date date = df.parse("2019"); 13 } catch (ParseException e) { 14 e.printStackTrace(); 15 } 16 } 17 }
常见的运行时异常
ArithmeticException:数学计算异常。比如除数为0
InputMismatchException:输入不匹配异常
ArrayIndexOutofBoundsException:数组下标越界异常。
NullPointException:空指针异常,对象没有初始化就使用时,jvm会抛出该异常
IllegalArgumentException:非法参数异常。
ClassCastException:强制类型转换异常。
NumberFormatException:数字格式化异常。比如把“abc”格式化成数字。
常见检查时异常:
ClassNotFoundException:类没有被发现异常。
SQLException:数据库相关异常
IOException:IO操作异常
ParseException:解析错误异常
FileNotFoundException:文件未发现异常。
运行时异常和检查时异常的区别
运行时异常:包括RuntimeException及其所有子类。不要求程序必须对它们作出处理,比如InputMismatchException、ArithmeticException、NullPointerException等。即使没有使用try-catch或throws进行处理,仍旧可以进行编译和运行。如果运行时发生异常,会输出异常的堆栈信息并中止程序执行。
Checked异常(非运行时异常):除了运行时异常外的其他异常类都是Checked异常。程序必须捕获或者声明抛出这种异常,否则出现编译错误,无法通过编译。处理方式包括两种:通过try-catch捕获异常,通过throws声明抛出异常从而交给上一级调用方法处理.
2.5.1
throws关键字
当一个方法可能存在异常,而此时自身又无法更好的处理时,可以用将异常交给外界处理。此时用throws关键字声明异常并抛出。
1 public class Test01 { 2 3 public static int div(int a, int b) throws ArithmeticException{ 4 int r = 0; 5 r = a / b; 6 return r; 7 } 8 9 public static void main(String[] args) { 10 try { 11 Test01.div(10, 0); 12 } catch (ArithmeticException e) { 13 System.out.println("除数不能为0"); 14 } 15 } 16 }
开发者可以根据需要声明检查时异常(Exception或者非运行时异常)和运行时异常(RuntimeException或其子类)
如果调用处也不知道如何处理异常,可选择继续声明异常,我们把这个过程称为异常上抛。
1 public class Test01 { 2 3 public static int div(int a, int b) throws Exception{ 4 int r = 0; 5 r = a / b; 6 return r; 7 } 8 9 public static void main(String[] args) throws Exception{ 10 11 //【1】 调用处知道如何处理! 12 /* 13 try { 14 Test01.div(10, 0); 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } 18 */ 19 20 // 【2】调用处也不知道如何处理 21 Test01.div(10, 0); 22 23 } 24 }
2.5.2 声明异常和重载的关系
发明异常和重载没有关系。
方法重载
【1】方法名相同
【2】参数列表不同(个数,类型,不同类型的顺序不同)
【3】和返回值,修饰符,声明异常无关
2.5.3 声明异常和重写的关系
声明异常和方法重写有关系。
【1】父类方法声明了异常(检测时或运行时),子类可以不声明任何异常。
【2】父类方法声明没有声明任何异常(检测时或运行时),子类也不声明异常或者声明运行时异常。
【3】父类方法声明了异常(检测时或运行时),子类也声明相同的异常
2.6 手动抛出异常
2.6.1 throw
除了系统自动抛出异常外,有些问题需要开发者自己手动抛出。使用关键字throw
1 package cn.sxt02.exception06; 2 3 public class Student { 4 private String name; 5 private String gender; 6 7 public String getName() { 8 return name; 9 } 10 11 public void setName(String name) { 12 this.name = name; 13 } 14 15 public String getGender() { 16 return gender; 17 } 18 19 public void setGender(String gender) throws Exception{ 20 if(gender.equals("男") || gender.equals("女")) { 21 this.gender = gender; 22 }else { 23 throw new Exception("性别不合法!"); 24 } 25 } 26 27 public Student(String name, String gender) { 28 super(); 29 this.name = name; 30 this.gender = gender; 31 } 32 33 public Student() { 34 super(); 35 } 36 37 }
1 public class Test01 { 2 public static void main(String[] args){ 3 Student stu = new Student(); 4 stu.setName("二狗"); 5 try { 6 stu.setGender("xxx"); 7 } catch (Exception e) { 8 System.out.println(e.getMessage()); 9 } 10 } 11 }
2.7 自定义异常
如果开发者需要手动抛出的异常在系统不存在,可以自定义异常。
如果要自定义异常,首先要确定异常类型,如果异常是运行时异常,必须继承RuntimeException或其子类;如果异常是检查时异常,必须继承Exception或其子类。
异常的命名方式,参考系统命名方式,以Exception结尾。
1 public class AgeException extends Exception{ 2 3 public AgeException() { 4 super(); 5 } 6 7 public AgeException(String message) { 8 super(message); 9 } 10 11 }