本文属于面试题梳理系列:问题:java反射类的訪问私有方法与普通方法相比,须要多处理什么? 
之前梳理类载入的时候,介绍到初始化的时机之中的一个:用java.lang.reflect包的方法对类进行反射调用的时候,假设类没有进行过初始化。则须要先触发其初始化。以下梳理相关知识。文件夹例如以下:
1.什么是JAVA的反射机制

2.JAVA反射机制API及功能

获取类的Class对象

获取类的Fields

获取类的Method

获取类的Constructor

新建类的实例

       Class<T>的函数newInstance

       通过Constructor对象的方法newInstance

3调用类的函数

         调用private函数

4设置/获取类的属性值

         private属性

以下段落展开详述。
什么是JAVA的反射机制

Java反射是Java被视为动态(或准动态)语言的一个关键性质。

这个机制同意程序在执行时透过Reflection APIs取得不论什么一个已知名称的class的内部信息,包含其modifiers(诸如public, static 等)、superclass(比如Object)、实现之interfaces(比如Cloneable)。也包含fields和methods的全部信息,并可于执行时改变fields内容或唤起methods。

Java反射机制容许程序在执行时载入、探知、使用编译期间全然未知的classes。换言之,Java能够载入一个执行时才得知名称的class,获得其完整结构。

这样的“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。

java反射API及功能
在JDK中,主要由下面类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中

  Class类:代表一个类,位于java.lang包下。

  Field类:代表类的成员变量(成员变量也称为类的属性)。

  Method类:代表类的方法。

  Constructor类:代表类的构造方法。

  Array类:提供了动态创建数组,以及訪问数组的元素的静态方法。

获取类的Class对象:

要想使用反射,首先须要获得待操作的类所相应的Class对象。

  Java中,不管生成某个类的多少个对象。这些对象都会相应于同一个Class对象。

  这个Class对象是由JVM生成的,通过它可以获悉整个类的结构。

经常使用的获取Class对象的3种方式:

  1).使用Class类的静态方法。比如:  Class.forName("java.lang.String");

 	2).使用类的.class语法。如: 	String.class;
    3).使用对象的getClass()方法。如:
	String str = "aa";
Class<? > classType1 = str.getClass();


获取类的Fields

能够通过反射机制得到某个类的某个属性,然后改变相应于这个类的某个实例的该属性值。JAVA 的Class<T>类提供了几个方法获取类的属性。
public FieldgetField(String name) 返回一个
Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段
public Field[]
getFields()
返回一个包括某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的全部可訪问公共字段
public FieldgetDeclaredField(Stringname) 返回一个
Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
public Field[]
getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的全部字段

public static void main(String[] args) throws ClassNotFoundException {
// TODO Auto-generated method stub Class<?> pc3 = Class.forName("reflect.Person");
Field[] fields=pc3.getFields();
for (Field f : fields)
{
System.out.println("field:"+f);
}
Field[] fields2= pc3.getDeclaredFields();
for (Field f : fields2)
{
System.out.println("d field:"+f);
}
}

执行结果:

field:public java.lang.String reflect.Person.name
d field:private java.lang.String reflect.Person.password
d field:public java.lang.String reflect.Person.name
 可见getFields和getDeclaredFields差别:

getFields返回的是申明为public的属性,包含父类中定义。

getDeclaredFields返回的是指定类定义的全部定义的属性,不包含父类的。

获取类的Method

通过反射机制得到某个类的某个方法,然后调用相应于这个类的某个实例的该方法

Class<T>类提供了几个方法获取类的方法。

public MethodgetMethod(String name,Class<?>...
parameterTypes)

返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法

public Method[]
getMethods()

返回一个包括某些 Method 对象的数组。这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member
方法

public MethodgetDeclaredMethod(Stringname,Class<?>...
parameterTypes)

返回一个 Method 对象。该对象反映此 Class 对象所表示的类或接口的指定已声明方法

public Method[]
getDeclaredMethods()

返回 Method 对象的一个数组。这些对象反映此 Class 对象表示的类或接口声明的全部方法,包含公共、保护、默认(包)訪问和私有方法,但不包含继承的方法

依据输出。注意getMethods与getDeclaredMethods的差别。

获取类的Constructor

通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例

Class<T>类提供了几个方法获取类的构造器。

public Constructor<T>
getConstructor(Class<?

>...
parameterTypes)

返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法

public Constructor<?>[]
getConstructors()

返回一个包括某些 Constructor 对象的数组。这些对象反映此 Class 对象所表示的类的全部公共构造方法

public Constructor<T>
getDeclaredConstructor(Class<?>...
parameterTypes)

返回一个 Constructor 对象。该对象反映此 Class 对象所表示的类或接口的指定构造方法

public Constructor<?>[]
getDeclaredConstructors()

返回 Constructor 对象的一个数组。这些对象反映此 Class 对象表示的类声明的全部构造方法。它们是公共、保护、默认(包)訪问和私有构造方法


Constructor<?>[] cons=pc3.getConstructors();
for (int i = 0; i < cons.length; i++) {
System.out.println("cons: "+cons[i]);
}

输出:

cons:  public reflect.Person()
cons:  public reflect.Person(java.lang.String)
 生成对象:
1.先获得Class对象,然后通过该Class对象的newInstance()方法直接生成就可以:
2.先获得Class对象。然后通过该对象获得相应的Constructor对象。再通过该Constructor对象的newInstance()方法生成
3.带參

3.调用方法:

通过反射获取类Method对象。调用Field的Invoke方法调用函数。

通过输出,能够看出setName的方法能够调用。可是私有方法调用失败。也就是本文最開始的问题。

4设置/获取类的属性值

通过反射获取类的Field对象,调用Field方法设置或获取值

跟上面类似。訪问私有属性失败。

一问题解答:訪问私有方法,须要调用Class.getDeclaredMethod(String name,Class[] parameterTypes)或者Class.getDeclaredMethods()方法。方法Class.getMethod(String name,Class[] parameterTypes)和Class.getMethods()只返回公有方法。

此外还须要设置訪问权限。

例如以下:

Method method2 = pc3.getDeclaredMethod("setPri",String.class);
method2.setAccessible(true);
method2.invoke(pp, "test");
为什么呢?
二须要结合method的invoke方法源代码去看下。


2.1.先检查 AccessibleObject的override属性是否为true。

AccessibleObject是Method,Field,Constructor的父类,override属性默觉得false,可调用setAccessible方法改变,假设设置为true,则表示能够忽略訪问权限的限制,直接调用。

2.2.假设不是ture,则要进行訪问权限检測。用Reflection的quickCheckMemberAccess方法先检查是不是public的,假设不是再用Reflection.getCallerClass(1)方法获得到调用这种方法的Class,然后做是否有权限訪问的校验,校验之后缓存一次。以便下次假设还是这个类来调用就不用去做校验了。直接用上次的结果,(非常奇怪用这样的方式缓存,由于这样的方式假设下次换个类来调用的话,就不用会缓存了,而再验证一遍,把这次的结果做为缓存。但上一次的缓存结果就被冲掉了。这是一个非常easy的缓冲机制,仅仅适用于一个类的反复调用)。 

2.3.调用MethodAccessor的invoke方法。每一个Method对象包括一个root对象,root对象里持有一个MethodAccessor对象。我们获得的Method独享相当于一个root对象的镜像,全部这类Method共享root里的MethodAccessor对象,(这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类中是static final的由native方法实例化)。

深入分析MethodAccessor的创建机制
  能够看到Method.invoke()实际上并非自己实现的反射调用逻辑,而是托付给sun.reflect.MethodAccessor来处理。 

每一个实际的Java方法仅仅有一个相应的Method对象作为root,这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。

在第一次调用一个实际Java方法相应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完毕反射调用。

     那么MethodAccessor是啥呢? jdk源代码没有了,从openjdk看下源代码。

能够看到它仅仅是一个单方法接口,其invoke()方法与Method.invoke()的相应。 
再沿着上面的分析。看看MethodAccessor是怎么创建的。代码片段分析:

推断为空 要先reflectionFactory创建,

假设noInflation的属性为true则直接返回MethodAccessorGenerator创建的一个MethodAccessor。否则返回DelegatingMethodAccessorImpl。并将他与一个NativeMethodAccessorImpl互相引用。
实际的MethodAccessor实现有两个版本号,一个是Java实现的,还有一个是native code实现的。Java实现的版本号在初始化时须要较多时间,但长久来说性能较好;native版本号正好相反。启动时相对较快。但执行时间长了之后速度就比只是Java版了。这是HotSpot的优化方式带来的性能特性,同一时候也是很多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联。于是执行时间长了之后反而是托管版本号的代码更快些。

为了权衡两个版本号的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时。开头若干次使用native版。等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类。生成当中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。

以下相应源代码:

  

每次NativeMethodAccessorImpl.invoke()方法被调用时,都会添加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,而且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。兴许经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。注意到关键的invoke0()方法是个native方法。它在HotSpot
VM里是由JVM_InvokeMethod()函数所支持的,扯个额外话:想想大多数java有关的东西,假设你愿意去看源代码分析的话。总会找到jvm这层。当然还有大神继续研究Hotspot的里面的C源代码,我认为主要知识点到这一层就算是比較深入了 。

再看DelegatingMethodAccessorImpl:

这是一个间接层,方便在native与Java版的MethodAccessor之间实现切换。

native结束,再看java版本号:MethodAccessorGenerator

它的基本工作就是在内存里生成新的专用Java类。并将其载入。

 private static synchronized String generateName(boolean isConstructor,
boolean forSerialization)
{
if (isConstructor) {
if (forSerialization) {
int num = ++serializationConstructorSymnum;
return "sun/reflect/GeneratedSerializationConstructorAccessor" + num;
} else {
int num = ++constructorSymnum;
return "sun/reflect/GeneratedConstructorAccessor" + num;
}
} else {
int num = ++methodSymnum;
return "sun/reflect/GeneratedMethodAccessor" + num;
}
}

上面贴出来的方法就是产生名字的。至于大神RednaxelaFX贴出来解释GeneratedMethodAccessor1
这段看不懂。如有大神理解,还请多多留言指正。

http://rednaxelafx.iteye.com/blog/548536

****************总结********************

JAVA反射原理分析及JAVA反射的应用待整理。

參考:
http://blog.csdn.net/yongjian1092/article/details/7364451
http://www.cnblogs.com/mengdd/archive/2013/01/26/2878136.html

http://www.cnblogs.com/onlywujun/p/3519037.html

http://rednaxelafx.iteye.com/blog/548536

java梳理-反射的更多相关文章

  1. JAVA的反射理解

    1----------------------------反射的概念----------------------------------------------- JAVA的反射机制是在运行状态中,对 ...

  2. java的反射

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

  3. iOS运行时编程(Runtime Programming)和Java的反射机制对比

    运行时进行编程,类似Java的反射.运行时编程和Java反射的对比如下:   1.相同点   都可以实现的功能:获取类信息.属性设置获取.类的动态加载(NSClassFromString(@“clas ...

  4. Java 类反射机制分析

    Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...

  5. java的反射机制

    一.java的反射机制浅谈 最近研究java研究得很给力,主要以看博文为学习方式.以下是我对java的反射机制所产生的一些感悟,希望各位童鞋看到失误之处不吝指出.受到各位指教之处,如若让小生好好感动, ...

  6. Java:反射

    初识Java反射机制: 从上面的描述可以看出Java的反射机制使得Java语言可以在运行时去认识在编译时并不了解的类/对象的信息,并且能够调用相应的方法或修改属性的值.Java反射机制的核心就是允许在 ...

  7. Java中反射机制和Class.forName、实例对象.class(属性)、实例对象getClass()的区别

    一.Java的反射机制   每个Java程序执行前都必须经过编译.加载.连接.和初始化这几个阶段,后三个阶段如下图:   其中

  8. java笔记--反射进阶之总结与详解

    一.反射进阶之动态设置类的私有域 "封装"是Java的三大特性之一,为了能更好保证其封装性,我们往往需要将域设置成私有的, 然后通过提供相对应的set和get方法来操作这个域.但是 ...

  9. java笔记--反射机制之基础总结与详解

    一.反射之实例化Class类的5种方式: java的数据类型可以分为两类,即引用类型和原始类型(即基本数据类型). 对于每种类型的对象,java虚拟机会实例化不可变的java.lang.Class对象 ...

随机推荐

  1. Pro ASP.NET Core MVC 第6版翻译 目录页

    Pro ASP.NET Core MVC 第6版 目录 第一部分 第一章 ASP.NET Core MVC 的前世今生 第二章 第一个MVC应用程序(上) 第二章 第一个MVC应用程序(下) 第三章 ...

  2. Layui数据表单的编辑

    使用layui对单元格进行编辑并保存 先是要引入layui的JS和CSS 然后创建一个表格 而重要的是edit这个属性,只有使用了这个属性的一列数据表格才可以编辑,其余的都不可以进行编辑 然后使用la ...

  3. Flask框架 之第一个Flask程序

    from flask import Flask # 创建flask应用对象 # __name__ 代表当前模块名称 # flask以当前目录为总目录,static目录为静态目录,templates为模 ...

  4. Java笔记——String、StringBuffer和StringBuilder类

    String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符串序列是不可改变的,直至这个对象被销毁.   StringBuffer对象则代表一个字符序列可变的字符串,当一个S ...

  5. [Python3网络爬虫开发实战] 1.1-Python3的安装

    既然要用Python 3开发爬虫,那么第一步一定是安装Python 3.这里会介绍Windows.Linux和Mac三大平台下的安装过程. 1. 相关链接 官方网站:http://python.org ...

  6. HTTP服务和APACHE

    HTTP服务和APACHE 1. 跨Internet的主机间通讯 要通过Internet进行通信,至少需要一对套接字:其中一个运行在客户端,定义了一个唯一的客户进程,称之为ClientSocket,另 ...

  7. 爬虫-----HTML解析

    对HTML的解析: 在解析复杂的HTML的页面时,需要避免一些问题,好让爬虫工作变得得心应手. • 寻找“打印此页”的链接,或者看看网站有没有HTML样式更友好的移动版(把自己 的请求头设置成处于移动 ...

  8. 【C++】实现记录软件计时时间

    利用getTickCount()和getTickFrequency()函数实现计时 double time0 = static_cast<double>(getTickCount()); ...

  9. 数据库中间件MyCat学习总结(1)——MyCat入门简介

    为什么需要MyCat? 虽然云计算时代,传统数据库存在着先天性的弊端,但是NoSQL数据库又无法将其替代.如果传统数据易于扩展,可切分,就可以避免单机(单库)的性能缺陷. MyCat的目标就是:低成本 ...

  10. MongoDB 数据文件备份与恢复

    备份与恢复数据对于管理任何数据存储系统来说都是非常重要的. 1.冷备份与恢复——创建数据文件的副本(前提是要停止MongoDB服务器),也就是直接copy MongoDB将所有数据都存储在数据目录下, ...