1.1 什么是反射
反射是一种机制/功能,利用该机制/功能可以在程序运行过程中对类进行解剖并操作类中的构造方法, 成员方法,成员属性。
反射乃框架之灵魂JAVAEE
反射就是把java的各种成分(字段,方法)映射成相应的java类。
开发工具之所能够把该对象的方法和属性展示出来就使用利用了反射机制对该对象所有类进行了解剖获取到了类中的所有方法和属性信息,这是反射在IDE中的一个使用场景。
3.获取Class对象的信息有三种方法:
对象.getClass()
使用Class类的forName(“类的全限定名”)最常见的方式
使用类.class
//a. 获得字节码对象
public void fun01() throws Exception {
//1. 对象.getClass()
Student student = new Student();
Class clazz = student.getClass();
//2. Class.forName("类的全限定名");
Class clazz02 = Class.forName("com.itheima.bean.Student");
//3. 类.class(JDBCTemplate里面就用过)
Class clazz03 = Student.class;
}
4.构造函数的反射
4.1概述
Constructor是构造方法类,类中的每一个构造方法都是Constructor的对象,通过Constructor对象可以实例化对象。
4.2 Class类中与Constructor相关方法
clazz.getDeclaredConstructors(); //获得所有的构造方法(包含私有的)
clazz.getConstructor(Class... paramType); //获得特定的构造方法
constructor.newInstance(Object...params);// 根据构造方法创建对象
示例代码
//c. 反射构造方法
public void fun03() throws Exception {
//1. Class.forName("类的全限定名");
Class clazz = Class.forName("com.itheima.bean.Student");
//2. 获得所有的构造方法(包含私有的)
Constructor[] constructors = clazz.getDeclaredConstructors();
System.out.println(constructors.length);
//3. 反射获得某一个特定的构造方法
//eg: public Student(String name, int age, String sex)
Constructor constructor = clazz.getConstructor(String.class, int.class, String.class);
Student student = (Student) constructor.newInstance("张三",18,"男");//根据构造方法创建一个对象
System.out.println(student.getAge());
}
5.属性的反射
5.1概述
Field是属性类,类中的每一个属性都是Field的对象,通过Field对象可以给对应的属性赋值和取值。
5.2 Class类中与Field相关方法
1. Field[] getFields()
获取所有的public修饰的属性对象,返回数组
2. Field[] getDeclaredFields()
获取所有的属性对象,包括private修饰的,返回数组
3. Field getField(String name)
根据属性名获得属性对象,只能获取public修饰的
4. Field getDeclaredField(String name)
根据属性名获得属性对象,包括private修饰的
5.3Field类中常用方法
set(obj,value);通用方法都是给对象obj的属性设置使用
get(obj); 通用方法是获取对象obj对应的属性值的
void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性
示例代码
//d. 反射字段
public void fun04() throws Exception {
Student student = new Student("张三", 18, "男");
//1. Class.forName("类的全限定名");
Class clazz = Class.forName("com.itheima.bean.Student");
//2. 获得所有的字段(包含私有的)
Field[] fields = clazz.getDeclaredFields();
//System.out.println(fields.length);
for (Field field : fields) {
//取值
//field.get()
//设置值
//field.set();
}
//3. 获得某一个特定的字段 String name;
Field nameFiled = clazz.getDeclaredField("name");
nameFiled.setAccessible(true);//暴力破解(可以访问私有的字段或者方法)
//取值
Object nameValue = nameFiled.get(student);
System.out.println(nameValue);
//赋值
nameFiled.set(student,"李四");
System.out.println(student.getName());
}
6.方法的反射
6.1概述
Method是方法类,类中的每一个方法都是Method的对象,通过Method对象可以调用方法。
6.2Class类中与Method相关方法
1. Method[] getMethods()
获取所有的public修饰的成员方法,包括父类中
2. Method[] getDeclaredMethods()
获取当前类中所有的方法,包含私有的,不包括父类中
3. Method getMethod("方法名", 方法的参数类型... 类型)
根据方法名和参数类型获得一个方法对象,只能是获取public修饰的
4. Method getDeclaredMethod("方法名", 方法的参数类型... 类型)
根据方法名和参数类型获得一个方法对象,包括private修饰的
6.3Method类中常用方法
1. Object invoke(Object obj, Object... args)
根据参数args调用对象obj的该成员方法
如果obj=null,则表示该方法是静态方法
2. void setAccessible(true)
暴力反射,设置为可以直接调用私有修饰的成员方法
示例代码
@Test
//d. 反射方法
public void fun05() throws Exception {
//1. 获得字节码
Class clazz = Class.forName("com.itheima.bean.Student");
//2. 获得公共的方法(包含父类的)
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//System.out.println("方法名="+method.getName());
}
//3. 获得所有的方法(包含私有的,但是不包含父类的)
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
//System.out.println("方法名="+method.getName());
}
//4. 反射某一个特定的方法
//eg:public void speak()
Method method = clazz.getMethod("speak");
//method.invoke(clazz.newInstance());
//eg: private void speak(String name)
Method declaredMethod = clazz.getDeclaredMethod("speak", String.class);
declaredMethod.setAccessible(true);//暴力破解
declaredMethod.invoke(clazz.newInstance(),"李四");
}
二,注解
1.案例需求
在一个类(测试类,TestDemo)中有三个方法,其中两个方法上有@MyTest,另一个没有.还有一个主测试类(MainTest)中有一个main方法. 在main方法中,让TestDemo类中含有@MyTest方法执行. 自定义@MyTest, 模拟单元测试.
class TestDemo{
@MyTest
public void fun01(){
}
@MyTest
public void fun02(){
}
public void fun03(){
}
}
class MainTest{
public staic void main(String[] args){
//获得TestDemo里面的方法(反射)
//让含有@MyTest的方法执行(method.invoke())
}
}
2,技术分析
2.1注解概述
annotation,是一种代码级别的说明,和类 接口平级关系.
注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上定义
2.2.注解的作用
执行编译期的检查 例如:@Override
分析代码(主要用途:替代配置文件); 用在框架里面, 注解开发
2.3.JDK提供的三个基本的注解
@Override:描述方法的重写.
@SuppressWarnings:压制警告.
@Deprecated:标记过时
2.4.自定义注解
2.4.1注解格式
定义一个类:class
定义一个接口:interface
定义一个枚举:enum
定义一个注解:@interface
本质上就是一个接口,接口中可以定义变量(常量)和方法(抽象),注解中的方法叫注解属性
语法: @interface 注解名{}
- 示例代码
/**
* 定义了注解
*
*/
public @interface Annotation01 {
}
2.4.2注解属性
2.4.2.1属性类型,巧记肯定用到基本数据类型,字符串,查看Override知道还有枚举,底层用反射解析自然用字节码类型CLass和他们的一维数组
1.基本类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2rkxg0lh-1598866395334)(img/tu_4.png)]
2.String
3.枚举类型
4.注解类型
5.Class类型
6.以上类型的一维数组类型
注意:
一旦注解有属性了,使用注解的时候,属性必须有值
- 示例代码
/**
注解的属性; 格式和接口的方法很类似
1.基本类型
2.String
3.枚举类型
4.注解类型
5.Class类型
6.以上类型的一维数组类型
*/
public @interface Annotation02 {
int a();//基本类型
String b();//String
Color c();//枚举类型
Annotation01 d();//注解类型
Class e();//Class类型
String[] f();//一维数组类型
}
2.4.2.2注解属性赋值
-
格式
@注解名(属性名=值,属性名2=值2) eg:@MyAnnotation3(i = 0,s="23")
注:
-
若属性类型的一维数组的时候,当数组的值只有一个的时候可以有以下下两种写法
@MyAnnotation4(ss = { "a" }) @MyAnnotation4(ss = "a")
-
若属性名为value的时候,且只有这一个属性需要赋值的时候可以省略value【重点】
-
注解属性可以有默认值
属性类型 属性名() default 默认值;
2.5.元注解
2.5.1概述
定义在注解上的注解,不会查看Override注解即可
2.5.2类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AmiaM4Za-1598866395336)(img/tu_2.png)]
@Target:定义该注解作用在什么上面(位置),默认注解可以在任何位置. 值为:ElementType的枚举值
METHOD:方法
TYPE:类 接口
FIELD:字段
CONSTRUCTOR:构造方法声明
@Retention:定义该注解保留到那个代码阶段, 值为:RetentionPolicy类型,默认只在源码阶段保留
SOURCE:只在源码上保留(默认)
CLASS:在源码和字节码上保留
RUNTIME:在所有的阶段都保留
.java (源码阶段) ----编译—> .class(字节码阶段) ----加载内存–> 运行(RUNTIME)
eg:
@Target(value = {ElementType.METHOD,ElementType.TYPE })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyAnnotation03 {
int a();
String b();
}
关于@Documented注解标记的使用介绍,了解一下即可
地址如下:<https://www.cnblogs.com/uoar/p/8036642.htm