前言
反射可以说是Java中最强大的技术之一了,很多框架都是基于反射来实现的,反射可以动态获取任意一个类的方法或属性,是不是听起来就厉害!
一、获取class对象
首先我们来认识一下java.lang.Class这个类,这个类没有公共构造方法,该类的实例由虚拟机和类加载器自动构造完成,那我们怎么获取class对象呢?
1.1 类名.class
通过类名.class的方式获得该类的class对象
Class c = Student.class;
1.2 getClass方式
Object类中有一个getClass()方法,而所有类都是Object子类,天然的继承了该方法
Student student = new Student();
Class c = student.getClass();
需要注意的是,基本数据类型不是对象,因此不能通过int.getClass()
这种方式获取class对象,需要用到它的包装类,包装类的TYPE属性可以获取基本数据类型的class对象
Class c = Integer.TYPE;
1.3 Class.forName()
直接传入包名.类名,就可以获取该类的对象
Class.forName("com.lagou.Student");
通过以上三种方式均可以获得class对象,不同的是第一种方式需要导入对应类的包,第二种方式则先要获取到类的实例才行,而我们一般用到反射都是为了创建实例,因此常用的是第三种方式,快捷灵活。
二、获取方法
有了该类的class对象,我们就能对该类为所欲为了,包括私有函数、私有变量,通通都能访问!你就是这个世界的老大!
2.1 获取构造函数
获取构造函数,需要用到class对象的getConstructor()方法
首先我们来定义一个Student类
public class Student {
private String name;
private String sex;
private String birth;
public Student() {
}
public Student(String name,String sex,String birth) {
this.name = name;
this.birth = birth;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
private String getBirth() {
return birth;
}
private void setBirth(String birth) {
this.birth = birth;
}
}
我们来获取一下无参构造函数
Class c = Class.forName("Student");
Constructor constructor = c.getConstructor();
获取有参构造函数时,需要传递参数的类型.class
Class c = Class.forName("Student");
Constructor constructor = c.getConstructor(String.class,String.class,String.class);
获取所有构造函数
Class c = Class.forName("Student");
Constructor[] constructor = c.getConstructors();
2.2 获取普通函数
通过class的method()
方法,我们可以获取到类的普通对象
先来获取无参函数:
Method m = c.getDeclaredMethod("getName");
System.out.println(m); // 输出public java.lang.String Student.getName()
再来获取一个有参函数:
Method m = c.getDeclaredMethod("getName");
System.out.println(m); // 输出public void Student.setName(java.lang.String)
如果想要获得私有函数呢?
Method m = c.getDeclaredMethod("setBirth", String.class);
System.out.println(m);
看来getDeclaredMethod()
这个方法私有公有都可以获得呀!太万能了!
同理,获得私有构造函数也可以用getDeclaredConstructor()
方法,认准Declared,什么方法都能访问!
三、获取变量
除了获得构造方法,我们还可以获取变量
Field name = c.getDeclaredField("name");
System.out.println(name); // 输出:private java.lang.String Student.name
通过输出我们可以发现,私有变量获取到了!神不神奇!
用getDeclaredFields()
方法,可以获取到所有变量,和获得方法一样,无脑Declared!
Field[] name = c.getDeclaredFields("name");
想要什么,遍历就完了!
四、获取其他信息
仍然是class对象中的方法,如下表所示,比较简单,就不做过多演示了
作用 | 方法名 |
---|---|
获取包名 | getPackage() |
获取父类 | getSuperClass() |
获取接口 | getInterfaces() |
获取注解 | getAnnotations() |
获取泛型 | getGenericInterfaces() |
五、获取对象实例
我们得到了class对象,不能光是看看都有哪些方法吧,肯定需要调用的呀,而调用首先就要用到实例,来看一下如何通过反射获得对象实例吧!
Class c = Class.forName("Student");
Constructor declaredConstructor = c.getDeclaredConstructor();
Student student = (Student) declaredConstructor.newInstance();
student.setName("12");
System.out.println(student.getName()); // 12
看,就是这么轻松加easy!
那如果想要调用私用方法getBirth()
呢?用student.私有方法是不可能的,这就需要用到下面的知识了
六、获取Method
method眼熟呀,刚才在前面不还见到了,通过class对象的getDeclaredMethod()
方法就可以访问到私有方法了,但是访问归访问,如何调用呢?
// 获得class对象
Class c = Class.forName("Student");
// 获得构造函数
Constructor declaredConstructor = c.getDeclaredConstructor(String.class, String.class, String.class);
// 通过构造函数获得对象实例
Object o = declaredConstructor.newInstance("张三", "女", "1995年1月1日");
// 获得私有方法
Method getBirth = c.getDeclaredMethod("getBirth");
// 设置访问权限为true,即可以访问
getBirth.setAccessible(true);
System.out.println(getBirth.invoke(o)); // 输出1995年1月1日
如果调用的私用方法需要传递参数呢?
// 获得class对象
Class c = Class.forName("Student");
// 获得构造函数
Constructor declaredConstructor = c.getDeclaredConstructor(String.class, String.class, String.class);
// 通过构造函数获得对象实例
Object o = declaredConstructor.newInstance("张三", "女", "1995年1月1日");
// 获得私有方法,该私有方法有一个String类型的参数
Method setBirth = c.getDeclaredMethod("setBirth",String.class);
// 设置访问权限为true,即可以访问
setBirth.setAccessible(true);
// 调用方法并传递string类型的参数
setBirth.invoke(o,"1996年12月12日");
Method getBirth = c.getDeclaredMethod("getBirth");
getBirth.setAccessible(true);
System.out.println(getBirth.invoke(o)); // 输出为1996年12月12日,证明setBirth方法生效了!
至此,反射的主要内容都说完了!毕竟我们最主要的就是用反射来动态的创建对象并调用方法嘛,套路就是这么个套路,学会了吗?
七、总结
如果阅读API文档,以及前面所写,会发现class对象中有这么几个方法:
getMethod();
getMethods();
getDeclaredMethod();
getDeclaredMethods();
getMethod()
只能返回当前类及所有继承的父类的public修饰的方法。仅包括public,而
getDeclaredMethod()
可以获得当前类的所有方法,因此私有的就也能访问到啦,但是要注意,它不能获得父类的方法哦!
复数的方法就不用说了,那自然是获得所有方法了,返回的是一个数组列表。
虽说反射大法好,但还是有一定弊端的:
- 破坏了程序的安全性,连private都能访问,还有什么隐私可言啊喂!
- 反射包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。
因此反射虽好,可不要乱用哦!如果一个功能可以不用反射完成,那么最好就不用。