当前位置:   article > 正文

Android开发你需要掌握的java反射机制原理_android 反射

android 反射

1. 背景

        在andorid开发中,经常遇见在某些工具类中没有Context上下文对象时,一些系统服务的代理对象无法创建出来,举个例子:比如在源码(framework/base/graphics/java/android/graphics)路径下的Canvas.java   Bitmap.java  Picture.java  Paint.java 类就没有上下文对象。 当时有需要调用AMS的API获取当前界面activity的名称作为判断条件去修改这些工具类中的参数,但是这几个工具类中又没有上下文对象,怎么办呢?

        一般情况下,获取顶层activity名称的方法如下:

  1. //1. 获取 AMS 的代理对象 ActivityManager
  2. ActivityManager am = (ActivityManager) context
  3. .getSystemService(Context.ACTIVITY_SERVICE);
  4. //2. 调用相关API 获取顶层activity的名称
  5. String activityName = am.getRunningTasks(1).get(0).topActivity.getClassName();

上述代码是在有上下文对象context的情况下,直接获取。 

        而在上面提及到的 Picture.java等类中, 根本就没有上下文,怎么搞? 这个时候我们通过java反射来实现,接下来,本篇文章就来讲解一下java反射机制,并利用反射实现获取顶层activity。

2. java反射

2.1 什么是反射?

        反射(Reflection)是程序的自我分析能力,通过反射可以确定类中有哪些方法、有哪些构造方法以及有哪些成员变量,在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

2.2 什么情况下要用反射?

      首先java是面向对象编程的,万物皆对象, 当我们拿到这个类对象后,就可以访问类中的成员变量,调用类中的方法了,但是呢?对于private变量和方法,它的访问权限作用域只在本类中,在外部类或者继承类中,就算你创建了该类对象,也是无法直接调用的。来看下这个例子:

      普通的一个User 类

  1. public class User {
  2. private String name = "墨子"; //私有变量
  3. public int age = 18;
  4. //私有方法
  5. private int test() {
  6. System.out.println("私有成员方法");
  7. return 1;
  8. }
  9. protected String test2() {
  10. System.out.println("protected成员方法");
  11. return "protected";
  12. }
  13. public static void main(String[] args) {
  14. //如果在本类中创建了对象,则所有的方法和变量都可以直接访问
  15. User user = new User();
  16. System.out.println(user.name); //访问私有变量
  17. System.out.println(user.test());//调用私有方法
  18. }
  19. }
  20. ____________________________________________________________
  21. 打印结果如下:
  22. 墨子
  23. 私有成员方法
  24. 1

  如果在外部内中创建该类对象,还能直接访问私有方法和变量吗? 测试一下:

  1. public class PrivateTest {
  2. public static void main(String[] args) {
  3. User user = new User();
  4. //外部内中创建的对象直接私有变量 报错
  5. //System.out.println(user.name);
  6. //外部内中创建的对象,直接调用private方法,也报错
  7. //System.out.println(user.test());
  8. //但是可以直接访问 protected public的方法和变量
  9. System.out.println(user.age);
  10. System.out.println(user.test2());
  11. }
  12. }
  13. ——————————————————————————————————————————————————————————
  14.  报错的两行代码注释掉了,可以把代码复制过去验证一下。

        当然了,在andorid开发中,还有其他场景会使用到反射,  很多类或方法中经常加上了“@hide”注释标记,这些API是不允许在第三方应用直接调用的  比如:SystemProperties类在android.os下,但这个类是隐藏的,没有系统权限(android:sharedUserId="android.uid.system")的app无法直接使用,只能通过java反射来调用。

        还有 Andorid中动态代理模式  , Android插件化(Hook)这些都会用到反射,这里不一一说明,因为每个技术点都需要去深挖才能理解透彻。

2.3 反射的优缺点

优点: 

1. 可以调用私有方法、变量

2. 可以调用隐藏api接口

3. 能够运行时动态获取类的实例,提高代码灵活性 

缺点: 

1. 安全问题:反射可以获取到类对应的所有方法和属性,如果存在外部调用的情况,可能出现超出预期的调用,从而导致安全风险, 而且也破坏java面向对象编程的封装这一特性。

2. 性能问题:无论是通过字符串获取Class、Method还是Field,都需要JVM的动态链接机制动态的进行解析和匹配,势必造成性能开销。每一次的反射调用都会造成Java安全机制进行额外的安全性验证,造成性能开销。反射代码使得许多JVM的运行时优化无法进行。

3. Java反射机制API

Java反射机制API主要是 java.lang.Class类 和 java.lang.reflect包。

说明
java.lang.Class 代表整个字节码。代表一个类型,代表整个类。
java.lang.reflect.Constructor 代表字节码中的构造方法字节码。代表类中的构造方法。
java.lang.reflect.Method 代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Field 代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)

3.1 获取Class对象

方式说明

对象.getClass();

类名.class

Class.forName(“类的全路径”);

推荐此种写法
  1. package com.example.javademo.reflect;
  2. /**
  3. * author : me
  4. * date : 22-10-21下午4:39
  5. * desc : 获取Class对象的3种方式
  6. * version: 1.0
  7. */
  8. public class ClassObject {
  9. public static void main(String[] args) throws ClassNotFoundException {
  10. //第一种: 对象.getClass();
  11. People people = new People();
  12. Class clazz1 = people.getClass();
  13. System.out.println(clazz1);
  14. //第二种: 类名.class
  15. Class clazz2 = People.class;
  16. System.out.println(clazz2);
  17. //第三种: Class.forName(包名.类名)
  18. Class clazz3 = Class.forName("com.example.javademo.reflect.People");
  19. System.out.println(clazz3);
  20. }
  21. }

第一种:直接new出对象,然后通过对象获取class对象, 但是你想想类对象都可以直接new出来,在外部内中除了private 变量和方法不能直接访问外,都可以直接调用,我还反射干嘛?写那么多代码岂不是为难自己,没事找事,不推荐!

第二种:通过类名.class 获取字节码class对象,需要导入包,依赖性比较强,不然会编译报错

第三种:Class的静态方法,安全性强, 参数为:类包名+类名

运行结果如下:

  1. class com.example.javademo.reflect.People
  2. class com.example.javademo.reflect.People
  3. class com.example.javademo.reflect.People

 三个都是同一个Class对象, 在运行期间,一个类,只有一个Class对象产生

        

3.2 反射调用类构造方法

方法名解释说明

public Constructor[] getConstructors()
所有"公有的"构造方法
public Constructor getConstructor(Class... parameterTypes)获取指定参数的共有构造方法
public Constructor[] getDeclaredConstructors()获取所有的构造方法(包括私有、受保护、默认、公有)
public Constructor getDeclaredConstructor(Class... parameterTypes):获取"指定参数的构造方法"可以是私有的,或受保护、默认、公有;
 
newInstance(Object... initargs)

使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

它的返回值是T类型,所以newInstance是创建了一个构造方法的声名类的新实例对象,相当于People  p = new People();

我们看下Demo

  1. package com.example.javademo.reflect;
  2. /**
  3. * author : me
  4. * date : 22-10-20??10:08
  5. * desc : 供反射调用的构造方法类
  6. * version: 1.0
  7. */
  8. public class People {
  9. private String name;
  10. private int age;
  11. public String sex;
  12. private long height;
  13. public People() {
  14. System.out.println("调用了public无参构造方法 执行完毕");
  15. }
  16. public People(String name) {
  17. this.name = name;
  18. System.out.println("调用public有参构造方法 String:name = " + name);
  19. }
  20. private People(int age, String sex) {
  21. this.age = age;
  22. this.sex = sex;
  23. System.out.println("调用private有参数构造方法 age :"+ age + " sex:" + sex);
  24. }
  25. People(long height) {
  26. this.height = height;
  27. System.out.println("调用default有参数构造方法 height :"+ height );
  28. }
  29. }

反射测试类:

  1. package com.example.javademo.reflect;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.InvocationTargetException;
  4. /**
  5. * author : me
  6. * date : 22-10-20上午10:12
  7. * desc : 反射调用构造方法的实现类
  8. * version: 1.0
  9. */
  10. public class ReflectPeople {
  11. public static void main(String[] args) {
  12. try {
  13. //获取People class对象
  14. Class peopleclass = Class.forName("com.example.javademo.reflect.People");
  15. System.out.println("**********************获取反射后类对象*********************************");
  16. System.out.println("反射类对象: " + peopleclass);
  17. //获取所有构造方法
  18. System.out.println("**********************打印所有构造方法*********************************");
  19. Constructor[] conArray = peopleclass.getDeclaredConstructors();
  20. for(Constructor c : conArray){
  21. System.out.println(c);
  22. }
  23. System.out.println("**************第一种:通过反射调用默认无参构造方法并创建people类实例对象***************************");
  24. //返回一个 Constructor对象, 该对象反映Constructor对象表示的类的指定的公共类函数。
  25. Object constructorPublic = peopleclass.getConstructor();
  26. System.out.println("无参构造方法的对象:" + peopleclass.getConstructor());
  27. //使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
  28. //newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以
  29. Object peopleobject1 = ((Constructor) constructorPublic).newInstance();
  30. System.out.println("创建people1对象新实例: " + (People)peopleobject1);
  31. System.out.println("**************第二种:通过反射调用public有参构造方法并创建People类实例对象***************************");
  32. Object peopleobject2 = peopleclass.getConstructor(String.class).newInstance("甲方");
  33. System.out.println("创建people2对象新实例: " + (People)peopleobject2);
  34. System.out.println("**************第三种:通过反射调用private有参构造方法并创建People类实例对象***************************");
  35. //注意,如果访问protect private相关的构造方法,需要用getDeclaredConstructor这个API
  36. //返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定类函数。
  37. Constructor constructorPrivate = peopleclass.getDeclaredConstructor(int.class, String.class);
  38. //访问私有构造方法,这句话一定要写,不然就会报错java.lang.IllegalAccessException
  39. constructorPrivate.setAccessible(true);
  40. System.out.println("有参private私有构造方法的对象:" + peopleclass.getDeclaredConstructor(int.class, String.class));
  41. Object peopleobject3 = constructorPrivate.newInstance(20, "men");
  42. System.out.println("创建people3对象新实例: " + peopleobject3);
  43. System.out.println("**************第四种:通过反射调用default有参构造方法并创建People类实例对象***************************");
  44. Constructor constructorDefault = peopleclass.getDeclaredConstructor(long.class);
  45. //访问public protect default构造方法,这句话可以不用写
  46. //constructorDefault.setAccessible(true);
  47. System.out.println("有参default构造方法的对象:" + peopleclass.getDeclaredConstructor(long.class));
  48. Object peopleobject4 = constructorDefault.newInstance(185);
  49. System.out.println("创建people4对象新实例: " + peopleobject4);
  50. } catch (ClassNotFoundException | NoSuchMethodException e) {
  51. e.printStackTrace();
  52. } catch (IllegalAccessException e) {
  53. e.printStackTrace();
  54. } catch (InstantiationException e) {
  55. e.printStackTrace();
  56. } catch (InvocationTargetException e) {
  57. e.printStackTrace();
  58. }
  59. }
  60. }

打印log:

  1. **********************获取反射后类对象*********************************
  2. 反射类对象: class com.example.javademo.reflect.People
  3. **********************打印所有构造方法*********************************
  4. com.example.javademo.reflect.People(long)
  5. private com.example.javademo.reflect.People(int,java.lang.String)
  6. public com.example.javademo.reflect.People(java.lang.String)
  7. public com.example.javademo.reflect.People()
  8. **************第一种:通过反射调用默认无参构造方法并创建people类实例对象***************************
  9. 无参构造方法的对象:public com.example.javademo.reflect.People()
  10. 调用了public无参构造方法 执行完毕
  11. 创建people1对象新实例: com.example.javademo.reflect.People@2a139a55
  12. **************第二种:通过反射调用public有参构造方法并创建People类实例对象***************************
  13. 调用public有参构造方法 String:name = 甲方
  14. 创建people2对象新实例: com.example.javademo.reflect.People@15db9742
  15. **************第三种:通过反射调用private有参构造方法并创建People类实例对象***************************
  16. 有参private私有构造方法的对象:private com.example.javademo.reflect.People(int,java.lang.String)
  17. 调用private有参数构造方法 age :20 sex:men
  18. 创建people3对象新实例: com.example.javademo.reflect.People@6d06d69c
  19. **************第四种:通过反射调用default有参构造方法并创建People类实例对象***************************
  20. 有参default构造方法的对象:com.example.javademo.reflect.People(long)
  21. 调用default有参数构造方法 height :185
  22. 创建people4对象新实例: com.example.javademo.reflect.People@7852e922

通过上面的例子,总结:

1. 反射Class对象, 反射Constructor对象, 反射类实例对象 是3个不同的对象,如果刚开始不熟悉API,建议分开创建;

2. getConstructor() 和 getDeclaredConstructor(类<?>... parameterTypes)方法的区别  前者是获取public类型构造方法的Constructor对象,后者是获取全类型(public protect default private)类型的构造方法的Constructor对象

3. 反射私有构造方法的时候,一定要setAccessible(true),不然会报错java.lang.IllegalAccessException错误 * true表示:禁止java语言使用时安全检查,暴力访问

4.  调用Constructor类中newInstance()这个方法时,注意newInstance没有带参数(不带参数可以不写,或写成null 都可以行) 此方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以,否则会抛出java.lang.InstantiationException异常。

3.3 反射调用类中方法

方法名说明
public Method[] getMethods()获取所有"公有方法";(包含了父类的方法也包含Object类)
public Method getMethod(String name,Class<?>... parameterTypes)获取指定参数的公共成员方法 对象。
public Method[] getDeclaredMethods()获取所有的成员方法,包括私有的(不包括继承的)
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)获取指定参数的方法对象
public Object invoke(Object obj,Object... args)在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。

我们看看这个例子

  1. package com.example.javademo.reflect;
  2. /**
  3. * author : me
  4. * date : 22-10-20下午12:03
  5. * desc : 供反射调用的方法类
  6. * version: 1.0
  7. */
  8. public class MethodTest {
  9. public MethodTest() {
  10. System.out.println("调用 MethodTest默认的共有构造方法");
  11. }
  12. String fruit = "苹果";
  13. int milliliter = 0;
  14. char game = 'X';
  15. String mood = "开心";
  16. public void eat(String fruit) {
  17. this.fruit = fruit;
  18. System.out.println("调用public成员方法 eat 吃的水果为 :" + fruit);
  19. }
  20. int drink(int milliliter) {
  21. this.milliliter = milliliter;
  22. System.out.println("调用default成员方法 drink 喝水毫升量 :" + milliliter);
  23. return milliliter;
  24. }
  25. protected void play(char game) {
  26. this.game = game;
  27. System.out.println("调用protected成员方法 play 玩游戏 :" + game);
  28. }
  29. private String happy(String mood) {
  30. this.mood = mood;
  31. System.out.println("调用private成员方法 happy 今日心情:" + mood);
  32. return mood;
  33. }
  34. }

反射测试类

  1. package com.example.javademo.reflect;
  2. import java.lang.reflect.InvocationTargetException;
  3. import java.lang.reflect.Method;
  4. /**
  5. * author : me
  6. * date : 22-10-20下午12:03
  7. * desc : 反射MethodTest类中的成员方法实现类
  8. * version: 1.0
  9. */
  10. public class ReflectMethod {
  11. public static void main(String[] args) {
  12. try {
  13. System.out.println("**********************获取反射后类对象*********************************");
  14. //获取方法类的class对象
  15. Class methodClass = Class.forName("com.example.javademo.reflect.MethodTest");
  16. System.out.println("反射类对象 :" + methodClass);
  17. System.out.println("**********************获取反射类中所有的成员方法**************************");
  18. //返回一个"方法数组对象":反射类或接口中所有声明的方法,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
  19. Method[] methods = methodClass.getDeclaredMethods();
  20. for (Method m : methods) {
  21. System.out.println(m);
  22. }
  23. System.out.println("***************第一种*******调用反射类中public成员方法**************************");
  24. //返回一个方法对象,它表示此表示的类或接口的指定声明的方法对象。
  25. //第一个参数为方法名,第二个可变参数 : 为该方法传入的参数
  26. Method methodPublic = methodClass.getDeclaredMethod("eat", String.class);
  27. //PUBLIC :1 PRIVATE:2 PROTECTED:4 PACKAGE(default):8
  28. System.out.println("打印该public方法的修饰符 :" + methodPublic.getModifiers());
  29. System.out.println("打印该public方法对象 表示方法的名称: "+methodPublic.getName());
  30. System.out.println("打印该public方法对象 可执行文件的形式参数的数量 :" + methodPublic.getParameterCount());
  31. //实例化一个MethodTest类对象,每个类有一个默认的无参构造方法,可以写或不写
  32. //但是如果类中有带参数的构造方法,那无参构造方法是必须要写出来的,不然new对象的时候,就会报错
  33. MethodTest objMethodTest = (MethodTest)methodClass.getDeclaredConstructor().newInstance();
  34. System.out.println("MethodTest类对象 :" + objMethodTest);
  35. //invoke方法 第一个参数: 从底层方法被调用的对象 如果底层方法是静态的,则第一个参数obj对象传递null。
  36. //第二个参数: 该方法调用的参数
  37. //如果方法正常完成,则返回的值将返回给调用者.
  38. Object objectreturn1 = methodPublic.invoke(objMethodTest, "菠萝");
  39. //eat方法返回值为 null
  40. System.out.println("eat方法 返回值为 :" + objectreturn1);
  41. System.out.println("***************第二种*******调用反射类中private成员方法**************************");
  42. Method methodPrivate = methodClass.getDeclaredMethod("happy", String.class);
  43. System.out.println("打印该private方法的修饰符 :" + methodPrivate.getModifiers());
  44. System.out.println("打印该private方法对象 表示方法的名称: "+methodPrivate.getName());
  45. System.out.println("打印该public方法对象 可执行文件的形式参数的数量 :" + methodPrivate.getParameterCount());
  46. //如果是访问私有方法,则要加上这句话,禁止java语言使用时安全检查
  47. methodPrivate.setAccessible(true);
  48. Object objectreturn2 = methodPrivate.invoke(objMethodTest, "超级nice");
  49. System.out.println("happy方法 返回值为:"+objectreturn2);
  50. System.out.println("***************第三种*******调用反射类中protected成员方法**************************");
  51. Method methodProtected = methodClass.getDeclaredMethod("play", char.class);
  52. System.out.println("打印该protected方法的修饰符 :" + methodProtected.getModifiers());
  53. System.out.println("打印该protected方法对象 表示方法的名称: "+methodProtected.getName());
  54. System.out.println("打印该protected方法对象 可执行文件的形式参数的数量 :" + methodProtected.getParameterCount());
  55. Object objectreturn3 = methodProtected.invoke(objMethodTest, 'Y');
  56. System.out.println("play方法 返回值为:"+objectreturn3);
  57. System.out.println("***************第四种*******调用反射类中default成员方法**************************");
  58. Method methodDefault = methodClass.getDeclaredMethod("drink", int.class);
  59. System.out.println("打印该protected方法的修饰符 :" + methodDefault.getModifiers());
  60. System.out.println("打印该protected方法对象 表示方法的名称: "+methodDefault.getName());
  61. System.out.println("打印该protected方法对象 可执行文件的形式参数的数量 :" + methodDefault.getParameterCount());
  62. Object objectreturn4 = methodDefault.invoke(objMethodTest, 180);
  63. System.out.println("drink方法 返回值为:"+objectreturn4);
  64. } catch (ClassNotFoundException | NoSuchMethodException e) {
  65. e.printStackTrace();
  66. } catch (IllegalAccessException e) {
  67. e.printStackTrace();
  68. } catch (InstantiationException e) {
  69. e.printStackTrace();
  70. } catch (InvocationTargetException e) {
  71. e.printStackTrace();
  72. }
  73. }
  74. }

打印log:

  1. **********************获取反射后类对象*********************************
  2. 反射类对象 :class com.example.javademo.reflect.MethodTest
  3. **********************获取反射类中所有的成员方法**************************
  4. public void com.example.javademo.reflect.MethodTest.eat(java.lang.String)
  5. private java.lang.String com.example.javademo.reflect.MethodTest.happy(java.lang.String)
  6. protected void com.example.javademo.reflect.MethodTest.play(char)
  7. int com.example.javademo.reflect.MethodTest.drink(int)
  8. ***************第一种*******调用反射类中public成员方法**************************
  9. 打印该public方法的修饰符 :1
  10. 打印该public方法对象 表示方法的名称: eat
  11. 打印该public方法对象 可执行文件的形式参数的数量 :1
  12. 调用 MethodTest默认的共有构造方法
  13. MethodTest类对象 :com.example.javademo.reflect.MethodTest@15db9742
  14. 调用public成员方法 eat 吃的水果为 :菠萝
  15. eat方法 返回值为 :null
  16. ***************第二种*******调用反射类中private成员方法**************************
  17. 打印该private方法的修饰符 :2
  18. 打印该private方法对象 表示方法的名称: happy
  19. 打印该public方法对象 可执行文件的形式参数的数量 :1
  20. 调用private成员方法 happy 今日心情:超级nice
  21. happy方法 返回值为:超级nice
  22. ***************第三种*******调用反射类中protected成员方法**************************
  23. 打印该protected方法的修饰符 :4
  24. 打印该protected方法对象 表示方法的名称: play
  25. 打印该protected方法对象 可执行文件的形式参数的数量 :1
  26. 调用protected成员方法 play 玩游戏 :Y
  27. play方法 返回值为:null
  28. ***************第四种*******调用反射类中default成员方法**************************
  29. 打印该protected方法的修饰符 :0
  30. 打印该protected方法对象 表示方法的名称: drink
  31. 打印该protected方法对象 可执行文件的形式参数的数量 :1
  32. 调用default成员方法 drink 喝水毫升量 :180
  33. drink方法 返回值为:180

代码中基本都做了注释,通过上面的例子,总结:

1.  反射class对象, 反射Method对象  反射类实例对象 是3个不同的对象,要区分开

2.  在反射调用private方法时,一定要禁止java语言使用时安全检查,要setAccessible(true),其他修饰符方法可以不用加这句话

3.  (反射Method对象).invoke(Object obj, Object ...args) 方法参数的理解 :

1. 第一个参数为 通过用反射方法构造出来的 反射类实例对象 

2. 第二个可变参数为: 该方法需要传入的参数

3. 如果方法正常完成,则将返回值返回

3.4 反射类中的成员变量

方法说明
Field[] getFields()获取所有的"公有字段"
public Field getField(String fieldName)获取指定参数的共有字段
.Field[] getDeclaredFields()获取所有字段,包括:私有、受保护、默认、公有;
public Field getDeclaredField(String fieldName)获取某个字段(包括私有的)
set(Object obj, Object value)将指定Field对象  设置为指定的新值

我们通过Demo讲解:

  1. package com.example.javademo.reflect;
  2. /**
  3. * author : me
  4. * date : 22-10-20下午4:06
  5. * desc : 供反射调用的FieldTest类
  6. * version: 1.0
  7. */
  8. public class FieldTest {
  9. public FieldTest() {
  10. System.out.println("调用FieldTest 默认无参构造方法");
  11. }
  12. public int age = 18;
  13. private String name = "张三";
  14. String sex = "男";
  15. protected String phoneNum = "123456789";
  16. public int getAge() {
  17. return age;
  18. }
  19. public String getName() {
  20. return name;
  21. }
  22. public String getSex() {
  23. return sex;
  24. }
  25. public String getPhoneNum() {
  26. return phoneNum;
  27. }
  28. public void setAge(int age) {
  29. this.age = age;
  30. }
  31. public void setName(String name) {
  32. this.name = name;
  33. }
  34. public void setSex(String sex) {
  35. this.sex = sex;
  36. }
  37. public void setPhoneNum(String phoneNum) {
  38. this.phoneNum = phoneNum;
  39. }
  40. @Override
  41. public String toString() {
  42. return "FieldTest{" +
  43. "age=" + age +
  44. ", name='" + name + '\'' +
  45. ", sex='" + sex + '\'' +
  46. ", phoneNum='" + phoneNum + '\'' +
  47. '}';
  48. }

反射测试类:

  1. package com.example.javademo.reflect;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. /**
  6. * author : me
  7. * date : 22-10-20下午4:11
  8. * desc : 反射FiledTest类中各字段(Field)的实现类
  9. * version: 1.0
  10. */
  11. public class ReflectField {
  12. public static void main(String[] args) {
  13. //用正常的new对象的方式,给字段赋值,打印对象值, 测试用
  14. /* FieldTest objcetFieldTest = new FieldTest();
  15. objcetFieldTest.setAge(15);
  16. objcetFieldTest.setName("xiaomao");
  17. objcetFieldTest.setPhoneNum("12345678");
  18. objcetFieldTest.setSex("男");
  19. System.out.println(objcetFieldTest.toString());*/
  20. //通过反射的方式,修改字段值
  21. try {
  22. //通过反射获取Class对象
  23. Class FieldTestClass = Class.forName("com.example.javademo.reflect.FieldTest");
  24. //用反射的方式来修改字段(成员变量)的值
  25. System.out.println("************获取所有的字段(包括共有、私有、受保护、默认的)********************");
  26. Field[] fields = FieldTestClass.getDeclaredFields();
  27. for (Field f : fields) {
  28. System.out.println(f);
  29. }
  30. //用反射的方式来构造FieldTestl类对象
  31. FieldTest objFieldTest = (FieldTest) FieldTestClass.getDeclaredConstructor().newInstance();
  32. System.out.println("*************反射获取public字段并调用***********************************");
  33. //返回一个 Field对象,它表示的类或接口的指定已声明字段对象。
  34. Field fieldPublic = FieldTestClass.getDeclaredField("age");
  35. System.out.println("该字段的修饰符 :" + fieldPublic.getModifiers());
  36. System.out.println("该字段的名称 :" + fieldPublic.getName());
  37. /*
  38. * 获取字段的值:有 getInt(Object obj) getLong(Object obj) get(Object obj)等方法
  39. *
  40. * obj :为通过反射方法 构造出来的类对象
  41. * */
  42. //获取int类型 实例字段的默认值
  43. System.out.println("通过反射方式获取age默认值 : " + fieldPublic.getInt(objFieldTest));
  44. /* 设置字段的值:
  45. Field public void set(Object obj,Object value):
  46. 参数说明:
  47. 1.obj: 通过反射方法 构造出来的类对象
  48. 2.value:要为字段设置的值;
  49. */
  50. fieldPublic.set(objFieldTest, 20);
  51. //通过反射的方式调用 getAge() 方法
  52. Method method = FieldTestClass.getDeclaredMethod("getAge");
  53. Object objectReturn = method.invoke(objFieldTest);
  54. System.out.println("通过反射方式set新值之后, age的值 :" + objectReturn);
  55. System.out.println("*************反射获取private字段并调用***********************************");
  56. //在项目实际中,因为私有变量和方法,无法在外部类去调用它们,所以反射在这点上就派上用场了
  57. Field fieldPrivate = FieldTestClass.getDeclaredField("name");
  58. //private字段一定要调用setAccessible(true) 禁止java语言使用时安全检查
  59. fieldPrivate.setAccessible(true);
  60. System.out.println("通过反射方式获取age默认值 : " + fieldPrivate.get(objFieldTest));
  61. //修改private变量name的值
  62. fieldPrivate.set(objFieldTest, "李四");
  63. //通过反射的方式调用 getName() 方法
  64. Method objectMethod = FieldTestClass.getDeclaredMethod("getName");
  65. Object objectReturn1 = objectMethod.invoke(objFieldTest);
  66. System.out.println("通过反射方式set新值之后, name的值 :" + objectReturn1);
  67. } catch (ClassNotFoundException | NoSuchMethodException e) {
  68. e.printStackTrace();
  69. } catch (InstantiationException e) {
  70. e.printStackTrace();
  71. } catch (InvocationTargetException e) {
  72. e.printStackTrace();
  73. } catch (IllegalAccessException e) {
  74. e.printStackTrace();
  75. } catch (NoSuchFieldException e) {
  76. e.printStackTrace();
  77. }
  78. }

打印log:

  1. ************获取所有的字段(包括共有、私有、受保护、默认的)********************
  2. public int com.example.javademo.reflect.FieldTest.age
  3. private java.lang.String com.example.javademo.reflect.FieldTest.name
  4. java.lang.String com.example.javademo.reflect.FieldTest.sex
  5. protected java.lang.String com.example.javademo.reflect.FieldTest.phoneNum
  6. 调用FieldTest 默认无参构造方法
  7. *************反射获取public字段并调用***********************************
  8. 该字段的修饰符 :1
  9. 该字段的名称 :age
  10. 通过反射方式获取age默认值 : 18
  11. 通过反射方式set新值之后, age的值 :20
  12. *************反射获取private字段并调用***********************************
  13. 通过反射方式获取age默认值 : 张三
  14. 通过反射方式set新值之后, name的值 :李四

通过例子,总结如下:

1.  反射class对象, 反射Field对象 反射类实例对象 是3个不同的对象,要区分开

2. 在反射调用private字段时,一定要禁止java语言使用时安全检查,要setAccessible(true)

3.  Field public void set(Object obj,Object value):

   参数说明: 1.obj: 通过反射方法 构造出来的类对象

                      2.value:要为字段设置的值;

3.5 反射类中静态方法和变量

怎么修改static修饰的变量呢?静态变量和方法是在类的实例化之前就进行了初始化(类的初始化阶段),静态变量和方法是从属于类本身的,跟new出来的具体对象无关,所以我们获取变量就不需要传入对象,直接传入null即可。

Demo如下:

  1. package com.example.javademo.reflect;
  2. /**
  3. * author : me
  4. * date : 22-10-24 上午10:33
  5. * desc : 静态变量和方法 类
  6. * version: 1.0
  7. */
  8. public class StaticFieldTest {
  9. public StaticFieldTest() {
  10. System.out.println("调用 StaticFieldTest 无参构造方法");
  11. }
  12. //静态变量
  13. public static int age = 15;
  14. public static void setAge(int age) {
  15. StaticFieldTest.age = age;
  16. }
  17. //静态方法
  18. public static int getAge() {
  19. return age;
  20. }
  21. }

反射测试类:

  1. package com.example.javademo.reflect;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. /**
  6. * author : me
  7. * date : 22-10-24上午10:37
  8. * desc : 反射类中的静态变量和方法
  9. * version: 1.0
  10. */
  11. public class ReflectStatic {
  12. public static void main(String[] args) {
  13. try {
  14. //获取 StaticFieldTest class对象
  15. Class staticClazz = Class.forName("com.example.javademo.reflect.StaticFieldTest");
  16. //获取静态Field对象
  17. Field staticField = staticClazz.getDeclaredField("age");
  18. //因为是pulic修饰符,这句话也可以不用写
  19. staticField.setAccessible(true);
  20. //通过set方法,修改值, 静态变量 是从属于类,可以不用传入类实例对象,直接传入null
  21. staticField.set(null, 25);
  22. //验证修改后的值
  23. Method agetet = staticClazz.getDeclaredMethod("getAge");
  24. // 静态方法 是从属于类, 可以不用传入类实例对象,直接传入null
  25. int agetest = (int) agetet.invoke(null);
  26. System.out.println("**********************打印修改后的age值**************************");
  27. System.out.println(agetest);
  28. } catch (ClassNotFoundException | NoSuchMethodException e) {
  29. e.printStackTrace();
  30. } catch (IllegalAccessException e) {
  31. e.printStackTrace();
  32. } catch (InvocationTargetException e) {
  33. e.printStackTrace();
  34. } catch (NoSuchFieldException e) {
  35. e.printStackTrace();
  36. }
  37. }

打印结果: 

  1. **********************打印修改后的age值**************************
  2. 25

总结如下:

1. 对于static 变量和方法,因为它是从属于类本身,在类的实例化之前就进行了初始化,当传入obj对象参数的时候,直接传入null即可。

4. 反射在Android中的应用

4.1 反射实现获取顶层activity的名称

如前言背景中的问题描述,有了上面的理论知识做为基础,实现代码如下:

  1. /**
  2. * @description: 通过反射方式获取 顶层activity名称
  3. * @date: 22-10-24 下午3:29
  4. * @author: me
  5. * @param: null
  6. * @return: boolean 如果是目标activity 则返回true
  7. */
  8. public boolean isTopTargetActivity() {
  9. try {
  10. //通过包名和类名反射获取 class 对象
  11. Class activityThreadClass = Class.forName("android.app.ActivityThread");
  12. //第二种: 反射创建 activiyThread对象 反射调用静态方法,第一个参数obj对象传递 null
  13. Object activityThreadObj = activityThreadClass.getMethod("currentActivityThread").invoke(null);
  14. Log.e("test", "====activityThreadObj: "+activityThreadObj);
  15. //获取 mActivities Field对象 final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
  16. Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
  17. //禁止Java语言访问使用时进行检查
  18. activitiesField.setAccessible(true);
  19. //返回该所表示的字段的值 Field
  20. Map activities = (Map) activitiesField.get(activityThreadObj);
  21. for (Object activityClientRecord : activities.values()) {
  22. //获取 ActivityClientRecord class对象
  23. Class activityRecordClass = activityClientRecord.getClass();
  24. //获取 boolean paused 字段 对象
  25. Field pausedField = activityRecordClass.getDeclaredField("paused");
  26. //允许暴力访问,禁止java语言运行时安全检查
  27. pausedField.setAccessible(true);
  28. //Activity onResume的判断条件
  29. if (!pausedField.getBoolean(activityClientRecord)) {
  30. //获取 Activity activity field对象
  31. Field activityField = activityRecordClass.getDeclaredField("activity");
  32. activityField.setAccessible(true);
  33. //获取当前显示的activity的对象
  34. Activity activity = (Activity) activityField.get(activityClientRecord);
  35. //获取当前activity的名称
  36. String className = activity.getClass().toString();
  37. Log.e("test", "====通过反射获取的className===="+className);
  38. if ("cn.com.test.activity.MainActivity".equals(className)) {
  39. return true;
  40. }
  41. }
  42. }
  43. } catch (ClassNotFoundException e) {
  44. Log.e("test", "======ClassNotFoundException===="+e.getMessage());
  45. } catch (InvocationTargetException e) {
  46. Log.e("test", "======InvocationTargetException===="+e.getMessage());
  47. } catch (NoSuchMethodException e) {
  48. Log.e("test", "======NoSuchMethodException===="+e.getMessage());
  49. } catch (NoSuchFieldException e) {
  50. Log.e("test", "======NoSuchFieldException===="+e.getMessage());
  51. } catch (IllegalAccessException e) {
  52. Log.e("test", "======IllegalAccessException===="+e.getMessage());
  53. }
  54. return false;
  55. }

4.2 反射调用SystemProperties中set   get方法

再者,比如第三方应用是无法直接调用 SystemProperties中 get  set 方法,我们也可以通过反射来实现, 这个工具类已经实现好,可以拿去直接用:

  1. public class SystemPropertiesUtils {
  2. private static final String TAG = "SystemPropertiesUtils";
  3. private static Class<?> mClassType = null;
  4. private static Method mGetMethod = null;
  5. private static Method mGetIntMethod = null;
  6. private static Method mGetBooleanMethod = null;
  7. private static Class<?> getSystemPropertiesClass() throws ClassNotFoundException {
  8. if (mClassType == null) {
  9. mClassType = Class.forName("android.os.SystemProperties");
  10. }
  11. return mClassType;
  12. }
  13. private static Method getMethod() throws Exception {
  14. if (mGetMethod == null) {
  15. Class clazz = getSystemPropertiesClass();
  16. mGetMethod = clazz.getDeclaredMethod("get", String.class);
  17. }
  18. return mGetMethod;
  19. }
  20. private static Method getIntMethod() throws Exception {
  21. if (mGetIntMethod == null) {
  22. Class clazz = getSystemPropertiesClass();
  23. mGetIntMethod = clazz.getDeclaredMethod("getInt", String.class, int.class);
  24. }
  25. return mGetIntMethod;
  26. }
  27. private static Method getBooleanMethod() throws Exception {
  28. if (mGetBooleanMethod == null) {
  29. Class clazz = getSystemPropertiesClass();
  30. mGetBooleanMethod = clazz.getDeclaredMethod("getBoolean", String.class, boolean.class);
  31. }
  32. return mGetBooleanMethod;
  33. }
  34. public static String get(String key, String def) {
  35. try {
  36. String value = (String) getMethod().invoke(null, key);
  37. if (!TextUtils.isEmpty(value)) {
  38. return value;
  39. }
  40. } catch (Exception e) {
  41. Log.d(TAG, "Unable to read system properties");
  42. }
  43. return def;
  44. }
  45. public static int getInt(String key, int def) {
  46. int value = def;
  47. try {
  48. value = (int) getIntMethod().invoke(null, key, def);
  49. } catch (Exception e) {
  50. Log.d(TAG, "Unable to read system properties");
  51. }
  52. return value;
  53. }
  54. public static boolean getBoolean(String key, boolean def) {
  55. boolean value = def;
  56. try {
  57. value = (Boolean) getBooleanMethod().invoke(null, key, def);
  58. } catch (Exception e) {
  59. Log.d(TAG, "Unable to read system properties");
  60. }
  61. return value;
  62. }
  63. }

4.3 通过反射创建Bitmap缩略图

  1. public static Bitmap createVideoThumbnail(String filePath) {
  2. // MediaMetadataRetriever is available on API Level 8
  3. // but is hidden until API Level 10
  4. Class<?> clazz = null;
  5. Object instance = null;
  6. try {
  7. clazz = Class.forName("android.media.MediaMetadataRetriever");
  8. instance = clazz.newInstance();
  9. Method method = clazz.getMethod("setDataSource", String.class);
  10. method.invoke(instance, filePath);
  11. // The method name changes between API Level 9 and 10.
  12. if (Build.VERSION.SDK_INT <= 9) {
  13. return (Bitmap) clazz.getMethod("captureFrame").invoke(instance);
  14. } else {
  15. byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance);
  16. if (data != null) {
  17. Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
  18. if (bitmap != null) return bitmap;
  19. }
  20. return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance);
  21. }
  22. } catch (IllegalArgumentException ex) {
  23. // Assume this is a corrupt video file
  24. } catch (RuntimeException ex) {
  25. // Assume this is a corrupt video file.
  26. } catch (InstantiationException e) {
  27. Log.e(TAG, "createVideoThumbnail", e);
  28. } catch (InvocationTargetException e) {
  29. Log.e(TAG, "createVideoThumbnail", e);
  30. } catch (ClassNotFoundException e) {
  31. Log.e(TAG, "createVideoThumbnail", e);
  32. } catch (NoSuchMethodException e) {
  33. Log.e(TAG, "createVideoThumbnail", e);
  34. } catch (IllegalAccessException e) {
  35. Log.e(TAG, "createVideoThumbnail", e);
  36. } finally {
  37. try {
  38. if (instance != null) {
  39. clazz.getMethod("release").invoke(instance);
  40. }
  41. } catch (Exception ignored) {
  42. }
  43. }
  44. return null;
  45. }

MediaMetadataRetriever.java 类中有个public无参构造方法, 可以通过反射方式clazz.getDeclaredConstructor().newInstance() 拿到这个 类对象

接下来,就是获取Method  Field  对象  再通过invoke   set 方法去修改方法  变量的值。

5. 总结

        反射在Andorid中开发应用的比较多,Class对象, Constructor 对象,Method 对象  Field对象各自的常用API要理解并熟练运用, 结合源码多阅读多仿写,相信你也可以carry住反射。

6. 更新

#2023.02.07

在代码编译的时候,有Warnning警告:

  1. Method getVolumeList = clz.getMethod("getVolumeList", null);
  2. ^
  3. 对于 varargs 调用, 应使用 Class
  4. 对于非 varargs 调用, 应使用 Class[], 这样也可以抑制此警告
  5. 警告: 最后一个参数使用了不准确的变量类型的 varargs 方法的非 varargs 调用;
  6. StorageVolume[] result = (StorageVolume[]) getVolumeList.invoke(storageManager, null);
  7. 对于 varargs 调用, 应使用 Object
  8. 对于非 varargs 调用, 应使用 Object[], 这样也可以抑制此警告

修改方法如下:

警告: 最后一个参数使用了不准确的变量类型的 varargs 方法的非 varargs 调用;
[javac] 对于 varargs 调用,应使用 Java.lang.Object
[javac] 对于非 varargs 调用,应使用 java.lang.Object[],这样也可以抑制此警告程序是一样的,在jdk1.4下可以编译通过,但在1.5就不行。

  1. Method getVolumeList = clz.getMethod("getVolumeList", new Class[0]);
  2. StorageVolume[] result = (StorageVolume[]) getVolumeList.invoke(storageManager, new Object[]{});


#2023.02.14

关于StorageManager.java 文件中的反射方法

  1. /**
  2. * 通过反射 返回已挂载成功的外设的StorageVolume数组
  3. */
  4. public StorageVolume[] getVolumeList(StorageManager storageManager) {
  5. try {
  6. Class clz = Class.forName("android.os.storage.StorageManager");
  7. Method getVolumeList = clz.getMethod("getVolumeList", new Class[0]);
  8. StorageVolume[] result = (StorageVolume[]) getVolumeList.invoke(storageManager, new Object[]{});
  9. return result;
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. }
  13. return null;
  14. }
  15. StorageVolume[] result = getVolumeList(manager);
  16. mVolume = manager.findVolumeByUuid(result[1].getUuid());
  1. // get StorageManager
  2. StorageManager sm = null;
  3. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
  4. sm = getSystemService(StorageManager.class);
  5. }
  6. if (sm != null) {
  7. List<VolumeInfo> volumes = null;
  8. try {
  9. volumes = (List<VolumeInfo>) sm.getClass().getMethod("getVolumes").invoke(sm);
  10. } catch (Exception e) {
  11. Log.e(TAG, "exception: " + e.getMessage());
  12. }
  13. if (volumes != null) {
  14. for (VolumeInfo vol : volumes) {
  15. DiskInfo diskInfo = vol.getDisk();
  16. if (diskInfo != null && diskInfo.isSd()) {
  17. // is SD
  18. Log.d(TAG, "sdcard disk id: " + diskInfo.getId());
  19. try {
  20. // format
  21. sm.getClass().getMethod("partitionPublic", String.class).invoke(sm, diskInfo.getId());
  22. } catch (Exception e) {
  23. Log.e(TAG, "partitionPublic exception: " + e.getMessage());
  24. }
  25. }
  26. }
  27. }
  28. }

#2023.09.25

反射调用静态方法

  1. public class Util {
  2. //静态方法
  3. public static void testFive() {
  4. Log.e("test", "======");
  5. }
  6. //普通私有方法
  7. private void testsix() {
  8. Log.e("test", "===xxxx====");
  9. }
  10. }
  11. //反射调用
  12. try {
  13. Class claz = Class.forName("com.example.okhttptest.Util");
  14. Method method = claz.getDeclaredMethod("testFive");
  15. Util utilobj = (Util) claz.newInstance();
  16. //调用静态方法,传递object为 null即可
  17. method.invoke(null);
  18. Method method1 = claz.getDeclaredMethod("testsix");
  19. method1.setAccessible(true);
  20. method1.invoke(utilobj);
  21. } catch (ClassNotFoundException e) {
  22. e.printStackTrace();
  23. } catch (NoSuchMethodException e) {
  24. e.printStackTrace();
  25. } catch (IllegalAccessException e) {
  26. e.printStackTrace();
  27. } catch (InvocationTargetException e) {
  28. e.printStackTrace();
  29. } catch (InstantiationException e) {
  30. e.printStackTrace();
  31. }
  32. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/540374
推荐阅读
相关标签
  

闽ICP备14008679号