JAVA高新技术反射机制的原理之构造函数、普通方法和字段

时间:2023-02-17 22:49:06


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));

}
}