反射实际开发使用
2023-07-14 18:24:15 76浏览
反射实际开发使用,01.获得Class对象方式获得Class对象三种方式每个类被加载之后,系统就会为该类生成一个对应的Class对象。通过该Class对象就可以访问到JVM中的这个类。在Java程序中获得Class对象通常有如下三种方式:1.使用Class类的forName(StringclazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定名(必须添加完整包名)。2.调用某个类的
01.获得Class对象方式
- 获得Class对象三种方式
- 每个类被加载之后,系统就会为该类生成一个对应的Class对象。通过该Class对象就可以访问到JVM中的这个类。
- 在Java程序中获得Class对象通常有如下三种方式:
- 1.使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定名(必须添加完整包名)。
- 2.调用某个类的class属性来获取该类对应的Class对象。
- 3.调用某个对象的getClass()方法。该方法是java.lang.Object类中的一个方法。
//第一种方式 通过Class类的静态方法——forName()来实现
class1 = Class.forName("com.lvr.reflection.Person");
//第二种方式 通过类的class属性
class1 = Person.class;
//第三种方式 通过对象getClass方法
Person person = new Person();
Class<?> class1 = person.getClass();
- 第一种:Class.forName()
- 1.通过JVM查找并加载指定的类(上面的代码指定加载了com.fanshe包中的Person类)
- 2.调用newInstance()方法让加载完的类在内存中创建对应的实例,并把实例赋值给p
- 注意:如果找不到时,它会抛出 ClassNotFoundException 这个异常,这个很好理解,因为如果查找的类没有在 JVM 中加载的话,自然要告诉开发者。
Class<?> cls=Class.forName("com.yc.Person"); //forName(包名.类名)
Person p= (Person) cls.newInstance();
- 第二种:类.class
- 1.获取指定类型的Class对象,这里是Person
- 2.调用newInstance()方法在让Class对象在内存中创建对应的实例,并且让p引用实例的内存地址
Class<?> cls = Person.class;
Person p=(Person)cls.newInstance();
- 第三种:对象.getClass()
- 1.在内存中新建一个Person的实例,对象p对这个内存地址进行引用
- 2.对象p调用getClass()返回对象p所对应的Class对
- 3.调用newInstance()方法让Class对象在内存中创建对应的实例,并且让p2引用实例的内存地址
Person p = new Person();
Class<?> cls= p.getClass();
Person p2=(Person)cls.newInstance();
- 获取Class父类对象
- 先看一下代码
//在AppBarLayout类中
public static class Behavior extends AppBarLayout.BaseBehavior<AppBarLayout>
//BaseBehavior的父类
protected static class BaseBehavior<T extends AppBarLayout> extends HeaderBehavior<T>
- 反射获取父类
Class<?> superclass = AppBarLayout.Behavior.class.getSuperclass();
- 反射获取父类的父类
Class<?> superclass = AppBarLayout.Behavior.class.getSuperclass();
headerBehaviorType = superclass.getSuperclass();
- 注意事项
- 生成类的实例对象
- 1.使用Class对象的newInstance()方法来创建该Class对象对应类的实例。这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。
- 2.先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。
//第一种方式 Class对象调用newInstance()方法生成
Object obj = class1.newInstance();
//第二种方式 对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成
Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数
obj = constructor.newInstance("hello");
- new对象和反射得到对象的区别
- 在使用反射的时候,必须确保这个类已经加载并已经连接了。使用new的时候,这个类可以没有被加载,也可以已经被加载。
- new关键字可以调用任何public构造方法,而反射只能调用无参构造方法。
- new关键字是强类型的,效率相对较高。 反射是弱类型的,效率低。
- 反射提供了一种更加灵活的方式创建对象,得到对象的信息。如Spring 中AOP等的使用,动态代理的使用,都是基于反射的。解耦。
02.反射调用类的方法
- 调用类的方法
- 1.通过Class对象的getMethods\(\)方法或者getMethod\(\)方法获得指定方法,返回Method数组或对象。
- 2.调用Method对象中的
Object invoke(Object obj, Object... args)
方法。第一个参数对应调用该方法的实例对象,第二个参数对应该方法的参数。
private void method8() {
try {
Class<?> cl = Class.forName("com.ycbjie.other.ui.activity.Student");
// 生成新的对象:用newInstance()方法
Student obj = (Student) cl.newInstance();
String student1 = obj.getStudent();
LogUtils.i("反射调用类的方法1:"+student1);
//首先需要获得与该方法对应的Method对象
Method method = cl.getDeclaredMethod("setAge", int.class);
//设置权限
method.setAccessible(true);
//调用指定的函数并传递参数
method.invoke(obj, 28);
String student2 = obj.getStudent();
LogUtils.i("反射调用类的方法2:"+student2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
//打印值
2019-06-11 18:24:40.146 23666-23666/com.ycbjie.other I/yc: 反射调用类的方法1:yc---26
2019-06-11 18:24:40.146 23666-23666/com.ycbjie.other I/yc: 反射调用类的方法2:yc---28
- 获取方法的参数
- 当通过Method的invoke()方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private方法,则可以先调用Method对象的如下方法。
- setAccessible(boolean flag):将Method对象的acessible设置为指定的布尔值。值为true,指示该Method在使用时应该取消Java语言的访问权限检查;值为false,则指示该Method在使用时要实施Java语言的访问权限检查。
03.反射访问成员变量值
- 反射访问成员变量值
- 1.通过Class对象的getFields()方法或者getField()方法获得指定方法,返回Field数组或对象。
- 2.Field提供了两组方法来读取或设置成员变量的值:
- getXXX(Object obj):获取obj对象的该成员变量的值。此处的XXX对应8种基本类型。如果该成员变量的类型是引用类型,则取消get后面的XXX。
- setXXX(Object obj,XXX val):将obj对象的该成员变量设置成val值。
private void method9() {
try {
Class<?> cl = Class.forName("com.ycbjie.other.ui.activity.Student");
// 生成新的对象:用newInstance()方法
Student obj = (Student) cl.newInstance();
int age = obj.getAge();
LogUtils.i("反射访问成员变量值1:"+age);
//获取age成员变量
//Field field = cl.getField("age");
Field field = cl.getDeclaredField("age");
//设置权限
field.setAccessible(true);
//将obj对象的age的值设置为10
field.setInt(obj, 10);
//获取obj对象的age的值
int anInt = field.getInt(obj);
LogUtils.i("反射访问成员变量值2:"+anInt);
//反射修改私有变量
// 获取声明的 code 字段,这里要注意 getField 和 getDeclaredField 的区别
Field gradeField = cl.getDeclaredField("name");
// 如果是 private 或者 package 权限的,一定要赋予其访问权限
gradeField.setAccessible(true);
// 修改 student 对象中的 Grade 字段值
gradeField.set(obj, "逗比");
Object o = gradeField.get(obj);
LogUtils.i("反射访问成员变量值3:"+o.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
2019-06-11 19:06:59.380 12313-12313/com.ycbjie.other I/yc: 反射访问成员变量值1:26
2019-06-11 19:06:59.380 12313-12313/com.ycbjie.other I/yc: 反射访问成员变量值2:10
2019-06-11 19:06:59.380 12313-12313/com.ycbjie.other I/yc: 反射访问成员变量值3:逗比
04.调用共有和私有区别
- 修改Student类,将get方法都指定为公有的,将set方法指定为私有的
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
System.out.println("调用了getName方法,Name:" + name);
return name;
}
public int getAge() {
System.out.println("调用了getAge方法,Age:" + age);
return age;
}
private void setName(String name) {
this.name = name;
System.out.println("调用了setName方法,name:" + name);
}
private void setAge(int age) {
this.age = age;
System.out.println("调用了setAge方法,age:" + age);
}
}
- 反射调用公有方法
- java.lang.reflect.Method 实例是方法的代表对象,可以使用 invoke() 方法来动态调用指定的方法
- 首先来调用公有方法
public class Main {
public static void main(String[] args) throws Exception {
Class cl = Class.forName("com.czy.demo.Student");
// 指定构造函数
Constructor constructor = cl.getConstructor(String.class, Integer.TYPE);
// 根据指定的构造函数来获取对象
Object object = constructor.newInstance("杨充逗比", 25);
// 指定方法名称来获取对应的公开的Method实例
Method getName = cl.getMethod("getName");
// 调用对象object的方法
getName.invoke(object);
// 指定方法名称来获取对应的公开的Method实例
Method getAge = cl.getMethod("getAge");
// 调用对象object的方法
getAge.invoke(object);
}
}
- 输出结果如下所示,可以知道Student对象的两个get方法成功被调用了。
调用了getName方法,Name:杨充逗比
调用了getAge方法,Age:25
- 反射调用私有方法
- 一般情况下,类的私有方法只有在其内部才可以被调用,通过反射我们可以来突破这一限制
- 受保护或私有方法的调用步骤略有不同
public class Main {
public static void main(String[] args) throws Exception {
Class cl = Class.forName("com.czy.demo.Student");
// 指定构造函数
Constructor constructor = cl.getConstructor(String.class, Integer.TYPE);
// 根据指定的构造函数来获取对象
Object object = constructor.newInstance("杨充逗比", 25);
// 指定方法名称来获取对应的私有的Method实例
Method setName = cl.getDeclaredMethod("setName", String.class);
setName.setAccessible(true);
setName.invoke(object, "潇湘剑雨");
// 指定方法名称来获取对应的私有的Method实例
Method setAge = cl.getDeclaredMethod("setAge", Integer.TYPE);
setAge.setAccessible(true);
setAge.invoke(object, 100);
}
}
- 输出结果如下所示,可以看到私有方法一样在外部被调用了
调用了setName方法,name:潇湘剑雨
调用了setAge方法,age:100
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
暂无评论,快来写一下吧
展开评论