第二十四部分_Java反射详解

Java语言的反射机制

在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java语言的反射(Reflection)机制。

Java反射机制主要提供了以下功能

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。

Reflection是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息。包括其modifiers(诸如public、static等)、 superclass(例如Object)、实现了的 interfaces (例如Serializable)、也包括其fields和methods的所有信息,并可于运行时改变fields内容或调用methods。

一般而言,开发者社群说到动态语言,大致认同的一个定义是"程序运行时,允许改变程序结构或者变量类型,这种语言称为动态语言"。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是:反射、映像、倒影,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。

Java Reflection API简介

在JDK中,主要由以下类来实现Java反射机制,这些类(除了Class类)都位于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 = "hello world";

     Class<?> classType = str.getClass();

     (getClass()方法定义在Object类中,不是静态方法,需要通过对象来调用,并且它声明为final,表明不能被子类所覆写。)

实例:

例程DumpMethods类演示了Reflection API的基本作用,它读取命令行参数指定的类名,然后打印这个类所具有的方法信息。 

package com.test.reflection;

import java.lang.reflect.Method;

public class DumpMethods
{
	public static void main(String[] args) throws Exception
	{
		// 加载并初始化命令行参数指定的类(运行期的行为)
		Class<?> classType = Class.forName(args[0]);

		// 获得类的所有方法
		Method[] methods = classType.getMethods();

		for(int i = 0; i < methods.length; i++)
		{
			System.out.println(methods[i]);
		}
	}
}

运行时参数设为java.lang.Object输出如下:

public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

例程ReflectTester类进一步演示了Reflection API的基本使用方法。ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将它返回。

package com.test.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectTester
{
	public Object copy(Object object) throws Exception
	{
		// 获得对象的类型
		Class classType = object.getClass();
		System.out.println("Class:" + classType.getName());

		// 通过默认构造方法创建一个新的对象
		Object objectCopy = classType.getConstructor(new Class[] {})
				.newInstance(new Object[] {});

		// 获得对象的所有属性
		Field fields[] = classType.getDeclaredFields();

		for (int i = 0; i < fields.length; i++)
		{
			Field field = fields[i];

			String fieldName = field.getName();
			String firstLetter = fieldName.substring(0, 1).toUpperCase();
			// 获得和属性对应的getXXX()方法的名字
			String getMethodName = "get" + firstLetter + fieldName.substring(1);
			// 获得和属性对应的setXXX()方法的名字
			String setMethodName = "set" + firstLetter + fieldName.substring(1);

			// 获得和属性对应的getXXX()方法
			Method getMethod = classType.getMethod(getMethodName,
					new Class[] {});
			// 获得和属性对应的setXXX()方法
			Method setMethod = classType.getMethod(setMethodName,
					new Class[] { field.getType() });

			// 调用原对象的getXXX()方法
			Object value = getMethod.invoke(object, new Object[] {});
			System.out.println(fieldName + ":" + value);
			// 调用拷贝对象的setXXX()方法
			setMethod.invoke(objectCopy, new Object[] { value });
		}
		return objectCopy;
	}

	public static void main(String[] args) throws Exception
	{
		Customer customer = new Customer("Tom", 21);
		customer.setId(new Long(1));

		Customer customerCopy = (Customer) new ReflectTester().copy(customer);
		System.out.println("Copy information:" + customerCopy.getId() + " "
				+ customerCopy.getName() + " " + customerCopy.getAge());
	}
}

class Customer
{
	private Long id;

	private String name;

	private int age;

	public Customer()
	{
	}

	public Customer(String name, int age)
	{
		this.name = name;
		this.age = age;
	}

	public Long getId()
	{
		return id;
	}

	public void setId(Long id)
	{
		this.id = id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public int getAge()
	{
		return age;
	}

	public void setAge(int age)
	{
		this.age = age;
	}
}

/*
 output:
	Class:com.test.reflection.Customer
	id:1
	name:Tom
	age:21
	Copy information:1 Tom 21
*/

这个例子只能复制简单的JavaBean,假定JavaBean的每个属性都有public类型的getXXX()和setXXX()方法。

ReflectTester类的copy(Object object)方法依次执行以下步骤
(1)获得对象的类型:
–Class classType=object.getClass();
–System.out.println("Class:"+classType.getName());

在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API 中的核心类,它有以下方法
–getName():获得类的完整名字。
–getFields():获得类的public类型的属性。
–getDeclaredFields():获得类的所有属性。
–getMethods():获得类的public类型的方法。
–getDeclaredMethods():获得类的所有方法。

-getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
-getConstructors():获得类的public类型的构造方法。
-getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
-newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

(2)通过默认构造方法创建一个新对象:
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。

(3)获得对象的所有属性:
Field fields[]=classType.getDeclaredFields();
Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性

(4)获得每个属性相应的getXXX()和setXXX()方法,然后执行这些方法,把原来对象的属性拷贝到新的对象中

关于Class类:

众所周知Java有个Object class,是所有Java classes的继承根源,其内声明了数个应该在所有Java class中被改写的methods:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class object。Class class十分特殊。它和一般classes一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class object。如果您想借由“修改Java标准库源码”来观察Class object的实际生成时机(例如在Class的constructor内添加一个println()),不能够!因为Class并没有public constructor。Class是Reflection起源。针对任何您想探勘的class,唯有先为它产生一个Class object,接下来才能经由后者唤起为数十多个的Reflection APIs。

"Class" object 的获取途径

  1.运用getClass()方法,返回Class对象。

  运用Class.getSuperclass()可以得到父类的Class对象,如果是Object类则返回null。

  2.运用静态方法Class.forName()

  3.运用.class语法。类名.class。

  其中,还可以通过int[].class的形式获得整形数组的Class对象。

  包装类的.TYPE语法实际返回的是所对应的原生数据类型的Class对象。

利用反射调用私有方法、访问私有属性

  利用反射,首先是Class对象的获取,之后是Method和Field对象的获取。

  以Method为例,从文档中可以看到:getMethod()方法返回的是public的Method对象,而getDeclaredMethod()返回的Method对象可以是非public的。

  Field的方法同理。

  访问私有属性和方法,在使用前要通过AccessibleObject类(Constructor、 Field和Method类的基类)中的setAccessible()方法来抑制Java访问权限的检查

实例1,调用私有方法:假设有这样一个类,其中包含私有方法。

public class PrivateClass
{
    private String sayHello(String name)
    {
        return "Hello: " + name;
    }

}

  利用反射机制在外部访问该方法: 

import java.lang.reflect.Method;

public class TestPrivate
{

    public static void main(String[] args) throws Exception
    {
        PrivateClass p = new PrivateClass();

        Class<?> classType = p.getClass();

        // 获取Method对象
        Method method = classType.getDeclaredMethod("sayHello",
                new Class[] { String.class });

        method.setAccessible(true); // 抑制Java的访问控制检查
        // 如果不加上上面这句,将会Error: TestPrivate can not access a member of class PrivateClass with modifiers "private"
        String str = (String) method.invoke(p, new Object[] { "zhangsan" });

        System.out.println(str);
    }
}

实例2,访问私有属性,直接访问私有属性,将例子中的私有属性改值。

  一个包含私有属性的类:

public class PrivateClass2
{
    private String name = "zhangsan";

    public String getName()
    {
        return name;
    }
}

  利用反射修改其私有属性的值:  

import java.lang.reflect.Field;

public class TestPrivate2
{
    public static void main(String[] args) throws Exception
    {
        PrivateClass2 p = new PrivateClass2();
        Class<?> classType = p.getClass();

        Field field = classType.getDeclaredField("name");

        field.setAccessible(true); // 抑制Java对修饰符的检查
        field.set(p, "lisi");

        System.out.println(p.getName());
    }

}
时间: 2024-07-30 23:54:27

第二十四部分_Java反射详解的相关文章

“全栈2019”Java第五十四章:多态详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第五十四章:多态详解 下一章 "全栈2019"Java第五十五章:方法的静态绑定与动态绑定 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学习小组"

OSChina 技术周刊第二十二期 —— DUBBO 配置规则详解

每周技术抢先看,总有你想要的! 移动开发 [翻译]为你的 Android 应用增加本地搜索功能 前端开发 [软件]AngularJS 的剪贴板扩展 ngClip [软件]国际化和本地化 JavaScript 库 Globalize [资讯]为网站开发准备的 30 个惊艳的 jQuery 插件 服务端开发/管理 [翻译]一年之后重新审视 Docker -- 根本性缺陷和炒作 [翻译]单线程 1KB 的 Redis 写操作有 84% 都是耗费在内核上 [翻译]使用 HAProxy 基于 HTTP 头

(十四)桥接模式详解(都市异能版)

魔都国贸附近,某天夜里十一点半. 那一晚,魔都出奇的没有做只打雷不下雨的勾当,老天似乎是要将魔都淹没一般,倾盆大雨像不要命似的拍打着地面. 漆黑的夜幕中,一道黑影从一个十字路口狂奔而出,而就在此时,一辆红色宝马急速穿过,一瞬间,黑影就犹如被巨力抛飞一般腾空而起,直到几秒钟后,才重重的摔落在车前足足二十多米处. "吱...兹...." 一声凄厉的刹车声划破了魔都的长空. 之后,一个身材高挑的女子,慌忙的从车中奔跑到黑影落下的地方,或许是由于看到倒下之人满身鲜血的模样,女子吓的不禁发出一声

linux架构学习第二十五天HTTP协议详解

内容: 1.http协议概述 2.http协议特点 3.http的工作模式(过程) 4.http请求报文.响应报文格式.常见状态码解析 5.web资源概述(静态资源.动态资源) 1.http协议概述 http协议工作在TCP/IP模型的应用层,其定义web服务间通信的约定通信方式,HTTP基于tcp传送数据,默认是80端口(服务器端) 几个名词: http:hyper text transfer protocol,超文本传输协议,超文本传输协议(HTTP)是一种通信协议,它允许将超文本标记语言(

Java学习系列(二十)Java面向对象之反射详解

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/45289391 前言 今天讲讲Java中的反射.我们常见的反射多用于JDBC中的加载驱动程序Class.forName("com.mysql.jdbc.Driver");.Struts的MVC.Hibernate中的ORM.Spring中的IOC还有一些其他框架等等.那它有什么好处呢?它的好处就是能够动态的创建对象和编译且能够访问某个类中的所有(包括私有)属性方法及对象的属性方法

Swoole源码学习记录(十四)——Server模块详解(下)

swoole版本:1.7.6-stable 上一章已经分析了如何启动swServer的相关函数.本章将继续分析swServer的相关函数, 1.swServer函数分析 swServer_addListener 该函数用于在swServer中添加一个需要监听的host及port.函数原型如下: // Server.h 438h int swServer_addListener(swServer *serv, int type, char *host,int port); 参数 说明 swServ

.net反射详解(转)

摘自:http://www.cnblogs.com/knowledgesea/archive/2013/03/02/2935920.html 概述反射 通过反射可以提供类型信息,从而使得我们开发人员在运行时能够利用这些信息构造和使用对象. 反射机制允许程序在执行过程中动态地添加各种功能.  运行时类型标识 运行时类型标识(RTTI),可以在程序执行期间判定对象类型.例如使用它能够确切地知道基类引用指向了什么类型对象. 运行时类型标识,能预先测试某个强制类型转换操作,能否成功,从而避免无效的强制类

数据挖掘十大算法之决策树详解(2)

在2006年12月召开的 IEEE 数据挖掘国际会议上(ICDM, International Conference on Data Mining),与会的各位专家选出了当时的十大数据挖掘算法( top 10 data mining algorithms ),可以参见文献[1].本博客已经介绍过的位列十大算法之中的算法包括: [1] k-means算法(http://blog.csdn.net/baimafujinji/article/details/50570824) [2] 支持向量机SVM

三十九、git add详解

一.前言git add命令主要用于把我们要提交的文件的信息添加到索引库中.当我们使用git commit时,git将依据索引库中的内容来进行文件的提交.二.基本git add <path>表示 add to index only files created or modified and not those deleted 我通常是通过git add <path>的形式把我们<path>添加到索引库中,<path>可以是文件也可以是目录.git不仅能判断出&