You are currently viewing Android开发之JAVA层反射调用

Android开发之JAVA层反射调用

一、JAVA反射思维和NDK开发

Java反射定义:java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意类的静态属性和方法,都能够完成对静态属性的获取和设置以及静态方法的调用:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

1.1 JAVA反射使用的相关类(部分)

类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量(成员变量也称为类的属性 )
Method类代表类的方法
Constructo代表类的构造方法

1.1.1 获得class类

代表类的实体,在运行的Java应用程序中表示类和接口

获取类相关的方法(主要)

getClassLoader() : 获得类的加载器

getClasses() : 返回一个数组,包含该类中的所有公共类和接口类的对象

getDeclaredClasses(): 返回一个数组,包含该类中的所有类和接口类的对象

gerName(): 获得类的完整路径名字

getPackage(): 获得类的包名

1.1.2 获得类的属性

getField(String name)获取某个公有的属性对象
getFields()获取所有公有属性对象
getDeclaredField(String name)获得某个属性对象
getDeclaredFields()获得所有属性对象

1.1.3 获取类中的方法

getMethod(String name, Class….<?> parameterTypes)获得该类某个公有的方法
getMethods()获得该类所有公有的方法
getDeclaredMethod(String name, Class…<?> parameterTypes)获得该类某个方法
getDeclaredMethods()获得该类所有方法

1.1.4 获取类中的构造方法

getConstructor(Class…<?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class…<?>parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法

1.1.5 类中其他重要的方法

isAnnotation()isAnnotation()如果是注解类型则返回true
isAnnotationPresent(Class<? extends Annotation>annotationClass)如果是指定类型注解类型则返回true
isAnonymousClass()如果是匿名类则返回true
isArray()如果是一个数组类则返回true
isEnum()如果是枚举类则返回true
islnstance(Object obj)如果obj是该类的实例则返回true
isInterface()如果是接口类则返回true
isLocalClass()如果是局部类则返回true
isMemberClass()如果是内部类则返回true

1.1.6 Field类

equals(Object obj) 属性与obj相等则返回true get(Object obj)获得obi中对应的属性值 set(Object obj, Object value)设置obj中对应属性值

1.1.7 Method类

invoke : 传递Object对象及参数调用该对象对应的方法

1.1.8 访问private域和方法

类AccessibleObject中有函数public void setAccessible(boolean flag),该函数在传入true作为参数后,让访问private修饰的域和函数成为可能。而Method、Field和Constructor类共同继承了Accessibleobject类,该基类有两个setAccessible方法能在运行时压制Java语言访问控制检查(Javalanguage access control checks),从而能任意调用被私有化保护的方法域和构造方法

首先新建一个Moonlight类,代码如下

package com.example.reflection;

import android.util.Log;

public class MoonlightTest {
   public String flag = null;
   public MoonlightTest(){
       flag = "MoonlightTest()";
  }
   public MoonlightTest(String content){
       flag = "public MoonlightTest(String content)";
  }
   public MoonlightTest(String content,int arg){
       flag = "public MoonlightTest(String content,int arg)";
  }
   public static StringpublicStaticField= "I am public StaticField";
   public String publicNotStaticField = "I am public Not StaticField";

   private static StringprivateStaticField= "I am private StaticField";
   private  String privateNotStaticField = "I am private Not StaticField";

   public static void publicStaticFunction(){
       Log.i("Moonlight","I am from publicStaticFunction");
  }
   public void publicNotStaticFunction(){
       Log.i("Moonlight","I am from publicNotStaticFunction");
  }

   private static void privateStaticFunction(){
       Log.i("Moonlight","I am from privateStaticFunction");
  }
   private void privateNotStaticFunction(){
       Log.i("Moonlight","I am from privateNotStaticFunction");
  }
}

里面包括了常用的构造方法,字段和方法

然后再JAVA层反射调用Moonlight类中的方法和属性

二、获取类加载器

2.1 第一种方式

使用getClassLoader来获取指定的类,getClassLoader() : 获得类的加载器

Class testJavaReClazz;
testJavaReClazz = MainActivity.class.getClassLoader().loadClass("com.example.reflection.MoonlightTest");

2.2 第二种方式

Class.forName返回一个给定类或者接口的一个 Class 对象,如果没有给定 classloader, 那么会使用根类加载器

Class testJavaReClazz2;
testJavaReClazz2 = Class.forName("com.example.reflection.MoonlightTest");

2.3 第三种方式

this指的是当前正在访问这段代码的对象,当在内部类中使用this指的就是内部类的对象, 为了访问外层类对象,就可以使用外层类名.this来访问,一般也只在这种情况下使用这种形式

Class testJavaReClazz3;
testJavaReClazz3 = MoonlightTest.class;

三、Java层反射调用Field属性

getDeclaredFields():获取某个类的自身的所有字段,不包括父类的字段

Field publicStaticField_field = testJavaReClazz3.getDeclaredField("publicStaticField");
Field[] fields = testJavaReClazz3.getDeclaredFields();
for(Field i:fields){
  Log.i("Moonlight","getDeclaredFields()-> "+i);
}

getFields(): 获取某个类的所有的public字段,其中是包括父类的public字段的

Field[] fields_2 = testJavaReClazz3.getFields();
           for(Field i:fields_2){
               Log.i("Moonlight","getFields()-> "+i);
}

执行后,可以发现

getDeclaredFields :获得所有属性对象

getFields : 获取公有属性的对象

从上面就可以看出来,getDeclaredFields 可以获取所有属性对象,

当我们需要获取到某个指定的属性时,我们应该如何去获取呢?当然也比较简单,需要在getDeclaredFields 和 getFields 方法中指定需要使用的属性名称。

Field publicStaticField_field = testJavaReClazz3.getDeclaredField("publicStaticField");
Field privateStaticField_field = testJavaReClazz3.getDeclaredField("privateStaticField");

Log.i("Moonlight","publicStaticField_field-> "+ publicStaticField_field);
Log.i("Moonlight","privateStaticField_field-> "+ privateStaticField_field);

编译后运行,可以看见结果已经打印出来了

那么还存在一个问题,如果用getField去获得一个私有属性(上面说过它只能访问公有属性)。那么会怎么样呢,代码如下:

Field privateField_field = testJavaReClazz3.getField("privateStaticField");
Log.i("Moonlight","getField->privateStaticField_field-> "+ privateField_field);

结果就是程序异常崩溃!!

3.1 获取私有属性和公有属性的值

在上面已经获取到了属性Field,那么如果获取他们的值呢。

公有属性的值:

Field publicStaticField_field = testJavaReClazz3.getDeclaredField("publicStaticField");
String publicStaticField_content = (String)publicStaticField_field.get(null);
Log.i("Moonlight","publicStaticField_content-> "+ publicStaticField_content);

获取私有属性的值:

需要设置权限

Field privateStaticField_field = testJavaReClazz3.getDeclaredField("privateStaticField");
privateStaticField_field.setAccessible(true); //访问私有属性时,记得加上setAccessible,设置它的权限检查
String privateStaticField_content = (String)privateStaticField_field.get(null);
Log.i("Moonlight","privateStaticField_content-> "+ privateStaticField_content);

编译运行—>结果

可以看到值已经获取到了,

3.2 改变私有属性和公有属性的值

上面说到获取到一个私有属性和公有属性的值,那么如何去改变属性的值呢,那么就是set啦。

publicStaticField_field.set(null,"publicStaticField->modified");
privateStaticField_field.set(null,"privateStaticField->modified");

set方法,第一个参数为对象,第二个参数即为需要更改的值

注意,如果是非静态的属性,对象不能为null,

四、Java层反射调用Method方法

4.1 反射访问指定类中所有方法

在反射Java层方法时,需要引入几个方法

getDeclaredMethods:获取该类的所有方法

getField : 获取该类的所有公有方法(其中包括在反射过程中所使用到的公有方法)

首先编写代码如下:

public void TestJavaMethod(){
       Class MoonlightTestClazz = MoonlightTest.class;
       Method[] getDeclaredMethods = MoonlightTestClazz.getDeclaredMethods();
       for(Method i : getDeclaredMethods){
           Log.i("Moonlight","getDeclaredMethods-> "+i);
      }
       Method[] getMethods = MoonlightTestClazz.getMethods();
       for(Method i : getMethods){
           Log.i("Moonlight","getMethods-> "+i);
      }
  }

编译—>查看日志

getDeclaredMethods() 获取了该类的所有方法,而getMethods 则只获取在反射过程中的所有公有方法,

那么我们就去找找 getMethods 是不是 只获取在反射过程中的所有公有方法?

找到 Object.class文件,新建一个Object 对象,点击就可以进入Object 。打开Object.class文件,可以发现getClass是一个公有的方法,那么,证明了getMethods 会打印反射过程中的所有公有方法

4.2 反射访问类中指定方法

代码如下所示:

Method publicStaticFunction_method = MoonlightTestClazz.getDeclaredMethod("publicStaticFunction");
publicStaticFunction_method.invoke(null); //获取公有静态的无参方法,直接写null即可
Method privateStaticFunction_method = MoonlightTestClazz.getDeclaredMethod("privateStaticFunction");
privateStaticFunction_method.setAccessible(true);//和访问静态属性一致,需要设置权限
privateStaticFunction_method.invoke(null);

其中,getDeclaredMethod方法,定义如下

public Method getDeclaredMethod(@RecentlyNonNull String name, @RecentlyNullable Class<?>... parameterTypes)

第一个参数为类名,之后的参数都为所调用参数的参数值

4.3 反射访问类中构造方法

Constructor[] constructors = MoonlightTestClazz.getDeclaredConstructors(); //获取所有的构造函数
Constructor MoonlightTestConstructors = MoonlightTestClazz.getDeclaredConstructor(String.class); //获取指定的构造函数
Log.i("Moonlight","MoonlightTestConstructors-> "+MoonlightTestConstructors);
for(Constructor i : constructors){
      Log.i("Moonlight","getDeclaredConstructors-> "+i);
}

访问私有属性

Object MoonlightTestObj = MoonlightTestConstructors.newInstance("MoonLight"); //建立一个obj对象
Field privateNotStaticField_field = MoonlightTestClazz.getDeclaredField("privateNotStaticField");
privateNotStaticField_field.setAccessible(true); //设置权限
String privateNotStaticField_field_content = (String) privateNotStaticField_field.get(MoonlightTestObj);
Log.i("Moonlight","privateNotStaticField_field_content-> "+privateNotStaticField_field_content);

其中,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?它们的区别在于创建对象的方式不一样,前者newInstance()是使用类加载机制(重点),后者new关键字是创建一个新类

但是使用newInstance时候,就必须保证:

     1、这个类已经加载;
    2、这个类已经连接了。

newInstance(): 弱类型。低效率。是实现IOC、反射、面对接口编程和依赖倒置等技术方法的必然选择

new: 强类型。相对高效。能调用任何public构造。new只能实现具体类的实例化,不适合于接口编程