Reflection
一 反射的应用场景
1.1 场景一:JDBC封装的工具类DBUtils组件
- 问题:写DBUtils在前,我们使用在后,那么当时作者在写DBUtils时候,作者是否知道后面我们使用时,需要查询返回某个类型的对象?
例如:Student stu =qr.query(sql,new BeanHandler(),xxx);
作者在写queryRunning时时怎么知道我们需要Student这种实体类的返回类型的呢?
结论:在DBUtils源码中一定有一段在运行期动态的根据传入的类型创建指定类型对象的代码
1.2 场景二:JAVAWEB中的Servlet
- 问题:Tomcat容器管理Servlet,Tomcat的代码先实现,我们写Servlet在后,那么问题就来了,先实现的Tomcat怎么管理后面写的Servlet类的呢?
说白了Tomcat为什么会认识我们后面运行期才写的Servlet?
结论:在Tomcat中servlet容器实现的过程中也有一段在运行期根据web.xml或者注解的配置动态创建Servlet的对象的代码
1.3 场景三:MyBatis
- 问题:
Sql:<insert id ="add" parameterType="stu">insert into stu(name,age)values(#{name},#{age})</insert>
session.insert(“add”,stu);
Mybatis 实现在前,使用Mybatis添加Student对象在后
结论:Mybatis中也有一段在运行期根据属性名字到执行对象中执行getter方法的代码
二 反射的概念
Reflection反射是Java语言提供的一套在运行期动态获得四大类中信息的API
四大类:编译后都会生成.Class文件
类 class
接口 interface
枚举:enum
注解:@interface
它是可以分析后面使用使用反射技术的框架的这个类的一种技术
三 反射的基础类(平台类)java.lang.Class
- 我们编译上述的四大类后后产生的.class文件,也可以用一个Class类来描述,那么一个Class对象代表的就是一份某一份具体的.class文件
而Class类内部,每一个Class对象对应一个四大类的.class文件,大概就像下面这样👇
- Class类没有公共的构造方法,它只能通过JVM调用类加载后生成的.class文件自动在内存堆区构造存放该类的信息 [我们不能new]
四 通过Class获得实体类的信息
1.有实体类 用类名字.class (固定形式)
Class cls = Student.class//cls里面装的是Student这个类的所有属性和方法
Class clas1 = User.class//同理
2.有实体类的对象 用.getClass()
Class cls = stu.getClass()//这个stu是Student这个类的对象,通过这种方法也可以拿到"Class类"的对象
3.字符串类名字
//例如
Class cla =Class.forName("java.util.ArrayList");//包名+类名
这三种途径得到的都是一个实体类
五 反射的应用
5.1 数组
int set1[] ={1,2,3,4};
int set2[] ={11,22,33,44};
System.out.println(set1.getClass()==set2.getClass());//true
数组而言,类型和维数相同则他们对应的class对象就一样
5.2 实体类
举个String的例子
String str =new String("ET");
String str1 =new String("122331");
System.out.println(str.getClass()==str1.getClass());//true
对于实体类而言只要是一个类,所有对象都对应一个class对象
5.3 通过Class创建对象
还是String来举例子:
Class cls =String.class;
String str4=(String) cls.newInstance();
newInstance() 就相当于调用实体类的空参构造方法,倘若实体类中没有空参构造方法则直接报错 比如:👇
Class cla = Student.class;
Student stu =(Student) cla.newInstance();
实体类的构造方法:👇
public Student(int a) {
System.out.println("....");
}
运行报错:
5.4 通过Class拿实体类属性
Class clas =Student.class;
Field[] fs =clas.getDeclaredFields();
for (Field f : fs) {
String name =f.getName();//属性名
Class clsType =f.getType();//返回类型也可获得一个class对象
String ftype =clsType.getSimpleName();//得到返回类型去掉包名的名称
String mod = Modifier.toString(f.getModifiers());//getModifiers返回一个int代表修饰符的16进制的数字 toString可以把这个数字转成String类型
System.out.println(mod+"\t"+ftype+"\t"+name);
}
实体类中我们自己写的属性👇
private String username;
public String password;
运行👇
这里需要提一嘴 我们这里用的getDeclaredFields(),拿到的就是这个当下这个类的属性 如果用getFields()则会拿到本类public的属性和直接父类public的属性
5.5 通过拿到的属性,来拿对象的属性值
Student stu =new Student();
stu.setUsername("shang");
Class cla =stu.getClass();
Field f =cla.getDeclaredField("username");//拿指定名称的属性
f.setAccessible(true);//破封装
String str =(String) f.get(stu);//类似常用的没封装属性时候的stu.username
System.out.println(str);
运行👇
5.6 通过主方法传值(args[])的方式来获得所有传入对象的所有属性
首先封装个方法👇
public static void testFields(Class cls)throws Exception{
System.out.println(cls.getSimpleName());//得到除去包名的类名
Field[] fd=cls.getDeclaredFields();//得到所有属性
for (Field f : fd) {
String name =f.getName();//属性名
Class clsType =f.getType();
String fType =clsType.getSimpleName();//返回类型名
String mod =Modifier.toString(f.getModifiers());//修饰符
System.out.println(mod+" "+fType+" "+name);
}
}
主方法👇
testFields(Class.forName(args[0]));
通过IDEA传值,因为笔者开发工具用的IDEA👇
我这里拿java.util.ArrayList举例子 尝试拿它所有属性
首先main方法是测试类
打开后传参👇
运行👇
ok,ArrayList中所有的属性我们就展现出来了
总结
关于反射今天先说这么多,下一篇详细介绍以下反射里面的Method反射中的Method详解,纯原创,喜欢记得点赞+关注哦~