文档章节

Java反射(reflection)机制

fairy1674
 fairy1674
发布于 2015/12/21 20:18
字数 2170
阅读 13
收藏 1

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

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

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

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

这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods

尽管Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。

java中,无论生成某个类的多少个对象(实例),这些对象都会对应同一个Class对象。

3、在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:

    –Class类:代表一个类(这个类很特殊,位于java.lang包下)。
    –Field 类:代表类的成员变量(成员变量也称为类的属性)。
    –Method类:代表类的方法。
    –Constructor 类:代表类的构造方法。
    –Array类:提供了动态创建数组,以及访问数组的元素的静态方法

使用反射生成对象和调用方法:

import java.lang.reflect.Method;

public class InvokeTester
{
	public int add(int param1,int param2)
	{
		return param1 + param2;
	}
	
	public String echo(String message)
	{
		return "hello : " + message;
	}
	
	public static void main(String[] args) throws Exception
	{
		Class<?> classType = InvokeTester.class;
		
		Object invokeTester = classType.newInstance(); 
		
		Method addMethod = classType.getMethod("add", new Class[]{int.class,int.class});
		
		Object result = addMethod.invoke(invokeTester, new Object[]{1,2});
		
		System.out.println((Integer)result);
		
		System.out.println("----------");
		
		Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
		
		Object result1 = echoMethod.invoke(invokeTester, new Object[]{"world"});
		
		System.out.println((String)result1);
	}
}

使用反射调用方法的步骤:

      a)要想使用反射,首先需要获得待处理类或对象所对应的Class对象。

      b)获取某个类或某个对象所对应的Class对象的常用的3种方式:

           1)使用Class类的静态方法forName,Class.forName(“java.lang.String”)

           2)使用类的.class语法:String.class,如这个例子中的Class<?> classType = InvokeTester.class;

           3)使用对象的getClass()方法:String s = “aa”;Class<?> clazz=s.getClass();

      c)通过获取的Class对象,产生一个实例

      d)获得想要调用方的Method对象,如Method addMethod = classType.getMethod("add", new Class[]{int.class,int.class});

      e)通过获得的Method对象,在特定实例(对象)上调用方法:Object result = addMethod.invoke(invokeTester, new Object[]{1,2});

4、Class的newInstance()方法相当于调用不带参数的构造方法来构建对象,如果构造方法带参数,就不能使用这种方法。

使用反射实现对象拷贝的程序:

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

public class ReflectTester
{
	// 该方法实现对Custmoer对象的拷贝
	public Object copy(Object object) throws Exception
	{
		Class<?> classType = object.getClass();

		// Constructor cons = classType.getConstructor(new Class[]{});
		//		
		// Object obj = cons.newInstance(new Object[]{});
		// 以上两行代码等价于下面一行,不带参数
		// Object obj2 = classType.newInstance();
		Object objectCopy = classType.getConstructor(new Class[] {}).newInstance(new Object[] {});
		
		Field[] fields = classType.getDeclaredFields();
		
		for (Field field : fields)
		{
			String name = field.getName();
			String firstLetter = name.substring(0, 1).toUpperCase();
			String getMethodName = "get" + firstLetter + name.substring(1);
			String setMethodName = "set" + firstLetter + name.substring(1);

			Method getMethod = classType.getMethod(getMethodName,new Class[] {});
			Method setMethod = classType.getMethod(setMethodName,new Class[] { field.getType() });

			Object value = getMethod.invoke(object, new Object[] {});

			setMethod.invoke(objectCopy, new Object[] { value });
		}
		return objectCopy;
	}

	public static void main(String[] args) throws Exception
	{
		Customer cu = new Customer("tom", 30);
		cu.setId(1L);
		ReflectTester test = new ReflectTester();
		Customer cu1 = (Customer) test.copy(cu);

		System.out.println(cu1.getId() + cu1.getName() + cu1.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;
	}

}

若想通过类的不带参数的构造方法来生成对象,两种方式:

   1)先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:

   Class<?> classType=String.class;

   Object obj=classType.newInstance();

   2)先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成:

   Class<?> classType = Customer.class;

   Constructor cons =classType.getConstructor(new Class[]{});

   Object obj = cons.newInstance(new Object[]{})

   若想通过类的带参数的构造方法生成对象,只能使用下面一种方式:

   Class<?> classType = Customer.class;

   Constructor cons =classType.getConstructor(new Class[]{String.class,int.class});

   Object obj = cons.newInstance(new Object[]{hello,4});

5、在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():通过类的不带参数的构造方法创建这个类的一个对象。

6、关于java.lang.reflect.Array类,Array 类提供了动态创建和访问数组元素的各种静态方法。

import java.lang.reflect.Array;

public class ArrayTest
{
	public static void main(String[] args) throws Exception
	{
		Class<?> classType = Class.forName("java.lang.String");
		
		Object array = Array.newInstance(classType,10);
		
		Array.set(array,5,"hello");
		String str = (String)Array.get(array,5);
		
		System.out.println(str);
	}
}

Array类提供了newInstance方法,对于一维数组,第一个参数相当于数组中元素的类型,第二个参数是数组的长度。set方法用于给特定数组对象的第几个元素赋值。

7、Integer.Type返回的是int,而Integer.class返回的是Integer类所对应的Class对象:java.lang.Integer

对于多维数组

import java.lang.reflect.Array;

public class ArrayTest2
{
	public static void main(String[] args)
	{
		int[] dims =new int[]{5,10,15};
		
		Object array = Array.newInstance(Integer.TYPE,dims);
		
		Object arrayObj = Array.get(array,3);
		
		//Class<?> classType = arrayObj.getClass().getComponentType();
		
		arrayObj = Array.get(arrayObj,5);
		
		Array.set(arrayObj,10,34);
		
		int[][][] arrayCast = (int[][][])array;
		
		System.out.println(arrayCast[3][5][10]);
		
	}
}


Array.newInstance(Integer.TYPE,dims);相当于定义了一个多维数组,数组的维度使用一个int数组指定,这里是dims,也就是说arra是一个三维数组,维度分别是5,10,15,

Object arrayObj = Array.get(array,3);是取array数组的第一维的下标为3的对象,返回一个二维数组,arrayObj = Array.get(arrayObj,5);就是取二维数组的下标为5的一行,得到一维数组,Array.set(arrayObj,10,34);设置一维数组的下标10位置元素为34。array就是一个三维数组,所以可以转换。

8、对私有方法的访问

class Private2  
{  
    public String name = "zhangsan";  
    private String getName()  
    {  
        return this.name;  
    }  
}  

public class ArrayTest
{
	
	public static void main(String[] args) throws Exception
	{

		Private2 p = new Private2();  
        
        Class<?> classType = p.getClass(); 
        
        p.name = "lisi";     
        
        Method method2 = classType.getDeclaredMethod("getName",null);// getStr : 方法名. String.class:参数类型  
       
        method2.setAccessible(true);  
        
        Object str = method2.invoke(p, null);  
        
        System.out.println(str);   
		
	}
}

对私有成员变量的访问:

public class Private2
{
	private String name = "zhangsan";
	public String getName()
	{
		return this.name;
	}
}

public class TestPrivate2
{
	public static void main(String[] args) throws Exception
	{
		Private2 p = new Private2();
		
		Class<?> classType = p.getClass();
		
		Field field = classType.getDeclaredField("name");
		
		field.setAccessible(true);
		
		field.set(p,"lisi");
		
		String str = p.getName();
		
		System.out.println(str);
		
	}
}


9、关于Class class(Class类)

众所周知Java有个Object class,是所有Java classes的继承根源,其内声明了数个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的getSuperclass()返回类的父类的Class对象。

 


© 著作权归作者所有

fairy1674
粉丝 4
博文 99
码字总数 136311
作品 0
成都
程序员
私信 提问
Android 4.4(KK)中利用APP打开关闭数据流量

在Android 4.4中,在app中打开或关闭数据流量 如果有这方面需求可以参考。 思路 利用JAVA的反射机制(Reflection),来调用CONNECTIVITY_SERVICE完成相关操作。 关于JAVA的反射机制,可以参考...

W_X
2014/12/17
761
4
模拟spring IOC 实现

利用java的反射和动态代理实现IOC 在Java中,其反射和动态代理机制极其强大,我们可以通过其反射机制在运行时获取信息。而代理是一种基本的设计模式,它是一种为了提供额外的或不同的操作而插...

candies
2014/03/02
179
0
【免费】全网独家:详解Java反射机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/silencezwm/article/details/85115991 【免费】全网独家:这是一份非常值得珍藏的Android知识体系!!! 本文...

silencezwm
2018/12/20
0
0
Java反射机制(未完成,还缺最后一个)

1、背景 1)Reflection也就是反射 是Java被视为动态(或准动态)语言的一个关键性质 2)反射机制指的是程序在运行时能够获取任何类的内部所有信息 2、实现功能概述 1)只要给定类的全名,即可...

萧小蚁
2015/05/03
256
0
java反射机制在项目中的运用

定义: Reflection是java开发语言特性之一,它允许运行中的java程序对自身进行检测,自审,并能操作程序内部的属性和方法,Reflection是java被视为动态语言关键之一。允许程序从执行期的Ref...

zhoulc
2014/02/24
1K
2

没有更多内容

加载失败,请刷新页面

加载更多

一、docker 入坑(win10和Ubuntu 安装)

前言 终究还是绕不过去了,要学的知识真的是太多了,好在我们还有时间,docker 之前只闻其声,不曾真正的接触过,现在docker 越来越火,很多公司也都开始使用了。所以对于我们程序员而言,又...

quellanan2
8分钟前
4
0
AutoCompleteTextView

小技巧按菜单键 当菜单打开之前会调用onMenuOpened(int featereId,Menu menu),可以重写这个方法,弹出对话框或者Popmenu 再布局中添加控件AutoCompleteTextView. <AutoCompleteTextVie...

逆天游云
11分钟前
3
0
谷歌软件商店:推出5美元会员 可用数百个软件

腾讯科技讯,谷歌和苹果是全球两大智能手机操作系统的运营者,两家公司旗下分别拥有占据行业垄断地位的谷歌软件商店和苹果软件商店。据外媒最新消息,手机软件商店的商业模式正在发生一些变化...

linuxCool
33分钟前
2
0
RocketMQ 多副本前置篇:初探raft协议

Raft协议是分布式领域解决一致性的又一著名协议,主要包含Leader选举、日志复制两个部分。 温馨提示: 本文根据raft官方给出的raft动画进行学习,其动画展示地址:http://thesecretlivesofda...

中间件兴趣圈
33分钟前
2
0
elasticsearch 6.8.0 添加认证

1. 修改elasticsearch-6.8.0/config/elasticsearch.yml 最后添加一行:xpack.security.enabled: true 2. 初始化用户和密码 ./bin/elasticsearch-setup-passwords interactive 我这里初始化为......

coord
35分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部