1. 异常和泛型 #
1.1 异常 #
(1) 代码程序出现的问题
- 文件不存在
- 断网
- …
(2) 异常体系
- Error(系统级别的错误,如内存溢出,程序员解决不了)
- Exception(异常)
- RuntimeException(运行异常):编译时不会出错,如索引越界
- 其他异常(编译时异常):写代码就有错误
(3) 处理方式
- 抛出异常(
throw,throws) - 捕获异常(
try … catch)
(4) 作用
- 定位程序BUG信息
- 作为方法内部的一种特殊返回值,以便通知上层调用者
(5) 自定义异常(定义一个异常类继承Exception)
- 自定义运行异常
- 自定义编译时异常
最好不要使用自定义异常,需要一层一层的上报异常很麻烦,可使用运行时异常
2.2 泛型 #
本质:把具体的数据类型参数类型作为参数传给类型变量
(1)定义类,接口,方法时,同时声明一个或者多个类型变量<E>,称为泛型类,泛型接口,泛型方法
public class ArrayList<E>{}
(2) 作用
约束数据类型,避免强制类型转换
(3)自定义泛型类、接口、方法
<E, T, K, V>可以不止一个类型参数
// 泛型类
public class MyArrayList<E>{}
MyArrayList<E> myList = new MyArrayList<>();
//泛型接口
public interface Data<T>{
void add(T t);
T qurry(int id);
}
public class StudentData implements Data<Student>{}
//泛型方法
public static <T> void test(T t){}
(4)通配符
“?”,在使用泛型时代表一切类型
(5)上下限
限制通配符?
- 泛型上限:?extends Car :接受的必须是Car或者子类
- 泛型下限:?super Car: 必须是Car或者父类
public static void test(ArrayList<? extends Car> cars){}
public static void test(ArrayList<? super Car> cars){}
(6)泛型支持的类型
-
不支持基本数据类型
-
只支持对象类型(引用类型)
-
包装类:基本的数据类型包装成对象类型
- byte Byte
- short Short
- int Integer(不同)
- long Long
- char Character(不同)
- float Float
- double Double
- boolean Boolean
注:
Integer.valueOf()封装了(-128~127)对象Integer it1 = 100; Integer it2 = 100;是指向同一个对象Integer it1 = 130; Integer it2 = 130;不是指向同一个对象,已经越界自动包装和去拆包装,集合存的是对象,取出来可以自动变成一般数据类型
(7)包装类的其他功能(要记住)
-
把基本数据类型变成字符串:Integer.toString()
-
把字符串类型数值转成基本数据类型(重点)
String str = "99"; int i = Integer.parseInt(str); int i2 = Integer.valueOf(str);//也行
2. 集合框架 #
Collection:单列集合(单个值)Map:双列集合(键值对)
2.1 Collection:祖先接口,下面是实现类
#
-
List
:数据有序,可重复,有索引 -
ArrayList
: 数组(根据索引查询数据速度快,所有数据查询时间相同,增删数据效率低) 注意: 在添加数据时,会在第一次添加数据时扩容
grow()到10个容量,加满后再次扩容到原来的1.5倍 -
LinkedList
:链表(查询数据慢,增删效率高)(节点组成,每个节点包含元素和下一个节点的地址) 注意:LinkedList是基于双链表实现的,包含了下一节点和上一节点地址,可以
从前往后或者从后往前查询,有很多对首尾节点的操作方法(增删取)
-
-
Set
:数据无序,不重复,无索引 -
HashSet
哈希值:Java对象调用hashCode()返回哈希值,同以个对象多次调用是相同的,不同对象有小概率相同(哈希碰撞)
无序的底层原理:基于哈希表存储(jdk8之前数组+链表,
大小默认16,加载因子0.75;之后是数组+链表+红黑树),计算后的哈希地址大小导致数据无序存储,出现同一位置会将新数据用链表挂在老元素下面(jdk8之前反过来)扩容:元素的数量超过
大小*加载因子就会扩容到之前2倍注意:JDK8开始,当链表长度超过8,且数组长度≥64,自动转换成红黑树(提升检索效率)
- LinkedHashSet
:(有序)
- LinkedHashSet
-
TreeSet
:(按照大小默认升序)
HashSet集合元素去重操作(经典操作)
//创建多个学生对象,若学生对象中的的成员变量相同则就是同一个对象 Student s1 = new Student("张三",19); Student s2 = new Student("张三",19); //重写hashCode()和equals() //计算哈希地址一样,再计算两个对象的内容一致,(不同的对象哈希地址有一点的概率相同) -
(1)Collection遍历方式
-
迭代器遍历:遍历集合的专用方式(数组没有)
Iterator<E> it = names.iterator();//返回集合中的迭代对象 String name = it.next();//取出元素,并移位 // it.hashNext() 判断当前位置是否有数据 -
增强for循环
for (元素的数据类型 变量名 : 数组或者集合){ } Collection<String> c = new ArrayList<>(); for (String s : c ){ } -
Lambda 表达式:
forEach()Collection<String> c = new ArrayList<>(); c.forEach(new Consumer<String>() { @Override public void accept(String s){ //输出 System.out.println(s); } }); //简化 c.forEach(s -> System.out.println(s));
(2)Collection3个遍历方式的区别
场景:认识并发修改异常问题:遍历集合同时存在集合的增删元素出现业务异常
- (有索引)for 循环在删除数据时进行一次减一(
i--)操作或者倒序遍历 - 迭代器遍历:(没有索引)使用迭代器自带的remove()方法可以避免这个问题(索引会自动同步)
- 增强for循环和Lambda 表达式都没办法解决(集合自己删除,不会控制索引)
在遍历并删除时,有索引的集合可以根据for循环操作,没有索引的集合只能使用迭代器遍历使用迭代器的方法
2.2 Map<K,V>:祖先接口 #
map集合:键值对,格式
{key1=value1,key2=value2,…}
键不能重复,值可以重复,键值一一对应
-
HashMap<K,V>:(由键决定)无序,不重复,无索引
底层原理与
HashSet()一样,基于哈希表实现,HashSet()是调用HashMap()实现-
LinkedHashMap<K,V>:有序,不重复,无索引
LinkedHashSet()的底层原理就是LinkedHashMap()
-
-
TreeMap<K,V>:升序,不重复,无索引
-
…
Map<String, Integer> map = new HashMap<>();
map.put("张三", 12);
map.put("李三", 12);
map.get("张三");//根据键取值
map.cantainsKey("张三");//返回true
map.cantainsValue("12");//返回true
map.remove("张三");//删除键值对,返回值
map.isEmpty();//判断是否为空
map.size();//返回键值对个数
//重要,**获取所有键,返回一个Set键集合**
Set<String> keys = map.keySet();
//重要,**获取所有value,返回一个Collection键集合**
Collection<Integer> values = map.values();
(1)Map遍历方式
-
键找值(for 循环)
Map<String, Integer> map = new HashMap<>(); map.put("张三", 12); map.put("李三", 12); Set<String> keys = map.keySet(); for (String key :keys){ Integer value = map.get(key); //输出 } -
键值对整体遍历(Entry对象封装整个键值对)
Set <Map.Entry<String,Double>> entries = map.entrySet(); for (Map.Entry<String,Double> entry:entries ){ String key = entry.getKey(); double value = entry.getValue(); //输出 } -
Lambda表达式(forEach方法)
Map<String, Integer> map = new HashMap<>(); map.put("张三", 12); map.put("李三", 12); //简化 map.forEach((k, v) -> System.out.println(k + v));