跟我学Java反射——三步曲

上一篇文章我们通过反射得到运行类的构造函数、对象实例、父类、实现的接口、所在包、以及注解,这篇文章我们将学习通过反射得到运行类的相关属性信息以及相关的方法信息。

获取类的完整结构

运行类的Field

通过运行类来获取它的相关属性,这一点在开发中用途还是很广泛的,下面我们就来看下跟Field相关的知识。

1.获取到运行类中及其父类中声明为public的属性

//获取到运行类中及其父类中声明为public的属性
@Test
public void Test1(){
	Class clazz = Person.class;

	//1.getFields,只能获取到运行类中及其父类中声明为public的属性
	Field[] fields = clazz.getFields();
	for (int i = 0; i < fields.length; i++) {
		System.out.println(fields[i]);
	}
}

运行结果:

publicjava.lang.String com.tgb.reflect.common.Person.name

public doublecom.tgb.reflect.common.Creature.weight

2.获取运行类本身声明的所有属性

//获取运行类本身声明的所有属性
@Test
public void test2(){
	Class clazz = Person.class;
	//2.getDeclaredFields,只能获取运行类本身声明的所有属性
	Field[] fields1 = clazz.getDeclaredFields();
	for (int i = 0; i < fields1.length; i++) {
		System.out.println(fields1[i]);
	}
}

运行结果:

private int com.tgb.reflect.common.Person.age

public java.lang.String com.tgb.reflect.common.Person.name

int com.tgb.reflect.common.Person.id

我们发现用不同权限修饰符修饰的属性,我们想通过反射来获取时也是需要用不同的方法的,类的getFields()只能获取到运行类中及其父类中声明为public的属性,而类的getDeclaredFields()只能获取运行类本身声明的所有属性。

有时候我们只是获取整个属性还是不够的,我们还需要获取属性字段是权限修饰符、变量类型、属性名字。

3.获取属性的权限修饰符和数据类型和属性名字

	/**
	 * 获取属性的权限修饰符和数据类型和属性名字
	 */
	@Test
	public void Test3(){
		Class clazz = Person.class;
		Field[] fields1 = clazz.getDeclaredFields();
		for (Field f:fields1) {
			//获取属性权限修饰符
			int i=f.getModifiers();
			String str1=Modifier.toString(i);
			System.out.print("权限修饰符是:"+str1+" ");

			//获取属性的变量类型
			Class type = f.getType();
			System.out.print("变量类型是:"+type.getName()+" ");

			//获取属性名字
			System.out.println("属性名字是:"+f.getName());
		}
	}

运行结果:

权限修饰符是:private变量类型是:int 属性名字是:age

权限修饰符是:public变量类型是:java.lang.String 属性名字是:name

权限修饰符是: 变量类型是:int属性名字是:id

这样我们通过字段的getModifiers()方法可以获取变量的权限修饰符、字段的getType()得到属性变量类型、type.getName()获取属性名字。

运行类的Method

1.获取运行类及其父类中的所有声明为public的方法

//获取运行类及其父类中的所有声明为public的方法
@Test
public void test1() {
	Class clazz = Person.class;

	// 1. getMethods 获取运行类及其父类中的所有声明为public的方法
	Method[] m1 = clazz.getMethods();
	for (Method m : m1) {
		// String name=m.getName();
		System.out.println(m);
	}
}

运行结果:

publicjava.lang.String com.tgb.reflect.common.Person.toString()

public int com.tgb.reflect.common.Person.compareTo(java.lang.Integer)

public int com.tgb.reflect.common.Person.compareTo(java.lang.Object)

public java.lang.String com.tgb.reflect.common.Person.getName()

public void com.tgb.reflect.common.Person.setName(java.lang.String)

public static void com.tgb.reflect.common.Person.info()

public void com.tgb.reflect.common.Person.show()

public int com.tgb.reflect.common.Person.getAge()

public void com.tgb.reflect.common.Person.setAge(int)

public void com.tgb.reflect.common.Creature.breath()

public final void java.lang.Object.wait() throws java.lang.InterruptedException

public final void java.lang.Object.wait(long,int) throws   java.lang.Interrupted Exception

public final native void java.lang.Object.wait(long) throws       java.lang.Interrupted Exception

public boolean java.lang.Object.equals(java.lang.Object)

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()

2.获取运行时类本身声明的所有的方法

//获取运行时类本身声明的所有的方法
@Test
public void test2() {
	Class clazz = Person.class;

	// 2. getDeclaredMethods获取运行时类本身声明的所有的方法
	Method[] m2 = clazz.getDeclaredMethods();
	for (Method m : m2) {
		System.out.println(m);
	}
}

运行结果:

public java.lang.String com.tgb.reflect.common.Person.toString()

public int com.tgb.reflect.common.Person.compareTo(java.lang.Integer)

public int com.tgb.reflect.common.Person.compareTo(java.lang.Object)

public java.lang.String com.tgb.reflect.common.Person.getName()

public void com.tgb.reflect.common.Person.setName(java.lang.String)

public static voidcom.tgb.reflect.common.Person.info()

public void com.tgb.reflect.common.Person.show()

private void com.tgb.reflect.common.Person.display(java.lang.String)

public int com.tgb.reflect.common.Person.getAge()

public void com.tgb.reflect.common.Person.setAge(int)

通过class类的getMethods()方法可以得到运行类本身的所有public方法及其父类中的所有声明为public的方法;通过class类的getDeclaredMethods()可以得到运行类本身的所有方法。

3.获取方法的权限修饰符、返回值类型、方法名、形参列表、注解、异常

	// 权限修饰符、返回值类型、方法名、形参列表、注解、异常
	@Test
	public void Test3() {
		Class clazz = Person.class;
		Method[] m2 = clazz.getDeclaredMethods();
		for (Method m : m2) {

			//打印下方法
			System.out.print("方法是:"+m);

			// 1.权限修饰符
			String str1 = Modifier.toString(m.getModifiers());
			System.out.print(" 权限修饰符是:" + str1);

			// 2.返回值类型
			Class class1 = m.getReturnType();
			System.out.print("  返回值类型是:" + class1.getName());

			// 3.方法名
			System.out.print("  方法名是:" + m.getName());

			// 4.形参列表
			Class[] paras = m.getParameterTypes();
			if (paras.length != 0) {
				for (int i = 0; i < paras.length; i++) {
					System.out.print(" 形参是:" + paras[i].getName()+" args"+i+" ");
				}
			}

			// 5.注解
			Annotation[] annotations = m.getAnnotations();
			if (annotations.length!=0) {
				for (Annotation ans : annotations) {
					System.out.print(" 注解是:" + ans);
				}
			}

			// 6.异常类型
			Class[] exps= m.getExceptionTypes();
			if (exps.length!=0) {
				for (int i = 0; i < exps.length; i++) {
					System.out.print(" 异常类型"+exps[i].getName());
				}
			}

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

运行结果:

方法是:publicjava.lang.String com.tgb.reflect.common.Person.toString() 权限修饰符是:public  返回值类型是:java.lang.String  方法名是:toString

方法是:public intcom.tgb.reflect.common.Person.compareTo(java.lang.Integer) 权限修饰符是:public  返回值类型是:int 方法名是:compareTo 形参是:java.lang.Integer args0

方法是:public intcom.tgb.reflect.common.Person.compareTo(java.lang.Object) 权限修饰符是:publicvolatile  返回值类型是:int  方法名是:compareTo 形参是:java.lang.Object args0

方法是:publicjava.lang.String com.tgb.reflect.common.Person.getName() 权限修饰符是:public  返回值类型是:java.lang.String  方法名是:getName

方法是:public voidcom.tgb.reflect.common.Person.setName(java.lang.String) 权限修饰符是:public  返回值类型是:void 方法名是:setName 形参是:java.lang.String args0

方法是:public staticvoid com.tgb.reflect.common.Person.info() 权限修饰符是:public static  返回值类型是:void 方法名是:info

方法是:public voidcom.tgb.reflect.common.Person.show() 权限修饰符是:public  返回值类型是:void 方法名是:show 注解是:@com.tgb.reflect.common.MyAnnotation(value=abc123)

方法是:private voidcom.tgb.reflect.common.Person.display(java.lang.String) throwsjava.io.IOException 权限修饰符是:private 返回值类型是:void  方法名是:display形参是:java.lang.String args0 异常类型java.io.IOException

方法是:public intcom.tgb.reflect.common.Person.getAge() 权限修饰符是:public  返回值类型是:int 方法名是:getAge

方法是:public voidcom.tgb.reflect.common.Person.setAge(int) 权限修饰符是:public 返回值类型是:void 方法名是:setAge 形参是:int args0

通过这个方法我们可以通过反射得到运行类方法的权限修饰符、返回值类型、方法名、形参列表、注解、异常,这些功能还是比较常用的。

获取类的属性方法也介绍完了,到这来关于利用反射得到运行类的完整类结构都介绍完毕了,下面我们来接着学习在反射中用的比较多的,通过反射调用类中指定属性和指定方法的例子。

通过反射调用类中的指定方法、指定属性

通过反射调用类中的指定属性

类中变量的声明有几种,分别是private、public和默认、static,这几种权限修饰符修饰的变量,如果我们想通过反射来调用,那么应该怎么调用呢。

1.调用运行类中public修饰的指定的属性

// 调用运行类中由public修饰的指定的属性  public String name;
@Test
public void test4() throws Exception {
	// 通过反射获取person类的对象实例
	Class class1 = Person.class;
    //获取运行类的空参构造函数
    Constructor constructor = class1.getConstructor();
    //创建对应的运行类对象时,使用newInstance,实际上就是调用了运行时类的空参的构造器;
    Person person = (Person)constructor.newInstance();
	//获取name属性
	Field fieldName = class1.getField("name");
	//给name属性赋值
	fieldName.set(person, "jack");
	System.out.println("name:"+person.getName());
}

运行结果:

我是Person的父类Creature<T,N>类的空参构造函数

我是Person类的public修饰的空参构造函数Person()

name:jack

2.调用运行类中private修饰的指定的属性

//调用运行类中private修饰的指定的属性 private int age;
@Test
public void test5()throws Exception{
	// 通过反射获取person类的对象实例
	Class class1 = Person.class;
    //获取运行类的空参构造函数
    Constructor constructor = class1.getConstructor();
    //创建对应的运行类对象时,使用newInstance,实际上就是调用了运行时类的空参的构造器;
    Person person = (Person)constructor.newInstance(); 

    //得到private修饰的age变量
    Field fieldAge =class1.getDeclaredField("age");
    //设置允许操作的权限
    fieldAge.setAccessible(true);
    fieldAge.set(person, 20);
    System.out.println("age:"+person.getAge());
}

运行结果:

我是Person的父类Creature<T,N>类的空参构造函数

我是Person类的public修饰的空参构造函数Person()

age:20

3.调用运行类中默认的指定的属性

//调用运行类中默认的指定的属性   int id;
@Test
public void test6() throws Exception{
	// 通过反射获取person类的对象实例
	Class class1 = Person.class;
    //获取运行类的空参构造函数
    Constructor constructor = class1.getConstructor();
    //创建对应的运行类对象时,使用newInstance,实际上就是调用了运行时类的空参的构造器;
    Person person = (Person)constructor.newInstance(); 

    //获取id属性
	Field fieldId = class1.getDeclaredField("id");
	  //设置允许操作的权限
	fieldId.setAccessible(true);
	//给id属性赋值
	fieldId.set(person, 20);
	System.out.println("id:"+person.getId());

}

运行结果:

我是Person的父类Creature<T,N>类的空参构造函数

我是Person类的public修饰的空参构造函数Person()

id:20

4.调用运行类中static修饰的指定的属性

//调用运行类中static修饰的指定的属性 private static String desc;;
	@Test
	public void test7() throws Exception{
		// 通过反射获取person类的对象实例
		Class class1 = Person.class;
	    //获取运行类的空参构造函数
	    Constructor constructor = class1.getConstructor();
	    //创建对应的运行类对象时,使用newInstance,实际上就是调用了运行时类的空参的构造器;
	    Person person = (Person)constructor.newInstance(); 

	    //获取id属性
		Field fieldDesc = class1.getDeclaredField("desc");
		  //设置允许操作的权限
		fieldDesc.setAccessible(true);//static有无均可
		//给id属性赋值
		fieldDesc.set(person, "描述");
		System.out.println("desc:"+person.getDesc());

	}

运行结果:

我是Person的父类Creature<T,N>类的空参构造函数

我是Person类的public修饰的空参构造函数Person()

desc:描述

通过以上对public、private、默认、static修饰的属性的调用,我们发现运行结果都会打印运行类Person和父类Creature的空参构造函数呢,这是因为生成Person类的实例对象时调用的是Person类的空参构造函数,而空参构造函数是由类的getConstructor()方法得到的,这个方法会默认得到运行类和父类的构造函数,所有实例化时会一起执行。

通过代码我们发现,只有public修饰的属性和其它的不一样,其它的像private、默认、static修饰的属性使用是一样的,权限修饰符不是public的,得到Field字段必须用类的getDeclaredField()方法,否则取不到属性,并且调用前必须给赋予权限,也就是Field字段的setAccessible(true)方法,这样的话才可以完成调用。

通过反射调用类中的指定方法

1.通过反射调用类中public修饰的指定方法

//通过反射调用类中public修饰的指定方法
@Test
public void test4() throws Exception{
	// 通过反射获取person类的对象实例
	Class class1 = Person.class;
    //创建对应的运行类对象时,使用newInstance,实际上就是调用了运行时类的空参的构造器;
    Person person = (Person)class1.newInstance(); 

    //利用method方法调用
	Method method= class1.getMethod("toString");
	System.out.println("方法是:"+method);

	//方法执行返回值
	Object returnValue=method.invoke(person);
	System.out.println("返回值是:"+returnValue);
}

运行结果:

我是Person的父类Creature<T,N>类的空参构造函数

我是Person类的public修饰的空参构造函数Person()

方法是:publicjava.lang.String com.tgb.reflect.common.Person.toString()

我是Person类的public修饰的空参方法toString()

返回值是:Person[name=null, age=0]

2.通过反射调用类中非public修饰的带参数的指定方法

//通过反射调用类中非public修饰的带参数的指定方法
@Test
public void test5() throws Exception{
	// 通过反射获取person类的对象实例
	Class class1 = Person.class;
    //创建对应的运行类对象时,使用newInstance,实际上就是调用了运行时类的空参的构造器;
    Person person = (Person)class1.newInstance(); 

    //利用method方法调用
	Method method= class1.getDeclaredMethod("display",String.class);
	System.out.println("方法是:"+method);

	//方法执行返回值
	method.setAccessible(true);
	method.invoke(person,"china");
}

运行结果:

我是Person的父类Creature<T,N>类的空参构造函数

我是Person类的public修饰的空参构造函数Person()

方法是:private voidcom.tgb.reflect.common.Person.display(java.lang.String)   throwsjava.io.IOException

我是Person类的public修饰的带注解的display()有参方法,参数是:china

3.通过反射调用类中static修饰的不带参数的指定方法

//通过反射调用类中static修饰的不带参数的指定方法
@Test
public void test6() throws Exception{
	// 通过反射获取person类的对象实例
	Class class1 = Person.class;
    //创建对应的运行类对象时,使用newInstance,实际上就是调用了运行时类的空参的构造器;
    Person person = (Person)class1.newInstance(); 

    //利用method方法调用
	Method method= class1.getDeclaredMethod("info");
	System.out.println("方法是:"+method);

	//方法执行返回值
	method.setAccessible(true);//有无皆可
	method.invoke(person);
}

运行结果:

我是Person的父类Creature<T,N>类的空参构造函数

我是Person类的public修饰的空参构造函数Person()

方法是:private staticvoid com.tgb.reflect.common.Person.info()

我是Person类的public修饰的static的info()空参静态方法

通过反射调用运行类中不同修饰符修饰的方法,跟调用指定的属性的方法差不多,public修饰的用类的getMethod()方法获得指定方法,非public修饰的用类的getDeclaredMethod()方法获取指定方法,调用时需要给予权限用method.setAccessible(true)即可,static方法权限不赋予也可以使用。

后记

这篇文章学习了通过反射得到运行类的相关属性、方法信息,重点是如何通过反射调用运行类中的指定方法和属性,关于不同的权限修饰符,调用也不一样,关于反射的基本知识,就介绍到这里,下篇文章讲解一个反射的典型应用动态代理。

时间: 2024-10-05 07:08:55

跟我学Java反射——三步曲的相关文章

跟我学Java反射——一步曲

反射是什么 Reflection(反射)就是Java程序在运行时可以动态得到程序内部所有类的内部信息,并能动态调用任意对象的内部属性和方法. 为什么需要反射 我们为什么要用反射,这主要是反射的动态性决定的,由于反射可以实现动态创建对象,这就很大程度发挥了java的灵活性,降低了程序调用的耦合性,使系统可以更加的灵活,可以更好的应对变化. 反射应用 运行反射我们可以做到: 在运行时判断任意一个对象所属的类 在运行时构造任意一个类的对象 在运行时判断任意一个类所具有的成员变量和方法 在运行时调用任意

跟我学Java反射——二步曲

上一篇文章我们已经将反射的基本知识和class类以及类的加载器进行了介绍,在上一篇我们还学习了四种得到Class类对象的方式,但是有了class对象我们能做些什么呢,学习完这篇文章,就可以得到答案了. 获取类的完整结构 这篇文章我们主要通过demo来学习,我先将demo需要用到的代码进行简单介绍. 一个接口MyInterface代码: package com.tgb.reflect.common; import java.io.Serializable; public interface MyI

高效阅读文章的“三步曲”

高效阅读文章的“三步曲” 通读杨春玲老师的两篇博文“我科研过程中走过的弯路及纠偏探索 ”.“如何有效阅读文献(图) ”及其中链接的文章How to Read a Paper http://blog.sciencenet.cn/home.php?mod=attachment&filename=howtoread.pdf&id=47254,现给出自己对于这一问题的思考,以下没有标注的引号里的内容均来自杨老师的两篇博文中. 一.认真研读自己专业的经典教材,“教材是一个领域里最佳参考,研究之前先查

OpenCV-2.4.2 安装三步曲

注意:本人未使用  ffmpeg 的全部依赖库,比如AAC 音频编码库(libfaac-dev),MP3 编码库(ibmp3lame-dev),具体的配置为: ./configure --enable-shared --enable-gpl --enable-version3 --enable-nonfree --enable-x11grab --enable-libx264 --enable-libxvid ===========================================

VC控件自绘制三步曲

http://blog.csdn.net/lijie45655/article/details/6362441 实现自定义绘制的三步曲 既然您已经了解了绘制控件可用的各种选项(包括使用自定义绘制的好处),那么,让我们来看看实现一个自定义绘制控件需要的三个主要步骤. 执行一个 NM_CUSTOMDRAW 消息处理程序. 指定处理所需的绘制阶段. 筛选特定的绘制阶段(在这些阶段中,您需要加入自己的特定于控件的绘制代码). 执行一个NM_CUSTOMDRAW 消息处理程序 当需要绘制一个公共控件时,M

LAMP环境配置三步曲之(一) CentOS 编译安装 Apache

LAMP环境的配置现今虽然已比之前大大的简化了,但对于一些不熟悉Linux系统的朋友来说,还是有一定难度的,这里将本人的配置过程记录下来,希望能对大家有一些帮助. 本期介绍CentOS下编译安装Apache的方法: 1. 下载Apache服务器 httpd-2.2.26 wget http://apache.fayea.com/apache-mirror//httpd/httpd-2.2.26.tar.gz 2. 安装gcc等必须的编译器 yum install autoconf automak

[转]Membership三步曲之入门篇 - Membership基础示例

本文转自:http://www.cnblogs.com/jesse2013/p/membership.html Membership三步曲之入门篇 - Membership基础示例 Membership 三步曲之入门篇 - Membership基础示例 Membership三步曲之入门篇 -  Membership基础示例 Membership三步曲之进阶篇 -  深入剖析Provider model Membership三步曲之高级篇 -  从Membership 到 .NET 4.5 之 A

企业核心人才培养三步曲

信息的价值,思考的价值,学习的价值,连接的价值,时间的价值,无形的价值,人才需要不断灌输有用的信息. 人是企业最宝贵的资源,是企业巨大的财富,更是企业核心竞争力的资本,企业核心人才决定企业是否能成为百年老店?基业长青.因此,稳定核心人才,培育核心人才是企业家和企业人力资源管理者的核心战略任务,需长抓不懈.IBM前任总裁郭仕纳曾经说过:"21世纪获得成功的企业,将会是那些尽力开发.培育核心人才的组织". 一.什么是企业核心人才? 要培养好企业核心人才,企业就要建立和制定核心人才标准.笔者

Office操作:图文混排三步曲

我们在编辑Word文档的时候,图片和文字的混排编辑可能是我们遇到的最为主要的情况,恰到好处的图文混排不仅可以起到美化文档的效果,对于阅读者阅读并理解文档内容也是大有裨益.因此,熟练掌握Word的图文混排是我们制作比如海报.杂志.刊物.贺卡等这类非纯文本文档的第一步,今天,我在这给大家介绍图文混排的基本知识及技巧,先给大家展示一个效果图. Office操作:图文混排三步曲效果展示准备工作:1.准备好文字材料:2.准备好图片素材:3.构思好排版效果. 图文混排第一步:在文档中插入图片.方法:通过[插