异常

throw

异常就是当程序执行到一段代码,发生异常后,就new一个对应的类的对象,这个类是一个异常类,如:NullPointerExceptionNumberFormatException,然后throw或者catchthrow就是抛出一个异常,和return对比。

throw return
异常退出 正常退出
throw后执行的代码不定,看异常处理机制动态决定 返回位置确定:上一级调用者

try/catch

1
2
3
4
5
6
7
System.out.println("请输入一个数字");
try {
int num = Integer.parseInt(args[0]);
System.out.println(num);
} catch(NumberFormatException e) {
System.out.println("参数" + arg[0] + "不是有效数字,请输入数字");
}

如果在try内跑出来异常,可以由catch来捕获,捕获后程序还会继续运行而不会退出,只是try后续的代码不会执行。

异常类体系(记下来)

异常类体系图.webp

Throwable是所有异常类的基类,他有两个子类Error和Exception。

Error表示系统错误或资源耗尽,应用程序不抛出或处理,虚拟机错误及其子类,内存溢出错误和栈溢出错误。Error及子类都是未受检异常。

Exception中有很多子类,也可以继承Exception自定义异常,其中列出来数据库异常,IO异常,和运行时异常(未受检异常)受检异常不能通过编译。

异常 说明
NullPointerException 空指针异常
IllegalStateException 非法状态
ClassCastException 非法强制类型转换
IllegalArgumentException 参数错误
异常 说明
NumberFormatException 数字格式错误
IndexOutOfBoundsException 索引越界
ArrayIndexOutOfBoundsException 数组索引越界
StringIndexOutOfBoundsException 字符串索引越界

这些异常大部分只是定义了几个父类的构造函数。

自定义异常

然后自定义异常继承的RuntimeException或他的某个子类,则自定义异常也是未受检异常;如果继承的是Exception和Exception其他子类,则自定义异常是受检异常。

1
2
3
4
5
6
public class AppException extends Exception {
public AppException {super();}
public AppException(String message, Throwable cause) {super(message, cause);}
public AppException(String message) {super(message);}
public AppException(Throwable cause) {super(cause);}
}

异常处理

异常处理包括catch、throw、finally、try-with-resources和throws

catch匹配

1
2
3
4
5
try{
}catch(NumberFormatException e){
}catch(RuntimeException e){
}catch(Exception e){
}

上方异常要是下方异常的子类,java7开始支持一种新语法

1
2
try{
} catch (ExceptionA | ExceptionB e) {}

throw和finally

处理完异常之后,可以通过throw抛出异常给上一层,可以是原来的异常,也可以是新异常。

finally内代码,无论有无异常发生都会执行。

  1. 如果没有异常发生,try执行后执行finally

  2. 如果有异常被catch,catch执行后执行finally

  3. 如果有异常发生但没被catch,在异常被抛出前执行finally

注意:如果try或catch中有return时,也会先执行完finally中的代码再去执行return;如果finally内有return或抛出异常,则会覆盖掉catch和try中的return和异常。

try-with-resources

finally一般用于释放资源,java7之前要实现AutoCloseable接口之后,在finally中调用close()方法,代码如

1
2
3
4
5
6
7
8
9
10
public interface AutoCloseable {
void close() throws Exception;
}
public static void useResource() throws Exception {
AutoCloseable r = new FileInputStream("hello"); //创建资源
try{ //使用资源
} finally {
r.close();
}
}

java7支持了一种新语法,在执行完try语句之后自动执行close()方法。

1
2
3
4
5
public static void useResource() throws Exception {
try (AutoCloseable r = new FileInputStream("hello")){ //创建资源
//使用资源
}
}

throws

throws跟在方法后面,可以声明多个异常,以逗号分隔。表示:这个方法可能会抛出这些异常,且没有处理或者没有处理完,调用者必须进行处理。

集合

单列集合图片.webp

双列集合图片.webp

Collection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//添加
boolean add(Object obj);
boolean addAll(Collection c);
//delete
void clear();
boolean remove(Object obj);
boolean removeAll(Collection c);
//justice
boolean contains(Object obj);
boolean containsAll(Collection c);
boolean isEmpty();
//iterator
Iterator<E> iterator();
//length
int size();
boolean retainAll(Collection c);

List

存取有序,有索引,元素可重复

ArrayList(增删慢,改查快)

  • 底层是数组

  • 默认参数化容量是10,每次扩容为原先容量的1.5倍

  • 增删时,用数组拷贝复制

  • 删除元素时不会减少容量,若希望减少容量则调用trimToSize()

Linkedlist(改查慢,增删快)

底层是双向链表【双向列表便于实现往前遍历】

Vector

底层是数组,基本被ArrayList代替,原因如下:

  1. 所有方法都是同步的,有性能损失。

  2. 初始长度是10,超过后以100%增长,相比ArrayList更消耗内存

对比

ArrayList的增删慢并不是绝对的

  1. 如果增加的一直是add()增加到末尾的话

  2. 一直删除末尾的元素

  3. 如果删除中间位置的元素,还是ArrayList快

但是一般来说:增删多还是用LinkedList

Set

存取无序,元素不可重复,set也是Collection的子接口

  • 可以使用迭代器

  • 可以使用增强for

  • 不能使用索引

HashSet(数组+链表+红黑树)

原则参考HashMap

  1. 实现了Set接口

  2. 实际上是HashMap

  3. 可以存放一个null

  4. 不能重复,存取无序

LinkedHashSet

LinkedHashSet继承了HashSet,底层是一个LinkedHashMap,底层维护了一个双向链表。每一个节点都有before和after属性,添加元素的原则和hashset一样

Map

  1. map和collection并列,用于保存有映射关系的数据:ket-value

  2. key和value可以是任何引用类型的数据,会被封装到HashMap$Node对象中

  3. key值不可以重复,value可以重复

  4. key值只能有一个null,value可以有多个null

  5. 常用String类作为Map的key

  6. 添加相同的key,相当于修改value

Map的遍历

1.通过set取出所有的key,然后通过for进行遍历

1
2
3
4
Set keyset = map.Keyset();
for(Object key : keyset){
System.out.println(key + " - "+ map.get(key));
}

2.迭代器

1
2
3
4
5
Iterator iterator = keyset.iterator();
while(iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + " - "+ map.get(key));
}

3.4.同1、2取出value

5.通过EntrySet

1
2
3
4
5
Set entryset = map.entrySet();
for(Object entry:entrySet) {
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}

HashMap(数组+链表+红黑树)

数组用来存储元素数据,链表解决冲突,红黑树提高查询效率。

  1. 如果链表长度>8&数组大小≥64,链表转为红黑树

  2. 红黑树节点数<6,转为链表

put的过程:

哈希值为32位int类型,计算的新哈希值是16高位和16低位异或,然后对数组长度(初始16)取模,取模的方法是和15进行&操作,直接保留后几位二进制数。

如果传入的不是2 的整数次方时,则向上找到2的整数次方大小,比如传入17是应该找到32。

哈希函数的构造方法:除留取余法、直接定址法、数字分析法、平方取中法、折叠法

扩容要rehash,十分耗时,为什么扩容因子取0.75?如果取得比较大,元素很多空位很少才扩容,那么发生哈希冲突的概率就增大了,查找的时间成本增加。如果设置比较小,查找时间成本降低,但是需要更多的空间,空间成本增加了。

HashTable

hashtable和hashmap的使用方法基本一样,他是线程安全的,但键和值都不能为null

线程安全 效率 运行null键null值
HashMap 不安全 可以
Hashtable 安全 较低 不可以

Properties

使用特点和Hashtable类似,properties还可以用于从xxx.properties文件中加载数据到properties对象进行读取和修改,xxx.properties文件通常作为配置文件

总结

1.先判断存储的类型(单列或双列)

2.单列:collection接口

允许重复:List

  1. 增删多:LinkedList

  2. 改查多:ArrayList

不允许重复:Set

  1. 无序:hashset

  2. 排序:TreeSet

  3. 插入和取出顺序一致:LinkedHashSet

3.双列:Map

  1. 键无序:HashMap

  2. 键排序:TreeMap

  3. 键插入和取出顺序一致:LinkedHashMap

  4. 读取文件Properties