目录
1.异常
1.1认识异常
1.1.1异常是什么
异常代表程序出现的问题
1.1.2异常的分类
【1】运行时异常
继承自RuntimeException的异常或其子类,编译阶段不报错,运行时出现的。(如:数组索引越界异常)
public static void show(){
System.out.println("==程序开始。。。。==");
// 运行时异常的特点:编译阶段不报错,运行时出现的异常,继承自 RuntimeException。
int[] arr = {1,2,3};
// System.out.println(arr[3]); // ArrayIndexOutOfBoundsException
// System.out.println(10/0); // ArithmeticException
// 空指针异常
String str = null;
System.out.println(str);
System.out.println(str.length()); // NullPointerException
System.out.println("==程序结束。。。。==");
}
【2】编译时异常
没有继承RuntimeExcpetion的异常,编译阶段就会出错。(如:日期解析异常)
public static void show2() throws Exception {
System.out.println("==程序开始。。。。==");
// 编译异常:编译阶段报错,编译不通过。
String str = "2024-07-09 11:12:13";
// 把字符串时间解析成Java中的一个日期对象。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = sdf.parse(str); // 编译时异常,提醒程序员这里的程序很容易出错,请您注意!
System.out.println(date);
InputStream is = new FileInputStream("D:/meinv.png");
System.out.println("==程序结束。。。。==");
}
1.1.3异常的基本处理
【1】抛出异常(throws)
在方法上使用throws关键字,可以将方法内部出现的异常抛出去给调用者处理。
public static void show2() throws Exception
【2】捕获异常(try...catch)
try {
// 监视代码,出现异常,会被catch拦截住这个异常
show2();
} catch (Exception e) {
e.printStackTrace(); // 打印这个异常信息
}
1.1.4异常的作用
【1】异常是用来定位程序bug的关键信息
【2】可以作为方法内部的一种特殊返回值。以便通知上层调用者,方法的执行问题
1.2自定义异常
1.2.1为什么自定义异常
Java无法为这个世界上全部的问题都提供异常类来代表, 如果企业自己的某种问题,想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类了。
1.2.2两种自定义异常
【1】自定义运行时异常
定义一个异常类继承RuntimeException. 重写构造器。 通过throw new 异常类(xxx)来创建异常对象并抛出。
public ItheimaAgeIllegalRuntimeException(String message) {
super(message);
}
throw new ItheimaAgeIllegalRuntimeException("年龄非法 age 不能低于1岁不能高于200岁");
【2】自定义编译时异常
定义一个异常类继承Exception. 重写构造器。 通过throw new 异常类(xxx) 创建异常对象并抛出。
public ItheimaAgeIllegalException(String message) {
super(message);
}
throw new ItheimaAgeIllegalException("年龄非法 age 不能低于1岁不能高于200岁");
1.3最常见的异常处理方案
底层的异常抛出去给最外层,最外层集中捕获处理。
2.泛型
2.1认识泛型
2.1.1什么是泛型
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>)。称为泛型类、泛型接口,泛型方法、它们统称为泛型。
2.1.2泛型的作用
泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。
2.1.3泛型的本质
把具体的数据类型作为参数传给类型变量。
2.2泛型类
2.2.1格式
修饰符 class 类名<类型变量,类型变量,…> { }
public class ArrayList<E>{
. . .
}
2.3泛型接口
2.3.1格式
修饰符 interface 接口名<类型变量,类型变量,…> { }
public interface A<E>{
. . .
}
2.4泛型方法、通配符、上下限
2.4.1泛型方法
修饰符 <类型变量,类型变量,…> 返回值类型 方法名(形参列表) { }
public static <T> void test(T t){
}
2.4.2通配符
就是 “?” ,可以在“使用泛型”的时候代表一切类型; E T K V 是在定义泛型的时候使用。
2.4.3上下限
【1】泛型上限: ? extends Car: ? 能接收的必须是Car或者其子类 。
【2】泛型下限: ? super Car : ? 能接收的必须是Car或者其父类。
2.5泛型支持的类型
2.5.1特点
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
2.5.2包装类
【1】包装类就是把基本类型的数据包装成对象的类型。
基本数据类型 | 对应的包装类(引用数据类型) |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
【2】基本类型的数据包装成对象的方案
public static Integer valueOf(int i)
// 把基本数据类型变成包装类对象。
// 手工包装:
// Integer i = new Integer(100); // 过时
Integer it1 = Integer.valueOf(100); // 推荐的
Integer it2 = Integer.valueOf(100); // 推荐的
System.out.println(it1 == it2);
【3】自动装箱
基本数据类型可以自动转换为包装类型。
// 自动装箱成对象:基本数据类型的数据可以直接变成包装对象的数据,不需要额外做任何事情
Integer it11 = 130;
Integer it22 = 130;
System.out.println(it11 == it22);
【4】自动拆箱
包装类型可以自动转换为基本数据类型。
// 自动拆箱:把包装类型的对象直接给基本类型的数据
int i = it11;
System.out.println(i);
2.5.3包装类其他的功能
【1】可以把基本类型的数据转换成字符串类型。
// 包装类新增的功能:
// 1、把基本类型的数据转换成字符串。
int j = 23;
String rs1 = Integer.toString(j); // "23"
System.out.println(rs1 + 1); // 231
Integer i2 = j;
String rs2 = i2.toString(); // "23"
System.out.println(rs2 + 1 ); // 231
String rs3 = j + "";
System.out.println(rs3 + 1 ); // 231
【2】可以把字符串类型的数值转换成数值本身对应的真实数据类型。
// 2、把字符串数值转换成对应的基本数据类型(很有用)。
String str = "98";
// int i1 = Integer.parseInt(str);
int i1 = Integer.valueOf(str);
System.out.println(i1 + 2);
String str2 = "98.8";
// double d = Double.parseDouble(str2);
double d = Double.valueOf(str2);
System.out.println(d + 2);
3.集合框架
3.1认识集合
3.1.1什么是集合
集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用。
3.1.2集合体系结构
【1】Collection代表单列集合,每个元素(数据)只包含一个值。
【2】Map代表双列集合,每个元素包含两个值(键值对)。
3.1.3Collection集合特点
【1】List系列集合:添加的元素是有序、可重复、有索引。
【2】Set系列集合:添加的元素是无序、不重复、无索引。
3.2Collection的功能
3.2.1常用方法
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
方法名 | 说明 |
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数。 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
public static void main(String[] args) {
// 目标:搞清楚Collection提供的通用集合功能。
Collection<String> list = new ArrayList<>();
// 添加元素
list.add("张三");
list.add("李四");
list.add("王五");
System.out.println(list); // [张三, 李四, 王五]
// 获取集合的元素个数
System.out.println(list.size());
// 删除集合元素
list.remove("李四");
System.out.println(list);
// 判断集合是否为空
System.out.println(list.isEmpty()); // false
// 清空集合
// list.clear();
// System.out.println(list);
// 判断集合中是否存在某个数据
System.out.println(list.contains("张三"));
// 把集合转换成数组
Object[] arr = list.toArray();
System.out.println(Arrays.toString(arr));
// 把集合转换成字符串数组(拓展)
String[] arr2 = list.toArray(String[]::new);
}
3.2.2三种遍历方式
【1】迭代器遍历
(1)迭代器是什么
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
(2)通过迭代器获取集合的元素,如果取元素越界会出现什么异常?
会出现NoSuchElementException异常。
public static void main(String[] args) {
// 目标:掌握Collection的遍历方式一:迭代器遍历
Collection<String> names = new ArrayList<>();
names.add("张无忌");
names.add("玄冥二老");
names.add("宋青书");
// names.add("殷素素");
System.out.println(names); // [张无忌, 玄冥二老, 宋青书]
// it
// 1、得到这个集合的迭代器对象
Iterator<String> it = names.iterator();
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next()); // NoSuchElementException
// 2、使用一个while循环来遍历
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}
}
【2】增强for循环
增强for可以用来遍历集合或者数组。本质就是迭代器遍历集合的简化写法。
public static void main(String[] args) {
// 目标:掌握Collection的遍历方式二:增强for
Collection<String> names = new ArrayList<>();
names.add("张无忌");
names.add("玄冥二老");
names.add("宋青书");
names.add("殷素素");
for (String name : names) {
System.out.println(name);
}
String[] users = {"张无忌", "玄冥二老", "宋青书", "殷素素"};
for (String user : users) {
System.out.println(user);
}
}
【3】Lambda表达式
public static void main(String[] args) {
// 目标:掌握Collection的遍历方式三:lambda
Collection<String> names = new ArrayList<>();
names.add("张无忌");
names.add("玄冥二老");
names.add("宋青书");
names.add("殷素素");
// names.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// names.forEach(s -> System.out.println(s));
names.forEach(System.out::println);
}
3.2.3三种遍历方式的区别
【1】如果集合支持索引,可以使用for循环遍历,每删除数据后做i--;或者可以倒着遍历
// 需求1:删除全部枸杞
for (int i = 0; i < list2.size(); i++) {
String name = list2.get(i);
if(name.contains("枸杞")){
list2.remove(name);
i--; // 解决方案1:删除数据后做一步i--操作 (前提是支持索引)
}
}
for (int i = list3.size() - 1; i >= 0; i--) {
String name = list3.get(i);
if(name.contains("枸杞")){
list3.remove(name);
}
}
【2】可以使用迭代器遍历,并用迭代器提供的删除方法删除数据。
// 需求1:删除全部枸杞
// 方案一:迭代器遍历并删除默认也存在并发修改异常问题。
// 可以解决3:使用迭代器自己的方法来删除
Iterator<String> it = list4.iterator();
while(it.hasNext()){
String name = it.next();
if(name.contains("枸杞")){
it.remove(); // 可以解决3:使用迭代器自己的方法来删除当前数据
}
}
System.out.println(list4);
【3】增强for循环/Lambda遍历均不能解决并发修改异常问题,因此增它们只适合做数据的遍历,不适合同时做增删操作。
3.3List集合
3.3.1List系列集合特点
有序,可重复,有索引
3.3.2List集合的特有方法
List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
方法名称 | 说明 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
public static void main(String[] args) {
// 目标:掌握List系列集合独有的功能。
ArrayList<String> names = new ArrayList<>(); // 一行经典代码
// 添加数据
names.add("张三");
names.add("李四");
names.add("王五");
names.add("赵六");
System.out.println(names); // [张三, 李四, 王五, 赵六]
// 给第三个位置插入一个数据:赵敏
names.add(2, "赵敏");
System.out.println(names);
// 删除李四
System.out.println(names.remove(1)); // 根据下标删除,返回删除的数据
System.out.println(names);
// 把王五修改成:金毛
System.out.println(names.set(2, "金毛")); // 根据下标修改,返回修改前的数据
System.out.println(names);
// 获取张三
System.out.println(names.get(0));
}
3.3.3List集合支持的遍历方式
【1】for循环
【2】迭代器
【3】增强for循环
【4】Lambda表达式
public static void main(String[] args) {
// 1、for循环
for (int i = 0; i < names.size(); i++) {
System.out.println(names.get(i));
}
// 2、迭代器
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}
// 3、增强for
for (String name : names) {
System.out.println(name);
}
// 4、lambda表达式
names.forEach(name -> System.out.println(name) );
System.out.println(15 >> 1);
}
3.4ArrayList
ArrayList底层是基于数组存储数据的。
3.4.1特点
【1】有序,可重复,有索引。
【2】查询速度快(注意:是根据索引查询数据快),查询数据通过地址值和索引定位,查询任意数据耗时相同。
【3】增删数据效率低,可能需要把后面很多的数据进行前移。
3.5LinkedList
LinkedList底层是基于链表存储数据的。
3.5.1特点
【1】有序,可重复,有索引。
【2】链表中的数据是一个一个独立的结点组成的,结点在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
【3】查询慢,无论查询哪个数据都要从头开始找。
【4】链表增删相对快。对首尾元素进行增删改查的速度是极快的。
3.5.2 LinkedList的特有方法
很多首尾操作的特有方法。
方法名称 | 说明 |
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
3.5.3应用场景
【1】设计队列
队列的特点:先进先出,后进后出
//排队问题
LinkedList<String> list=new LinkedList<>();
list.addLast("西门吹雪");
list.addLast("赵敏");
list.addLast("金毛狮王");
list.addLast("陆小凤");
System.out.println(list);
list.removeFirst();
list.removeFirst();
System.out.println(list);
【2】设计栈
栈的特点:先进后出,后金先出
数据进入栈模型的过程称为:进栈(push)
数据离开栈模型的过程称为:出栈(pop)
//栈
LinkedList<String> list1=new LinkedList<>();
//压栈
list1.push("第一颗子弹");
list1.push("第二颗子弹");
list1.push("第三颗子弹");
list1.push("第四颗子弹");
System.out.println(list1);
//出栈
list1.pop();
list1.pop();
System.out.println(list1);