JavaEE中的interface,匿名内部类、泛型、异常【第三篇】
学习目标
学会里面的全部内容,特别是在泛型中需要非常特别特别注意。
ArrayList源码是需要会的,可以在理解的基础上,自己实现一个MyArrayList实现类,并且可以实现ArrayList中的大部分的方法
并且匿名内部类是非常的重要,匿名内部类虽然没有lambda好用,但是在公司的项目中匿名内部类的使用也是非常常见的!!!
学习内容
interface,泛型,异常,ArrayList 底层源码编写,匿名内部类!!!
学习产出
一、interface接口
interface接口定义方式:
inteeface 接口名 {
成员变量;//缺省属性为 public static final
成员方法;//缺省属性为 public abstract
默认方法;//JDK1.8 以上支持,default关键字修饰的默认方法,允许有方法体。
}
实现类遵循接口的方式:
class 实现类类名 implemenets 接口名 {
//方法里面必须重写接口中所有的缺省属性为【public abstract】成员方法
}
接口的特点:
- 接口可以多继承其他接口
- 一个类可以遵从多个接口
二、多态
编译看左,运行看右
父类的引用数据类型变量,指向子类或者间接子类的对象
接口的引用数据类型变量,指向遵从接口的实现类对象。
利用多态:
拓宽了方法的数据类型支持范围,但是又满足数据类型一致化,同时支持数据类型多样化
拓宽方法返回值类型,可以支持类型统一。
下面进行代码测试:
public class Demo13 {
public static void main(String[] args) {
Son son = new Son();
//子类中没有test方法但是子类继承了父类,所有可以直接调用父类的test方法
son.test();
Father father = new Son();
//多态的思想,编译看左,运行看右,但是子类中没有test方法,所以调用流程如上
father.test();
//子类中对父类中的int类型的age参数进行重新赋值
System.out.println(son.age);
}
}
class Father{
public int age = 20;
public void test(){
System.out.println("fu" + age);
}
}
class Son extends Father{
int age = 10;
}
如果子类重写了test方法:
public class Demo13 {
public static void main(String[] args) {
//这里是用了多态,得到是father数据类型是Father类,但是实际上运行的是子类的方法
Father father = new Son();
father.test();
Son son = new Son();
son.test();
System.out.println(son.age);
}
}
class Father{
public int age = 20;
public void test(){
System.out.println("fu" + age);
}
}
class Son extends Father{
int age = 10;
@Override
public void test() {
System.out.println("zi类test"+age);
}
}
三、匿名内部类【重点】
为啥要用匿名内部类呢:在开发中,如果一个类/接口,在某个执行方法中,只需要创建一次或者几次。但是又必须使用,我们需要创建它的实现类/子类去实现/重写。此时可以使用匿名内部类的方式,这样不用创建新的类,减少代码的冗余,并且没有对象
按步骤来想:
- 我们有一个test()方法在一个名为Test的接口中,假设这个方法只需要调用一次或者几次。
interface Test{
void test();
}
- 一般的想法是为了调用这个接口中的方法,需要去写一个实现类然后重写接口中的test()方法
class demo implements Test{
@Override
public void test() {
}
}
public class Demo14{
public static void main(String[] args) {
demo demo = new demo();
demo.test();
}
}
- 但是如果这个接口中的方法只是用一次或者没几次而去创建一个实现类,这样太过于麻烦,这时我们需要用匿名内部类来解决这个办法。
public class Demo14{
public static void main(String[] args) {
new Test(){
@Override
public void test() {
System.out.println("匿名内部类");
}
}.test();
}
}
我们这样就没有创建新的对象去调用方法,也可以重写调用接口中的test() 方法。
- 通常我们也可以通过匿名内部类的方法来创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是一个线程");
}
}).start();
总结:
Test() :
Test 在这里提供数据类型,并且隐含遵从关系,告知编译器当前实例化对象是遵从接口 A 的实现类对象。
Test ==> XXX implements Test
() 借用接口 Test 的构造方法,实际上实例化对象是接口 A 的实现类对象
但是没有类名。
四、泛型
<表示一个占位符,没有特殊的意义>
T Type 类型
E Element 元素
K Key 键
V Value 值
R Return 返回值
目的是为了增强方法:
1、增强一个方法
2、增强多个方法
类声明泛型,类内的成员方法使用
接口声明泛型,接口内成员方法使用
4.1 增强单个方法
权限修饰符 [static] <自定义泛型占位符> 返回值 方法名 (形式参数列表) {
方法体;
}
要求:形式参数列表中必须有一个参数对应具体的自定义泛型,用于明确约束泛型的具体数据类型。
4.2 增强多个方法
- 类声明泛型,类内的成员方法使用
class 类名<自定义泛型占位符> {
成员方法可以使用泛型
}
//在类中的方法中定义了带有具体数据类型的方法,并且使用了泛型
public class Demo11 {
public static void printStringArr(String[] strings){
printArray(strings);
}
public static void printIntArr(Integer[] integers){
printArray(integers);
}public static void printDoubleArr(Double[] doubles){
printArray(doubles);
}
/*
工具类私有化方法,使用泛型约束方法的参数类型,增强方法功能,用于处理
数组类型数据展示打印功能,提供给以上三个 public 修饰公开方法使用。
可以同时满足用户操作使用工具的多样性,同时满足数据处理一致性,节约代码资源
*/
public static <T> void printArray(T[] arr){
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
类名带有自定义泛型,需要通过【实例化】对象过程进行泛型对应具体数据类型确定。
- 接口声明泛型,接口内成员方法使用
interface 接口名<自定义泛型占位符> {
接口中的成员变量无法使用泛型,因为缺省属性为public static final,定义时需要初始化,无法对于泛型进行初始化操作。
接口声明泛型有且只用于成员方法。
1. 缺省属性为 public abstract 方法可以使用自定义泛型
2. default 默认方法也可以使用自定义泛型
}
实现类实现接口有两种遵从方式:
1、约束模式【妻管严】
在实现类中就直接写出实现的接口中对应泛型的具体数据类型。
然后接下来实现的所有方法对应的泛型都是自定义的数据类型
interface A<T> {
T getType(T t);
degfault T testT(T t){
return t;
}
}
class Demo implements A{
@Override
public String getType(String s) {
return s;
}
@Override
public String testDefault(String s) {
return s;
}
}
2、自由模式【老婆回娘家】
在实现类中,没有直接写出实现的接口中对应泛型的具体数据类型。
实现类中的方法,对应的泛型可可以自定义去定义
/*
媳妇回娘家,自由模式
TypeC 类遵从接口 A 并且 TypeC 类声明和接口一致的泛型占位符。
泛型对应的具体数据类型,是通过实例化对象操作约束。
*/
class TypeC<T> implements A<T> {
@Override
public T getType(T t) {
return t;
}
@Override
public T testDefault(T t) {
return t;
}
}
五、异常
所有异常的基类
Throwable
Throwable有两个子类
Exception:异常可以抛出或者捕获
Error:错误,只能改代码
涉及到的构造方法Constructor:
Throwable();
无参构造方法,用于初始化异常/错误信息为null的构造方法
Throwable(String message);
有参构造方法,使用message来描述当前的异常/错误信息
涉及到的成员方法Method:
printStackTrace();
在控制台展示错误/异常导致的方法调用流程,和错误信息,红色
String toString();
获取当前异常/错误简要信息描述,异常/错误的类型
String getMessage();
获取当前异常/错误的信息
异常有编译时异常和运行时异常 :
编译时异常:Java 编译器不会提示当前异常的处理要求,JVM 针对于这些异常是有对应的处理手段,通常是
printStackTrace 方法。
运行时异常:【提示】告知程序员当前代码出现异常的情况非常紧急,极易出现。代码编写过程中,针对于当前异常进行预处理【捕获,抛出】
捕获操作
try {
} catch(具体的异常方法1 自定义的形式引用参数) {
引用参数.printStackTrace();
} catch(具体的异常方法2 自定义的形式引用参数) {
引用参数.printStackTrace();
}
抛出异常
throw new 抛出异常对象名;
public void 方法() throws 异常类名 {
}
throw和throws关键字区别
throws
用在方法声明后面,跟的是异常类名
表示抛出异常,由该方法的调用者来处理
表示出现异常的一种可能性,并不一定会发生这些异常
throw
用在方法体内,跟的是异常对象名
表示抛出异常,由方法体内的语句处理
执行 throw 一定抛出了某种异常