异常处理
基本概念
- 异常(exception):是指在硬件和操作系统正常时,程序遇到的运行错误。如数组下标越界、除数为0、用户输入非法、打开一个不存在的文件、网络连接中断、正在加载的类文件丢失等都会引发异常。异常会导致程序非正常终止。
- Java中不同的异常用不同的异常类来表示。当程序运行过程中发生一个可识别的异常时,系统会产生一个相应的异常类对象,即产生一个异常。
- 在“异常”类层次上的最上层有一个单独的类叫做Throwable,它是java.lang包中的一个类。它派生了两个子类:Error类和Exception类。
- Error类:定义了程序中不能恢复的严重错误。如内存溢出、类文件格式错误等。这一类错误由Java运行系统处理,不需要编程者处理。
- Exception类:定义了程序中可能遇到的轻微错误。可以写代码来捕捉和处理这些异常。由它派生出很多子类,每个子类代表了一种运行错误。
- 自动捕获和处理异常:把可能产生异常的代码用try包起来,try之后,通过catch来捕捉异常并处理。
处理过程
- Java程序在执行过程中如出现异常,系统会自动生成一个异常类对象,该异常对象将被提交给Java系统,这个过程称为抛出(throw)异常。
- 当Java系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
- 如果Java系统找不到可以捕获异常的方法,则系统将自动做一些处理,并中止程序。
- 使用try-catch-finally语句捕获和处理异常:
try
{
要检查的语句序列 ;
}
catch (异常类名 形参对象名)
{
异常发生时的处理语句序列;
}
finally
{
一定会运行的语句序列
}
public static void main (String args[]){
int i=0;
String greetings []={
"Hello world!","No,I mean it!","HELLO WORLD!!"
};
while (i<4){
try {System.out.println(greetings[i]);
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("Re-setting Index Value");
}
finally{
System.out.println(“This is always printed”);
}
i++;
}
- 异常处理有两种方法:
- 如果知道如何处理异常,则用try-catch语句捕获异常并处理它。
- 如果在一个方法中,有的异常不知如何处理或不想处理,可将异常抛出到其上一级,即调用它的方法。上一级的方法必须处理:捕获或继续抛出到更上一级。一直可追溯到main()方法。最后抛给系统,由系统处理。
- 抛出异常的两种方式:
- 系统自动抛出的异常:所有RuntimeException由系统自动地抛出。
- throw语句抛出的异常:除了RuntimeException外的其他异常,以及用户自定义的异常,不可能依靠系统自动抛出,必须用throw语句明确地抛出一个异常。
- 异常抛出
- 使用
throws
关键字抛出异常:throws关键字通常被应用在声明方法时,用来指定方法可能抛出的异常。多个异常可使用逗号分开。 - 使用
throw
关键字抛出异常:throw关键字通过用于方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即终止,它后面的语句都不执行。
void MyMethod() throws Exception1,Exception2,Exception3
{
}
void MyMethod() throws Super_Exception
{
}
public static void main(String[] args) {
try
{
throw new MyException("This is a custom exception");
}
catch (MyException e)
{
System.out.println(e.getMessage());
}
}
- 自定义异常类:因为所有的异常类均继承自Exception类,所以自定义类通常继承这个类。用户自定义异常必须用throw来抛出。
class 自定义异常类名 extends Exception
{
类体
}
Java泛型与容器类
泛型
- 实质就是将数据的类型参数化(即数据的类型可变),通过为类、接口及方法设置类型参数来定义泛型。
- 泛型类的定义:
[修饰符] class 类名<T>
- 泛型接口的定义:
[public] interface 接口名<T>
- 泛型方法的定义:
[public] [static] <T> 返回值类型 方法名(T 参数)
- 要实例化泛型类对象,也使用new运算符,但在类名后面需加上要传递的具体类型。泛型类生成对象时,实际类型必须是引用类型(
String
、Integer
、Double
等),不能是int、double、char等基本类型。 - 当我们定义一个方法,希望它能遍历各种类型的ArrayList(泛型类)集合。我们不知道ArrayList集合使用什么数据类型,此时可以使用泛型通配符"?" 来接收各种不同的数据类型。
- 限制泛型的可用类型:
- 上界用extends指定,例如:
List<? extends Number>
- 下界用super指定,例如:
List<? super Integer>
public class Node<T> {
private T data;
public Node(){}
public Node(T data){
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public void showType(){
System.out.println("T的类型是:"+ data.getClass().getName());
}
}
var intNode = new Node<Integer>();
public interface Entry<K, V> {
public K getKey();
public V getValue();
}
public class Pair<K, V> implements Entry<K, V>{
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public void setKey(K key) { this.key = key; }
public K getKey() { return key; }
public void setValue(V value) { this.value = value; }
public V getValue() { return value; }
}
var p1 = new Pair<Integer, String>(20, “twenty”);
var p2 = new Pair<String, String>(“china”, “Beijing”);
public static <T> void swap(T[] array,int i, int j){
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public static void printList(ArrayList<?> list)
{
System.out.println(list);
}
ArrayList<String> list1 = new ArrayList();
list1.add("123");
list1.add("abc");
ArrayList<Integer> list2 = new ArrayList();
list2.add(123);
list2.add(456);
printList(list1);
printList(list2);
容器类
- 容器类是Java以类库的形式 提供给用户开发程序时可直接使用的各种数据结构。
- Java提供了一个容器框架,也叫集合框架(Collections Framework),其中包含了一组接口和类,可以很方便地对一组对象进行操作。
- 常用的类型参数名有:E表示元素;K表示键;N表示数字;T表示类型;V表示值等。
- 容器框架由两种类型构成:
- Collection,用于存放一组对象。包含有List、Set、Queue。Set:不能包含重复的元素。SortedSet是一个按照升序排列元素的Set。List:是一个有序的集合,可以包含重复的元素。提供了按索引访问的方式。
- Map ,用于存放一组“关键字/值 对”的对象。包含了key-value对。Map不能包含重复的key。SortedMap是一个按照升序排列key的Map。
- ArrayList:即顺序表。我们可以将其看作是能够自动增长容量的数组。利用ArrayList的
toArray()
返回一个数组,利用Arrays.asList()
返回一个列表。常用方法:
void add(int index, E element)
E get(int index)
E set(int index, E element)
E remove(int index)
size()
- java.util.Iterator接口(迭代器)的功能:对集合进行遍历。主要有以下三个方法:
boolean hasNext()
:判断是否有下一个元素E next()
:返回下一个元素void remove()
:删除最近一个next()所返回的元素
- Collection接口中有一个iterator()方法,该方法返回的就是迭代器的实现类对象。
- 迭代器的使用步骤:
- 调用集合对象的iterator()方法获取Iterator对象,即迭代器的实现类对象,使用Iterator接口接收(多态)。
- 使用Iterator接口中的hasNext()方法判断还有没有下一个元素。
- 使用Iterator接口中的next()方法取出集合中的下一个元素。
- Collections类:java.util .Collections类是工具类,该类中的所有方法都是static的。
Collections.sort()
的实现方法:
- 实现Comparable接口中的compareTo()方法。按compareTo()方法进行的排序叫自然排序。
- 实现比较器(Comparator)接口,把比较器跟某个类相关联。
- Comparable接口:
- 如果一个类实现了Comparable接口(需要重写compareTo方法),则该类支持排序。
- 如果一个List或者一个数组中的对象是实现了Comparable接口的类,则可以通过Collections.sort()或者Arrays.sort()来对该列表或数组进行排序。
- ArrayList和LinkedList(链表类)的比较:LinkedList是采用双向循环链表实现的。
- ArrayList底层采用数组完成,而LinkedList则是以一般的双向链表(double-linked list)完成,其内每个对象除了数据本身外,还有两个 引用,分别指向前一个元素和后一个元素。
- 如果我们经常在List的开始处增加元素,或者在List中进行插入和删除操作,我们应该使用LinkedList,否则的话,使用ArrayList将更加快速。
- Set接口及实现类:
- Set接口实现类的对象类似于数学上的集合概念,其中不允许有重复的元素。
- Set接口没有定义新的方法,只包含从Collection接口继承的方法。
- Set接口的常用实现类有:HashSet类、TreeSet类。
- 散列存储的基本思想:把线性表中每个元素的关键字值与存放该元素的地址(数组下标值)之间建立起一个函数关系h。
- 散列表又称为哈希表。散列表算法的基本思想是:以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中的地址。
- HashSet类用散列方法存储元素,具有最好的存取性能,但元素没有顺序。TreeSet实现一种树集合,它使用红-黑树算法为元素排序。添加到TreeSet中的元素必须是可比较的,即元素的类必须实现
Comparable<T>
接口。 - Map接口及实现类:
- Map是用来存储“键/值”对的对象,也称为“映射”。
- 在Map中存储的关键字和值都必须是对象,并要求关键字是唯一的,而值可以重复。
- HashMap类以散列方法存放“键/值”对,TreeMap类保证Map中的“键/值”对按关键字(key)升序排序。