Java 反射机制

Java反射机制是移动架构的重要知识体系中的一部分,可以说想要成为合格的移动架构师必须了解Java的反射机制

(1)概念

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

以上定义是在科学百科中找到的。

(2)本质

所谓反射,其实就是获取字节码文件,即.class文件,最后生成Class对象,拿到这个对象之后就可以对这个对象为所欲为了。

(3)获取Class对象的三种方式

上面说到,反射机制的本质是获取.class文件,生成Class对象,那么该如何获取Class对象呢?


css

复制代码

[第一种方式]

如果我们可以拿到某对象的实例如对象Test,那么获取Test的Class对象的代码如下:


ini

复制代码

Class<?> cls = new Test().getClass(); [第二种方式]

在知道类名的情况下:


ini

复制代码

Class<?> cls = Test.class; [第三种方式]

前两种获取Class对象的方式比较简单,但是必须保证Test对象能直接访问,但是,在很多时候,我们想要获取的Class对象不能直接访问,这个时候必须采用第三种获取Class对象的方式,如下:


ini

复制代码

   Class<?> cls = null;    try {        cls = Class.forName("com.juexing.mytest.Test");        System.out.println(cls.getName());   } catch (ClassNotFoundException e) {        e.printStackTrace();   }

forName()方法里传递的参数是Class的Name,所以,如果Test对象可以直接访问,也可以写成:


ini

复制代码

   Class<?> cls = null;    try {        cls = Class.forName(Test.class.getName());        System.out.println(cls.getName());   } catch (ClassNotFoundException e) {        e.printStackTrace();   } [三种方式如何选择]

第一种方式显然已经获取到对象了,所以没必要使用复杂化的反射机制; 第二种方式会导致模块与模块之间的耦合性,也不建议使用; 第三种方式是反射机制常用的方法,建议使用。

(4)获取构造方法

scss

复制代码

// 1.获取参数列表是parameterTypes,访问控制符是public的构造函数 public Constructor getConstructor(Class[] parameterTypes) ​ // 2.获取所有访问控制符是public的构造函数 public Constructor[] getConstructors() ​ // 3.获取参数列表是parameterTypes,并且是类自身声明的构造函数,访问控制符包含public、protected和private的函数。 public Constructor getDeclaredConstructor(Class[] parameterTypes) ​ //4.获取类自身声明的全部的构造函数,访问控制符包含public、protected和private的函数。 public Constructor[] getDeclaredConstructors() ​ //5.如果类声明在其它类的构造函数中,返回该类所在的构造函数,如果存在则返回,不存在返回null public Constructor getEnclosingConstructor()

举例:


arduino

复制代码

Constructor<?> constructor = cls.getDeclaredConstructor();//没有参数 Constructor<?> constructor = cls.getDeclaredConstructor(int.class);//一个参数 Constructor<?> constructor = cls.getDeclaredConstructor(String.class, String.class);//两个参数

拿到constructor对象之后可以新建当前对象的一个实例:


ini

复制代码

constructor.newInstance();

这个方法在实际运用中会经常用到。

(5)获取成员函数

scss

复制代码

// 1.获取函数名是name,参数是parameterTypes的public的函数(包括从基类继承的、从接口实现的所有public函数) public Method getMethod(String name, Class[] parameterTypes) //2.获取全部的public的函数(包括从基类继承的、从接口实现的所有public函数) public Method[] getMethods() //3.获取函数名name,参数是parameterTypes,并且是类自身声明的函数,包含public、protected和private方法。 public Method getDeclaredMethod(String name, Class[] parameterTypes) //4.获取全部的类自身声明的函数,包含public、protected和private方法。 public Method[] getDeclaredMethods() //5.如果这个类是其它类中某个方法的内部类,调用getEnclosingMethod()就是这个类所在的方法;若不存在,返回null。 public Method getEnclosingMethod()

举例:


ini

复制代码

Class<?> cls = Class.forName("com.juexing.mytest.ATest"); Method methodA = cls.getDeclaredMethod("testA"); System.out.println(methodA.getName()); Method methodB = cls.getDeclaredMethod("testB", String.class); System.out.println(methodB.getName()); Method[] methods = cls.getDeclaredMethods(); for(Method method : methods){ System.out.println(method.getName()); }

那么,得到Method对象之后又可以做什么操作呢?


css

复制代码

[获取方法名]


ini

复制代码

methodA.getName(); [调用方法]

使用invoke方法调用指定方法,如:


arduino

复制代码

methodA.invoke(constructor.newInstance());

invoke方法第一个参数传递指定类的实例(如果能获取到对应类的实例最好,如果获取不到,只用使用constructor.newInstance()代替了),后面几个参数传递方法的参数。

假设有这样一个类


csharp

复制代码

public class ATest { public ATest(){ } public void testA(){ System.out.println("调用了testA"); } public String testB(String a1, int b, float c){ System.out.println(a1); return a1; } private void testC(){ System.out.println("调用了testC"); } }

那么,该如何使用反射机制调用testA、testB、testC这三个方法呢?

调用testA(最简单的调用方式)


ini

复制代码

Class<?> cls = Class.forName("com.juexing.mytest.ATest"); Constructor constructor = cls.getDeclaredConstructor(); Method methodA = cls.getDeclaredMethod("testA"); methodA.invoke(constructor.newInstance());

调用testB(注意返回值和参数)


ini

复制代码

Class<?> cls = Class.forName("com.juexing.mytest.ATest"); Constructor constructor = cls.getDeclaredConstructor(); Method methodB = cls.getDeclaredMethod("testB", String.class, int.class, float.class); String aa = (String) methodB.invoke(constructor.newInstance(), "调用了testB", 2, 2.0f); System.out.println(aa);

invoke方法除了第一个参数外,剩下的几个参数都是testB方法的参数传值,aa是返回值。

调用testC(注意private修饰符)


ini

复制代码

Class<?> cls = Class.forName("com.juexing.mytest.ATest"); Constructor constructor = cls.getDeclaredConstructor(); Method methodC = cls.getDeclaredMethod("testC"); methodC.invoke(constructor.newInstance());

我们都知道,Java是无法访问其他类中的私有方法,如果使用以上代码调用testC方法会直接报错,如下:

img

图片.png

那么,反射机制能否调用私有方法?答案是可以的。

在代码中加入以下代码:


arduino

复制代码

methodC.setAccessible(true);

可以解决访问受限的问题。

(6)获取成员变量

scss

复制代码

//1.获取“名称是name”的public的成员变量(包括从基类继承的、从接口实现的所有public成员变量) public Field getField(String name) ​ //2.获取全部的public成员变量(包括从基类继承的、从接口实现的所有public成员变量) public Field[] getFields() ​ //3.获取“名称是name”,并且是类自身声明的成员变量,包含public、protected和private成员变量。 public Field getDeclaredField(String name) ​ //4.获取全部的类自身声明的成员变量,包含public、protected和private成员变量。 public Field[] getDeclaredFields()

拿到字段信息就可以访问这些字段了,如:


ini

复制代码

       field.get(obj);        field.getBoolean(obj);        field.getByte(obj);        field.getChar(obj);        field.getFloat(obj);        field.getDouble(obj);        field.getLong(obj);

当然,如果字段的修饰否是private,需要添加以下代码:


arduino

复制代码

      field.setAccessible(true);

既然可以获取字段,当然也可以修改字段:


ini

复制代码

       field.set(obj, null);        field.setBoolean(obj, false);        field.setByte(obj, (byte)1);        field.setChar(obj, 'C');        field.setFloat(obj, 1.0f);        field.setDouble(obj, 1.0);        field.setLong(obj, 1);

(7)总结

反射机制的相关方法还有很多,我想,了解以上提到的内容应该就差不多了,在一些主流框架中,反射机制运用非常的广泛,了解反射机制是成为架构师的必备功课之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值