目录
反射机制的概念
反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法
在java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息
反射是Java语言中的一种机制,通过这种机制可以动态地实例化对象、读写属性、调用方法
反射也是很多框架开发的基础,比如hibernate,struts等框架
早在我们使用jdbc连接数据库时就使用到了java的反射机制
Class.forName('com.mysql.jdbc.Driver.class');//加载MySQL的驱动类
反射的优缺点
优点:
能够运行时动态获取类的实例,提高灵活性;
与动态编译结合;
缺点:
使用反射性能较低,需要解析字节码(.class文件),将内存中的对象进行解析;
相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性);
解决方案:
1、通过setAccessible(true)关闭JDK的安全检查来提升反射速度;
2、多次创建一个类的实例时,有缓存会快很多;
3、ReflectASM工具类,通过字节码生成的方式加快反射速度 ;
反射的本质
拿Student举例(如下图),当Student被实例化时,实际是通过JVM(Java虚拟机)编译成.class文件,而反射机制就是在类被加载出来时,会生成一个Class对象,而这个被编译完成的.class文件生成的Student模型相当于是Class对象的子类(每一个class文件都有一个对应的Class模型,Class模型用于表示这个类的类型信息),并且Student类对于Class模型来说是全透明的,包括private属性或方法都是可以直接修改或者调用的。所以反射实际上是在被编译后的文件上动手脚
获取Class对象的方法
创建一个测试用的Student类
public class Student {
private String sid;
private String sname;
public Integer age;
static{
System.out.println("加载进jvm中!");
}
public Student() {
super();
System.out.println("调用无参构造方法创建了一个学生对象");
}
public Student(String sid) {
super();
this.sid = sid;
System.out.println("调用带一个参数的构造方法创建了一个学生对象");
}
public Student(String sid, String sname) {
super();
this.sid = sid;
this.sname = sname;
System.out.println("调用带二个参数的构造方法创建了一个学生对象");
}
@SuppressWarnings("unused")
private Student(Integer age) {
System.out.println("调用Student类私有的构造方法创建一个学生对象");
this.age = age;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public void hello() {
System.out.println("你好!我是" + this.sname);
}
public void hello(String name) {
System.out.println(name + "你好!我是" + this.sname);
}
@SuppressWarnings({ "unused", "deprecation" })
private Integer add(Integer a, Integer b) {
return new Integer(a.intValue() + b.intValue());
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
}
}
获取Class对象的三种常用方法
//1.Class.forName("类的权限命名") 获取Class
Class<Student> clazz =(Class<Student>)Class.forName("com.test.reflect.Student");
//2. 类.class
Class clazz02 = Student.class;
//3. 对象.getClass()
Student stu = new Student();
Class clazz03 = stu.getClass();
反射实例化对象
//获取无参构造,Constructor:构造函数
Constructor<Student> c1 = clazz.getConstructor();
//实例学生对象
Student stu01 = c1.newInstance();
stu01.setSname("小明");
System.out.println(stu01);
//获取有参构造
Constructor<Student> c2 = clazz.getConstructor(String.class);
Student stu02 = c2.newInstance("1");
stu02.setSname("小黑");
System.out.println(stu02);
//获取有参构造
Constructor<Student> c3 = clazz.getConstructor(String.class,String.class);
Student stu03 = c3.newInstance("2", "小哈");
System.out.println(stu03);
//获取私有化的有参构造:Declared
Constructor<Student> c4 = clazz.getDeclaredConstructor(Integer.class);
//允许访问,Accessible访问控制符
c4.setAccessible(true);
Student stu04 = c4.newInstance(22);
System.out.println(stu04);
反射获取构造函数中判断参数个数的方法:使用可变参数(如果前面还有参数,必须放在最后),查看clazz.getConstructor源码即可发现:
反射调用方法
Method method = clazz.getMethod("hello");
stu04.setSname("lihao");
//调用方法
method.invoke(stu04);
Method method02 = clazz.getMethod("hello",String.class);
method02.invoke(stu04, "zengfanyan");
//获取私有方法
Method method03 = clazz.getDeclaredMethod("add",Integer.class, Integer.class);
method03.setAccessible(true);
int rv = (int)method03.invoke(stu04, 1,1);
System.out.println(rv);
反射读写属性
Field f = clazz.getField("age");
f.set(stu04, 78);
System.out.println(stu04);
System.out.println(f.get(stu04));
//获取私有属性
Field f01 = clazz.getDeclaredField("sname");
f01.setAccessible(true);
//调用属性
f01.set(stu04, "lihao is hao ren");
System.out.println(stu04);
BeanUtils
除了上述获取属性的方法之外,还可以通过BeanUtil.getproperty(对象名,“属性”)的方式获取属性,其原理是先调用类中的公开get属性的方法获取再通过反射机制进行获取
👆和JavaBean的封装的设计有关:一个对象类中私有属性,公开get(得到值)、set(注入值)方法的意义是提供统一控制获取和处理类中私有属性的方法入口