一、注解
声明一个注解使用@interface关键字;
public @interface Test{
String value(); //无默认值
int age() default 1; //有默认值
}
1、元注解
对注解类进行注解的注解我们称之为元注解;常用的元注解有四个;
@Target
标记另一个注解类,声明该注解可以作用的范围;
ElementType.ANNOTATION_TYPE 可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。
@Retention
标记另一个注解类,声明该注解的保留策略;
RetentionPolicy.SOURCE - 标记的注解仅保留在源级别中,并被编译器忽略。
RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。
RetentionPolicy.RUNTIME - 标记的注解由 JVM 保留,因此运行时环境可以使用它。
@Documented
标记另一个注解类,声明该注解用于被javadoc工具提取成文档;
@Inherited
标记另一个注解类,表示允许子类继承父类中定义的注解;
2、注解应用场景
RetentionPolicy.SOURCE源码级别的注解:提供给IDE语法检查、APT等场景使用; RetentionPolicy.CLASS class级别的注解:字节码操作,直接修改class字节码文件; RetentionPolicy.RUNTIME 运行级别的注解:结合反射技术获取注解中的所有信息;
二、反射
反射是Java被视为动态语言的关键;
反射是指在运行过程中,对于任何一个类,都能够知道这个类所有属性和方法,都能创建该类的对象;对于任意一个对象,都能够调用它的任意方法,能够设置它的任意属性;
反射始于Class,Class是一个类,封装了当前对象所对应的类的信息;一个类中有方法,属性,构造方法等,现在需要一个类,用来描述类,这就是Class,他应该有类名、属性、方法和构造器等;Class是用来描述类的类;
Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性、方法、构造器,继承了那些类、实现了哪些接口等等;对于每个类,JRE都为其保留一个不变的Class类型的对象,一个Class对象包含了特定某个类的有关信息;一个类在JVM中只会有一个Class实例;
1、获取Class对象的三种方式
- 通过类名获取 类名.class
- 通过对象获取 对象名.getClass()
- 通过全类名获取 Class.forName(全类名)
2、判断对象类型及Class对象类型
我们可以使用instanceof关键字判断一个对象是否是某个类的实例;
在Class类中也有两个方法用于类似的判断; public native boolean isInstance(Object var)方法用于判断一个对象是否属于某个类; public native boolean isAssignableFrom(Class<?> var)方法用于判断两个Class对象是否表示的同一个Class;
3、创建实例
通过反射生成对象主要有两种方式: 方法一:通过Class对象的newInstance方法构造对应类的对象; 方法二:通过Class对象的getConstructor方法获得Constructor对象,再调用Constructor对象的newInstance方法构造对象,这种方法可以用指定的构造器创建对象;
4、获取构造器对象的方法
Constructor getConstructor(Class[] params) 获得使用特殊的参数类型的public构造函数(包括父类)
Constructor[] getConstructors() 获得类的所有公共构造函数 (包括父类)
Constructor getDeclaredConstructor(Class[] params) 获得使用特定参数类型的构造函数(包括私有)
Constructor[] getDeclaredConstructors() 获得类的所有构造函数(包括私有)
Constructor对象的newInstance方法用于获取类的实例对象;
5、获取类的成员变量(字段)的方法
Field getField(String name) -- 获得命名的公共字段(包括父类)
Field[] getFields() -- 获得类的所有公共字段(包括父类)
Field getDeclaredField(String name) -- 获得类声明的命名的字段(包括私有)
Field[] getDeclaredFields() -- 获得类声明的所有字段(包括私有)
Field对象的set和get方法用于获取和设置属性值;
6、获取类的方法
Method getMethod(String name, Class[] params) 使用特定的参数类型,获得命名的公共方法(包括父类)
Method[] getMethods() 获得类的所有公共方法 (包括父类)
Method getDeclaredMethod(String name, Class[] params)使用特写的参数类型,获得类声明的命名的方法(包括私有)
Method[] getDeclaredMethods() -- 获得类声明的所有方法(包括私有)
Method对象的invoke方法用来调用这个方法;
7、利用反射创建数组
我们可以使用Array类的此方法创建数组;
public static Object newInstance(Class<?> var0, int var1)
8、反射获取泛型真实类型
当我们对一个泛型类进行反射时,需要得到泛型中的真实数据类型,例如来完成如json反序列化的操作;此时需要通过Type体系来实现,Type接口包含一个实现类Class和四个实现接口,分别如下:
TypeVariable
泛型类型变量。可以获得泛型上下限等信息;
ParameterizedType
具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
GenericArrayType
当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
WildcardType
通配符泛型,获得上下限信息;
以上四种接口的使用方法不再赘述,需要使用时查询对应方法即可;
三、设计模式之代理模式
代理模式是指给一个对象提供一个代理对象,并通过代理对象来操作实际的对象;
目的:通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不确定性;
一、静态代理
静态代理一般包括三个角色:
Interface 抽象接口,定义了行为方法
RealSubject 真实对象,实现抽象接口,实现具体的行为方法
ProxySubject 代理对象,也需要实现抽象接口,一般持有一个真实对象,然后在自己实现的方法中调用真实对象的方法,然后再添加上自己特有的逻辑;
静态代理中代理对象和真实对象可以是一对一或者一对多,一对一会导致接口爆炸,实现类太多;一对多又会导致扩展性不好。
二、动态代理
动态代理的角色:
1、接口类
public interface Api {
void run();
}
2、被代理类
public class ApiImpl implements Api {
@Override
public void run() {
System.out.println("ApiImpl run");
}
}
3、动态代理类
public class ApiDynamicImpl implements InvocationHandler {
private Object obj;
public ApiDynamicImpl(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if (method.getName().equals("run")){
result = method.invoke(obj, args);
}
return result;
}
}
4、Client测试代码,注释1处实现一个被代理对象;注释2处通过被代理对象创建代理对象;注释3处通过代理对象创建一个接口对象;注释4处调用接口的方法,实际就是调用的ApiImpl的实现方法;
public class Client {
public static void main(String[] args) {
Api api = new ApiImpl(); //1
ApiDynamicImpl dynamic = new ApiDynamicImpl(api); //2
Api apiImpl = (Api) Proxy.newProxyInstance(dynamic.getClass().getClassLoader(), new Class[]{Api.class}, dynamic); //3
apiImpl.run(); //4
}
}
动态代理优点:
1、真实主题类随时都会发生变化;但是因为它实现了公共的接口,所以代理类可以不做任何修改就能够使用。
2、真实主题类就是实现实际的业务逻辑,不用关心其他非本职的工作。