反射
什么是反射
在程序运行过程中,通过字节码对象,去获取类的信息(构造器,方法,字段)
为什么使用反射?
当我们知道要使用哪个类时,我们直接调用该类创建对象,调用方法即可,但是
当我们不知道使用哪个类时,我们可以通过反射去获取类的信息,使用反射获取到类的对象的真实类型以及调用其方法
什么是字节码对象?
把类想象成一个一个独立的个体,那么他们所共有的特性就是一个大Class来概括,把类抽象成一个一个对象,而字节码就是可以抽象成一个类,通过类去创建真实对象.
- 正常类创建对象:
Person p =new Person(); //p是类Person的一个实例
把Class想象成一个装载所有类共性的类,那么我们需要创建字节码对象,通过这个对象去最终能够拿到person这个类里的实例数据
- 字节码对象的创建
对象.getClass()
类.calss
class.forName("类的全限定名")
:会有异常 ClassNotFoundException:因为传入路径不知道是否真的存在
1.对象.getClass()
Person p =new Person();
//System.out.println(p.getClass());
Class<? extends Person> aClass1 = p.getClass();
2.类.class
//System.out.println(Person.calss);
Class<People> peopleClass = People.class;
3.Class.forName("类的全限定名"); //全限定名:包名路径加类名
Class<?> aClass = Class.forName("cn.k.day02_reflect.Person");
三种创建方式,第三种最灵活,因为使用前两种,都是先定义好类再去通过其字节码进行反射
但是第三种,可以先进行创建字节码对象,再去定义类,顺序不同,所以第三种更优,并且可以获取任意对象的字节码
反射操作
使用反射的目的就是想去操作类中的成员,那么一个类中有构造器,方法,字段,所以要想操作它们,得有对应的对象,构造器对象,方法对象等.
获取构造器对象
如果要想获取构造器对象,那么首先得有字节码对象,通过字节码对象去获取构造器对象,一个字节码对应了一个对象. Class提供了对应的创建构造器的API
- 获取字节码对象
Class<?> aClass = Class.forName("cn.k.day02_reflect.People");
- 获取所有公共构造器对象
getConstructors()
Constructor<?>[] constructors = aClass.getConstructors();
- 获取所有的构造器对象(含私有)
getDeclaredConstructors()
Constructor<?>[] constructors1 = aClass.getDeclaredConstructors();
- 获取公共的无参构造器对象
getConstructor()
Constructor<?> constructor = aClass.getConstructor();
- 获取公共带参数的构造器对象 (带两个参数,传入参数是字节码类型)
Constructor<?> constructor2 = aClass.getConstructor(String.class, String.class);
通过构造器对象去获取真实对象
构造器.newInstance([参数])
:参数看构造器是有参(传入对应的数量)还是无参
// 这里使用的是使用无参构造器去创建对象 底层其实就是 new People()
Object o = constructor.newInstance();
字节码对象也可以直接获取真实对象
Class 提供了通过字节码对象直接获取真实对象的API 字节码对象.newInstance()
注意
如果直接通过class字节码对象获取它的一个实例class.newInstance(); 其使用必须在真实类中,存在一个公共的无参数的构造方法
获取方法对象
想要获取方法对象,首先还是得有字节码对象,再通过Class提供的API去创建方法对象
- 获取字节码对象
Class<?> aClass = Class.forName("cn.k.day02_reflect.People");
- 获取所有公共的方法对象(包括了父类)
Method[] methods = aClass.getMethods();
- 获取所有的方法对象(不包括父类)
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
- 获取指定方法的方法对象
Method getNameMethod = aClass.getMethod("getName");
- 访问私有成员方法
Method printMethod = aClass.getDeclaredMethod("print",String.class);
printMethod.setAccessible(true); //开关 是否暴力访问 私有的成员
printMethod.invoke(o,"暴力访问");
执行方法invoke
传入使用字节码对象的newInstance()方法,构建真实对象
//需要传入String.class因为 setName方法中有参数,对应的也要传入参数,不然谁知道是什么类型
Method setNameMethod = aClass.getMethod("setName", String.class); //获取setter方法
setNameMethod.invoke(aClass.newInstance(),"ssss");//设置set值
Method getNameMethod = aClass.getMethod("getName");//获取getter方法
Object invoke1 = getNameMethod.invoke(aClass.newInstance());//执行该对象的getter值
System.out.println("invoke1 = " + invoke1);
结果 :打印为空
原因: 在执行方法时 ,setNameMethod.invoke 与 getNameMethod.invoke 传入的对象地址已经不一样了,相当于new了一个新的对象.
解决方案:应该把对象给提取出来,再去传入共同对象,或者使用构造器去获取真实对象
Object o =aClass.newInstance();
Method setNameMethod = aClass.getMethod("setName", String.class); //获取setter方法
setNameMethod.invoke(o,"ssss");//设置set值
Method getNameMethod = aClass.getMethod("getName");//获取getter方法
Object invoke1 = getNameMethod.invoke(o);//执行同一对象
输出invoke1结果 :invoke1 = ssss
获取字段
反射不建议直接操作字段(直接操作字段违背了封装的原则)
JavaBean引入
反射不建议操作类中的私有成员,因为这违反了封装的原则
-
Javabean是java中最重要的一个可重用的组件(减少代码重复)
-
组件:做到了可重用性,完成特定的功能
javabean规范
- 使用public修饰
- 字段私有化
- 提供get /set方法
- 公共的无参数的构造器
javabean三大成员
- 事件(基本不用了)
- 方法
- 属性
什么是属性(property)?
属性不是字段,property只关心getter或者setter,不关心定义,只要有getter或者setter其中一个就算是 属性 ,属性是通过getter,setter推断出来的
只要满足一个类中,有public修饰 ,有公共无参构造器,有getter或者setter其中一个方法,就算是满足了JavaBean
那么从而引入了一个机制 ,当我们去访问私有成员时,我们可以内省机制来去获取javabean中的方法
内省机制
内息的核心类 :Introspector
目的:获取JavaBean中的方法与属性
步骤
- 获取字节码对象,获取到到javabean的描述对象
Introspector.getBeanInfo(aClass, Object.class)
- 通过描述对象,获取到属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
- 通过属性描述器,来获取到属性名getName(), 读方法getReadMethod(),写方法getWriteMethod() ,读(getter),写(setter)
操作
public class BeanInfoDemo {
public static void main(String[] args) throws Exception {
//反射不建议直接操作字段,可以通过内省机制通过getter,setter操作
Class<?> aClass = Class.forName("cn.k.day02_reflect.People");
//获取构造器对象
Constructor<?> constructor = aClass.getConstructor();
//获取构造器中的实例
Object o = constructor.newInstance();
//获得描述对象
BeanInfo beanInfo = Introspector.getBeanInfo(aClass, Object.class);
//获得getter,setter的一个属性数组
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
//获取这个方法 (set)
Method writeMethod = propertyDescriptor.getWriteMethod();
//得到属性 (get)
Method readMethod = propertyDescriptor.getReadMethod();
//获取属性名字
String name = propertyDescriptor.getName();
//如果属性名字等于name
if (name.equals("name")){
//设置
writeMethod.invoke(o,"小月");
//获取设置值
Object invoke = readMethod.invoke(o);
System.out.println("invoke = " + invoke);
}
}
}
}
JavaBean与Map间的转换
javabean 转换 map
public static Map<String,Object> javaBeanToMap(Class aClass ,Object o) throws Exception {
HashMap<String, Object> map = new HashMap<>();
//获取javabean描述对象
BeanInfo beanInfo = Introspector.getBeanInfo(aClass, Object.class);
//获取属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//遍历获得属性
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
//获取key
String name = propertyDescriptor.getName();
//获取value
Method readMethod = propertyDescriptor.getReadMethod();
//执行
Object value = readMethod.invoke(o);
map.put(name,value);
}
return map;
}
map 转换 javabean
public static <T> T MapToJavaBean(Map<String,Object> map ,Class<T> cas) throws Exception {
T t = cas.newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(cas, Object.class);
//获取属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//遍历获得属性
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
//获取key
String name = propertyDescriptor.getName();
//获取value
Object value = map.get(name);
//set进去
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(t,value);
}
return t;
}
- test类
public class BeanMapDemo {
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("cn.k.day02_reflect.People");
People p = new People("轩爷", 22, "男");
Map<String, Object> map = javaBeanToMap(aClass,p);
System.out.println(map);
People people = MapToJavaBean(map, People.class);
System.out.println("people = " + people);
}
}