通过Java反射可以实现以下功能
就像照镜子可以看请自己,反射使程序可以看清一个类的情况并加以使用,Java反射机制能够探知类的基本结构,这种对Java类结构探知的能力,称为Java类的“自审”,并且,反射机制使构建框架的基础所在。
java反射常用API
一个类或者接口被加载后,从系统中都能获得一个代表该类或接口的Class实例,通过该实例就可以访问到Java虚拟机中这个类会接口。
获取Class实例
Java程序中获取Class实例通常有如下三种方式,根据实际情况选择。
从Class实例中获取信息
在获得了某个类型对应的Class实例后,就可以调用Class实例的方法来获得该类型的信息。Class提供了大量实例方法来获取对应类型的详细信息。
获取类型的基本信息
示例1:
创建一个User类用于测试:
package pm; public class User{ private String name; private int age; private String address; private char sex; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User [name=" + name + ", age=" + age + ", address=" + address + ", sex=" + sex + "]"; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public User() { super(); } public User(String name, int age, String address, char sex) { super(); this.name = name; this.age = age; this.address = address; this.sex = sex; } }
示例2:通过反射方式获取User类的基本信息
package pm; public class 获得Class实例 { /** * @param args * @throws ClassNotFoundException */ public static void main(String[] args) throws ClassNotFoundException { // TODO Auto-generated method stub //第一种获取Class实例这种方式需要在编译期就知道类或接口的名称 Class c1 = User.class; System.out.println(c1.getName()); System.out.println(c1.getSimpleName()); Package package1 = c1.getPackage(); System.out.println(package1.getName()); Class[] faces = c1.getInterfaces(); if(faces.length==0){ System.out.println("该类没有实现接口"); }else{ for(Class c : faces){ System.out.println(c.getName()); } } Class superClass = c1.getSuperclass(); System.out.println(superClass.getName()); //第二种获取Class实例 //getClass()方法是Object类的方法,所有类和接口都可以调用该方法 User user = new User(); Class c2 = user.getClass(); System.out.println(c2.getName()); //第三种获取Class实例 /*若编码时无法确定类型,需要程序在运行中根据情况灵活加载, 可以使用Class类的forName()方法。该方法时静态方法,需传入字符串参数, 该字符串参数的值是某个类的完全限定类名,即包含包名的完整类名。*/ Class c3 = Class.forName("pm.User"); System.out.println(c3.getName()); } }
获得对应类型所包含的构造方法
示例3:通过反射获取User类的构造方法信息
public static void main(String[] args) throws NoSuchMethodException, SecurityException { // TODO Auto-generated method stub Class<User> c1 = User.class; /*//获取单个参数构造方法 Constructor c = c1.getDeclaredConstructor(String.class,int.class,String.class,char.class); //获取访问修饰符 int mod = c.getModifiers(); String modStr = Modifier.toString(mod); //获得方法名 String name = c.getName(); //获得参数列表 Class[] params=c.getParameterTypes(); String paramsStr=""; for (int i = 0; i < params.length; i++) { if(i!=0){ paramsStr+=","; } paramsStr+=params[i].getSimpleName()+" p"+i; } String str=modStr+" "+name+"("+paramsStr+"){\n\n}"; System.out.println(str); */ //获取全部构造方法 Constructor[] constructors = c1.getDeclaredConstructors(); for (Constructor c : constructors) { //获取访问修饰符 int mod = c.getModifiers(); String modStr = Modifier.toString(mod); //获得方法名 String name = c.getName(); //获得参数列表 Class[] params=c.getParameterTypes(); String paramsStr=""; for (int i = 0; i < params.length; i++) { if(i!=0){ paramsStr+=","; } paramsStr+=params[i].getSimpleName()+" p"+i; } String str=modStr+" "+name+"("+paramsStr+"){\n\n}"; System.out.println(str); } }
获取对应类型所包含的属性
示例4:通过反射方式获取User类的属性信息
public static void main(String[] args) throws NoSuchFieldException, SecurityException { // TODO Auto-generated method stub //获得user实例对象 Class<User> c1 = User.class; //获得单个name属性 Field nameField = c1.getDeclaredField("name"); //获得属性访问修饰符 int mod = nameField.getModifiers(); String m = Modifier.toString(mod); //获得属性类型 Class type = nameField.getType(); //获得属性名称 String name = nameField.getName(); String str = m+" "+type.getSimpleName()+" "+name; System.out.println(str); //获取全部User属性值 Field[] fields = c1.getDeclaredFields(); for (Field field : fields) { //获得属性访问修饰符 int mod1 = field.getModifiers(); String m1 = Modifier.toString(mod1); //获得属性类型 Class type1 = field.getType(); //获得属性名称 String name1 = field.getName(); String str1 = m1+" "+type1.getSimpleName()+" "+name1+";"; System.out.println(str1); } }
访问对应类型所包含的方法
示例5:通过反射获取User类中方法信息
public class 获得方法 { public static void main(String[] args) throws NoSuchMethodException, SecurityException { Class<User> c1 = User.class;//获取实例对象 /*//获得setName方法 Method method = c1.getDeclaredMethod("setName", String.class); //获得方法访问修饰符 int mod = method.getModifiers(); String modStr = Modifier.toString(mod); //获得返回值类型 Class returntype = method.getReturnType(); //获得方法名 String name = method.getName(); //获得方法参数列表 Class[] params = method.getParameterTypes(); String paramsStr = ""; for (int i = 0; i < params.length; i++) { if(i!=0){ paramsStr+=","; } paramsStr+=params[i].getSimpleName()+" p"+i; } System.out.println(modStr+" "+returntype.getSimpleName()+" "+name+"("+paramsStr+")"+"{\n\n}");*/ //获取全部方法 Method[] methods = c1.getDeclaredMethods(); for (Method method : methods) { //获得方法访问修饰符 int mod = method.getModifiers(); String modStr = Modifier.toString(mod); //获得返回值类型 Class returntype = method.getReturnType(); //获得方法名 String name = method.getName(); //获得方法参数列表 Class[] params = method.getParameterTypes(); String paramsStr = ""; for (int i = 0; i < params.length; i++) { if(i!=0){ paramsStr+=","; } paramsStr+=params[i].getSimpleName()+" p"+i; } System.out.println(modStr+" "+returntype.getSimpleName()+" "+name+"("+paramsStr+")"+"{\n\n}"); } } }
总结:
Class实例可以获得相关类型中的构造方法,属性,方法等成员信息。其中,构造方法由Constructor类型表示,属性由Field类型表示,方法由Method类型表示。Constructor,Field,Method这3个类都都定义了在java.lang.reflect包下,并且都实现了java.lang.reflect.Member接口。程序可以通过Constructor实例来调用相应的构造方法创建相关类型的实例,通过Field实例直接访问并修改相关类型实例的属性值,通过Method实例来执行相关类型实例的方法。
通过反射来创建类型实例的方法有两种:
示例6:创建类型示例方式:
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub Class<User> c1 = User.class; //实例化对象 User user1 = c1.newInstance(); user1.setName("张三"); System.out.println(user1); //利用构造方法创建对象 //如果创建java实例时,需要使用指定构造方法,则可以使用该方式。 Constructor c=c1.getDeclaredConstructor(String.class,int.class,String.class,char.class); User user2 = (User)c.newInstance("李四",20,"beijing",'男'); System.out.println(user2); }
访问类的属性
调用类的方法
Method类中包含一个invoke()方法,通过invoke()方法,Method实例可以调用Java类的实例方法和静态方法,invoke()方法定义 如下:
若Method实例表示的是一个静态方法,则obj可以为null。
若该方法没有参数可以不填,或者填null
示例7:通过反射访问User类的属性,调用方法:
public class 操作方法和属性 { public static void main(String[] args) throws NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { Class c1 = User.class; //获得实例化对象 Object u = c1.newInstance(); //操作对象属性 //获得属性 Field f= c1.getDeclaredField("name");//获取name属性 f.setAccessible(true);//绕开访问权限 f.set(u, "Tom"); System.out.println(u); //操作对象方法 //获得方法 Method method = c1.getDeclaredMethod("setName",String.class); method.invoke(u,"tom"); Method methodget = c1.getDeclaredMethod("getName"); String nameString=(String)methodget.invoke(u); System.out.println(nameString); } }
注意:
受访问修饰符的限制,使用反射方式访问超出访问范围的构造方法,属性,方法时,会引发异常。若要禁止java语言访问检强行访问,需要设置相关实例即可访问,如下:
f.setAccessible(true);//绕开访问权限
扩展:
反射技术优点:
反射技术缺点:
总结