反射机制——如何获取Class对象,和其构造函数、字段、方法
获取字节码文件对象的三种方式:
具体看下面代码里的注释,推荐使用第三种方式,
因为只需要有类的完整的字符串名称就可以获取到类的字节码文件对象,很简单。
使用到Class类中的静态方法:
static Class<?> forName(StringclassName);
当找不到字节码文件时,此方法会抛出ClassNotFoundException异常
代码:
import bean.Person;
public class ReflectDemo_1 {
/**
* @param args
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws ClassNotFoundException {
demo1();//方式一 需要类的对象调用方法
demo2();//方式二 需要类调用属性
demo3();//方式三 只需要类的字符串名称即可,推荐使用此方法
}
public static void demo1() {
/*
* 通过类的对象调用getClass方法,获取类的字节码文件对象
*/
Person p1=new Person();//Person run......
Class clazz1=p1.getClass();
Person p2=new Person();//Person run......
Class clazz2=p2.getClass();
System.out.println(clazz1==clazz2);//true 一个类的字节码文件是唯一的,相关联的字节码文件对象也是唯一的。
}
public static void demo2() {
/*
* 通过类的静态属性class,获取类的字节码文件对象
*/
Class clazz=Person.class;
System.out.println(clazz);//class bean.Person
}
public static void demo3() throws ClassNotFoundException {
/*
* 只需要提供类的完整的字符串名称即可获取到该类的字节码文件对象
* 使用的是Class类的静态的forName方法
*/
Class clazz=Class.forName("bean.Person");
System.out.println(clazz);//class bean.Person
}
}
package bean;
public class Person {
public String name;
private int age;
public Person() {
super();
System.out.println("Person run......");
}
private Person(String name, int age) {
super();
this.name = name;
this.age = age;
System.out.println("param Person run "+name+":"+age);
}
public void show(){
System.out.println("show run");
}
private void paramMethod(String s,int i){
System.out.println("paramMethod run "+s+":"+i);
}
}
获取Class对象中的构造函数:
AccessibleObject类有三个直接已知子类:
Constructor类、Field类、Method类
常用方法:
当传入true时,该对象可以忽略访问限制修饰符,不受访问限制修饰符限制。
使用此方法也被称作暴力访问。
setAccessible(boolean flag);
通过Class对象创建类的对象
首先我们需要通过Class对象获取构造器,然后通过构造器获取类的对象
使用到Class类的方法:
1、返回包含所有公共构造器的数组
Constructor<?>[]getConstructors();
返回带有指定参数的公共构造器,其中的参数传入的是比如String.class,int.class等,
要根据类里面的有参构造函数来定,多参数之间用英文逗号隔开。如果传入null,则表示是无参构造函数。
Constructor<T>getConstructor(Class<?>... parameterTypes);
3、返回包含本类所有的构造器(包括私有的)的数组
Constructor<?>[]getDeclaredConstructors();
4、返回带有指定参数的构造器(包括私有的),其中的参数传入的要求和2方法的要求是一样的。
Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes);
其中2方法适合公共构造函数,4方法是适合私有构造函数
注意,4方法也可以获取公共构造器,针对性不同而已
另外,特别对于公共无参构造函数,有更方便的方法,可以跳过获取构造器的步骤。
使用Class类的方法
Object newInstance();直接获取类的对象,不需要获取无参构造器,其实就是完成了new Person();这句代码。
当是私有无参构造函数时,使用此方法会抛出IllegalAccessException异常
当存在有参构造函数,而公共无参构造函数没有写出来的时候,
使用此方法会抛出InstantiationException异常
Constructor类:构造器类
无构造函数,可以通过上面提到的Class类的方法获取到Constructor对象
常用方法:
获取类的对象,传入的参数是类中有参构造函数所要传入参数的类型值,无参构造函数传入null即可
Object newInstance(Object...initargs);
注意对于私有构造函数虽然获取到了私有构造器,
但是使用newInstance方法获取类的对象时会报IllegalAccessException异常。
解决办法就是进行暴力访问,使用父类AccessibleObject类的setAccessible方法。
示例:
import java.lang.reflect.Constructor;
public class ReflectDemo_2 {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//公共无参构造函数,直接Class对象调用newInstance方法获取类的对象
demo1();
//公共无参构造函数,先获取无参构造器,再通过无参构造器调用newInstance方法获取类的对象
demo2();
//私有有参构造函数
demo3();
}
public static void demo1() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
// TODO Auto-generated method stub
Class clazz=Class.forName("bean.Person");
Object obj=clazz.newInstance();
}
public static void demo2() throws Exception {
// TODO Auto-generated method stub
Class clazz=Class.forName("bean.Person");
Constructor constructor=clazz.getConstructor(null);
Object obj=constructor.newInstance(null);
}
public static void demo3() throws Exception {
// TODO Auto-generated method stub
Class clazz=Class.forName("bean.Person");
Constructor constructor=clazz.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);//暴力访问
Object obj=constructor.newInstance("小强",22);
}
}
运行输出如下:
Person run......
Person run......
param Person run 小强:22
获取Class对象中的字段:
使用到Class类的方法:
1、返回包含所有公共字段的数组
Field[] getFields();
2、返回指定公共字段,传入的是字段的名称
Field getField(String name);
3、返回包含本类所有字段(包括私有的)的数组
Field[] getDeclaredFields();
4、访问指定字段,传入的是字段的名称
Field getDeclaredField(String name);
其中2方法适合公共字段,4方法获取私有字段
Field类:字段类
没有构造函数,可以通过上面提到的Class类的方法获取到Field对象
常用方法:
对obj对象的此字段赋值为value
set(Object obj,Object value);
返回obj对象的此字段上的值
Object get(Object obj);
注意:若是私有字段,用获取到的字段调用set方法给字段赋值时,会报IllegalAccessException异常
解决办法就是进行暴力访问,使用父类AccessibleObject类的setAccessible方法。
示例:
import java.lang.reflect.Field;
public class ReflectDemo_3 {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
demo1();//获取公共字段,并赋值,取值
demo2();//获取私有字段,并赋值,取值
}
public static void demo1() throws Exception {
// TODO Auto-generated method stub
Class clazz=Class.forName("bean.Person");
Object obj=clazz.newInstance();
Field field=clazz.getField("name");
field.set(obj, "小明");
Object value=field.get(obj);
System.out.println(value);
}
public static void demo2() throws Exception {
// TODO Auto-generated method stub
Class clazz=Class.forName("bean.Person");
Object obj=clazz.newInstance();
Field field=clazz.getDeclaredField("age");
field.setAccessible(true);//暴力访问
field.set(obj, 15);
Object value=field.get(obj);
System.out.println(value);
}
}
运行输出如下:
Person run......
小明
Person run......
15
获取Class对象里的方法:
使用到Class类的方法:
1、返回包含所有公共方法的数组
Method[] getMethods();
2、返回指定方法名和参数的公共方法,这里的参数传入的是比如String.class,int.class等,
需要根据类中对应的方法来定,多参数之间用英文逗号隔开。如果传入null,则表示是空参方法。
Method getMethod(Stringname,Class<?>... parameterTypes);
3、返回包含本类中所有的方法(包括私有的)的数组
Method[] getDeclaredMethods();
4、返回指定方法名和参数的方法,这里的参数传入的要求和2方法的要求是一样的。
Method getDeclaredMethod(Stringname,Class<?>... parameterTypes);
其中方法2适合获取公共方法,方法4适合获取私有方法。
Method类:方法类
无构造函数,可以通过上面提到的Class类的方法获取到Method对象
常用方法:
执行obj对象的此方法,其中此方法里面传入需要的参数类型值,如果是空参方法,传入null
Object invoke(Object obj,Object...args);
对于获取到的私有方法的Method对象,调用invoke方法会报IllegalAccessException异常
解决办法同样是采用暴力访问方式,使用父类AccessibleObject类的setAccessible方法。
示例:
import java.lang.reflect.Method;
public class ReflectDemo_4 {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
demo1();//获取公共空参方法,并执行该方法
demo2();//获取私有有参方法,并执行该方法
}
public static void demo1() throws Exception {
// TODO Auto-generated method stub
Class clazz=Class.forName("bean.Person");
Object obj=clazz.newInstance();
Method method=clazz.getMethod("show", null);
Object obj1=method.invoke(obj,null);
// System.out.println(obj1);
}
public static void demo2() throws Exception {
// TODO Auto-generated method stub
Class clazz=Class.forName("bean.Person");
Object obj=clazz.newInstance();
Method method=clazz.getDeclaredMethod("paramMethod",String.class,int.class);
method.setAccessible(true);//暴力访问
Object obj1=method.invoke(obj,"张三",30);
// System.out.println(obj1);//这个obj1到底表示的是啥东西?
}
}
运行输出如下:
Person run......
show run
Person run......
paramMethod run 张三:30
总结:在获取Class对象里的构造函数、字段、方法时,具体要使用哪个方法我们主要看是私有的还是公共的,有参无参不要紧,因为有参里传入null就是说明无参了。