首页 / JAVA / 学习Java反射机制
学习Java反射机制
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了学习Java反射机制,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8865字,纯文字阅读大概需要13分钟。
内容图文
定义:
在Java的运行状态中对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够条用它的任意方法和属性;这种动态调用获取信息以及动态调用对象方法的功能称为Java的反射机制
一、Class类
在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。保存这些信息的类被称为Class。
1、获得Class类。
①、利用Object类中getClass()方法获得一个Class类型的实例
StudentBean studentBean = new StudentBean("小名",18,"男");
Class cl= studentBean.getClass();
②、通过forName方法获得对应的Class对象
String className = "java.lang.Double";
try {
Class cl = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
这里的类名是包含包名的,并且className必须是类名或者接口名,否则会报ClassNotFoundException异常。
③、使用T.class获得Class对象。T为任意Java类型
Class doubleClass = double.class;
Class studentClass = StudentBean.class;
2、判断Class对象相等
虚拟机为每一个类型管理一个Class对象,因此,可以利用 == 运算符实现两个类对象比较操作。(注:判断对象所属的类是否相等,不能判断对象是否相等)
使用Class的newInstance()方法创建一个空参的类的实例。
try {
Object o = Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
newInstance()方法默认会调用对象的空参的构造方法创建一个对象。如果对象没有空参的构造方法,会抛出IllegalAccessException 异常
二、利用反射检查类的结构
Java反射包java.lang.reflect
有三个非常重要的类: Field、Method、Constructor,分别描述类的 域(全局变量、常量)、方法、构造器。三个类中有共同的方法getName()
返回项目名称,getModifiers()
返回修饰符。
Field,Method和Constructor都可以通过Class的get方法获得。
Class对象提供了提供了两种获得方式。使用 getFields()
,getMethods()
,getConstructors()
返回的是public 类型的 域、方法、构造器。而使用getDeclaredFields()
,getDeclaredMethods()
,getDeclaredConstructors()
获得的是全部的 域、方法和构造器。
下面我们通过一个例子来说明:
public class StudentBean extends BaseBean {
private String name;
private int age;
private String sex;
public int id;
public StudentBean(){
}
public StudentBean(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
String getString() {
return "Student";
}
}
上面创建了一个StudentBean 的类型,继承BaseBean 。有四个参数,两个构造方法,sex的get、set方法和继承父类的getString()。方法
下面我们通过反射来解析StudentBean 这个类。
1、解析Filed(Filed域,我理解是全局变量,常量)
/**
* 打印Fields
*/
private static void printFields(Class cl) {
//获得全部的域(全局变量、常量)
Field[] fields = cl.getDeclaredFields();
for (Field field : fields) {
//获得域的类型(变量的类型)
Class type = field.getType();
//获得域名(变量名)
String name = field.getName();
System.out.print(" ");
//获得域的修饰符(变量的修饰符)
String modifiers = Modifier.toString(field.getModifiers());
System.out.print("域modifiers = " + modifiers);
System.out.print(" 域field type(类型) = " + type.getSimpleName() + " name: " + name + "\n");
}
}
getModifiers()
获得的是一个int类型的修饰值,不方便我们理解,可以使用Modifier.toString(field.getModifiers())
把int类型的修饰符转换成String类型,方便查看。
2、解析Method(方法)
/**
* 打印方法
* @param cl
*/
private static void printMethods(Class cl) {
//获得所有的方法
Method[] methods = cl.getDeclaredMethods();
//遍历方法
for (Method method : methods) {
//获得方法的返回类型
Class returnType = method.getReturnType();
//方法名
String name = method.getName();
System.out.print(" ");
//获得方法的修饰符
String modifiers = Modifier.toString(method.getModifiers());
System.out.print("方法modifiers = " + modifiers);
System.out.print(" 方法返回类型:" + returnType.getSimpleName() + " 方法名name:" + name + "(");
//获得方法参数的集合
Class[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
if (i > 0) {
System.out.print(", ");
}
//获得参数类型
System.out.print("参数类型:" + paramTypes[i].getSimpleName());
}
System.out.print(");\n");
}
}
3、解析构造器(构造方法)
/**
* 打印构造器
* @param cl
*/
private static void printConstructors(Class cl) {
//获得所有的构造器对象
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors) {
//构造方法名
String name = c.getName();
System.out.print(" ");
//构造方法的修饰符
String modifier = Modifier.toString(c.getModifiers());
if (modifier.length() > 0) {
System.out.print("构造器modifier = " + modifier);
}
System.out.print(" name = "+name + " (");
//构造方法参数集合
Class[] paramTypes = c.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
if (i > 0) {
System.out.print(", ");
}
//构造方法参数类型
System.out.print(" 参数类型:"+paramTypes[i].getSimpleName());
}
System.out.print(");\n");
}
}
4、主程序代码
public static void main(String[] args) {
System.out.println("请输入类名");
Scanner scanner = new Scanner(System.in);
String className = scanner.next();
try {
System.out.println(className);
Class cl = Class.forName(className);
//获得父类的Class
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0)
System.out.print("修饰符: " + modifiers);
if (supercl != null && supercl != Object.class) {
System.out.print(" extends " + supercl.getSimpleName());
}
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
getSuperclass()
获得超类的Class对象
5、运行程序,输入完整StudentBean类名,运行结果如下:
请输入类名
com.zy.test.reflect.StudentBean
com.zy.test.reflect.StudentBean
修饰符: public extends BaseBean
{
构造器modifier = public name = com.zy.test.reflect.StudentBean ();
构造器modifier = public name = com.zy.test.reflect.StudentBean ( 参数类型:String, 参数类型:int, 参数类型:String);
方法modifiers = 方法返回类型:String 方法名name:getString();
方法modifiers = public 方法返回类型:String 方法名name:getSex();
方法modifiers = public 方法返回类型:void 方法名name:setSex(参数类型:String);
域modifiers = private 域field type(类型) = String name: name
域modifiers = private 域field type(类型) = int name: age
域modifiers = private 域field type(类型) = String name: sex
域modifiers = public 域field type(类型) = int name: id
}
从输入结果可以看到,我们已经把StudentBean中所有的属性都解析出来了。
三、运行时使用反射解析对象
在项目运行时,我们可以通过反射获得类中当前域Field的值
try {
//获得指定域
Field field = studentClass.getDeclaredField("age");
//获得指定域的当前值
int age = (int) field.get(studentBean);
//为指定域设置新值
field.set(studentBean,20);
//输出结果。 getAge()后加的get方法
System.out.println("age:" + age + " getAge:" + studentBean.getAge());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
运行发现会报IllegalAccessException异常。
因为age修饰符是private,是一个私有域。只有getAge()方法才能访问到。Java安全机制只允许查看任意对象有哪些域,不允许读它的值。
Java反射机制的默认行为受限于Java的访问控制权限。可以调用Field、Method、Constructor的setAccessible()
获得访问权限
try {
//获得指定域
Field field = studentClass.getDeclaredField("age");
//设置权限
field.setAccessible(true);
//获得指定域的当前值
int age = (int) field.get(studentBean);
//为指定域重新赋值
field.set(studentBean,20);
System.out.println("age:" + age + " getAge:" + studentBean.getAge());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
输出结果:
age:18 getAge:20
可以看到,我们通过反射获得指定域的Field对象,申请权限后,我们就可以不使用get,set方法直接对域进行取值和赋值。
可以使用AccessibleObject.setAccessible();
对一组Field、Method、Constructor 对象设置权限
//为一组对象设置权限
AccessibleObject.setAccessible(studentClass.getDeclaredFields(),true);
四:调用任意方法
我们可以通过Field的get方法查看对象域的过程,与之相似在Method类中有个invoke()
方法(英文:调用的意思),可以调用包装在Method对象中的方法。
Object invoke(Object obj, Object... args)
有两个参数,第一个是隐式参数第二个是显示参数。静态方法第一个参数传null。
下面通过Method获得getSex()方法的返回值
try {
//通过指定方法名,获得Method对象
Method method = studentClass.getDeclaredMethod("getSex");
//获得权限
method.setAccessible(true);
//获得方法的返回值
String sex = (String) method.invoke(studentBean);
System.out.println("sex:" + sex);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
输出:
sex:女
因为 invoke() 方法返回值必须是Object类型的,这就导致必须就行多次类型转换,这样会编译器错过检查代码的机会。而且使用反射获得方法的Method对象比直接调用get方法效率要低。
以上就是关于Java反射的一些基本知识点了。使用时还是要结合项目和需求来看。
参考:《Java核心技术 卷一》
内容总结
以上是互联网集市为您收集整理的学习Java反射机制全部内容,希望文章能够帮你解决学习Java反射机制所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。