后端第二次培训 Java异常类和常用类 容器 泛型

异常类

什么是异常

异常就是在运行时产生的问题。通常用Exception描述,比如试图打开一个根本不存在的文件、除0溢出、数组下标越界等,异常处理将会改变程序的控制流程,让程序有机会对错误处理。 Java 使用 throw 关键字抛出一个 Exception 子类的实例表示异常发生,该异常实例封装了异常事件的信息并将被提交给 Java 运行时系统。

抛出异常throw

在java中,提供了一个throw关键字,用来抛出一个指定的异常。

使用方法:

1.创建一个异常对象。封装一些提示信息

2.将这个异常告知调用者

使用格式:

throw new 异常类名(参数);

例如:

throw new java.io.IOException("我是故意的");

声明异常throws

声明异常throws,方法可能抛出异常的声明

声明异常格式:

修饰符 返回值类型 方法名称 (参数)throws 异常1名称,异常2名称{

}
例如:

public  int  testMethod() throws ArithmeticExecption{
		int  a = 10/0;
		return a;
	}

捕获异常try…catch…finally

捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。

Java 使用try-catch 语句来处理异常,将可能出现的异常操作放在 try-catch语句的 try 部分,一旦 try 部分抛出异常对象,或调用某个可能抛出异常对象的方法,并且该方法抛出了异常对象,那么 try 部分将立刻结束执行,转向执行相应的 catch 部分。所以程序可以将发生异常后的处理放在 catch 部分。 try-catch 语句可以由几个 catch 组成,分别处理发生的相应异常。还可以加上 finally 语句。
格式:

try {

     //需要被检测的语句。

}

catch(ExceptionSubClass1 e) { //参数。

     //异常的处理语句。

}
catch(ExceptionSubClass2 e){
	//异常的处理语句
}

finally {
	//最终处理方式
     //一定会被执行的语句。

}

实例:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class ExceptionDemo {
    public static void main(String[] args) {
        Scanner scanner = null;
        try {
            // 尝试打开文件
            File file = new File("test.txt");
            scanner = new Scanner(file);

            // 读取文件内容
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                System.out.println(line);
            }

        } catch (FileNotFoundException e) {
            // 如果文件未找到
            System.out.println("File not found: " + e.getMessage());

        } catch (Exception e) {
            // 其他未知异常
            System.out.println("An error occurred: " + e.getMessage());

        } finally {
            // 关闭文件
            if (scanner != null) {
                scanner.close();
            }
            System.out.println("File closed.");
        }
    }
}

异常分类

Throwable类是所有错误跟异常类的超类(祖宗类)。

Exception异常类及其子类都是继承自Throwable类,用来表示java中可能出现的异常,并且合理的处理这些异常。

RuntimeException类是运行异常类,继承自Exception类,包括未受检查的异常的基类,包括NullPointerException、ArithmeticExecption。

Error类是与Exception的平级的类,用来表示Java中存在的严重错误,由系统级问题引起,不是由于代码逻辑引起,一般不能通过修改代码来解决问题,因此错误不应该被捕获。
在这里插入图片描述

常见异常类

算术异常类:ArithmeticExecption

空指针异常类:NullPointerException

类型强制转换异常:ClassCastException

数组负下标异常:NegativeArrayException

数组下标越界异常:ArrayIndexOutOfBoundsException

操作数据库异常:SQLException

输入输出异常:IOException

文件未找到异常:FileNotFoundException

常用类

String类

由于在程序设计中经常涉及处理和字符序列有关的算法,为此 Java 专门提供了用来处理字符序列的 String 类。 String 类在 java.lang 包中,由于 java.lang 包中的类被默认引入,因此程序可以直接使用 String 类。

说明:
当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
当对现的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
当调用String的 replace() 方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

常用方法:

public int length()//获取 String 对象的字符序列的长度
public boolean equals(String s)//比较当前对象是否与参数 s 指定的对象的字符序列相同
public boolean startsWith(String s)//判断当前对象的字符序列前缀是否是参数 s 指定对象的字符序列
public boolean endsWith(String s)//判断当前对象的字符序列后缀是否是参数 s 指定对象的字符序列
public int compareTo(String s)//按字典序与参数 s 指定对象的字符序列比较大小,相同返回 0 ,大于 s 返回正值,小于 s 返回负值
public boolean contains(String s)//判断当前对象字符序列是否包含参数 s 的字符序列
public int indexOf(String s)//当前对象字符序列 0 索引位置开始检索首次出现 s 字符序列的位置,并返回该位置,没有检索到返回 -1 
public int lastIndexOf(String s)//当前对象字符序列 0 索引位置开始检索最后一次出现 s 字符序列的位置,并返回该位置,没有检索到返回 -1 
public String substring(int startpoint)//获得一个新的 String 对象,新对象是复制当前对象 startpoint 位置至最后位置的字符
/*若调用 substring(int start,int end) ,则复制 start 到 end-1 */
public String trim()//得到当前对象去掉前后空格后的新字符序列

StringBuffer类

String 对象的字符序列的字符不能被修改、删除、即 String 对象的实体是不可以再发生变化的。与 String 类不同, StringBuffer 类的对象的实体的内存空间可以自动改变大小,便于存放一个可变的字符序列。

StringBuffer s = new StringBuffer("我喜欢");
s.append("睡觉");//追加字符序列

Date 类与 Calendar 类

Date 类:可获取本机的当前日期和时间

Date nowTime = new Date();//无参构造方法,有参构造方法返回公元前(参数取正)或后(参数取负)的时间
System.out.println(nowTime);

Math类和Random类

Math类常用方法:

public static long abs(double a)//返回 a 的绝对值
public static double max(double a,double b)//返回 a , b 最大值
public static double min(double a,double b)//返回 a , b 最小值
public static double random()//产生一个 0~1 的随机数,不包含 0 和 1
public static double pow(double a,double b)//返回 a 的 b 次幂
public static double sqrt(double a)//返回 a 的平方根
public static double log(double a)//返回 a 的对数
public static double sin(double a)//返回 a 的正弦值
public static double asin(double a)//返回 a 的反正弦值
public static double ceil(double a)//返回大于 a 的最小整数,并转化为 double 类型
public static double floor(double a)//返回小于 a 的最小整数,并转化为 double 类型
public static long round(double a)//返回值是 (long)Math.floor(a+0.5) ,即四舍五入(小于 0.5 舍)

Random类:

Rondom random = new Random();
random.nextInt();//返回一个随机整数
random.nextInt(100);//返回 [0,99) 之间的整数
random.nextBoolean();//返回一个随机 boolean 值

容器

概念

在Java当中,有一个类专门用来存放其它类的对象,这个类就叫做容器,它就是将若干性质相同或相近的类对象组合在一起而形成的一个整体 。

List,Map,Set,Queue

具体结构如下:

  • Collection
    • List
    • Set
    • Queue
  • Map
List

List存储的元素是有序的、可重复的。Java中的ArrayList和LinkedList都是List的实现。
举个例子:

import java.util.List;
import java.util.ArrayList;

public class ListDemo {
    public static void main(String[] args) {
        
        // 创建一个 ArrayList 对象
        List<String> list = new ArrayList<>();

        // 向 List 中添加元素
        list.add("apple");
        list.add("banana");
        list.add("orange");

        // 打印 List 中的元素
        System.out.println("List 中的元素:");
        for (String s : list) {
            System.out.println(s);
        }

        // 获取 List 中的元素数量
        System.out.println("List 中的元素数量:" + list.size());

        // 判断 List 是否为空
        System.out.println("List 是否为空:" + list.isEmpty());

        // 判断 List 中是否包含某个元素
        System.out.println("List 是否包含元素 \"banana\":" + list.contains("banana"));

        // 获取 List 中指定索引的元素
        System.out.println("List 中索引为 1 的元素:" + list.get(1));

        // 修改 List 中指定索引的元素
        list.set(1, "grape");
        System.out.println("List 中修改后索引为 1 的元素:" + list.get(1));

        // 删除 List 中指定元素
        list.remove("orange");
        System.out.println("List 删除元素 \"orange\" 后的元素:");
        for (String s : list) {
            System.out.println(s);
        }

        // 清空 List 中的所有元素
        list.clear();
        System.out.println("List 清空后的元素数量:" + list.size());
    }
}

Set

不允许存储重复的元素,并且不保证元素的顺序,并且提供了高效的查找和删除操作。在实际开发中,Set 经常被用来实现去重和查找元素的功能。Java中的HashSet和TreeSet都是Set的实现。
举个例子:

import java.util.Set;
import java.util.HashSet;

public class SetDemo {
    public static void main(String[] args) {

        // 创建一个 HashSet 对象
        Set<String> set = new HashSet<>();

        // 向 Set 中添加元素
        set.add("apple");
        set.add("banana");
        set.add("orange");

        // 打印 Set 中的元素
        System.out.println("Set 中的元素:");
        for (String s : set) {
            System.out.println(s);
        }

        // 获取 Set 中的元素数量
        System.out.println("Set 中的元素数量:" + set.size());

        // 判断 Set 是否为空
        System.out.println("Set 是否为空:" + set.isEmpty());

        // 判断 Set 中是否包含某个元素
        System.out.println("Set 是否包含元素 \"banana\":" + set.contains("banana"));

        // 删除 Set 中指定元素
        set.remove("orange");
        System.out.println("Set 删除元素 \"orange\" 后的元素:");
        for (String s : set) {
            System.out.println(s);
        }

        // 清空 Set 中的所有元素
        set.clear();
        System.out.println("Set 清空后的元素数量:" + set.size());
    }
}

Queue

是一种先进先出(FIFO)的数据结构,它允许在队尾插入元素,在队头删除元素。在实际开发中,Queue 经常被用来实现消息队列、任务队列等场景。
举个例子:

import java.util.Queue;
import java.util.LinkedList;

public class QueueDemo {
    public static void main(String[] args) {

        // 创建一个 LinkedList 对象
        Queue<String> queue = new LinkedList<>();

        // 向 Queue 中添加元素
        queue.offer("apple");
        queue.offer("banana");
        queue.offer("orange");

        // 打印 Queue 中的元素
        System.out.println("Queue 中的元素:");
        for (String s : queue) {
            System.out.println(s);
        }

        // 获取 Queue 中的元素数量
        System.out.println("Queue 中的元素数量:" + queue.size());

        // 判断 Queue 是否为空
        System.out.println("Queue 是否为空:" + queue.isEmpty());

        // 获取并删除 Queue 中的头部元素
        System.out.println("Queue 头部元素:" + queue.poll());

        // 打印 Queue 中的元素
        System.out.println("Queue 删除头部元素后的元素:");
        for (String s : queue) {
            System.out.println(s);
        }

        // 获取 Queue 头部元素但不删除
        System.out.println("Queue 头部元素:" + queue.peek());
    }
}

Map

可以存储键值对,其中每个键只能对应一个值,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。
举个例子:

import java.util.Map;
import java.util.HashMap;

public class MapDemo {
    public static void main(String[] args) {

        // 创建一个 HashMap 对象
        Map<String, Integer> map = new HashMap<>();

        // 向 Map 中添加键值对
        map.put("apple", 2);
        map.put("banana", 3);
        map.put("orange", 4);

        // 获取 Map 中的键值对数量
        System.out.println("Map 中的键值对数量:" + map.size());

        // 判断 Map 是否为空
        System.out.println("Map 是否为空:" + map.isEmpty());

        // 获取 Map 中指定键的值
        System.out.println("Map 中键为 \"apple\" 的值:" + map.get("apple"));

        // 判断 Map 中是否包含某个键
        System.out.println("Map 是否包含键 \"orange\":" + map.containsKey("orange"));

        // 删除 Map 中指定键的键值对
        map.remove("banana");

        // 打印 Map 中的所有键值对
        System.out.println("Map 中的所有键值对:");
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " : " + value);
        }

        // 清空 Map 中的所有键值对
        map.clear();
        System.out.println("Map 清空后的键值对数量:" + map.size());
    }
}

泛型

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

举个例子

使用泛型前:

List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

for(int i = 0; i< arrayList.size();i++){
    String item = (String)arrayList.get(i);
    Log.d("泛型测试","item = " + item);
}

毫无疑问,程序的运行结果会以崩溃结束:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。

使用泛型后:

List<String> arrayList = new ArrayList<String>();
arrayList.add("aaaa");
arrayList.add(100);// 在编译阶段,编译器就会报错

我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题,IDE在写代码的时候就已经会爆红了。

泛型特性

首先我们看一个代码:

public class Test {
 
    public static void main(String[] args) {
 
        ArrayList<String> list1 = new ArrayList<String>();
        list1.add("abc");
 
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        list2.add(123);
 
        System.out.println(list1.getClass() == list2.getClass());
    }
 
}

输出结果为true,我们发现,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段

对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

泛型的使用

泛型类

泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来,这样的话,用户明确了什么类型,该类就代表着什么类型…用户在使用的时候就不用担心强转的问题,运行时转换异常的问题了。
基本语法格式:

class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
  private 泛型标识 /*(成员变量类型)*/ var; 
  .....
  }
}

看一个实例代码:

public class Demo {
    public static void main(String[] args) {
        // 定义泛型类 Test 的一个Integer版本
        Test<Integer> intOb = new Test<Integer>(88);
        intOb.showType();
        int i = intOb.getOb();
        System.out.println("value= " + i);
        System.out.println("----------------------------------");
        // 定义泛型类Test的一个String版本
        Test<String> strOb = new Test<String>("Hello Gen!");
        strOb.showType();
        String s = strOb.getOb();
        System.out.println("value= " + s);
    }
}
/*
使用T代表类型,无论何时都没有比这更具体的类型来区分它。如果有多个类型参数,我们可能使用字母表中T的临近的字母,比如S。
*/
class Test<T> {
    private T ob;
 
    /*
    定义泛型成员变量,定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。
    注意,父类定义的类型参数不能被子类继承。
    */
 
    //构造函数
    public Test(T ob) {
        this.ob = ob;
    }
 
    //getter 方法
    public T getOb() {
        return ob;
    }
 
 
    //setter 方法
    public void setOb(T ob) {
        this.ob = ob;
    }
 
    public void showType() {
        System.out.println("T的实际类型是: " + ob.getClass().getName());
    }
}
泛型接口
public interface Generator<T> {
    public T method();
}
  • 实现泛型接口,不指定类型
class GeneratorImpl<T> implements Generator<T>{
    @Override
    public T method() {
        return null;
    }
}
  • 实现泛型接口,指定类型
class GeneratorImpl<T> implements Generator<String>{
    @Override
    public String method() {
        return "hello";
    }
}
泛型方法

判断一个方法是否是泛型方法关键看方法返回值前面有没有使用 <>标记的类型,有就是,没有就不是。

public class Normal {
  // 成员泛型方法
  public <E> String getString(E e) {
  return e.toString();
  }
  // 静态泛型方法
  public static <V> void printString(V v) {
  System.out.println(v.toString());
  }
 }
 // 泛型类中的泛型方法
 public class Generics<T> {
  // 成员泛型方法
  public <E> String getString(E e) {
  return e.toString();
  }
  // 静态泛型方法
  public static <V> void printString(V v) {
  System.out.println(v.toString());
  }
 }

泛型通配符

常用的 T,E,K,V,?

本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,? 是这样约定的:

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value)
  • 分别代表java键值中的Key Value
  • E (element) 代表Element

为什么要使用通配符而不是简单的泛型呢?通配符其实在声明局部变量时是没有什么意义的,但是当你为一个方法声明一个参数时,它是非常重要的。
我们首先看一个例子:

static int countLegs (List<? extends Animal > animals ) {
    int retVal = 0;
    for ( Animal animal : animals )
    {
        retVal += animal.countLegs();
    }
    return retVal;
}
 
static int countLegs1 (List< Animal > animals ){
    int retVal = 0;
    for ( Animal animal : animals )
    {
        retVal += animal.countLegs();
    }
    return retVal;
}
 
public static void main(String[] args) {
    List<Dog> dogs = new ArrayList<>();
 	// 不会报错
    countLegs( dogs );
	// 报错
    countLegs1(dogs);
}

运行结果:
在这里插入图片描述
由上我们可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。所以,对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(尖括号里一个问号,即 <?> ),表示可以持有任何类型。像 countLegs 方法中,限定了上届,但是不关心具体类型是什么,所以对于传入的 Animal 的所有子类都可以支持,并且不会报错。而 countLegs1 就不行。

上界通配符: < ? extends E>

用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。

在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:

如果传入的类型不是 E 或者 E 的子类,编译不成功泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用。

下界通配符:< ? super E>:

用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object

关于泛型更详细的知识可以查看
泛型详解

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值