Java基础进阶篇-第一天

80 阅读5分钟

Exception异常

异常类转存失败,建议直接上传图片文件

异常处理方式:抛出异常(throws)、捕获异常(try...catch)

// 抛出异常
方法 throws Exception {}

// 捕获异常
// 可以处理多个异常,子类异常在上面的catch,最后使用exception接收未知异常
try {
	// 可能出现异常的代码
}catch (Exception e){
	e.printStackTrace();
}

处理方案

  • 使用throws/throw往上抛出异常。最后进行处理,给用户返回友好的反馈。
  • 使用try~catch进行捕获,不抛出异常。catch进行处理,并恢复程序。

自定义异常

自定义异常类.png

常见异常

编译时异常(Exception)

  • IOException: I/O 操作中发生的异常

  • SQLException:数据库操作相关的异常

  • ClassNotFoundException:程序试图加载一个不存在的类

运行时异常(RuntimeException)

  • NullPointerException:尝试访问一个 null 引用的成员
  • ArrayIndexOutOfBoundsException:尝试访问数组的非法索引
  • ArithmeticExcepiton:算术运算中的异常
  • IllegalArgumentException:传递给方法的参数不合法
  • ClassCastException:非法类型转换
  • NoSuchMethodException:尝试访问一个不存在的方法
    • 使用反射(Reflection)API 时抛出
    • 方法名拼写错误、参数类型不匹配或方法访问权限不足等情况

错误(Errors)

  • OutOfMemoryError:JVM内存不足

    • 程序中存在内存泄露
    • 分配了过大的数组或集合
  • StackOverflowError:栈溢出

    • 无限递归

泛型

泛型(Generics)是编程语言中的一种特性,它允许开发者在定义类、接口或方法时使用类型参数(Type Parameters),从而实现代码的复用性和类型安全。泛型的核心思想是让代码能够处理多种数据类型,同时在编译时保证类型的安全性。

泛型不支持基本数据类型,只支持对象类型(引用数据类型)

泛型类

public class MyList<T> {
    ArrayList<T> list = new ArrayList<>();
    T getData(int index){
        return list.get(index);
    }
    void addData(T data){
        list.add(data);
    }
}

泛型接口

public interface Data<T> {
    void add(T t);
    void update(T t);
    T getDate(int id);
    void delete(int id);
}

泛型方法

public class GenericityDemo1 {
    public static void main(String[] args) {
        int data1 = 100;
        String str = "123123";
        myPrint(data1);
        myPrint(str);
    }

    // T可以接收任意类型,所以函数可以打印任何类型数据
    public static <T> void myPrint(T t) {
        System.out.println(t);
    }
}

泛型的类型参数

  • 类型参数的命名

    • T(Type):表示任意类型
    • E(Element):表示集合中的某个元素
    • K(Key):表示键的类型
    • V(Value):表示值的类型
    • N(Number):表示数字的类型
  • 类型参数的限制

    public class Box<T extends Number> {
    	// 上界限制
    	// T 必须是 Number 或其子类
        private T content;
        ...
    }
    
    public static <T super Integer> void printList(List<T> list) {
    	// 下界限制
    	// T 必须是 Integer 的父类
        ...
    }
    

通配符(Wildcards)

  • 无界通配符:? 表示任意类型

    例如:List<?> 表示一个未知类型的列表

  • 有界通配符:

    • 上界通配符:? extends T 表示类型参数是 T 或其子类。

      例如:List<? extends Number> 表示一个存储 Number 或其子类的列表

    • 下界通配符:? super T 表示类型参数是 T 或其父类。

      例如:List<? super Integer> 表示一个存储 Integer 或其父类的列表

包装类

基本数据类型对应的包装类(引用数据类型)
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

基本数据类型变成包装类对象

Integer data1 = Integer.valueOf(24); // -128~127之间的Integer对象是相同的,因为Integer类中维护了一个数组,只有超出各个范围才会创建一个新的对象。
Integer data2 = 24; // java支持自动装箱
int data = data2; // 自动拆箱

// 面试题
Integer data3 = 24;
Integer data4 = 24;
System.out.println(data1 == data2) // true
    
Integer data5 = 454;
Integer data6 = 454;
System.out.println(data1 == data2) // false 创建对象之后,`==`比较的就是两个引用类型的地址

包装类的其他功能

// 1. 可以把基本数据类型转换成字符串类型
// 2. 可以把字符串类型的数值转换成数字本身对应的真实数据
Integer i = 100;
String str = i.toString();
System.out.println(str);

Integer i2 = Integer.parseInt(str);
System.out.println(i2);

为什么要有包装类

为了万物皆可对象,并且泛型和集合都不支持基本数据类型,支持包装类。

集合体系结构

集合体系结构.png

Collection容器

常用方法

Collection常用方法.png

遍历方法
Collection<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");

// 普通for循环遍历,有索引的容器才能如此遍历
for (int i = 0; i < list.size(); i++) {
    String s = list.get(i);
    System.out.println(s);
}

// 方式一:迭代器遍历
Iterator<String> it = list.iterator();
while (it.hasNext()){
    String s = it.next(); // next()方法返回迭代器指向的元素,并迭代到下一个位置
    System.out.println(s);
}

// 方式二:增强for循环遍历
for (String s : list) {
    System.out.println(s);
}

// 方式三:Lambda表达式遍历
list.forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
});
// 函数式接口,所以可以简化
list.forEach( (s) -> {System.out.println(s);} );
list.forEach( s -> System.out.println(s); );
// 前后参数形式一致,可以使用方法引用(一般不使用)
list.forEach( System.out::println ); 

List容器

  • ArrayList底层是基于数组存储数据的

    • 查询速度快(根据索引查询快)
    • 增删数据效率较低

    底层源码: 第一次扩容为10,二次扩容为原来的1.5倍。

  • LinkedList底层是基于双向链表存储数据的**(queue、stack)**

    • 查询速度慢(无论查询哪个数据,都要从头开始找)
    • 链表增删相对快(首尾最快)
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");

// 通过index插入数据
list.add(2, "赵四");

// 通过index删除数据
list.remove(2);

// 通过index修改数据
list.set(2, "赵四");

// 获取数据
list.get(2);

// LinkedList也拥有以上函数方法,新增了许多针对于首尾元素的操作方法
LinkedList<String> list = new LinkedList<>();

// 列举 LinkedList 特有的函数
list.addFirst("a");
list.addLast("b");

System.out.println(list.getFirst());
System.out.println(list.getLast());

list.removeFirst();
list.removeLast();