1、Java异常分类及处理
异常处理是为了解决在程序处理时发生错误而无法退出的问题。
(1)异常分类
Object —— Throwable(所有错误或异常的超类)
- Error: 是指 Java 运行时系统的内部错误和资源耗尽错误,应用程序并不会抛出该对象,如果出现这样的错误,尽量保证系统安全结束
- Exception: Java 异常有两种异常类型,运行时异常、检查异常
- RuntimeException: 例如 NullPointerException、ClassCastException,基本上都是程序设计者设计不合理才会产生的异常
- CheckedException: 例如 IOException、SQLException,一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序捕获异常,即会出现要求你把这段可能出现异常的程序进行try…catch…,这类异常一般包括几个方面:
- 试图在文件尾部读取数据
- 试图打开一个错误格式的URL
- 试图根据给定的字符串查找 class 对象,而这个字符串表示的类并不存在
(2)异常的处理方式
自然抛出异常有三种形式:throw、throws、系统自动抛出异常
捕获异常并处理是以 try…catch… 作为捕获异常并处理代码的结构
(3)throw 与 throws 的区别
- 位置不同: throws 用在函数声明上,抛出的是异常类,并且可以抛出多个;throw 用在函数内,用来抛出异常对象
- 功能不同:
- throws用来声明异常,让调用者了解该功能可能出现的问题,预先给出处理方式;throw 抛出具体的问题对象,执行到throw,程序立即结束,跳转回调用者,并将具体的问题对象抛出调用者,即throw语句独立存在时,后续程序无法执行。
- throws表示的是异常的可能性,并不一定会发生异常;throw 则一定会抛出确定的异常
2、Java 反射
(1)动态语言: 是指程序运行时可以改变其结构,即新的函数可以引进,已有的函数可以被删除等结构上的变化。
(2)反射机制的概念: Java中的反射机制是在运行状态中,对于任意一个类都能获取这个类所有的属性和方法,并且对于任意一个对象,都能调用它的任意一个方法。
(3)反射的应用场合: 编译时类型和运行时类型
- 编译时类型: 由声明对象时实用的类型决定,并且无法获取具体方法,如
Person person = new Student();
,声明的是Person类型,但运行时是Student类型 - 运行时类型: 由赋值对象的类型决定
当程序运行时可能会接收外部传入的对象,该对象在编译时类型为Object,但程序调用需要知道该对象的运行时类型,所以可以利用反射来获取该对象的类型以及类信息。
(4)反射API
反射 API 用来生成JVM中的类、接口或对象的信息。
- Class类: 反射的核心类,可以获取类的属性、方法等信息。
- Field类: java.lang.reflec 包中的类,表示类的成员变量,用来获取和设置类中的属性值
- Method类: java.lang.reflec 包中的类,表示类的方法,用来获取类中的方法信息或调用方法
- Constructor类: java.lang.reflec 包中的类,表示类的构造方法
(5)反射的步骤
- 获取想要操作的Class对象
- 调用Class类中的方法
- 使用反射API操作这些信息
(6)获取Class对象的方法
- 实例对象.getClass(): 是实例对象获取Class对象的方法
- 类名.class: 是每一个类加载时,都会维护一个class 属性,class 属性中存储了该类的相关信息
- Class.forName(): Class 类提供基于全路径获取Class对象的静态方法
(7)创建Class对象的方法
- newInstence(): 这种创建方法要求 Class对象有默认的空构造器
- Constructor的newInstance(): 这种创建方法要求先获取Constructor,因为需要确认Class对象具有构造器
3、Java注解
(1)概念: Annotation 是 Java 提供的一种对元程序中关联信息和元数据(metadata)的途径和方法;它是一种接口,程序可以通过反射来获取指定程序中的Annotation对象,通过Annotation对象来获取元数据信息。
(2)元注解: 元注解的作用是负责注解说明其它注解,JDK 5 定义了 4 个标准的 meta-annotation 类型。
- @Target: 说明修饰的对象范围
- packages
- types: 类、接口、枚举、Annotation类型
- 类型成员: 方法、构造方法、成员变量、枚举值
- 方法参数
- 本地变量: 如循环变量、catch参数
- @Retention: 定义Annotation的生命周期
- @SOURSE: 在源文件中有效
- @CLASS: 在class文件中有效
- @RUNTIME: 在运行时有效
- @Documented: 描述-javadoc,用来描述其它类型的annotation应该被作为被标注的程序成员的公共API
- @Inherited: 描述某个被标注的类型是被继承的,若该注解在某个类上,则这个类的子类也会继承父类拥有的注解
(3)注解处理器
注解处理器是用于注解读取与使用注解的核心程序。
实现注解处理器:
//1、创建注解处理器
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {public int id() default -1;public String name() default "";public String address default "";
}//2、使用注解处理器
public class Apple{@FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路")private String appleProvider;public void setAppleProvider(String appleProvider){this.appleProvider = appleProvider;}public String getAppleProviderO (return appleProvider;}
}//3、注解处理器
public class FruitInfoUtil {public static void getFruitInfo(Class<?> clazz) {String strFruitProvicer = "供应商信息:";Field[] fields = clazz.getDeclaredFields(); //获取成员变量for (Field field : fields){FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class);strFruitProvicer = "供应商编号:" + fruitProvider.id() + "供应商名称:" + fruitProvider.name() + "供应商地址:" + fruitProvider.address();System.out.println(strFruitProvicer);}}
}
4、Java内部类
Java类中不仅可以定义变量和方法,还可以定义类,定义在类内部的类称为内部类,根据定义方式不同,内部类分为静态内部类、成员内部类、局部内部类、匿名内部类四种。
- 静态内部类: 使用static关键字修饰的内部类,这种内部类可以与外部类共享所有的静态资源,通过使用
外部类名.内部类名
获得编译时类型。例如,HashMap类内部就有一个静态内部类Entry。 - 成员内部类: 普通的内部类,这种类不能定义静态方法和变量(因为只有静态资源才属于类对象,并且JVM先加载静态资源,容易产生冲突)
- 局部内部类: 定义在方法内部的类,充当临时对象进行存储。
- 匿名内部类: 匿名内部类必须继承一个父类或只实现一个接口,同时也没有class关键字,通过new关键字直接生成一个对象的引用。
5、泛型
泛型是用来声明无法确定的对象类型,编译器会通过传入的值来确定对象类型。
(1)泛型方法<E>
public static <E> void printArray(E[] inputArray){for(E element : inputArray) {System.out.printf("%s", element);}
}
<? extends T>
: 表示该通配符代表T的所有子类<? super T>
:表示该通配符代表T的所有父类
(2)泛型类 <T>
public class Box<T>{private T t;public void add(T t){this.t = t;}public T get(){return t;}
}
(3)类型通配符 <?>
类型通配符通常使用 ?来代替具体的类型参数,例如List<?> 在逻辑上是所有List具有泛型约束的父类
(4)类型擦除
Java中的泛型基本上是在编译器层面的实现,在生成的字节码中是不存在泛型的,在编译时,自动将传入的数据类型覆盖到泛型使用的位置上,这样的过程叫类型擦除。
6、序列化
(1)序列化的作用: 序列化操作可以持久化对象及其状态到内存或者磁盘中
(2)序列化的存储: 序列化是将传入对象转为字节数组进行存储,但是序列化只关注其成员变量,而不关注静态资源
(3)序列化的使用场景: 持久化对象;网络传递对象等等…
(4)序列化的实现:
- Java提供了
java.io.Serializable
接口,只要类实现接口,就可以被序列化 - 利用ObjectOutputStream和ObjectInputStream进行序列化和反序列化
- 利用writeObject和readObject实现自定义序列化策略
(5)反序列化的关键点——序列化ID:
- 序列化ID: 虚拟机是否支持反序列化,不仅取决于类路径和功能代码是否一致,还取决于两个类的序列化ID是否一致
(6)序列化在子父类中的使用: 若子类实现序列化,而想要父类也能够序列化,那么父类必须也实现Serializable接口
(7)阻止某个变量不进行序列化: Java 提供了 Transient 关键字来声明变量不参与序列化过程,在反序列化时,对应的 trasient 变量则被初始化。
7、Java复制
Java 程序中若想要将一个对象复制给另一个对象,一共有三种方法:直接赋值、浅拷贝、深拷贝。
- 直接赋值: 直接引用同一对象,从表面上看确实是复制了一个对象,但在内部依旧是同一个内存空间
- 浅拷贝: 创建一个新对象,复制非静态字段到新对象,但若旧对象中有一个引用对象,则复制的是引用对象的地址,即直接赋值给新对象的引用对象变量。
- 深拷贝: 创建一个新对象,将旧对象的所有资源打个包,创建一个副本提供给新对象,即两个成为结构相同,但存储地址不同的对象。(实现方式可以通过序列化与反序列化操作进行对象创建)