Java语言基础(三)——异常、容器、泛型

目录

一、异常

1.1、简单异常

1.1.1、检查型异常

1.1.2、运行时异常

1.1.3、错误Error

1.2、异常处理机制

1.3、自定义异常

1.4、实际应用中的经验总结

二、容器

2.1、Collection

2.1.1、List接口

2.1.2、Set接口

2.2、Map接口

2.2.1、HashMap

2.3、总结

三、泛型

3.1、简介

3.2、泛型类

3.3、泛型接口

3.4、泛型方法

3.5、泛型字母的含义


一、异常

实际开发工作中,遇到的情况不可能是完全理想的,其遇到的问题就叫异常。Exception。

1.1、简单异常

1.1.1、检查型异常

最具代表性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如用户要打开一个不存在的文件时引发的异常,这些异常在编译时不能被简单地忽略。

1.1.2、运行时异常

是可能被程序员避免的异常,与检查性异常相反,运行时异常可以在编译时忽略

1.1.3、错误Error

错误不是异常,而是脱离程序员控制的问题。错误在代码经常被忽略。例如当栈溢出,一个异常就发生了,它们在编译也检查不到。

Error

  • java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
  • 在Java API中已经定义了许多异常类,这些异常分为两大类,错误Error和异常Exception

 

 Exception

  • 在Exception分支中有一个重要的子类RuntimeException(运行时异常)
    • ArrayIndexOutOfBoundsException数组下标越界
    • NullPointerException空指针异常
    • ArithmeticException算术异常
    • MissingResourceException丢失资源
    • ClassNotFoundException找不到类,等异常,这些异常是不检查异常,可以捕获处理也可以不处理
  • 这些异常一般都是由程序逻辑错误引起的,程序应该从逻辑角度去尽可能地避免这类异常地发生
  • Error和Exception的区别:Error通常是灾难性的致命错误,是程序无法控制和处理的,当这些异常出现时,JVM一般会选择终止线程;Exception通常情况下是可以被程序处理,并且程序中应该尽可能的去处理这些异常。

1.2、异常处理机制

  1. 抛出异常
  2. 捕获异常
  3. 异常处理关键字:try、catch、finally、throw、throws
public static void main(String[] args) {
    int a = 1;
    int b = 0;

    try { //try监控区域
        System.out.println(a/b);
    }catch (ArithmeticException e){ //catch 捕获异常
        System.out.println("程序出现异常,变量b不能为0");
    }catch (Exception e){
        e.printStackTrace();
    }finally { //一定会执行,处理善后工作,如关闭资源
        System.out.println("finally");
    }
    
    if(b==0){ //抛出异常一般在方法中使用
        throw new ArithmeticException(); //主动抛出异常
    }
}

1.3、自定义异常

  • java内置的异常类可以描述大部分常见异常,除此之外还能自定义异常。用户自定义异常类,只需继承Exception类即可。
  • 自定义异常类步骤如下
  1. 创建自定义异常类
  2. 在方法中通过throw关键字抛出异常
  3. 如果在当先抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声名处通过throws关键字指明要抛出给方法调用者的异常
  4. 在出现异常方法的调用者中捕获并处理异常
public class MyException extends Exception {
    private int detail;
    public MyException(int a) {
        this.detail = a;
    }

    //toString:异常的打印信息
    @Override
    public String toString() {
        return "MyException{" +
                "detail=" + detail +
                '}';
    }

}

public class ExceptionTest {

    public static void test(int a) throws MyException {
        System.out.println("传参为:" + a);
        if (a>10) {
            throw new MyException (a);
        }
        System.out.println("OK");
    }

    public static void main(String[] args) {
        try {
            test(11);
        } catch (MyException e) {
            System.out.println(e);
        }
    }
}

1.4、实际应用中的经验总结

  • 处理运行时异常时,采用逻辑去何理规避同时辅助try-catch处理
  • 在多重catch块后面,可以加一个catch(Exception)来处理有可能会被遗漏的异常
  • 对于不确定的代码,也可以加上try-catch,处理潜在的异常
  • 尽量去处理异常,切记只是简单的调用printStackTrace()去打印输出
  • 具体如何处理异常,要根据不同的业务需求和异常类型去决定
  • 尽量添加finally语句块去释放占用的资源。

二、容器

学到现在一说存放对象变量什么的,就会下意识的使用数组,数组在数据存取方面也确实很方柏霓,其存储效率高访问速度快,但是也有很多限制,比如数组长度以及类型,当我需要一组String类型同时还需要Integer类型时,就需要定义两次,同时,数组长度也受到限制,即使是动态定义数组长度,但长度依然固定在一个范围内,不方便也不灵活。

如果说我想消除这个限制和不便怎么办呢?Java提供的方法就是Java容器,java容器时JavaAPI所提供的一系列类的实例,用于在程序中存放对象,位于Java.util包,其长度不受限制,类型不受限制。

2.1、Collection

Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素。一些Collection允许元素重复,但有一些不行,一些有序而有一些无序。JavaSDK不提供直接继承自Collection的类,JavaSDK提供的类都是继承自Collection的子接口如List和Set。

public static void main(String[] args) {
        Collection<String> lstcoll = new ArrayList<>();
        lstcoll.add("China");
        lstcoll.add(new String("ZD"));
        System.out.println("size="+lstcoll.size());
        System.out.println(lstcoll);
    }

2.1.1、List接口

List是有序的Collection,使用此接口能精准的控制每个元素插入的位置,用户能通过索引来访问List中的元素,也就是说它是有序的。和Set不同,List允许元素重复

public static void main(String[] args) {
        List<String> l1 = new LinkedList<>();
        for (int i=0; i<=5; i++) {
            l1.add("a" + i);
        }
        System.out.println(l1);
        l1.add(3, "a100");
        System.out.println(l1);
        l1.add(6, "a200");
        System.out.println(l1);
        System.out.println((String)l1.get(2)+"");
        l1.remove(1);
        System.out.println(l1);
    }

2.1.1.1、ArrayList

简单来说ArrayList就相当于有序存储,包装了一个Object[]数组,实例化ArrayList是数组也被实例化,当向ArrayList中添加对象时,数组的大小也相应的改变。这样就带来了以下特点:

  • 快速访问,无需考虑性能问题,通过get(索引)就能快速访问对应元素。
  • 新增速度慢操作元素对象速度慢,操作有序数据结构会涉及到内存,导致速度慢。

2.1.1.2、LinkedList

和严格有序的ArrayList不同,LinkedList属于链式结构,通过节点彼此链接。每一个节点都包括了前一个节点的引用、后一个节点的引用和节点存储的值三部分。当一个新节点插入时,只需要修改其中保持先后关系的节点的引用即可,删除也是。所以:

  • 新增、操作元素对象速度快,只需要改变链接
  • 访问速度慢,需要一个个节点去索引

2.1.2、Set接口

与List接口不同,Set是一种不允许元素重复的Collection,比如任意元素e1和e2都有e1.equals(e2)=false,就连null元素也只能有一个

2.1.2.1、HashSet

HaskSet实现Set接口,有HashMap实例支持。它不保证set的迭代顺序;特别是它不保证顺序恒久不变,并且允许使用null元素。不能重复且无序。

public static void main(String[] args) {
        Set<String> s = new HashSet<String>();
        s.add("Hello");
        s.add("Hello");
        System.out.println(s);
    }

2.2、Map接口

值得注意的是Map没有继承Collection接口,Map接口是提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。即是一一映射,Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

2.2.1、HashMap

添加数据put(key, value),取出数据get(key),允许null,将HashMap视为Collection时,其迭代操作时间开销和HashMap的容量成正比。因此如果迭代操作的性能相当重要的话,不要将HashMap的的初始化容量设得太高,或者load factor过低。

public static void main(String[] args) {
        Map<String, String> M = new HashMap<String, String>();
        M.put("one", new String("1"));
        M.put("two", new String("2"));
        System.out.println(M);
    }

2.3、总结

Java容器事实上只有三种:Map、List和Set;但又有各种不同的实现版本,区别在于由什么支持。

比如:ArrayList和LinkedList都实现了List接口,因此无论选择哪一个,基本操作都一样。但ArrayList是由数组提供底层支持,而LinkedList是由双向链表实现的。所以,如果要经常向List里插入或删除数据,LinkedList会比较好,否则应该用速度更快的ArrayList。

HashSet总是比TreeSet 性能要好,而后者存在的理由就是它可以维持元素的排序状态,所以如果需要一个排好序的Set时,才应该用TreeSet。

三、泛型

3.1、简介

泛型:使得数据变得参数化

定义泛型时,对应的数据类型是不确定的,泛型方法被调用时,会指定具体的类型。

泛型的目的:解决容器类型在编译时安全检查问题

泛型分为:泛型类、泛型接口以及泛型方法

3.2、泛型类

class 类名 <泛型标识,即类型>{
    修饰符 泛型标识(成员变量类型);
    修饰符 构造函数(泛型表示 参数);
    ......
}

需要注意的是,泛型的参数不支持基本类型,在编译的时候就会用具体的数据类型替换掉泛型定义。

定义泛型类

import lombok.Data;
 
@Data
public class GenericClassExample<T> {
    private T member;
    public GenericClassExample(T member)
    {
        this.member = member;
    }
    public T handleSomething(T target)
    {
        return target;
    }
}

使用泛型类

public class GenericDemo {
    public static void main(String[] args) {
        GenericClassExample<String> stringExample = new GenericClassExample<String>("abc");
        GenericClassExample<Integer> integerExample = new GenericClassExample<Integer>(123);
        System.out.println(stringExample.getMember().getClass());
        System.out.println(integerExample.getMember().getClass());
    }
}

在泛型中使用具有继承关系的类,泛型是不接受的

比如即使Number是Integer的父类,当指定泛型Integer类,而调用时指定的类却是Number时,编译是无法通过的。

那有没有办法呢?当然是有的。

  1. 使用通配符【?】,但是这样的话就会使泛型的类型检查失去原有的意义。
  2. 加上上边界:extends E,指定传入的泛型类必须是继承E的。
  3. 加上下边界:super E,指定传入的泛型数据类型必须是E的父类。

3.3、泛型接口

与泛型类的用法基本一致,用于数据类型的生产工厂接口中

定义泛型接口

//泛型接口
public interface GenericIFactory<T,N> {
    T nextObject();
    N nextNumber();
}

定义实现类

public class RobotFactory implements GenericIFactory<String,Integer> {
    private String stringRobot[] = new String[]{"Hello","Hi"};
    private Integer integerRobot[] = new Integer[]{111,000};
    @Override
    public String nextObject() {
        Random random = new Random();
        return stringRobot[random.nextInt(2)];
    }
    @Override
    public Integer nextNumber() {
        Random random = new Random();
        return integerRobot[random.nextInt(2)];
    }
}

使用

 public static void main(String[] args) {
        RobotFactory factory = new RobotFactory();
        System.out.println(factory.nextObject()+" " +factory.nextNumber());
    }

3.4、泛型方法

能用在泛型类、泛型接口里,也能用在普通类或者接口里

定义一个泛型方法在我们之前定义的泛型类中

//泛型类
@Data
public class GenericClassExample<T> {
    private T member;
    public GenericClassExample(T member)
    {
        this.member = member;
    }
    public T handleSomething(T target)
    {
        return target;
    }
    //泛型函数
    public static<E> void printArray(E[] inputArray)
    {
        for(E element:inputArray)
        {
            System.out.printf("%s ",element);
        }
        System.out.println();
    }
 
}
public class GenericDemo {
    public static void main(String[] args) {
        //使用泛型函数
        GenericClassExample<String> example = new GenericClassExample<String>("abc");
        Integer[] integers = {1,2,3,4,5,6};
        Double[] doubles={1.1,2.2,3.3,4.4,5.5};
        Character[] characters={'A','B','C'};
        example.printArray(integers);
        example.printArray(doubles);
        example.printArray(characters);
    }
}

3.5、泛型字母的含义

  • E — Element:在集合中使用,因为集合存放的是元素
  • T — Type:Java类
  • K — Key:键
  • V — Value:值
  • N — Number:数值类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值