Java中有一个Class类用于代表某个类的字节码(如果读者不了解反射、字节码和类Class,可以看下我写的JAVA反射机制原理中的字节码和类Class)
Class类既然代表某个类的字节码,它当然就要提供加载摸个字节码的方法:forName(),forName方法用于加载某个类的字节码到内存中。
另外2种得到类字节码的方法:
类名.class和对象.getClass()
Class对象提供了如下常用方法:
下面是获取某个类中public修饰的成员
//根据参数,确定反射出哪个构造函数
public Constructor getConstructor(Class<?> ...parameterTypes)
//第一个参数:获取某个类中哪个方法;第二个参数:方法所需要的参数类型
//例如有一个方法叫void method(String name,int age)
//那么getMethod要传入的参数分别是getMethod("method",String.class,int.class);
public Method getMethod(String name,Class<?> ...parameterTypes)
//获取某个类中的某个字段(即某个属性)
public Field getField(String name)
下面是获取某个类中private修饰的成员,还有一个setAccessible是设置最大的访问权限,即暴力反射。
public Constructor getDeclaredConstructor(Class<?> ...parameterTypes)
public Method getDeclaredMethod(String name,Class<?> ...parameterTypes)
public Field getDeclaredField(String name)
下面是获取某个类中所有的成员
public Constructor[] getConstructors(Class<?> ...parameterTypes)
public Method[] getMethods(String name,Class<?> ...parameterTypes)
public Field[] getFields(String name)
下面看具体代码分析,解释都在代码中:主要讲三点构造方法的反射、普通方法的反射和字段的反射。
先写个Person类,里面主要有字段、构造函数和普通方法,我们就是对这个类进行反射。
public class Person {
public String name="lisi";
private int age=233;
private static int id=10023421;
public Person(){}
public Person(String name){
System.out.println("name:"+name);
}
public Person(String name,int age)
{
System.out.println("name:"+name+"age:"+age);
}
private Person(List list){}
public void run()
{
System.out.println("run.....");
}
public void run(String name,int age)
{
System.out.println("name"+name+"age:"+age);
}
public Class[] run(String name,int[] arr)
{
return new Class[]{String.class};
}
private void run(InputStream in)
{
System.out.println(in);
}
public static void run(int num)
{
System.out.println(num);
}
public static void main(String[] args)
{
System.out.println(args.length);
}
}
下面对Person类中的成员进行反射操作,(这里用到了junit测试)
public class TestPerson {
//////////////////////////////////////////////////////反射Person类中的构造函数(Constructor)
//反射构造函数:public Person()
@Test
public void test1() throws Exception
{
//你要使用一个类,就必须将其字节码加载到内存,然后给这个字节码取个名字(clazz)
//下面的做法就是获取某个类的字节码,通常我们采用下面这种方式
Class clazz=Class.forName("day16.reflect.base.Person");
//有了Person类在字节码后就可以用这个字节码得到Person类中的某个构造函数了
//传入参数是什么就是获取什么构造函数
Constructor con=clazz.getConstructor(null);
//有了构造函数后就可以创建对象了,
Person p=(Person) con.newInstance(null);
//下面是测试,用创建好的p对象,访问Person类的name属性
System.out.println(p.name);
}
//反射构造函数:public Person(String name)
@Test
public void test2()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
//获取哪个构造函数,当然是参数是String name的构造函数了
Constructor con=clazz.getConstructor(String.class);
//实例化一个Person类的一个实例对象,怎么做,要传入一个name,就像
//Person p=new Person("李四");
Person p=(Person) con.newInstance("李四");
System.out.println(p.name);
}
//反射构造函数:public Person(String name,int age)
@Test
public void test3()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Constructor con=clazz.getConstructor(String.class,int.class);
Person p=(Person) con.newInstance("王五",12);
System.out.println(p.name);
}
//反射构造函数:private Person(List list)
@Test
public void test4()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
//getDeclaredConstructor方法是获取private修饰的构造函数
Constructor con=clazz.getDeclaredConstructor(List.class);
//此方法是给Constructor最大的权利,就是强暴访问Person类的private的东西
con.setAccessible(true);
Person p=(Person) con.newInstance(new ArrayList());
System.out.println(p.name);
}
//创建无参对象的另一种途径
@Test
public void test5()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
//看到了创建Person类的无参对象时,直接用Class类中的newInstance方法创建对象了
//而不是用Constructor类中的newInstance方法创建对象,这是简化的操作,可以直接用Class
//类中的方法去创建对象,不过Person类要有一个无参的构造函数。
Person p=(Person) clazz.newInstance();
System.out.println(p.name);
}
////////////////////////////////////////////////////反射Person类中的普通方法(Method)
//反射方法:public void run()
@Test
public void test6()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
//创建Person类的一个对象,这里就不用反射的方式创建对象了,演示太麻烦
Person p=new Person();
/*Constructor con=clazz.getConstructor(null);
Person p=(Person) con.newInstance(null);*/
//利用Class类的getMethod方法获取Perosn类中的普通方法,返回值是Method类型
//第一个参数是要反射Person类中的那个方法,第二个参数是反射方法的要传入的参数类型
//第二个参数是决定到底调用Person类中同名方法的哪个方法
Method method=clazz.getMethod("run", null);
//利用Method类的invoke方法来运行反射得到的method方法,
//第一个参数是method要作用于哪个对象上,第二个参数是run方法的参数
method.invoke(p, null);
}
//反射方法:public void run(String name,int age)
@Test
public void test7()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Person p=new Person();
Method method=clazz.getMethod("run", String.class,int.class);
method.invoke(p, "时将",22);
}
//反射方法:public Class[] run(String name,int[] arr)
@Test
public void test8()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Person p=new Person();
Method method=clazz.getMethod("run", String.class,int[].class);
method.invoke(p, "都了",new int[]{1,23,3});
}
//反射方法:private void run(InputStream in)
@Test
public void test9()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Person p=new Person();
//注意run是private
Method method=clazz.getDeclaredMethod("run", InputStream.class);
//暴力反射
method.setAccessible(true);
method.invoke(p, new FileInputStream("e:\\test.txt"));
}
//反射方法:public static void run(int num)
@Test
public void test10()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Person p=new Person();
Method method=clazz.getMethod("run",int.class);
//因为run方法是static,类名可以直接调用,所以invoke中第一个参数可以不传入参数,
//如果有最好
method.invoke(null, 44);
//method.invoke(p, 44);
}
//反射方法:public static void main(String[] args)
@Test
public void test11()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Person p=new Person();
Method method=clazz.getMethod("main",String[].class);
//method.invoke(null, new String[]{"aaa","bbb"});//如果写成这样,会出错,看说明
//下面两个方法都可以
//method.invoke(null, (Object)new String[]{"aaa","bbb"});
method.invoke(null, new Object[]{new String[]{"aaa","bbb"}});
/*
说明:
jdk1.5: Method.invoke(String methodName,Object ...args)
jdk1.4: Method.invoke(String methodName,Object[] args)
1.5兼容1.4的做法,当你将new String[]{"aaa","bbb"}传入后,1.4会按照Object[] args
的方式把String数组拆开,变成了("aaa","bbb"),可是main(String[] args)函数要的是一个数组,你却传了("aaa","bbb")
那么main函数怎么能受得了。
* */
}
//////////////////////////////////////////////////反射Person类的字段(Filed)
//反射Person类字段:public String name="lisi";
@Test
public void test12()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Person p=new Person();
//使用Person类的字节码clazz获得字段的名字,返回的类型是Field
Field field=clazz.getField("name");
//获得字段的值,作用于Person类的一个实例对象p上
String value=(String) field.get(p);
System.out.println("字段的值:"+value);
//获取字段的类型
Class cValue=field.getType();
System.out.println("字段的类型"+cValue);
//我们获取到字段有什么好处,有什么用?看下面示例
//以后我们可以判断获取字段的类型是不是自己想要的,如果是就对其进行设值
if(cValue.equals(String.class))
{
field.set(p, "点击");
System.out.println("设置字段值,并获取:"+field.get(p));
}
}
//反射Person类字段:private int age;
@Test
public void test13()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Person p=new Person();
//age是private
Field field=clazz.getDeclaredField("age");
field.setAccessible(true);//暴力访问
System.out.println(field.get(p));
}
//反射Person类字段:private static int id;
@Test
public void test14()throws Exception
{
Class clazz=Class.forName("day16.reflect.base.Person");
Person p=new Person();
//int age 是private
Field field=clazz.getDeclaredField("id");
field.setAccessible(true);//暴力访问
//注意这里,id是static修饰的,可是我们利用field获取字段值时,还要传入一个对象
System.out.println(field.get(p));
}
}