天天看点

JAVA异常处理与容器(集合)

目录

一.异常处理

二.JAVA容器

1.迭代器

2.ArrayList 和 LinkedList

3.Map:

一个键值(key)对应一个对象(value)的容器接口,并且没有排序

1.异常

一、 异常机制的概述

      异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。

      程序错误分为三种:1.编译错误;2.运行时错误;3.逻辑错误。

      (1)编译错误是因为程序没有遵循语法规则,编译程序能够自己发现并且提示我们错误的原因和位置,这个也是大家在刚接触编程语言最常遇到的问题。

      (2)运行时错误是因为程序在执行时,运行环境发现了不能执行的操作,这种错误在编译时发现不了,只有在运行时才会报错。

      (3)逻辑错误是因为程序没有按照预期的逻辑顺序执行。异常也就是指程序运行时发生错误,而异常处理就是对这些错误进行处理和控制。

 1.在Java中当程序出现异常时,可以使用try···catch、try···catch···finally或try···finally进行处理。

1. 使用try···catch处理异常

try后是正常执行的语句,而catch后是对异常处理的语句,catch的括号中是程序需要处理的异常类型。语法格式如下:

try {

正常执行的语句

} catch(Exception e) {

对异常进行处理的语句

}

这里举一个算数异常的例子,如下。

public class ExceptionTest {

public static void main(String[] args) {

int result = 1 / 0;

try {

System.out.println(result);

} catch (Exception e) {

System.out.println("抛出异常:" + e.getMessage());

}}}

2. 使用try···catch···finally处理异常

这里try、catch后的语句与前面的相同,而finally后的语句无论是否发生异常都要执行,因此finally语句块通常用于执行垃圾回收。语法格式如下:

try {

正常执行的语句

} catch(Exception e) {

对异常进行处理的语句

} finally {

一定会被处理的语句

}

3. 使用try···finally处理异常

在程序发生异常时,就可以在finally语句块中对其进行相应的处理,另外当程序没有发生异常时,执行完try和finally之间的语句后,也将执行finally语句块中的代码。

举一个用try   catch的例子,如下所示:

【NumberFormatException异常】编写一个程序,提示用户输入两个整数,然后显示它们的和。用户输入错误时提示用户重新输入。

如下所示:

JAVA异常处理与容器(集合)
JAVA异常处理与容器(集合)
package example;

import java.util.Scanner;

public class Test1 {
	public static void main(String[] args) {
		String string = new String();
		Scanner input = new Scanner(System.in);
		System.out.println("输入数字");
		string = input.nextLine();
		int num = Integer.parseInt(string);
		System.out.println(num);
	}
}
           

进行异常抛出:

try{
				System.out.println("请输入两个数:");
				num1 = input.nextLine();
				num2 = input.nextLine();
				int sum = Integer.parseInt(num1.trim())+ 							Integer.parseInt(num2.trim());
				System.out.println(num1 + " + "+ num2 +" = " + sum);
				judge = false;
			}
			catch(NumberFormatException ex){
				System.out.println("请输入两个有意义的数");
	}
           

 注:

trim() 方法用于删除字符串的头尾空白符,空白符包括:空格、制表符 tab、换行符等其他空白符等。

更多String方法可参考我的java基础类库博客

JAVA基础类库(第四周)(三只松鼠,奥利给)_阿狗哲哲的博客-CSDN博客

https://blog.csdn.net/qq_52438590/article/details/116951958?spm=1001.2014.3001.5501

pareInt()方法如下所示,即将字符串转化为int类型 

var num1 = parseInt("1234blue"); // 1234
var num2 = parseInt(""); // NaN
var num3 = parseInt("0xA"); // 10(十六进制数)
var num4 = parseInt(22.5); // 22
var num5 = parseInt("070"); // 56(八进制数)
var num6 = parseInt("70"); // 70(十进制数)
var num7 = parseInt("0xf"); // 15(十六进制数)
var num8 = parseInt("AF", 16); //175
           
除了上面的try···catch语句处理之外,还可以使用throws声明或throws语句抛出异常。 

1. 使用throws声明抛出异常

throws用于方法声明,在声明方法时使用throws声明抛出异常,然后在调用该方法中对异常进行处理。

如需声明多个异常,各异常间要用逗号分隔,语法格式如下:

数据类型 方法名(形参列表) throws 异常类1, 异常类2, ……, 异常类n {

方法体;

}

2. 使用throw语句抛出异常

如果希望程序自行抛出异常,可以使用throw语句来实现。语法格式如下: throw new Exception("对异常的说明");

使用throw语句抛出的是异常类的实例,通常与if语句一起使用。

2.数组与容器

1.可与前面的博客作比较:链表c语言_阿狗哲哲的博客-CSDN博客

https://blog.csdn.net/qq_52438590/article/details/116943029?spm=1001.2014.3001.5501

c语言中的数组与链表的区别与JAVA中的数组与容器的区别有相似相通之处:

优势:数组是一种线性序列,本身是一种数组容器可快速访问数组元素,效率高。

劣势:数组容器需要事先定义好容量,不能随时扩容,因此需要一种功能更强大的数组 来存放数据,因此有了容器的概念java容器可以动态调节自己的大小,因此编程中可以将任意数量的对象放置到容器中,类似于c++中的vector容器,功能很强大。

容器嘛,所以里边的操作必然是添加、删除、判断、加获取。

JAVA异常处理与容器(集合)

 上图所示为所有容器的示意图:

其中Collection与Map是并列的概念,

Collection接口是List,Set等的父接口,该接口定义的方法既可以操作List也可以操作Set,

Map接口用于存放键值对形式的元素,描述了由不重复的键到值的映射。

1. List

存储的元素是有序的、可重复的。

Arraylist: Object[]数组

Vector:Object[]数组

LinkedList: 双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)

2. Set

存储的元素是无序的、不可重复的。

HashSet:无序,唯一,基于 HashMap 实现的,底层采用 HashMap 来保存元素

LinkedHashSet:LinkedHashSet 是 HashSet 的子类,并且其内部是通过 LinkedHashMap 来实现的。有点类似于 LinkedHashMap 其内部是基于 HashMap 实现一样

TreeSet:有序,唯一,红黑树

3. Map

HashMap:JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突⽽存在的。JDK1.8以后在解决哈希冲突时有了变化, 当链表⻓度⼤于阈值(默认为8)时,将链表转化为红⿊树,以减少搜索时间

LinkedHashMap:LinkedHashMap继承⾃HashMap,所以它的底层仍然是基于数组和链表/红⿊树组成。另外,LinkedHashMap 在上面结构的基础上,增加了⼀条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。

Hashtable: 数组+链表组成的,数组是 Hashtable 的主体,链表则是主要为了解决哈希冲突而存在的

TreeMap: 红黑树

其中,红黑树的理解感悟可参考csdn大佬的博客

漫画算法:5 分钟搞明白红黑树到底是什么?_CSDN资讯-CSDN博客

原文链接:https://data.newrank.cn/m/s.html?s=PigpOzA/LTE%3D

1.迭代器

在说具体容器之前,有必要先了解一下迭代器。作为一种设计模式,迭代器给我们提供了遍历容器中元素的方法, Iterator是作为一个接口存在的,它定义了迭代器所具有的功能,接口如下:

package java.util;    
public interface Iterator<E> {    
    boolean hasNext();    
    E next();    
    void remove();    
} 
           

迭代器只能通过容器本身得到,每个容器都通过自己的内部类完成迭代器,因此可以通过迭代器遍历容器,类似于c++中vector的遍历,也是需要通过迭代器遍历vector

其中,c++的遍历如下所示:

vector<Phonecard_contact>::iterator temp;                       //phone_contact类型的指针
        for(temp=phonecard_contact.begin();temp!=phonecard_contact.end();temp++)//遍历
        {
            cout<<(*temp);
            cout<<endl;
        }
           

java的遍历如下所示,区别非常大:

ArrayList<String> list = new ArrayList<String>();
//省略初始化list……
……
//从list得到其迭代器
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
    //
    String element = iterator.next();
    System.out.println(element);
}
           

如果只是向前遍历List,并不打算修改List对象本身,使用foreach语法会更加简洁。

for(String e : list){
    System.out.println(e);
}
           

2.ArrayList 和 LinkedList

ArrayList详解

ArrayList是顺序容器,底层是数组,允许放入null值。每个ArrayList都有一个容量capacity,表示底层实现数组的大小,当添加元素的时候,如果capacity不够,会自动增加数组的大小。

List 元素可重复,且有序。ArrayList和LinkedList是List的子类。

JAVA异常处理与容器(集合)

1.add()方法

(1)ArrayList末尾添加元素的方法是add(E e),指定位置插入元素的方法是add(int index, E e),在添加的过程中,可能会存在capacity容量不足的问题,在每次添加前都要进行容量检查,如果容量不足,需要使用grow()扩容方法进行自动扩容

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    //原来的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
        //扩展空间完成后复制
    elementData = Arrays.copyOf(elementData, newCapacity);
}
           

add(int index, E e)需要先对插入位置之后的元素进行移动,然后完成插入操作,数组size+1,方法有线性时间复杂度。

(2)Linkedlist

add()方法包含两个,add(E e)方法在链表末尾插入元素,借助last指针,末尾插入只需常数时间;add(int index, E element)方法在指定下标处插入元素,需要先查找位置再执行插入。

add(E e)方法与add(int index, E element)大同小异,只介绍难度更复杂的add(int index, E element),其实add(int index, E element)与c++中的动态链表也是大同小异,具体详情可翻阅之前的博客链表

JAVA异常处理与容器(集合)
public void add(int index, E element) {
    //下标越界检查(index >= 0 && index <= size)
    checkPositionIndex(index);
    if (index == size)
        //插入位置是末尾,或列表为空
        add(element);
    else{
        //先根据index找到要插入的位置
        Node<E> succ = node(index);
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
    }
           

3.Map:

一个键值(key)对应一个对象(value)的容器接口,并且没有排序

//遍历方式:由于Map没有提供iterator()函数,而是用keySet(),values()方法取代
		//方法一
		Collection<String> c=m.values();
		for(Iterator<String> it=c.iterator();it.hasNext();) {
			System.out.print(it.next()+" ");
		}
		System.out.println();
		//方法二
		Set<Integer> s=m.keySet();
		for(Iterator<Integer> it=s.iterator();it.hasNext();) {
			System.out.print(it.next()+" ");
		}
		System.out.println();
		
           

HashMap:

一个我们平常使用最最频繁的Map容器,它的查询速度是最快的,HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。 HashMap 最多只允许一条记录的键为 null,允许多条记录的值为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。

TreeMap:

TreeMap:和TreeSet类似,红黑树结构,同样进行排序(次序由存入TreeMap的类的compare()或compareTo()方法决定,也就是说装入TreeMap的类必须实现HashCode(),equals(),compare()三个方法),同时TreeMap还有一个subMap()用来返回子树。

继续阅读