反射
- Class类是用来表述字节码文件 .Class 的类
获取字节码对象与包名_
- 获取Class的三种方式
- 类的非静态方法:对象名.getClass()
- 类的静态方法:Class.forName(“类的全路径”);
- 类的静态属性:类名.Class
// 通过 类名.Class
Class cls1 = Demo.class;
// 通过对象名.Class
Demo d = new Demo();
Class cls2 = d.getClass();
// 通过Class.forName() 短号里写类的全路径 (常用)
Class cls3 = Class.forName("类的全路径");
获取类名的方法:
getName()
getSimpleName()
Demo d = new Demo();
Class cls = d.getClass();
// 得到完整的包名 + 类名
cls.getName();
// 得到类名
cls.getSimpleName();
类加载器_
Java中有三种类加载器,它们分别用于加载不同种类的class
- 类加载器的作用:
- 把Class文件加载到JVM中 - 三种类加载器
- BootStrapClassLoader (启动类加载器)
- 加载JVM运行的初始化的一些类
- ExtClassLoader (扩展类加载器)
- 加载jdk目录下,有一个文件夹ext里面的jar包
- AppClassLoader (应用程序类加载器)
- 加载我们项目里面写的类和导入的第三方的jar包的类
- BootStrapClassLoader (启动类加载器)
// 得到类的加载器
System.out.println(cls1.getClassLoader()); //输出 sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(String.class.getClassLoader()); // 输出 null (如果输出为null就表示用的BootStrapClassLoader类加载器)
如果类加载器无法加载指定的类,就会报错
反射
构造方法的反射_
- Constructor是描述字节码文件中构造方法的类,通过Constructor对象可以实例化这个类,得到对象
常用方法:
getConstructor(Class... parameterTypes)
- 根据参数类型获得对应的Constructor对象
- 只能获得public修饰的构造方法getDeclaredConstructor(Class... parameterTypes)
- 根据参数类型获得对应的Constructor对象
- 可以是public、protected、(默认)、private修饰符的构造方法getConstructors(Class... parameterTypes)
- 获得类中的所有构造方法对象,只能获得public的getDeclaredConstructors(Class... parameterTypes)
- 获得类中的所有构造方法对象
- 可以是public、protected、(默认)、private修饰符的构造方法
import org.junit.Test;
import java.lang.reflect.Constructor;
public class Text {
@Test
public void test() throws Exception {
Class cls1 = Demo.class;
// 得到一个无参的构造方法 (只能得到public修饰的构造方法)
Constructor constructor = cls1.getConstructor();
// 得到一个带有一个参数的构造方法
Constructor constructor1 = cls1.getConstructor(String.class);
// 得到一个无参的构造方法 (可以得到任意修饰的构造方法)
Constructor declaredConstructor = cls1.getDeclaredConstructor();
}
@Test
public void test2() throws Exception {
Class cls1 = Demo.class;
// 得到所有的构造方法 (只能是public修饰的构造方法)
Constructor[] constructors = cls1.getConstructors();
// 得到所有的构造方法 (可以得到任意修饰的构造方法)
Constructor[] declaredConstructors = cls1.getDeclaredConstructors();
// 遍历所有的构造方法
for (Constructor dc : declaredConstructors) {
System.out.println(dc);
}
}
}
通过反射执行构造方法_
Constructor类常用方法:
T newInstance(Object... initargs)
- 根据指定的参数创建对象
- 如果调用的构造方法是有参的,那么就需要提供实际参数void setAccessible(true)
- 是否取消权限检查,true取消权限检查,false表示不取消 (设置 暴力反射)
@Test
public void test3() throws Exception {
// 得到Demo类的Class文件
Class<Demo> cls1 = Demo.class;
// 获得无参构造方法
Constructor<Demo> constructor = cls1.getConstructor();
// 执行构造方法 (无参构造方法不需要传任何参数)
Demo dm = constructor.newInstance();
// 执行Demo中的show方法
dm.show();
// 获取有参构造方法
Constructor<Demo> constructor1 = cls1.getConstructor(String.class);
// 向有参构造方法传值
Demo dm2 = constructor1.newInstance("张三");
// 调用Demo中的方法
dm2.getName();
/*
// 如果被调用的构造方法是私有的需要设置暴力访问 (getDeclaredConstructor可以获取但 newInstance无法执行)
Constructor<Demo> constructor1 = cls1.getDeclaredConstructor(String.class);
// 设置方法可以访问
constructor1.setAccessible(true);
Demo dm2 = constructor1.newInstance("张三");
*/
}
方法的反射_
常用方法:
Method getMethod(String name,Class... args)
- 根据方法名和参数类型获得对应的成员方法对象,只能获得public的Method getDeclaredMethod(String name,Class... args)
- 根据方法名和参数类型获得对应的成员方法对象,包括public、protected、(默认)、private的
- 参数1:要获取的方法的方法名
- 参数2:要获取的方法的形参类型的Class对象Method[] getMethods()
- 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的Method[] getDeclaredMethods()
- 获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、private的
@Test
public void method() throws Exception {
Class cls1 = Demo.class;
// 获取所有包含父类的方法 (所有公开的方法)
Method[] methods = cls1.getMethods();
// 获取当前类的所有方法
Method[] declaredMethods = cls1.getDeclaredMethods();
// 获取指定的方法
Method show = cls1.getMethod("show");
// 如果方法是非静态的,那么就需要传递类的实例对象
show.invoke(cls1.getConstructor().newInstance());
// getMethod 参数1是方法名,后面的参数是参数类型.Class
Method MethodgetName = cls1.getDeclaredMethod("show", String.class);
// invoke执行方法需要有类的实例对象 参娄1是实例对象,后面的参数是传递的值
String s = (String) MethodgetName.invoke(cls1.getConstructor().newInstance(),"张三");
// 有参数有返回值的方法
System.out.println(s);
}
通过反射执行成员方法_
Method对象常用方法:
Object invoke(Object obj, Object... args)
- 参数1:调用该方法的对象
- 参数2:调用该法时传递的实际参数
- 返回值:该方法执行完毕后的返回值void setAccessible(true)
- true取消权限检查,false表示不取消
// 获取本类的一个方法 (只能获取被public修饰的)
cls1.getMethod(“show”);
// 获取所有包含父类的方法 (所有公开的方法,被public修饰的)
cls1.getMethods();
// 获取本类的方法 (任意修饰的方法)
cls1.getDeclaredMethod(“show”);
// 获取本类的所有方法 (任意修饰的方法)
cls1.getDeclaredMethods();
字段的反射
- 每一个成员变量都是一个Field类的对象
通过反射获取类的成员变量_
Field getField(String name)
- 根据成员变量名获得对应Field对象,只能获得public修饰
- 参数:属性名Field getDeclaredField(String name)
- 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
- 参数:属性名Field[] getFields()
- 获得所有的成员变量对应的Field对象,只能获得public的Field[] getDeclaredFields()
- 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的
@Test
public void field() throws Exception {
// 获取字节码对象
Class cls = Demo.class;
// 得到所有公开的public修饰的属性 (单个字段)
Field name = cls.getField("name");
// 得到所有不管修饰权限的单个字段
Field age = cls.getDeclaredField("age");
// 得到所有的字段 (只能获取public修饰的字段)
Field[] fields = cls.getFields();
// 得到所有不管修饰权限的所有字段
Field[] declaredFields = cls.getDeclaredFields();
}
通过反射访问成员变量_
Field对象常用方法:
void set(Object obj, Object value)
- 参数1: 给哪个对象的属性赋值—该类的对象
- 参数2: 给属性赋的值Object get(Object obj)
void setAccessible(true)
- 暴力反射 设置为可以直接访问私有类型的属性Class getType()
- 获取属性的类型,返回Class对象
@Test
public void field2() throws Exception {
// 获取字节码对象
Class cls = Demo.class;
// 获取一个字段
Field fields = cls.getField("name");
// 创建实例对象
Demo d = (Demo) cls.getConstructor().newInstance();
// 给字段赋值
fields.set(d,"张三");
// 获取name中的值
String s = (String) fields.get(d);
// 打印输出
System.out.println(s);
}
注解
- 注解:(annotation)是一种代码级别的说明,和类 接口平级关系
注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事
注解的作用:
- 生成帮助文档:@author和@version
- 执行编译期的检查:例如@Override
- 框架的配置
自定义注解_
格式:
public @interface 注解名{
注解属性
}
- 注解属性
- 格式:
数据类型 属性名();
- 属性类型
- 基本类型
- String
- Class类型
- 注解类型
- 枚举类型
- 以上类型的一维数组类型
- 格式:
使用注解并给注解属性赋值_
- 如果一个注解没有属性,那么就不需要给注解属性赋值,直接使用即可 // @注解名
- 如果一个注解中有属性,那么使用注解的时候一定要给注解所有属性赋值 // @注解名(属性名=值,属性名2=值2,…)
// 自定义注解
public @interface MyAnnotation {
// 可以在里面赋默认值 用default关键字
String s() default "Hello";
String name();
int age();
}
// 在类上添加注解并赋值
@MyAnnotation(name = "张三",age = 20)
public class Text {
}
使用注解的注意事项:
一旦注解有属性了,使用注解的时候,属性必须有值
若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{}
注解属性可以有默认值 格式:属性类型 属性名() defaul t 默认值
如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
元注解_
- 定义在注解上的注解(修饰注解的注解)
常见的元注解:
- @Target:表示该注解作用在什么上面(位置),默认注解可以在任何位置. 值为:ElementType的枚举值
- METHOD:方法
- TYPE:类 接口
- FIELD:字段
- CONSTRUCTOR:构造方法声明
- LOCAL_VARIABLE:局部变量
@Target(ElementType.TYPE)// 只能在类上使用
public @interface MyAnnotation1 {
}
//@MyAnnotation1
public class Test1 {
@MyAnnotation1 // 编译报错
int num;
//@MyAnnotation1 // 编译报错
public void show() {
}
}
- @Retention:定义该注解保留到那个代码阶段, 值为:RetentionPolicy类型,默认只在Class字节码阶段保留
- SOURCE:只在源码上保留
- CLASS:在源码和字节码上保留(默认)
- RUNTIME:在所有的阶段都保留
//@Retention(RetentionPolicy.SOURCE) // MyAnnotation2可以保留到源码阶段
//@Retention(RetentionPolicy.CLASS) // MyAnnotation2可以保留到字节码阶段(源码有)
@Retention(RetentionPolicy.RUNTIME) // MyAnnotation2可以保留到运行阶段(源码,字节码阶段都有)
public @interface MyAnnotation2 {
}
注解解析_
java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等实现了AnnotatedElement
常用方法:
T getAnnotation(Class annotationType) 得到指定类型的注解对象,没有返回null
boolean isAnnotationPresent(Class<?extends Annotation> annotationType): 判断指定的注解有没有
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
-----------------------------------------------------------------------------------------------
@MyAnnotation("类上注解赋的值")
public class Text {
@MyAnnotation("方法上注解赋的值")
public void show(){
}
}
-----------------------------------------------------------------------------------------------
public class Test1 {
@Test
public void show() throws Exception {
Class tx = Text.class;
// 得到类上的注解
// 判断这个类上面有没有MyAnnotation这个注解
System.out.println(tx.isAnnotationPresent(MyAnnotation.class)); // true
// 得到方法上的注解
Method show = tx.getDeclaredMethod("show");
// 判断这个方法上面有没有MyAnnotation这个注解
System.out.println(show.isAnnotationPresent(MyAnnotation.class)); //true
// isAnnotation 判断类本身是不是注解
Class ma = MyAnnotation.class;
System.out.println(ma.isAnnotation()); //true
// 得到指定类型的注解对象
MyAnnotation annotation = (MyAnnotation) tx.getAnnotation(MyAnnotation.class);
// 非空判断
if(annotation != null){
// 读取value的值
System.out.println(annotation.value());
}
}
}
代理
动态代理_
- 动态代理就是在程序运行期间,直接通过反射生成一个代理对象,代理对象所属的类是不需要存在的
动态代理的获取:
- jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象
- Java.lang.reflect.Proxy类可以直接生成一个代理对象
动态代理Api
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
生成一个代理对象- 参数1:ClassLoader loader 被代理对象的类加载器
- 参数2:Class<?>[] interfaces 被代理对象的要实现的接口
- 参数3:InvocationHandler h (接口)执行处理类
- 返回值: 代理对象
- 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法
- nvocationHandler中的
Object invoke(Object proxy, Method method, Object[] args)
方法:调用代理类的任何方法,此方法都会执行- 参数1:代理对象(慎用)
- 参数2:当前执行的方法
- 参数3:当前执行的方法运行时传递过来的参数
- 返回值:当前方法执行的返回值
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Testagent {
public static void main(String[] args) {
// 创建真实类对象
Sing ss = new SuperStart();
/**
* Proxy.newProxyInstance创建代理对象
* 参数一:给一个类加载器,真实类用的是什么类加载器,这里就写什么类加载器
* 参数二:要实现的接口,真实类实现什么接口,代理类就实现什么接口
* 参数三:调用处理器,用来让代理对象和真实对象沟通的地方
*/
Sing proxyObj = (Sing) Proxy.newProxyInstance(ss.getClass().getClassLoader(), ss.getClass().getInterfaces(), new InvocationHandler() {
/**
* 1.invoke方法的使用时机
* 只要外面使用代理对象做任何事情(调用方法),这个invoke方法都会执行
* 2.invoke方法的作用
* 让我们在里面调用真实对象的方法,顺便也可以给这个真实对象的方法进行包装|升级|扩展
*
* @param proxy 代理对象
* @param method 在外面代理对象调用的方法 method对象
* 如果外面执行的是这行代码:proxyObj.sing(),那么这个method即表示sing方法的反射method对象
* @param args 表示参数,在外面使用代理对象调用的方法传递的参数
* @return 可以把invoke方法执行的真实对象的方法返回值返回出去,那么外面的代理对象调用完方法,也就得到了结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(ss, args);
// 提供真实对象执行完后的返回值
return invoke;
}
});
proxyObj.sing();
}
}