关于Java的反射机制,百度百科上是这样解释的:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
从百度百科的解释中,我们可以知道反射其实就是可以动态的获取任意一个类的所有属性和方法,包括私有的。下面我们就通过几个简单的例子来学习反射的基本用法。
首先我们先创建一个Person类,这个类包括一些公共的,私有的属性和方法。
package test.base;
public class Person {
private String name; //这是私有的属性
int age;
public String address;
public Person(){} //无参公共的构造方法
private Person(String name,int age,String address){ //有参私有的构造方法
this.name = name;
this.age = age;
this.address = address;
}
public void setName(String name) {
this.name = name;
}
public void show(){ //无返回值,无参,公共的普通方法
System.out.println("这是公共的,无返回值的,无参数的show方法。。。。");
}
private String show(String methodName){ //有返回值,有参,私有的普通方法
return "这是私有的,有返回值的,参数为" + methodName + "的show方法。。。";
}
@Override
public String toString() {
return "姓名:"+ name + "\t年龄:" + age + "\t\t地址:" + address;
}
}
我们要使用反射,首先就先要获取类的字节码文件对象,现在我们通过三种方式来获取字节码文件对象,创建GetClass类。
package test.base;
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//方式一
Person p = new Person();
Class<?> c1 = p.getClass();
//方法二
Class<?> c2 = Person.class;
//方法三
Class<?> c3 = Class.forName("test.base.Person");//forName里面的参数要带上类的包名
System.out.println(c1 == c2); //运行结果:true
System.out.println(c1 == c3); //运行结果:true
}
}
从上面的运行结果我们可以得出,c1 = c2 = c3,即这三种方式得到的对象都是指向同一个Person。但我们建议以后使用方式三,因为方式三只要有名称即可,不用像方式一那样要创建对象,所以比较方便;而且也不用像方式二那样要明确用到类中的静态成员,所以扩展性更强。
接下来我们来获取类的构造方法,创建GetConstructor类。
package test.base;
import java.lang.reflect.Constructor;
public class GetConstructor{
public static void main(String[] args) throws Exception {
// 使用方式三获取字节码文件对象
Class<?> c = Class.forName("test.base.Person");
// 获取Constructor对象(4种)
// public Constructor<?>[] getConstructors():获得所有公共构造方法对象
// public Constructor<T> getConstructor(Class<?>... parameterTypes):返回反映此 Class 对象所表示的类的指定公共构造方法对象。
// public Constructor<?>[] getDeclaredConstructors():获得所有构造方法对象(包括私有的)
// public Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes) :返回反映此 Class 对象所表示的类或接口的指定构造方法对象。(包括私有的)
// 上面方法中返回的是Constructor对象,还要调用Constructor类的的newInstance方法才能使用person类的构造方法
// 将作为变量传递给构造方法调用的对象数组;基本类型的值被包装在适当类型的包装器对象(如 Float 中的 float)中。
// public T newInstance(Object... initargs)
// 获得所有构造方法对象
Constructor<?>[] cons = c.getDeclaredConstructors();
for (Constructor<?> con : cons) {
System.out.println(con);
}
System.out.println("---------------------------------------------------------");
// 获取单个Constructor对象并调用无参构造方法
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance(); // 这里就类似于Object per = new Person();对obj进行强制类型转化之后就可以利用obj对person类进行访问。
System.out.println(obj);
// 获取单个带参私有构造方法
Constructor<?> con1 = c.getDeclaredConstructor(String.class,int.class,String.class); // 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象(如果是无参构造方法的话就不用写参数)
//当要通过反射访问类的私有成员(包括成员变量,构造方法和成员方法)时,要将Constructor类中的setAccessible设置为true。
con1.setAccessible(true); // 值为true则指示反射的对象在使用时应该取消Java语言访问检查。
Object obj1 = con1.newInstance("独孤九剑",22,"南苑1栋407");
System.out.println(obj1);
}
}
运行结果:
第一行和第二行是用Constructor类的getDeclaredConstructors()方法获取所有的构造方法对象;
第三行是获取无参的构造方法对象并使用;
第四行是获取有参的构造方法对象并使用。
获取类的属性,创建GetField类。
package test.base;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class GetField{
public static void main(String[] args) throws Exception {
// 使用方式三获取类的字节码文件对象
Class<?> c = Class.forName("test.base.Person");
// 获取Field对象(4种)
// public Field getField(String name);
// public Field[] getFields();
// public Field getDeclaredField(String name);
// public Field[] getDeclaredFields()
// 上面方法中返回的是Field对象,还要调用Field类的的set方法才能设置person类的属性。
// 第一个参数表示应该修改其字段的对象,第二个参数表示正被修改的 obj 的属性的新值 。(当此属性为static属性时,第一个参数可以写成null)
// public void set(Object obj,Object value)
// 通过无参构造方法创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// 获取address并对其赋值(address为公共的属性)
Field addressField = c.getField("address");
addressField.set(obj, "南苑1栋407");
// 获取name并对其赋值(name为私有的属性)
Field nameField = c.getDeclaredField("name");
nameField.setAccessible(true); //私有的成员(包括属性和方法)要设置setAccessible方法为true。
nameField.set(obj, "独孤九剑");
// 获取age并对其赋值(age为默认的属性)
Field ageField = c.getDeclaredField("age"); //非公共的成员要用getDeclaredField()方法调用。
ageField.set(obj, 22);
System.out.println(obj);
//上面通过反射给person类的成员属性赋值的方式效果跟下面跟是一样的
Person p = new Person();
p.address = "南苑1栋407";
p.setName("独孤九剑");
p.age = 22;
System.out.println(p);
}
}
运行结果:
第一行的是通过反射获取每个属性对象并设置相应的值;
第二行是通过创建对象给每个属性赋值。
获取类的普通方法,创建GetMethod类。
package test.base;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class GetMethod{
public static void main(String[] args) throws Exception {
// 使用方式三获取类的字节码文件对象
Class<?> c = Class.forName("test.base.Person");
// 获取Method对象(4种)
// public Method[] getMethods(); // 获取自己的包括父亲的公共方法
// public Method getMethod(String name,Class<?>... parameterTypes); 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
// public Method[] getDeclaredMethods(); // 获取自己的所有的方法
// public Method getDeclaredMethod(String name,Class<?>... parameterTypes);
// 上面方法中返回的是Method对象,还要调用Method类的的invoke方法才能使用person类的方法
// 因为不知道返回值的确切类型,所以返回Object类型,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
// public Object invoke(Object obj,Object... args)
// 获取无参构造方法
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// 获取公共无参无返回值的方法并调用
Method m1 = c.getMethod("show");
m1.invoke(obj);
System.out.println("---------------------------------------------------");
// 获取私有的有参有返回值的方法并调用
Method m2 = c.getDeclaredMethod("show", String.class);
m2.setAccessible(true);
System.out.println(m2.invoke(obj, "独孤九剑"));
}
}
运行结果:
第一行是获取公共的,无参的方法对象并调用;
第二行是获取私有的,有参的方法对象并调用。
以上就是反射的基本用法。