马兵士课程笔记(续3)
马士兵课程笔记(续3)
抽象类
- 关键字abstract
- 含有抽象方法的类必须声明为抽象类;
- 抽象类必须先被继承,不能直接实例化;
- 抽象方法只需声明不需实现,所以必须在子类中被重写(类似C++中的纯虚函数)
abstract class A { ... public abstract void b(); // note: 这里没有“{}”了 ... }
- 如果并不打算由抽象类的子类来实现抽象方法,可以将该子类再声明为抽象类,在其中再次声明该抽象方法,由他的子类(“孙子类”)去实现。
final关键字
- final变量的值不能修改(相当于C++的const关键字,当常量用);
- final成员变量
- final局部变量(形参):用在引用型形参上就能防止形参指向别处造成的bug
- final方法不能被重写;
- final类不能被继承(如JDK中的String、Math、Boolean等就是final类)。
接口interface
- java本来是单继承的;
- 接口interface是抽象方法和常量值的定义的集合;
- 从本质上讲,interface是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有方法的实现(这个工作交给其实现类来完成);
public interface Runner { public static final int id = 1; // 常量前要加“public static final”关键字 public void start(); // interface内的抽象方法不需要加abstract关键字 public void run(); public void stop(); }
接口特性
- 接口支持多重实现;
- 多个无关的的类可以实现同一个接口;
- 一个类可以实现多个无关的接口;
- 接口中声明的属性默认且必须是public static final的;
这是为了避免像C++多继承那样多个父类有同名成员变量而出现的冲突。
- 接口中只能定义抽象方法,而这些方法默认且只能是public的;
- 接口可以继承其他接口,并添加新的属性和抽象方法。
- 与继承关系类似,接口与实现类之间存在多态性。
以接口引用实现类的对象,就只能调用本接口声明了的方法和属性。
定义JAVA类的语法格式:
<modifier> class <name> [extends <superclass>] [implements <interface> [, <interface>]*] { <declarations>* }
面向对象Summary
- 内存分析贯穿始终
- 对象和类的概念
- 类(对象)之间的关系
- 面向对象设计思想
- class
- new
- 引用的概念
- 构造方法的概念
- 方法重载
- 构造方法重载
- this
- static
- package 和 import
- private, default, protected, public
- extends
- overwrite重写
- final
- Object
- toString()
- equals()
- upcasting, downcasting
- polymophysm / dynamic binding / late binding
- abstract
- interface
- implements
异常exceptions
java异常概念
- 运行期出现的错误
- 观察错误的名字和行号最重要
程序,是调出来的。
- 异常是java提供的用于处理程序中错误的一种机制。
- 错误是指在程序运行过程中发生的一些异常事件(如:除0溢出,数组下标越界,所要读取的文件不存在 等);
public class TestEx { public static void main(String[] args) { int[] arr = {1, 2, 3}; System.out.println(arr[4]); // 与Java不同,在C/C++中并不检查数组越界 } } /* 输出: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4 at TestEx.main(TestEx.java:6) */
- 设计良好的程序应该在异常发生时提供处理这些错误的方法,使得程序不会因为异常的发生而阻断或产生不可预见的错误;
- java程序的执行过程中如出现异常事件,可以生成一个异常类对象,该对象封装了异常事件的信息并将被提交给java Runtime系统,这个过程称为抛出(throw)异常;
- 当Java runtime系统收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
异常的分类
- 异常类封装了异常事件发生的信息以及相应方法。
- Error:称为错误,由Java虚拟机生成并抛出,包括动态链接失败、虚拟机错误等,程序对其不做处理(也处理不了)。
- Exception:所有异常类的父类,其子类对应了各种各样可能出现的异常事件,一般需要用户显式地声明或捕获。
- RuntimeException:一类特殊的异常,如被0除、数组下标出界等,其产生比较频繁,处理麻烦,如果显式地声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交由缺省的异常处理程序(可不必处理),不过缺省处理程序是打印异常信息后就中止执行了。
- java.lang.Exception
- ClassNotFundException
- IOException
- InterruptedException
- ......
- RuntimeException
- ArithmeticException
- NullPointerException
- ......
- IndexOutOfBoundsException
- ArrayIndexOutOfBoundsException
- StringIndexOutOfBoundsException
import java.io.*; public class TestEx { public static void main(String[] args) { TestEx te = new TestEx(); te.m(0); // 对RuntimeException不做处理有虚拟机缺省处理,编译可以通过,但运行时缺省处理报异常后就停止不往下执行了 // te.n(0); // 而对非RuntimeException必须try/catch或声明,否则编译报错 /* 编译报错 TestEx.java:27: 未报告的异常 java.io.IOException;必须对其进行捕捉或声明以便抛出 te.n(0); ^ */ try { te.n(0); } catch(IOException e) { // 将try{}中抛出的某错误类的对象捕获传给引用e System.out.println("出错了!"); e.printStackTrace(); // 跟踪异常事件发生时执行堆栈的内容,并在控制台上打印出来,包含了getMessage()。 } /* 输出: 出错了! java.io.IOException: 而对非RuntimeException必须try/catch或声明,否则编译报错 at TestEx.n(TestEx.java:39) at TestEx.main(TestEx.java:22) 程序执行完毕 */ System.out.println("程序执行完毕"); } // 方法内创建异常类对象并抛出,注意语法 void m(int i) throws RuntimeException { // throws--声明异常 if(0 == i) throw new RuntimeException("对RuntimeException不做处理有虚拟机缺省处理,编译可以通过,但运行时缺省处理报异常后就停止不往下执行了"); } // note:如果throw语句后面方法内还有语句,当抛出异常时该方法内剩余的语句就不执行了 void n(int i) throws IOException { // IOException不属于RuntimeException if(0 == i) throw new IOException("而对非RuntimeException必须try/catch或声明,否则编译报错"); } }
异常的捕获和处理
-
try {}
- try代码段指定了一次捕获并处理例外的范围;
- 在执行过程中,该段代码可能会产生并抛出一种或多种类型的异常对象,那么它后面的catch语句要分别对它们做相应的处理;
- 若没有例外产生,所有的catch段都被跳过。
-
catch(SomeException e) {}
- SomeException e 封装了异常事件发生的信息,在catch段中可以使用该对象的一些方法获取这些信息。如:
- getMessage(),用来得到有关异常事件的信息;
- printStackTrace(),用来跟踪异常事件发生时执行堆栈的内容,包含了getMessage(),因此它更常用。
- 禁止空catch段,这是很危险的。
- note: 当捕获多类异常对象分别处理时,要将子类异常放在前面,否则编译报错“已捕捉到异常”。
- SomeException e 封装了异常事件发生的信息,在catch段中可以使用该对象的一些方法获取这些信息。如:
-
finally {}
- finally语句为异常处理提供一个统一的出口,使得在控制流程转到程序其他部分前,能够对程序的状态做统一的管理;
- 无论是否有例外,finally段中代码都要执行;
- 可以用来完成资源的清理工作,如:
- 关闭打开的文件;
- 删除临时文件
- ... ...
import java.io.*; public class TestEx { public static void main(String[] args) { // 用try/catch结构体捕捉处理异常,之后程序会继续往下执行,注意语法 FileInputStream is = null; try { // try代码段包含可能产生异常的代码,注意!最小包含原则 is = new FileInputStream("myfile.txt"); int b; b = is.read(); while (b != 1) { System.out.print((char) b); b = is.read(); } // 当异常发生时,程序会终止当前的流程,根据获取异常的类型去执行相应的catch段 } catch(FileNotFoundException e) { // catch代码段声明其能处理的某一类异常 e.printStackTrace(); // 并在段内提供处理的方法 } catch(IOException e) { // 对try中抛出的多类异常,分别catch处理 System.out.println(e.getMessage()); // getMessage()方法用来得到异常事件的信息。较少用 } finally { // finally段的代码无论是否发生异常都要执行 try { is.close(); } catch(Exception e) { e.printStackTrace(); } } System.out.println("程序执行完毕"); } }
-
声明方法抛出的异常——“矛盾上交”
- 若在本方法内处理不了,可以再throws交给外一层处理。
import java.io.*; public class TestEx { public static void main(String[] args) { TestEx te = new TestEx(); // f()方法抛出的异常最终"上交"给main()方法处理了 // 当然main()方法也可以向外声明(throws)上交给虚拟机处理,但这是错误的 // 应当尽力将异常处理在自家门里 try { te.ff(); } catch(IOException e) { System.out.println("f()方法抛出的异常最后\"上交\"给main()方法处理了"); e.printStackTrace(); } } // 声明方法抛出的异常 void f(boolean b) throws FileNotFoundException, IOException { // 尽管FileNotFoundException是IOException的子类,可以只throws IOException,但作为一种意识还是强烈建议分开抛 if(b) throw new FileNotFoundException("声明方法抛出的异常A"); else throw new IOException("声明方法抛出的异常B"); } void ff() throws IOException { // 对调用的方法所抛出的异常处理不了的也可以继续向外抛出 f(true); } }
使用自定义的异常
通过继承 java.lang.Exception(或RuntimeException) 类声明自己的异常类;然后用法跟普通异常一样。
class MyException extends Exception { private int id; public MyException(String message, int id) { super(message); this.id = id; } public int getId() { return id; } }
声明了异常的类的继承重写
note:重写方法只能抛出与原方法所抛出一样类型(不包括父类、子类)的异常,要么不抛出异常。
异常Summary
- 一个图
- 五个关键字:try,catch,finally,throw,throws
- 先逮小的,再逮大的
- 异常和重写的关系