1.Java 的异常体系.
描述Java标准库中提供的异常类都有哪些,并且分成哪几个类别
(1)图上的箭头其实就是"继承"或者"实现”这样的关系
Error是系统级别的异常,JVM内部使用的,普通程序猿不应该使用Error这个体系
Exception是应用级别的异常,普通程序猿应该要使用这一组系列.
(2)受查异常:如果某个方法中抛出了这个异常,那么就必须对这个异常进行显式的处理(显式处理包含两种方案: 1.直接try catch,2.使用throws声明可能会抛出这个异常)
(3)非受查异常:可以不显式处理.
(4)Error:
出现 Error的情况一定是非常严重的情况,一般就是JVM挂了,此时作为应用级别的程序猿,毫无办法,只能任凭其自生自灭.
(5)RuntimeException:
一些最常见的异常,影响没有很大.
(6)除了这两个以外其他的都是受查异常
泛型
一、定义
让一个类/一个方法,能够支持多种不同的数据类型.
二、为什么需要泛型
如果没有泛型机制,当前封装的数组类,只能针对int进行存储,如果需要针对String,就需要再搞一个类,把里面涉及到 int的类型改成String,后续如果需要新增其他类型,都得如法炮制,有了泛型之后,就可以让一套代码服务于多种类型.
如果把类中持有的数组搞成Object[ ],此时是可以用来存储多种不同类型数据的,这样搞,就需要写很多的"类型转换”相关的代码.
使用Object来凑合完成"泛型"效果,是比较麻烦.
(1)需要写一些类型转换的代码.
(2)类型转换的代码容易出错,缺少一些必要的类型检查.
这样的做法和C中使用void*来凑合泛型差不多
使用泛型就可以更好的解决这个问题了,使用泛型之后,在针对对象实例化的时候需要填写泛型参数的实际类型.
MyArray2<string>myArray2 = new MyArray2<>();
public MyArray2() {
//由于E这样的类型不确定,无法直接创建E类型的实例
//下面的写法是错误的,编译失败
data = new E[capacity];
data = (E[])new object [capacity];
}
public void add(E datay )
if (size >= capacity){
return;
}
this.data[size++] =data;
}
Public E get(int index) {
return this.data[ index];
}
把这个实际的类型填写进去之后,实际的泛型参数就都被自动的替换成String
Java的泛型只能是引用类型,如果是内置类型,就需要使用对应的包装类(包装类也是引用类型)
Java的泛型,本质上还是基于Object机制来实现的,private E[] data = null;
本质上还是一个Object[]
泛型存在的意义,就是帮助我们完成了类型校验,和类型转换.
泛型实际就是"语法糖"
如果是C++的泛型的话,C++的泛型和Java是两套机制.C++的泛型允许是内置类型.C++的泛型本质上是在编译期生成一套全新的代码.
三、泛型的分类
泛型主要包含:泛型类、泛型方法和泛型接口
四、泛型类的定义
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
// 类实现体
}
【规范】类型形参一般使用一个大写字母表示,常用的名称有:
E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type
S, U, V 等等 - 第二、第三、第四个类型
五、泛型类的实例化
1.实例化语法
泛型类<类型实参> 变量名; 定义一个泛型类引用。
new 泛型类<类型实参>(构造方法实参); 实例化一个泛型类对象。
2.注意:
(1)右侧的< >中的类型可以省略
MyArray<String> m = new MyArray< >(10);
在 new MyArray< >(10)对象时,< >中未明确指明类型,编译器就会根据=左侧<>中的类型来推演
(2)左侧< >中的类型不能省略
MyArray<> m = new MyArray<String>(10); // 省略之后,编译失败
编译在推演时,是根据左侧类型来推演右侧的
(3)虽然右侧< >可以不用写类型,但是< >不能省略
MyArray<String> m = new MyArray();//不能这样写,会产生编译警告
六、裸类型(Raw Type)(一般不使用)
裸类型是一个泛型类但没有带着类型实参,例如MyArray就是一个裸类型
MyArray List = new MyArray();
七、类型边界
定义泛型类的时候.对未来实例化的时候能传入的类型实参做出限制.泛型参数这里的类型不应该随便填,要根据实际情况做出一些约束和校验.
八、通配符
? 用于在泛型的使用,即为通配符
乍一看和类型边界有点像,也是限制泛型参数传入的条件,是在泛型类的使用的时候涉及的,尤其是泛型类作为某个方法的参数的时候涉及的
1.上界
<? extends 上界>
// 传入类型实参是 Animal 子类的任意类型的 MyArray
public static void printAll(MyArray<? extends Animal> m) {
...
}
// 以下调用都是正确的
printAll(new MyArray<Cat>());
printAll(new MyArray<Dog>());
// 以下调用是编译错误的
printAll(new MyArray<String>());
printAll(new MyArray<Object>());
Object是 Animal的父类,Animal是 Cat的父类.
但是MyArray不是MyArray的父类同理,MyArray也不是MyArray的父类
2.下界
<? extends 下界>
// 可以传入类型实参是 Cat 父类的任意类型的 MyArray
public static void printAll(MyArray<? super Cat> list) {
...
}
// 以下调用都是正确的
printAll(new MyArrayList<Cat>());
printAll(new MyArrayList<Animal>());
printAll(new MyArrayList<Object>());
// 以下调用是编译错误的
printAll(new MyArrayList<String>());
printAll(new MyArrayList<Dog>());
3.泛型中的父子类型
public class MyArray<E> { ... }
// MyArray<Object> 不是 MyArray<Animal> 的父类型
// MyArray<Animal> 也不是 MyArray<Cat> 的父类型
// 需要使用通配符来确定父子类型
// MyArray<?> 是 MyArray<? extends Animal> 的父类型
// MyArray<? extends Animal> 是 MyArrayList<Dog> 的父类型
九、类型擦除
1.描述的是Java泛型的底层实现原理
结论: 泛型是一个语法糖而已.本质上在底层是使用Object完成的.
编译器在编译过程中会自动记录泛型参数的实际类型,并且帮助我们完成一些类型转换和类型校验工作.一旦代码编译完成,到了字节码中,此时就不再涉及到任何和泛型相关的信息了
2.泛型的类型擦除原则是:
消除类型参数声明,即删除<>及其包围的部分。
根据类型参数的上下界推断并替换所有的类型参数为原生态类型:如果类型参数是无限制通配符或没有上下界限定则替换为Object,如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类)。
为了保证类型安全,必要时插入强制类型转换代码。
自动产生“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”
十、泛型的优缺点
1.泛型的优点:
(1)提高代码的复用性
(2)提高开发效率
(3)可以实现一些通用类型的容器或算法
2. 泛型的缺点:
(1)泛型类型参数不支持基本数据类型
(2)无法实例化泛型类型的对象
(3)无法使用泛型类型声明静态的属性
(4)无法使用 instanceof 判断带类型参数的泛型类型
(5)无法创建泛型类数组
(6)无法 create、catch、throw 一个泛型类异常(异常不支持泛型)
(7)泛型类型不是形参一部分,无法重载