什么是反射

通过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);//绕开访问权限
扩展:
- 当直接输出对象时,系统会自动调用对象的toString()方法
- 系统默认的toString()方法打印内容为:类全名@16进制哈希值
- toString()方法属于Object类,所有类都能继承该方法
反射技术优点:

反射技术缺点:

总结

本文介绍了Java反射机制,它是构建框架的基础,允许程序在运行时检查类的结构。通过反射,可以获取类的信息,包括构造方法、属性和方法,并能创建实例。详细展示了如何获取Class实例、获取构造方法、属性和方法的信息,以及如何通过反射创建实例和访问属性及方法。虽然反射带来强大的灵活性,但也需要注意其可能导致的安全性和性能问题。
188

被折叠的 条评论
为什么被折叠?



