Java反射机制是Java语言被视为准动态语言的关键性质。Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的相关信息,动态地生成此类,并调用其方法或修改其域(甚至是本身声明为private的域或方法)。
也许你使用Java已经很长时间了,可是几乎不会用到Java反射机制。你会嗤之以鼻地告诉我,Java反射机制没啥用。或许在J2EE、J2SE等平台,Java反射机制没啥用(具体我也不了解,不多做评论),但是在Android应用开发中,该机制会带给你许多惊喜。
如果熟悉Android,那么你应该知道,Google不知出于什么原因,在系统源码中一些类或方法中经常加上“@hide”注释标记。它的作用是使这个方法或类在生成SDK时不可见,因此由此注释的东西,你在编译期是不可见的。这就出现了一些问题。一些明明可以访问的东西编译期却无法访问了!这使得你的程序有些本来可以完成的功能无法编译通过。
当然,有一种办法是自己去掉Android源码中的所有“@hide”标记,然后重新编译一份自己的SDK。另一种办法就是使用Java反射机制。当然,你还可以利用反射来访问存在访问限制的方法和修改其域。不过这种使用方法比较特殊,我们在文章的最后单独讨论。
从Class类说起
如果你使用Java,那么你应该知道Java中有一个Class类。Class类本身表示Java对象的类型,我们可以通过一个Object(子)对象的getClass方法取得一个对象的类型,此函数返回的就是一个Class类。当然,获得Class对象的方法有许多,但是没有一种方法是通过Class的构造函数来生成Class对象的。
也许你从来没有使用过Class类,也许你曾以为这是一个没什么用处的东西。不管你以前怎么认为,Class类是整个Java反射机制的源头。一切关于Java反射的故事,都从Class类开始。
因此,要想使用Java反射,我们首先得到Class类的对象。下表列出了几种得到Class类的方法,以供大家参考。
Class object 诞生管道
|
示例
|
运用getClass()
注:每个class 都有此函数
|
String str = "abc";
Class c1 = str.getClass();
|
运用
Class.getSuperclass()
|
Button b = new Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();
|
运用static method
Class.forName()
(最常被使用)
|
Class c1 = Class.forName ("java.lang.String");
Class c2 = Class.forName ("java.awt.Button");
Class c3 = Class.forName ("java.util.LinkedList$Entry");
Class c4 = Class.forName ("I");
Class c5 = Class.forName ("[I");
|
运用
.class 语法
|
Class c1 = String.class;
Class c2 = java.awt.Button.class;
Class c3 = Main.InnerClass.class;
Class c4 = int.class;
Class c5 = int[].class;
|
运用
primitive wrapper classes
的TYPE 语法
|
Class c1 = Boolean.TYPE;
Class c2 = Byte.TYPE;
Class c3 = Character.TYPE;
Class c4 = Short.TYPE;
Class c5 = Integer.TYPE;
Class c6 = Long.TYPE;
Class c7 = Float.TYPE;
Class c8 = Double.TYPE;
Class c9 = Void.TYPE;
|
获取一些基本信息
在我们得到一个类的Class类对象之后,Java反射机制就可以大施拳脚了。首先让我们来了解下如何获取关于某一个类的一些基本信息。
Java class 内部模块
|
Java class 内部模块说明
|
相应之Reflection API,多半为Class methods。
|
返回值类型(return type)
|
package
|
class隶属哪个package
|
getPackage()
|
Package
|
import
|
class导入哪些classes
|
无直接对应之API。可间接获取。
|
|
modifier
|
class(或methods, fields)的属性
|
int getModifiers()
Modifier.toString(int)
Modifier.isInterface(int)
|
int
String
bool
|
class name or interface name
|
class/interface
|
名称getName()
|
String
|
type parameters
|
参数化类型的名称
|
getTypeParameters()
|
TypeVariable <Class>[]
|
base class
|
base class(只可能一个)
|
getSuperClass()
|
Class
|
implemented interfaces
|
实现有哪些interfaces
|
getInterfaces()
|
Class[]
|
inner classes
|
内部classes
|
getDeclaredClasses()
|
Class[]
|
outer class
|
如果我们观察的class 本身是inner classes,那么相对它就会有个outer class。
|
getDeclaringClass()
|
Class
|
上表中,列出了一些Java class内部信息的获取方式。所采用的方法几乎都是调用Class对象的成员方法(由此你就可以了解到Class类的用处了吧)。当然,表中所列出的信息并不是全部,有很大一部分没有列出,你可以通过查阅Java文档得到更全面的了解。另外,下面将重点介绍一下类的构造函数、域和成员方法的获取方式。
类中最重要的三个信息
如果要对一个类的信息重要性进行排名的话,那么这三个信息理应获得前三的名次。它们分别是:构造函数、成员函数、成员变量。
也许你不同意我的排名,没关系。对于Java反射来说,这三个信息与之前介绍的基本信息相比较而言,有着本质的区别。那就是,之前的信息仅仅是只读的,而这三个信息可以在运行时被调用(构造函数和成员函数)或者被修改(成员变量)。所以,我想无可否认,至少站在Java反射机制的立场来说,这三者是最重要的信息。
下面,让我们分别了解一下这三个重要信息的获取方式。另外,我们将在后面的章节,详细介绍他们的调用方式或者修改方式。
构造函数
如果我们将Java对象视为一个二进制的生活在内存中生命体的话,那么构造函数无疑可以类比为Java对象生命体的诞生过程。我们在构造函数调用时为对象分配内存空间,初始化一些属性,于是一个新的生命诞生了。
Java是纯面向对象的语言,Java中几乎所有的一切都是类的对象,因此可想而知构造函数的重要性。
Java反射机制能够得到构造函数信息实在应该是一件令人惊喜的事情。正因为此,反射机制实质上才拥有了孵化生命的能力。换句话言之,我们可以通过反射机制,动态地创建新的对象。
获取构造函数的方法有以下几个:
Constructor getConstructor(Class[] params)
Constructor[] getConstructors()
Constructor getDeclaredConstructor(Class[] params)
Constructor[] getDeclaredConstructors()
我们有两种方式对这四个函数分组。
首先可以由构造函数的确定性进行分类。我们知道,一个类实际上可以拥有很多个构造函数。那么我们获取的构造函数是哪个呢?我们可以根据构造函数的参数标签对构造函数进行明确的区分,因此,如果我们在Java反射时指定构造函数的参数,那么我们就能确定地返回我们需要的那个“唯一”的构造函数。getConstructor(Class[] params) 和getDeclaredConstructor(Class[] params)正是这种确定唯一性的方式。但是,如果我们不清楚每个构造函数的参数表,或者我们出于某种目的需要获取所有的构造函数的信息,那么我们就不需要明确指定参数表,而这时返回的就应该是构造函数数组,因为构造函数很可能不止一个。getConstructors()和getDeclaredConstructors()就是这种方式。
另外,我们还可以通过构造函数的访问权限进行分类。在设计类的时候,我们往往有一些构造函数需要声明为“private”、“protect”或者“default”,目的是为了不让外部的类调用此构造函数生成对象。于是,基于访问权限的不同,我们可以将构造函数分为public和非public两种。
getConstructor(Class[] params) 和getConstructors()仅仅可以获取到public的构造函数,而getDeclaredConstructor(Class[] params) 和getDeclaredConstructors()则能获取所有(包括public和非public)的构造函数。
成员函数
如果构造函数类比为对象的诞生过程的话,成员函数无疑可以类比为对象的生命行为过程。成员函数的调用执行才是绝大多数对象存在的证据和意义。Java反射机制允许获取成员函数(或者说成员方法)的信息,也就是说,反射机制能够帮助对象践行生命意义。通俗地说,Java反射能使对象完成其相应的功能。
和获取构造函数的方法类似,获取成员函数的方法有以下一些:
Method getMethod(String name, Class[] params)
Method[] getMethods()
Method getDeclaredMethod(String name, Class[] params)
Method[] getDeclaredMethods()
其中需要注意,String name参数,需要写入方法名。关于访问权限和确定性的问题,和构造函数基本一致。
成员变量
成员变量,我们经常叫做一个对象的域。从内存的角度来说,构造函数和成员函数都仅仅是Java对象的行为或过程,而成员变量则是真正构成对象本身的细胞和血肉。简单的说,就是成员变量占用的空间之和几乎就是对象占用的所有内存空间。
获取成员变量的方法与上面两种方法类似,具体如下:
Field getField(String name)
Field[] getFields()
Field getDeclaredField(String name)
Field[] getDeclaredFields()
其中,String name参数,需要写入变量名。关于访问权限和确定性的问题,与前面两例基本一致。
让动态真正动起来
在本文的一开始就说,Java反射机制是Java语言被视为准动态语言的关键性质。如果Java反射仅仅能够得到Java类(或对象)运行时的信息,而不能改变其行为和属性,那么它当然算不上“动态”。百度了一把何谓“动态语言”,解释如下:动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。由此看来,Java确实不能算作“动态语言”。但是和C、C++等纯静态语言相比,Java语言允许使用者在运行时加载、探知、使用编译期间完全未知的classes,所以我们说Java是“准动态”语言。
细心地读者可能已经发现,在“类中最重要的三个信息”一节中,我们获取的信息其实都是属于类的,而不是对象。对于类的信息提取,其实并不涉及到对象内存,在程序编译完成的那一刻起,一切都已经是确定的了。因此,它并不能算“动态”。而如何对对象内存进行操作和访问,才是“动”的真正含义。
说了这么多,关键还在于如何利用反射让Java真正动起来。下面我将按照创生、行为与属性三个方面来介绍反射机制是如何让Java动的。
创生
不知是否本性使然,人类偏爱于思索起源与终结的话题。如果将程序类比于一个二进制的世界的话,那么我们程序员则是这个世界的上帝。我们掌控着这个世界的起源和终结,熟悉世界中一草一木的属性和所有生灵的习性。现在就让我们开始创世纪吧!
在 “构造函数”那一小节中,我们列出了获取构造函数的四种方法。这四种方法的返回值不知是否引起了各位的注意,那就是Constructor类。Constructor就类比于女娲吹给泥人的那一口真气,有了它,一个生命才真正出现。
Constructor支持泛型,也就是它本身应该是Constructor<T>。这个类有一个public成员函数,T newInstance(Object... args),其中args为对应的参数。我们正是通过它来实现创生的过程。
行为
行为践行着生命的意义,而众多事物的行为才得以构成整个世界的运转。尽管道家的老子主张“无为而治”,宣扬“圣人处无为之事,行不言之教”,但那是因为他本身就是 “无”的信仰者(“道”即“无”)。我们是唯物主义的信徒,所以必然要以“有”为价值。那么,在二进制的世界里,我们如何调用Java对象的行为呢?
同样,我们首先回顾“成员函数”小节中四种方法的返回值。对,那就是Method类。此类有一个public成员函数,Object invoke(Object receiver, Object... args)。我们能很好理解此函数的第二个参数args,它代表这个方法所需要接收的参数。也许大家对第一个参数receiver还存在疑惑之处。这得从编程语言的发展历程讲起。
如果你关注几种主流编程语言的起源,那么你能有这样的印象:C从汇编而来,C++从C而来,而Java从C/C++而来。有这样一种印象就足够了。从这样的发展史我们可以看出,C++和Java这两种面向对象的编程语言都是从面向过程的C语言基础上发展而来的。OOP是一种思想,它本身与编程语言无关。也就是说,我们用C也能写出面向对象的程序,这也是C++和Java能够以C为基础的根本所在。然而,C无法实现类似object.method()这种表现形式,因为C语言的结构体中并不支持函数定义。那么我们用C实现OOP的时候,如何调用对象的方法呢?
本质上说,object.method()这种调用方式是为了表明具体method()的调用对象。而invoke(Object receiver, Object... args)的第一个参数正是指明调用对象。在C++中,object.method()其实是有隐含参数的,那就是object对象的指针,method原型的第一个参数其实是this指针,于是原型为method(void* this)。
这样一溯源,也许你更清楚了Object receiver参数的含义,或许更迷糊了?不管怎样,历史就是如此,只不过我个人能力有限,说不清楚而已。
另外需要注意的是,如果某个方法是Java类的静态方法,那么Object receiver参数可以传入null,因为静态方法不从属于对象。
属性
同样是人类,令狐冲和岳不群是如何被区分开的?那是因为他们有着不同的属性。同样,同一个类可以生成多个对象,几个同类型的对象之间如何区分?属性起着决定性的作用。说到这里,想起一个科幻故事。人体瞬移机,作用的根本原理就是人进入A位置,被完全扫描之后,再在B位置重新组成它的细胞、血肉等属性,从而完全创造出另一个一模一样的人。当然,这是唯物主义的极致,它假设了只要一切物质相同,连记忆和灵魂都不会出现偏差,另外还存在伦理的问题,例如A位置的人会被销毁掉吗?
尽管这是一个科幻,但是在程序的世界里,我们早已经用上了这类似幻想的技术。Java中如何远程传递一个对象?我们已经使用上了Java对象序列化的接口。不仅如此,利用序列化接口,我们甚至可以将一个生命保存起来,在需要的时候将它复活,这就是对象的持久化。不得不感慨,在程序的世界里,我们就是上帝啊!
对象序列化如此强大,那么它的本质是什么呢?它的工作原理是怎样的呢?简单的说,对象序列化的本质就是属性的序列化。原理就是我们崇尚的唯物主义,如果同一个类的两个对象所有属性值都完全相同,那么我们可以认为这是同一个对象。
说了这么多,只是想说明一件事情,属性对于对象而言是多么的重要。那么如何读写对象中属性的值呢?回顾获取属性信息的方法返回值类型,那是Field。Field类有两个public方法,分别对应读与写,它们是:
Object get(Object object)
void set(Object object, Object value)
object参数需要传入的对象,原理类似于成员方法需要指明对象一样。如果是静态属性,此值同样可以为null。
关于反射的一些高级话题
如果说前面那些属于Java反射的基本知识,那么在文章的最后,我们来探讨一下反射的一些高级话题。另外,本文对基础知识的讲解仅属于抓主干,具体的一些旁支可以自己参看文档。需要提一下的是,Java反射中对数组做过单独的优化处理,具体可查看java.lang.reflect.Array类;还有关于泛型的支持,可查看java.lang.reflect.ParameterizedType及相关资料。
暂时想到的高级话题有三个,由于对Java反射理解的也不算深入,所以仅仅从思路上进行探讨,具体实现上,大家可以参考其他相关资料,做更深入研究。
Android编译期问题
Android的安全权限问题我把它简单的划分成三个层次,最不严格的一层就是仅仅骗过编译器的“@hide”标记。对于一款开源的操作系统而言,这个标记本身并不具备安全上的限制。不过,从上次Google过来的负责Android工程师的说法来看,这个标记的作用更多的是方便硬件厂商做闭源的
分享到:
相关推荐
这篇博文"Java反射机制学习(二)"可能深入探讨了如何利用反射进行动态类型处理、访问私有成员以及创建对象等核心概念。在这里,我们将详细讨论Java反射的基本用法及其在实际开发中的应用。 1. **什么是反射**: ...
在"JAVA反射机制学习"资料中,你将深入理解这一关键概念。 首先,我们要明白什么是反射。在Java中,反射是一种在运行时分析类、接口、字段和方法的能力。通过使用`java.lang.Class`对象,我们可以获取任何类的信息...
Java反射机制是Java编程语言中的一种强大功能,它允许程序在运行时检查和操作类、接口、字段和方法的信息,即使在编译时这些信息是未知的。这一特性使得Java在某种程度上具备了动态语言的特性,尽管Java本身是一种...
在本篇“java反射机制学习(五):工厂模式”中,我们将探讨如何利用反射与工厂模式结合,提升代码的灵活性和可扩展性。 首先,工厂模式是一种常用的创建型设计模式,它提供了一种创建对象的最佳方式。传统的工厂模式...
Java反射机制是Java编程语言中一个强大的工具,它允许程序在运行时动态地获取类的信息并操作类的对象。反射机制的核心在于java.lang.reflect包中的API,主要包括Class类、Constructor类、Method类和Field类。以下是...
总结来说,Java反射机制是Java语言的一个重要特性,它使得程序能够在运行时动态地获取类的信息并进行操作,提高了代码的通用性和适应性。在实际开发中,反射机制常用于框架设计、插件系统、数据持久化等领域,但同时...
Java反射机制是Java编程语言的一项重要特性,它允许程序在运行时动态地获取类的信息,并且能够对类的属性和方法进行操作。反射机制的核心在于Java的`java.lang.reflect`包,其中包含了`Class`、`Constructor`、`...
Java反射机制是Java编程语言中一个强大的特性,它允许运行时动态地获取类的信息并进行操作,如创建对象、调用方法、访问属性以及处理数组等。在本篇讲解中,我们将深入理解如何通过反射来执行方法、操作属性以及处理...
Java反射机制是Java编程语言中的一个重要特性,它允许程序在运行时检查和操作类、接口、字段和方法等对象。这种能力使得Java具备了高度的灵活性和动态性,尤其是在处理未知类或者需要在运行时决定行为的情况下。以下...
通过本文的学习,我们深入了解了Java反射机制的基本概念、原理以及应用。Java反射机制为Java程序提供了一种强大的动态处理能力,使得Java不仅仅局限于静态类型语言的功能限制。掌握Java反射机制对于开发者来说是非常...
Java反射机制是Java语言提供的一种强大功能,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在本教程中,我们将深入探讨如何通过反射获取实体类的属性、方法、接口以及父类信息。 首先,让...
Java反射机制是Java语言提供的一种强大功能,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在本篇文章中,我们将深入探讨如何通过反射获取构造方法并实例化对象,这对于理解和使用Java的...
Java反射机制是Java语言中一个强大的工具,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在动态代理部分,我们将探讨如何利用反射机制实现动态代理,这在处理面向切面编程(AOP)或者为已...
Java反射机制是Java编程语言中的一个强大特性,它允许程序在运行时检查和操作类、接口、对象等的内部信息。这一机制对于理解和实现高级框架、动态代理、元数据处理等场景至关重要。以下是对Java反射机制的详细说明:...
在"北大青鸟java反射机制"的学习资料中,我们将会深入探讨这一核心特性。 首先,我们要理解反射的核心概念:在程序运行时,通过Class对象获取类的信息,包括类名、属性、方法等,并能够动态地创建对象和调用方法。...
总的来说,"JAVA反射机制的入门代码"是初学者理解Java反射机制的好教材,通过它,你可以学习如何动态地操作Java类,提高代码的灵活性,并掌握处理`properties`文件的基础方法。在深入学习和实践中,你将进一步理解...
Java反射机制是Java编程语言中的一个重要特性,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在Java中,反射机制的核心类集中...在实践中学习,你会更好地理解Java反射机制的威力和应用场景。
Java反射机制是Java编程语言中的一个强大特性,...通过学习和实践Java反射机制,你可以更深入地理解Java的运行机制,并在需要动态性、灵活性的场景中发挥其强大的功能。不过,使用反射时也应注意潜在的风险和性能影响。
Java反射机制是Java编程语言中的一个强大特性,它允许程序在运行时检查和操作类、接口、字段以及方法等对象。这一机制的核心在于`java.lang.Class`类和相关的API,它们提供了对类元数据的访问,使得我们能够在运行时...