内部类
1.成员内部类,普通类中使用
public class Outer extends A { private String name; String s = "666666"; //内部类 成员内部类是一个内部的成员 可以使用四个访问修饰符 与实例变量 实例方法同级别的类 //1.可以给外部类提供更好的封装 只有自己类中能知道,其他类知道不了 //2.对外部类提供实现的组件,只有在自己类中才使用的东西,设为private 成员内部类 //3.实现另类的多继承 //4.作为常量类,使用内部类来管理不同的常量 //内部类可以更好的隐藏起来 protected class inner extends B{ //外部类可以继承其他类 实现另类的多继承 //内部类可以使用外部类私有的成员,而不破坏对外界的封装 //static String is =""; //内部类中不能声明静态变量,因为加载机制不同,而静态常量就可以,因为存放的位置不同,放在常量池中,所以就可以是使用 static final String is =""; //编译时,加载常量时不需要加载类的。 String s; private int age; public void m1() { System.out.println(name); System.out.println(Outer.this.s); //当内部类成员变量与外部类成员变量同名时,优先访问内部类属性。 //要想使用只能是这样 } public inner(){} } public static void main(String[] args) { //创建对象的方式来创建内部类使用 Outer o = new Outer(); inner i = o.new inner(); //外部类也可以使用内部私有的成员 //局部内部类 依赖外部类使用 i.age =15; i.m1(); User u = new User(); //可以在接口中创建局部内部类,然后也可以对接口中的内部类进行实例化,这操作很骚 System.out.println(u.EM); System.out.println(Const.User.SSS); System.out.println(Const.Order.NO_GAT); System.out.println(i.age); } }
2.接口中使用,挺好玩的。出乎意料
//常量类的使用内部类 接口常量类的内部类 public interface Const { double PI = 3.14; // 成员内部类 // 接口也是特殊的类,里面也可以有内部类,和内部接口 public class User { public static final String SSS = ""; public static final String EM = "66"; String name; //实例属性同样需要对象访问,规则没有变 } // 接口内部类 public interface Order { int NO_PAY = 0; int IS_PAY = 1; int NO_GAT = 2; } }
成员内部类中不能有静态变量详解
非静态内部类为什么不能有静态成员,因为成员内部类必须依赖外部类存在,要是它能够定义静态变量,就可以自己进行自己的类加载,就违反了这一规定。 如下代码 Java代码 public class OuterClass{ class InnerClass{ private static int i; } } 对于java类加载顺序我们知道,首先加载类,执行static变量初始化,接下来执行对象的创建,如果我们要执行代码中的变量i初始化, 那么必须先执行加载OuterClass,再加载Innerclass,最后初始化静态变量i,问题就出在加载Innerclass上面,我们可以把InnerClass看成OuterClass的非静态成员,它的初始化必须在外部类对象创建后以后进行,要加载InnerClass必须在实例化OuterClass之后完成,java虚拟机要求所有的静态变量必须在对象创建之前完成,这样便产生了矛盾。
静态内部类
import com.jingtai.Outer.Inner; public class Test { public static void main(String[] args) { Outer.Inner in = new Inner(); in.num = 0; } } class Outer { String name; // 不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员。 // 只能直接访问外部类的静态成员(实例成员需实例化外部类对象)。 static class Inner { // 差不多属于平级的关系 // 静态内部类就比局部内部类多了里面可以声明静态成员变量 static int age; // 而且就不用依赖外部类对象存在,可以直接就使用,这样子就方便很多,但成员变量就是要实例化了 int num; } }
局部内部类
public class TestLocallClass { public static void main(String[] args) { A a = new A(); a.m1(); } } class A{ public void m1(){ String local = "Hello"; //生命周期 //局部内部类,定义在外部类方法中,作用范围和创建对象范围仅限于当前方法。使用很受限,局部变量不能有那四个访问修饰符 //局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同, //变量必须修饰为final 常见考点 因为局部变量的回收是当方法执行完后立即回收,但类又在方法中,方法中局部变量的生命周期和在类中局部变量生命周期不同,所以java就没有这种机制,但加final就可以把它放在常量池中,常量的加载机制就不一样。 class Inner{ int a = 20; public void m2(){ System.out.println("Inner execute" + local); } } Inner n = new Inner(); //局部内部类,必须在方法创建之后才能使用。 n.m2(); } }
如果局部内部类中访问了所在方法的某个变量,就将该方法中的变量复制一份作为内部类的成员变量,当内部类访问所在方法中的变量时,就让它去访问复制出来的成员变量。这样在内部类所在方法中的变量消失的之后,仍然可以访问它,当然这里并不是真正地访问它,而是访问它的复制品。这里需要注意,由于是将局部内部类所在方法的变量复制一份作为局部内部类的成员变量,故在定义局部内部类之前,一定要对局部内部类所在方法的变量进行初始化,没有初始化是无法复制的。 那就是必须时时刻刻保证复制得到的那一份成员变量的值和原来的局部变量的值相同。如果在外部类中修改了局部变量的值,那就要修改局部内部类中复制得到的那一份成员变量的值;如果在局部内部类中修改了复制得到的那一份成员变量的值,那就要修改外部类中局部变量的值(前提是这个局部变量还存在),这样做是非常困难的。于是Java干脆就不允许局部内部类要访问的局部变量的值发生改变,即局部内部类中只能访问所在方法的最终变量或实际上的最终变量。 这里也不全对
匿名内部类
public class Computer { public void word(Usb u) { u.Service(); // 接口回调 } // 没有类名的局部内部类,一切特征相同 // 写匿名内部类的前提必须是有多态关系,不如就用不到父类名,或者接口名 public static void main(String[] args) { // 当一个类实现接口但它只需要执行一次时,就不必要执行那么多的代码,反正都会只执行这个重写的方法一次,这个时候就可以使用匿名内部类 Computer c = new Computer(); c.word(new Usb() { //当一个类使用到父类或者接口的时候都有机会用到 @Override public void Service() { //写回调函数方便了许多 // 当使用这个重写方法仅仅一次的时候,没必要再创建一个类来实现接口,直接可以使用匿名内部类 System.out.println("风扇转"); } }); } }
常用类
getClass();//得到一个类对象,多个相同类型的对象都只有一个类对象。
public class GetClass { public static void main(String[] args) { Student s = new Student(); Object ss = new Student(); System.out.println(s.getClass()); // 返回class 加全限定名 主要是返回引用中存储的真实类型 System.out.println(ss.getClass()); // 通常用于判断两个引用中存储对象类型是否一致 Class c = s.getClass(); Class c1 = ss.getClass(); // 因为它们都同属于一个类,一个.class文件,所以真实类型是相等的 System.out.println(c == c1); // 拿类对象来比比的就是equals方法 System.out.println(c.equals(c1)); // System.out.println(s == ss); 错误示范不能用双等号来比,拿类对象来比 } } class Student { String name; int age; }
hashCode()
public class HashCode { public static void main(String[] args) { Dog a = new Dog(); //除了字符串引用数据类型哈希算法是根据对象的地址计算数值 System.out.println(a.hashCode()); //能保证的是相同对象肯定返回肯定是一样的哈希code,尽可能保证不同对象返回不同hashcode //因为不同对象也可能返回相同hashcode //返回该对象的十进制的哈希码值。 String sa = "a"; String sa1 = "b"; System.out.println(sa.hashCode()); System.out.println(sa1.hashCode()); //字符串的hashcode是根据字符串计算出来的,就是根据ascii或者万国码 int a1 = 10; int b1 = 11; //基本数据类型 不能点出东西来 String a2 = "企"; String b2 = "你"; System.out.println(a2.hashCode()); System.out.println(b2.hashCode()); //不同字符串hashcode一致 System.out.println("重地".hashCode()); System.out.println("通话".hashCode()); } } class Dog { }
equlas,父类object类中定义的比较方法,要想实现自己自定义类型的比较要重写该方法
@Override public boolean equals(Object obj) { // 参考字符串重写的方法进行比较 if (this == obj) { return true; // 如果内存地址相等,两个对象肯定 } // 然后接着判断传入参数的类型 是否和我自己的类型一致,一致才进入比较,如果类型不一样就没必要比较 if (obj instanceof Student1) { // 相等的话再对它进行向下转型,调用它的属性与我比较 Student1 stu = (Student1) obj; if (this.name.equals(stu.name) && this.age == stu.age) { //如果要比较的属性相等就返回true System.out.println("相等"); return true; // 就相当于它们是一样了 } } return false; }
String 重写的equals方法 /* public boolean equals(Object anObject) { if (this == anObject) { //首先字符一样的话内存地址就是在同一个字符串池中,一样就返回是 ==比较的不然是内存地址,不然就是值 return true; } //然后再相互比较类型,类型相同再继续进行比较 if (anObject instanceof String) { //将类型强转回本来类型 String anotherString = (String) anObject; int n = value.length; //拿自己的长度和传入字符串的长度相等,如果相等再继续进行比较 if (n == anotherString.value.length) { //通过长度次数再接着比较它们的字符数组,通过一个while循环,两边一个一个字符的比较,如果相等就判定为相等 char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; } */
finalize()方法
-
当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。
-
垃圾对象:没有有效引用指向此对象时,为垃圾对象。
-
垃圾回收: 由GC销毁垃圾对象,释放数据存储空间。
-
自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象。
-
手动回收机制:使用System.gc(); 通知JVM执行垃圾回收。
public class TestFinalize { public static void main(String[] args) { //当对象被判定为垃圾对象时,由jvm调用此方法,用于标记垃圾对象进入回收队列。 //注意该方法不是立即删除删除对象,而是先进入回收队列。 //当一个对象没有有效引用指向时,就为垃圾对象,就是在堆里都没有人知道 for (int i = 0; i < 1000; i++) { new Tiger(); } System.gc(); //手动回收机制:使用System.gc();通知JVM执行垃圾回收,只是通知,但不是马上执行,而是通知jvm快点进行自动回收 //自动回收机制:jvm的内存耗尽,一次性回收所有垃圾对象。 } } class Tiger{ @Override protected void finalize() throws Throwable { System.out.println("我被回收了"); super.finalize(); } }
垃圾回收
JVM调优
包装类
八种基本数据类型对应的引用数据类型
public static void main(String[] args) { int i = 200; int i2 = 200; Integer i3 = 200; Integer i4 = 200; System.out.println(i==i2); System.out.println(i3 == i4);//true //Java预先创建了256个常用的整数包装类型对象。 //在实际应用当中,对已创建的对象进行复用。 这个数正好在256个缓冲区其中之一,就复用其数组里面的对象内存 Integer i5=new Integer(100); Integer i6=new Integer(100); System.out.println("============="); System.out.println(i5==i6); //false System.out.println(i5==i3); //false //equals方法也是就是比较值用的,不要搞混了 } public static void 算术运算时都会转为基本数据类型进行计算() { // 当一个包装类类 Integer i = 20; int i1 = 10; System.out.println(i + i1); // 当一个包装类类型和基本数据类型还有两个都是包装类类型的时候都会转为基本数据类型进行计算 // 做所有运算都会基本数据类型进行计算 // 包装类和基础数据类型运算都会拆箱转为基础数据类型在进行运算 Float i2 = 3.1561f; System.out.println(i2 + 10); System.out.println(i + i2); } public static void parese字符串装为其本身基本类型方法() { Integer i = 200; i.parseInt("666"); // 将字符串转为原本基本数据类型 Byte b = 20; Short s = 20; Long l = 2000l; Float f = 3.151f; Double d = 15.1515; Character c = '干'; // 全部都有就character没有 Boolean b1 = true; } public static void 拆箱操作() { // 拆箱操作就是包装类转换为基本数据类型 Integer i = 200; i.valueOf("666");// 也可以将字符串类型里面的数值装箱,将字符串转为int包装类类型, Byte b = 20; // 用包装类类型引用指向基本数据类型都属于装箱操作 b.byteValue();// 拆箱操作,将包装类类型转回原本基本数据类型 Short s = 20; s.floatValue();// (float)value 相当于拿到自身类型 做个强转 拆箱成任何基本数据类型 Long l = 2000l; l.longValue(); Float f = 3.151f; f.floatValue(); Double d = 15.1515; d.doubleValue(); Character c = '干'; c.charValue(); // 字符包装类只能拆箱成字符型 Boolean b1 = true; // 八种包装类类型 b1.booleanValue(); // 布尔包装类也是 } public static void 八种基本类型装箱以及自动装箱操作() { // 八种数据类型的包装类好用一点,有更多方法封装在里面,而且利于判断 基本数据类型对应的引用数据类型,也有利于Object统一管理 Byte b = 20; // 用包装类类型引用指向基本数据类型都属于装箱操作 b = Byte.valueOf((byte) 20); // jdk1.5之后帮我们自动装箱 类似于这种 Short s = 20; s.valueOf((short) 20); // valueof是将基本数组类型和字符串转为本身包装类类型,parseXXX是将字符串转为基本数据类型 Integer i = 200; // 自动装箱进行比较 Integer.valueOf(20); Long l = 2000l; Long.valueOf(2000); Float f = 3.151f; f.valueOf(666); Double d = 15.1515; Double.valueOf(15.1515); Character c = '干'; Character.valueOf('搞'); Boolean b1 = true; // 八种包装类类型 Boolean.valueOf(true); } }
补充:
public class Test3 { public static void main(String[] args) { Integer i = 100; Integer i1 = 100; System.out.println(i==i1); Integer i7=200;//超出缓冲区值 new Integer Integer i8=200;//超出缓冲区值 new Integer // Boolean.valueOf(b) 忽略大小写的比较 Integer i2 = 100; System.out.println(i==i2+0); //当作算术运算时会转为基本数据类型 Integer.valueOf("66a");// NumberFormatException 当字符串无法完整转换为数值时会爆出的异常错误 //包装类和基础数据类型运算都会拆箱转为基础数据类型在进行运算 } }
包装类型间的相等判断应该用equals,而不是'=='
晚上写这个东西
String类 具有不可变性,首先是因为
StringBuffer的面试题,也是要注意的事项。
public static void main(String[] args) { String a ="a"+"b"; String b = "ab";//已经在字符串常量池中,所以会返回相同地址 System.out.println(a==b); String c = "a"; String d = c +"b"; //当jvm发现说如果有一边有一个变量就会自动帮你做优化,就会根据变量 相当于new StringBuilder(c).append("b"); System.out.println(d==b);//当有一个字符串的变量进行字符串拼接时,会自动调用 String e = "b"; String f = c+e; //当有一边是字符串的变量就会调用前面变量的追加,就会新new一个内存地址,不用去字符串池中找 System.out.println(a==f); }
String的常用方法
public static void main(String[] args) { String str = " java 天下第一! "; System.out.println(str.charAt(5));// 根据下标返回相对应的值 System.out.println(str.contains("jasa")); // 区分大小写 不论传入什么字符都把它当作一个整体来看待,如果整体都为true // 将一个字符串转成字符数据 char[] ar = str.toCharArray(); // 返回值为一个字符数组 // System.out.println(Arrays.toString(ar)); int it = str.indexOf("aba");// 根据字符串或者字符返回对应的下标 如果是一连串不正确的东西也会返回-1 // 不管传入的是一个字符或者是一个字符串,方法中都是把它看成一个整体,都是返回最前面的字符的下标位置 int it1 = str.lastIndexOf("a"); // 从后往前找 ,如果有符合的第一个就返回 System.out.println(it); // 如果传入的字符串不正确就返回-1 System.out.println(it1); // 返回字符串长度的方法 数据是调用length属性 System.out.println(str.length()); // System.out.println(str); // 有时候为了准确判断精确的字符串长度,比如说账号密码什么的,不能用空格充当账户名或密码,我们就得用trim方法,将前后的字符串去掉,判断真实的账号或者密码 String sn = str.trim(); // 返回一个String System.out.println(sn); // 将字符串中的字母全部转换为大写,有时候可以用来规定格式上面的 System.out.println(str.toUpperCase()); // 将字符串中的字母全部转换为小写,有时候可以用来规定格式上面的 System.out.println(str.toLowerCase()); String s = "干干干.java"; // 接下来这个就有用了,可以用来判断是以什么样的来结尾 可以用来判断是什么的文件名,更有利于用来传输文件 boolean bl = s.endsWith(".java"); System.out.println(bl); // 接下来这个也很有用 可以用来字符串替换 // System.out.println(str.replace("java", "php")); // //不是等字符串替换,可以用任何的字符串替换,就是长度不一致,如果没有的话就会替换不成功,不会报错。 String[] ss = str.split(" "); // 根据传入的str参数做拆分 然后返回一个string类型的数组 System.out.println(Arrays.toString(ss)); // 如果最后一个是满足拆分的字符串是不会进行拆分的 但是在前面的话就会拆分,拆分是直接是把要拆分的字符冲直接切掉。 String sss = str.substring(2, 7);// 从第三下标的值截取到7号下标的前一位。开始位置包括,结束位置不包括。后面的位置减去前面的位置,也会得到响应的位数。 System.out.println(sss); // 这些方法都挺常用的,要多敲,以后碰到有关字符串的操作,都要先考虑这些方法可不可以 } }
实体类 pojo类 专门用来存载数据的,就是为了装载数据的,当一个类中只有属性设置没有行为或者逻辑操作时,就可以放在一个实体类上。
面试题:String Stringbudelid和Stringbullder的区别 重点讲String的不可变性,在讲它们的优势。
集合顶级父接口Collection
simpleDatefatter 专门用来格式化日期的类,和Date日期来结合使用。
public static void main(String[] args) throws ParseException { //SimpleDateFormat 是专门用来格式化日期和解析日期专用的工具类。 //将日期输出为指定格式 就是相当于用这个工具类包装这个日期将它输出为这个包装所需要的格式 //格式化日期和解析都必须被 SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH时mm分ss秒"); //用那么多的符号表示主要是用来补位用的,防止没有那么多位来补全 Date d = new Date(); String s = sdf.format(d);//用于将日期按什么什么格式输出 System.out.println(s); //然后也可用于讲字符串重新转为日期,可以用于日期的解析操作 Date dd = sdf.parse("2012/09/28 13时08分35秒"); //用于讲一串字符串转为固定的日期格式 System.out.println(dd); //就是一个日期格式化工具 转过去再转回来 很简单的 }
记住这些就好
ArrayList方法很多都是从list上继承下来的,所以完全可以用接口指向实现类对象。
list中有28个方法,Arraylist有31个方法。
Arraylist集合刚创建出来,默认是空数组,首次添加元素时,数组长度为10,然后到达10长度后,默认就按1.5倍扩容。
List的removeIf方法,里面要传入一个重写过滤方法的实现类
public static void main(String[] args) { List<String> l = new ArrayList<String>(); l.add("6.txt"); l.add("5.txt"); l.add("6.jpg"); l.add("6.java"); //可以使用过滤器进行过滤 //有个如果删除的方法,就是给传入的参数过滤 l.removeIf(new Predicate<String>() { //这样子就过滤掉了 @Override public boolean test(String t) { //把这个对象当作过滤的容器里重写这个方法 if(t.endsWith(".txt")) { return true; } return false; } }); for (String string : l) { System.out.println(string); } }
for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; }
Arraylist的remove方法,如果存入的是元素也会调用equals方法,先来比较它们是不是相等的,再对他们进行删除。
重点hashmap实现原理
Author:King
Version:9.0.2
一、异常1.1 概念1.2 异常的必要性二、异常分类2.1 错误2.2 异常三、异常产生和传递3.1 异常产生3.2 异常传递四、异常处理【重点
】4.1 try...catch...4.2 try...catch...finally...4.3 多重catch4.4 try…finally...4.5 小结五、声明、抛出异常5.1 声明异常5.2 抛出异常六、自定义异常6.1 编写自定义异常6.2 异常中方法覆盖
一、异常
1.1 概念
异常:程序在运行过程中出现的特殊情况。
1.2 异常的必要性
任何程序都可能存在大量的未知问题、错误。
如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失。
二、异常分类
Throwable:可抛出的,一切错误或异常的父类,位于java.lang包中。
2.1 错误
Error: JVM、硬件、执行逻辑错误,不能手动处理。
常见错误: StackOverflowError、 OutOfMemoryError等。
2.2 异常
Exception:程序在运行和配置中产生的问题,可处理。
RuntimeException:运行时异常,可处理,可不处理。
CheckedException:检查时异常,必须处理。
常见运行时异常:
异常 | 描述 |
---|---|
NullPointerException | 空指针异常 |
ArrayIndexOutOfBoundsException | 数组越界异常 |
ClassCastException | 类型转换异常 |
NumberFormatException | 数字格式化异常 |
ArithmeticException | 算术异常 |
案例演示:
public class Demo1 { public static void main(String[] args) { //常见运行时异常 //1NullPointerException String name=null; System.out.println(name.equals("zhangsan")); //2ArrayIndexOutOfBoundsException int[] arr= {10,30,50}; System.out.println(arr[3]); //3ClassCastException Object str="hello"; Integer i=(Integer)str; //4NumberFormatException int n=Integer.parseInt("100a"); System.out.println(n); //5ArithmeticExceptioin int n=10/0; System.out.println(n); try { FileInputStream fis=new FileInputStream("d:\\hell.txt"); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
三、异常产生和传递
3.1 异常产生
自动抛出异常:当程序在运行时遇到不符合规范的代码或结果时,会产生异常。
手动抛出异常:语法:throw new 异常类型(“实际参数”)。
产生异常结果:相当于遇到 return语句,导致程序因异常而终止。
3.2 异常传递
异常的传递:
按照方法的调用链反向传递,如始终没有处理异常,最终会由JVM进行默认异常处理(打印堆栈跟踪信息)。
受查异常:throws 声明异常,修饰在方法参数列表后端。
运行时异常:因可处理可不处理,无需声明异常。
案例演示:异常的产生、传递。
/** * 演示异常的产生和传递 * 要求:输入两个数字实现两个数字相除 */ public class TestException1 { public static void main(String[] args) { operation(); } public static void operation() { System.out.println("---opration-----"); divide(); } public static void divide() { Scanner input=new Scanner(System.in); System.out.println("请输入第一个数字"); int num1=input.nextInt();//出现异常,没有处理,程序中断 System.out.println("请输入第二个数字"); int num2=input.nextInt(); int result=num1/num2;//出现异常没有处理,所以程序中断 System.out.println("结果:"+result); System.out.println("程序执行完毕了..."); } }
四、异常处理【重点
】
Java的异常处理是通过5个关键字来实现的:
try:执行可能产生异常的代码 。
catch:捕获异常 ,并处理。
finally:无论是否发生异常,代码总能执行。
throw: 手动抛出异常 。
throws:声明方法可能要抛出的各种异常。
4.1 try...catch...
语法:
try {
//可能出现异常的代码
} catch(Exception e) {
//异常处理的相关代码,如:getMessage()、printStackTrace()
}
public class TestException2 { public static void main(String[] args) { Scanner input=new Scanner(System.in); int result=0; try { System.out.println("请输入第一个数字"); int num1=input.nextInt();//InputMismatchException System.out.println("请输入第二个数字"); int num2=input.nextInt(); result=num1/num2;//发生异常// ArethmicException }catch (Exception e) {//捕获 Exception:是所有异常的父类 //处理 //e.printStackTrace(); System.out.println(e.getMessage()); } System.out.println("结果是:"+result); System.out.println("程序结束了..."); } }
4.2 try...catch...finally...
语法:
try { //可能出现异常的代码 } catch(Exception e) {
//异常处理的相关代码,如:getMessage()、printStackTrace()
} finally{ //是否发生异常都会执行,可以释放资源等。 }
public class TestException3 { public static void main(String[] args) { Scanner input=new Scanner(System.in); int result=0; try { System.out.println("请输入第一个数字"); int num1=input.nextInt();//InputMismatchException System.out.println("请输入第二个数字"); int num2=input.nextInt(); result=num1/num2;//发生异常// ArethmicException //手动退出JVM //System.exit(0); }catch (Exception e) {//捕获 Exception:是所有异常的父类 //处理 //e.printStackTrace(); System.out.println(e.getMessage()); }finally { System.out.println("释放资源..."); } System.out.println("结果是:"+result); System.out.println("程序结束了..."); } }
注:1、finally块是否发生异常都执行,释放资源等 2、finally块不执行的唯一情况,退出java虚拟机。
4.3 多重catch
语法:
try{
//可能出现异常的代码。
}catch(异常类型1){
//满足异常类型1执行的相关代码。
}catch(异常类型2){
//满足异常类型2执行的相关代码。
}catch(异常类型3){
//满足异常类型3执行的相关代码
}
public class TestException4 { public static void main(String[] args) { Scanner input=new Scanner(System.in); int result=0; try { // String string=null; // System.out.println(string.equals("hello")); System.out.println("请输入第一个数字"); int num1=input.nextInt();//InputMismatchException System.out.println("请输入第二个数字"); int num2=input.nextInt(); result=num1/num2;//发生异常// ArethmicException }catch (ArithmeticException e) {//捕获 Exception:是所有异常的父类 System.out.println("算术异常"); }catch (InputMismatchException e) { System.out.println("输入不匹配异常"); }catch (Exception e) { System.out.println("未知异常"); } System.out.println("结果是:"+result); System.out.println("程序结束了..."); } }
4.4 try…finally...
try...finally...不能捕获异常 ,仅仅用来当发生异常时,用来释放资源。
一般用在底层代码,只释放资源不做异常处理,把异常向上抛出。
语法:
try{ //可能出现异常的代码 }finally{ //是否发生异常都会执行,可以释放资源等 }
public class TestException5 { public static void main(String[] args) {//JVM try { divide(); }catch (Exception e) { System.out.println("出现异常:"+e.getMessage()); } } public static void divide() { Scanner input=new Scanner(System.in); int result=0; try { System.out.println("请输入第一个数字"); int num1=input.nextInt();//InputMismatchException System.out.println("请输入第二个数字"); int num2=input.nextInt(); result=num1/num2;//发生异常// ArethmicException }finally { System.out.println("释放资源"); } System.out.println("结果是:"+result); System.out.println("程序结束了..."); } }
4.5 小结
try{ } catch{ }
try{ } catch{ } catch{ }
try{ } catch{ } finally{ }
try{ } catch{ } catch{ } finally{ }
try{ } finally{ }
注:多重catch,遵循从子( 小 )到父( 大 )的顺序,父类异常在最后。
五、声明、抛出异常
5.1 声明异常
如果在一个方法体中抛出了异常,如何通知调用者?
throws关键字:声明异常
public class TestException6 { public static void main(String[] args){//JVM try { divide(); } catch (Exception e) { // TODO Auto-generated catch block //e.printStackTrace(); System.out.println(e.getMessage()); } } public static void divide() throws Exception { Scanner input=new Scanner(System.in); System.out.println("请输入第一个数字"); int num1=input.nextInt(); System.out.println("请输入第二个数字"); int num2=input.nextInt(); int result=num1/num2; System.out.println("结果:"+result); } }
5.2 抛出异常
除了系统自动抛出异常外,有些问题需要程序员自行抛出异常。
throw关键字:抛出异常
public class Person { private String name; private String sex; private int age; public Person() { // TODO Auto-generated constructor stub } public Person(String name, String sex, int age) { super(); this.name = name; this.sex = sex; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { if(sex.equals("男")||sex.equals("女")) { this.sex = sex; }else { throw new RuntimeException("性别不符合要求"); } } public int getAge() { return age; } public void setAge(int age) { if(age>0&&age<=120) { this.age = age; }else { //抛出异常 throw new RuntimeException("年龄不符合要求"); } } @Override public String toString() { return "Person [name=" + name + ", sex=" + sex + ", age=" + age + "]"; } }
六、自定义异常
6.1 编写自定义异常
需继承Exception或Exception的子类,代表特定问题。
异常类型名称望文生义,可在发生特定问题时抛出对应的异常。
常用构造方法:
无参数构造方法。
String message参数的构造方法。
public class AgeException extends RuntimeException{ public AgeException() { super(); // TODO Auto-generated constructor stub } public AgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto-generated constructor stub } public AgeException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public AgeException(String message) { super(message); // TODO Auto-generated constructor stub } public AgeException(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } }
6.2 异常中方法覆盖
带有异常声明的方法重写:
方法名、参数列表、返回值类型必须和父类相同。
子类的访问修饰符合父类相同或是比父类更宽。
子类中的方法,不能抛出比父类更多、更宽的检查时异常。
public class Animal { public void eat(){ System.out.println("父类吃方法.........."); } }
public class Dog extends Animal{ @Override public void eat() throw Exception{ //出现错误,父类没有声明异常,子类不能声明异常 System.out.println("子类的吃的方法.........."); } }