java反射机制:
指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能成为java语言的反射机制。
而要想解剖一个类,必须先获取该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类对象。
jdk1.6官方解释Class类:
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。 Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
乍一看有点懵,我们来看下面这张图
我们可以看到通过编译后的class文件通过jvm使用类加载器加载到内存而Class类我们就可以理解为class文件在内存中的东东。
知道了Class是啥东西,接下来直接上代码看看它怎么用。
先给一个Person类
package reflect;
public class Person {
private String name;
int age;
public String sex;
public Person() {
}
Person(String name){
this.name = name;
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public Person(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
package reflect;
public class GetClass {
/**三种方式获取字节码文件对象
* @param args
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws ClassNotFoundException{
//1 类名.class
Class clazz1 = Person.class;
System.out.println(clazz1.getName());
//2 对象.getClass()
Person p = new Person();
Class clazz2 = p.getClass();
System.out.println(clazz2.getName());
//3 Class.forName("包名.类名"); 注意要写全
Class clazz3 = Class.forName("reflect.Person");
System.out.println(clazz3.getName());
}
}
打印结果:
reflect.Person
reflect.Person
reflect.Person
获取完字节码对象到底有啥用处呢?
其实反射就是通过字节码文件对象完成的反射动作,来获取一个类的构造方法、成员变量、普通方法等操作,而不管你是public的还是private的,可以看出反射确实非常暴力。
接下来说说如何通过反射获取构造方法:
还是使用上面的Person类,在这里我们就随便用一种方式获取字节码对象了,想试试别的方式的,同学们自己回去试。
1,获取public的构造方法:
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException,
SecurityException
public Constructor<?>[] getConstructors()
throws SecurityException
上面的是有参数返回一个公共Constructor方法,而这个参数注意是指参数的数据类型的字节码文件对象(如String类型则参数是String.class,是int则参数就是int.Class ),下面的是无参数返回所有公共的构造方法(数组)。
package reflect;
import java.lang.reflect.Constructor;
public class TestReflect {
/**通过反射获取public构造方法
*
* public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException,
SecurityException
public Constructor<?>[] getConstructors()
throws SecurityException
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("reflect.Person");
//获取无参数的构造方法
Constructor constructor1 = clazz.getConstructor();
//获取有参数的构造方法
Constructor constructor2 = clazz.getConstructor(String.class,int.class);
System.out.println(constructor1.getName());
System.out.println(constructor2.getName());
}
}
打印结果可以看到返回两个public的构造方法名,如果Person类中构造方法是private的会报错,同学们可以下面自己试试。
reflect.Person
reflect.Person
说到这插一句,可能会有同学问这个Constructor是个啥东西,
jdk 1.6中解释:
Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,但是如果发生收缩转换,则抛出 IllegalArgumentException。
其实就是相当于是构造方法的封装类。
那么获取完构造方法怎么操作呢
(1)操作无参数的 构造方法(两种方式):
package reflect;
import java.lang.reflect.Constructor;
public class TestReflect {
/**通过反射获取public构造方法
*
* public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException,
SecurityException
public Constructor<?>[] getConstructors()
throws SecurityException
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("reflect.Person");
//获取无参数的构造方法
Constructor constructor1 = clazz.getConstructor();
//获取有参数的构造方法
Constructor constructor2 = clazz.getConstructor(String.class,int.class);
/* System.out.println(constructor1.getName());
System.out.println(constructor2.getName());*/
testConstructor1();
}
/**操作构造方法(无参数)两种方式
* @throws Exception
*/
public static void testConstructor1() throws Exception{
Class clazz = Class.forName("reflect.Person");
//1 获取到Constructor,通过construct对象实例化peron
Constructor constructor1 = clazz.getConstructor();
Person p1 = (Person)constructor1.newInstance();
//2 直接通过字节码文件对象实例化person
Person p2 = (Person) clazz.newInstance();
//设置值
p1.setName("李四");
p2.setName("李四");
System.out.println(p1.getName());
System.out.println(p2.getName());
}
}
打印结果:
李四
李四
(2)操作有参数的 构造方法
package reflect;
import java.lang.reflect.Constructor;
public class TestReflect {
/**通过反射获取public构造方法
*
* public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException,
SecurityException
public Constructor<?>[] getConstructors()
throws SecurityException
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("reflect.Person");
//获取无参数的构造方法
Constructor constructor1 = clazz.getConstructor();
//获取有参数的构造方法
Constructor constructor2 = clazz.getConstructor(String.class,int.class);
/* System.out.println(constructor1.getName());
System.out.println(constructor2.getName());*/
//testConstructor1();
testConstructor2();
}
/**操作构造方法(有参数)
* @throws Exception
*/
public static void testConstructor2() throws Exception{
Class clazz = Class.forName("reflect.Person");
Constructor constructor = clazz.getConstructor(String.class,int.class);
//获取实例
Person p = (Person)constructor.newInstance("王五",20);
System.out.println(p.getName());
System.out.println(p.getAge());
}
}
打印结果:
王五
20
返回是数组的情况没啥区别,回头大家自己试试
说完获取public的构造方法,下面该说说非public的是如何获取的(下面才是重点,嘎嘎~)
2、 获取所有类型的构造方法(不管你公有、私有还是没有的统统拿到手)
同样有以下两个方法:
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
public Constructor<?>[] getDeclaredConstructors()
throws SecurityException
将上面的Person类中无参的构造方法改为private修饰
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("reflect.Person");
Constructor constructor1 = clazz.getDeclaredConstructor();
Constructor constructor2 = clazz.getDeclaredConstructor(String.class,int.class,String.class);
System.out.println(constructor1.getName());
System.out.println(constructor2.getName());
}
打印结果:
reflect.Person
reflect.Person
同样构造方法的调用
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("reflect.Person");
Constructor constructor1 = clazz.getDeclaredConstructor();
Constructor constructor2 = clazz.getDeclaredConstructor(String.class,int.class,String.class);
/*System.out.println(constructor1.getName());
System.out.println(constructor2.getName());*/
//public的构造方法直接通过newInstance的方式获取实例person
Person p = (Person) constructor2.newInstance("zhangs",16,"女");
System.out.println(p);
//private的构造方法需要设置可访问,之后再newInstance获取实例
constructor1.setAccessible(true);
Person p2 = (Person) constructor1.newInstance();
System.out.println(p2);
}
打印结果
Person [name=zhangs, age=16, sex=女]
Person [name=null, age=0, sex=null]
这里就要注意获取实例对象的时候公有方法可以通过constructor对象直接newInstance,而私有构造方法需要先设置可访问,constructor.setAccessible(true),之后再newInstance即可,这也是反射暴力的所在。