Java反射机制

目录

1. 什么是反射

2. Class类

3. Constructor类

4. Method类

5. Field类

6. Parameter类


       程序运行时,允许改变程序结构或变量类型的语言称为动态语言。从这个观点看,Python、Ruby、JavaScript是动态语言,而C、C++、Java不是动态语言。尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:反射(Reflection)。反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。反射的一个重要应用就是解析注解,Spring框架中的控制反转就是通过Java的反射机制来实现的。

1. 什么是反射

       Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。简单来说,只要给定类的名字,就可以通过反射机制来获得类的所有信息。反射是Java被视为动态(或者准动态)语言的一个关键性质。

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

       (1) 在运行时判断任意一个对象所属的类。

       (2) 在运行时构造任意一个类的对象。

       (3) 在运行时判断任意一个类所具有的成员变量和方法。

       (4) 在运行时调用任意一个对象的方法。

       (5) 生成动态代理。

       实现Java反射机制的类位于包java.lang.reflect中,主要包括:Class、Constructor、Method、Filed等。

        Class:代表一个类,是反射的起源,用于获取与该类相关的各种信息。

        Constructor:代表某个类中的一个构造方法。

        Method:代表某个类中的一个成员方法。

        Fileld:代表某个类中的一个成员变量。

2. Class类

       Class类是实现Java反射机制的核心类,这个类是所有.class文件的抽象体现。也就是说,所有类的字节码文件,都可以看做是Class类的实例化对象。在Java中,每一个类通过编译后会生成一个.class文件的字节码文件,而同一个类实例化出来的所有对象,都会对应于同样一个.class文件。也就是说,在Java中,一个类无论实例化多少对象,这些对象都会对应同一个Class对象。图1描述了类、字节码文件、Class类和类实例之间的关系。

图1. 类、字节码文件、Class类与类实例关系

     在Java中有一个Object类,它是所有Java类的继承根源,Object类内部有一个方法:getClass(),其返回一个Class对象。Class类和一般类一样继承自Object类,其对象用以表示Java程序运行时的类和接口,也用来表达枚举、数组、基本数据类型以及关键词void。Class没有公共构造方法,Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass()方法自动构造的,因此不能显示地声明一个Class对象。通过Class对象可以获得其所表示的类型的所有信息,表1列出了Class类的常用方法。

表1.Class类的主要方法
方法功能描述
public static Class<?> forName(String className)该方法返回给定串名相应的Class对象。
public String getName()返回Class对象表示的类型的完整路径名称
public Class getSuperclass()返回Class对象表示的类的父类对象
public Constructor getConstructor(Class...  parametTypes)返回Class对象表示的类的指定参数列表的公有构造方法
public Constructor[] getConstructors()返回Class对象表示的类的所有公有构造方法
public Constructor getDeclaredConstructor(class... parameterTypes)返回Class对象表示的类的指定参数列表的构造方法,与访问权限无关
public Constructor[] getDeclaredConstructors()返回Class对象表示的类的所有构造方法,与访问权限无关
public Field getField(String name)返回Class对象表示的类或接口中指定名称的公有成员变量
public Field[] getFields()返回Class对象表示的类或接口中所有公有成员变量
public Field getDeclaredField(String name)返回Class对象表示的类或接口中指定名称的成员变量,与访问权限无关
public Field[] getDeclaredFields()返回Class对象表示的类或接口中所有成员变量,与访问权限无关
public Method getMethod(String name, class...  parameterTypes)返回Class对象表示的类或接口中指名称和定参数列表的公有成员方法
public Method[] getMethods()返回Class对象表示的类或接口中的所有公有成员方法,包括从父类或父接口中继承的方法
public Method[] getDeclaredMethod(String name, class¼ parameterTypes)返回Class对象表示的类或接口中指定名称和参数列表的成员方法,与权限无关
public Method[] getDeclaredMethods()返回Class对象表示的类或接口中所有成员方法,与权限无关
public Class[] getInterfaces()返回Class对象表示的类所实现的所有接口

       Class类是反射的起源,要想操纵类中的属性和方法,都必须从获取Class对象开始。获取Class对象主要有三种方式:

       方式一:使用类Class的静态方法forName(String className)获得与字符串className对应的Class对象,其中,className是类的全名。

         例如,

        Class c1=Class.forName("java.lang.String"); //获取String类的Class对象

        Class c2=Class.forName("my.student.domain.Student");//获得Student类的Class对象

        另外,forName()方法声明抛出ClassNotFoundException异常,因此调用该方法时必须捕获或抛出该异常。

       方式二:使用一个对象的getClass()方法获得该对象所属的类的Class对象。

        例如,

        String str="Zhangsan";   //str为一个String类型的对象

        Class c3=str.getClass();  //通过str的getClass()方法获取String类的Class对象

        Student stu=new Student();  //stu为Student类的一个对象

        Class c4=stu.getClass();  //通过stu的getClass()方法Student类获取Class对象

       方式三:通过类的class属性获得Class对象。

        例如,

        Class c5=String.class; //获取String类的Class对象

        Class c6=Student.class;//获取Student类的Class对象

         Class c7=Integer.TYPE; //获取基本类型int的Class对象

        Class c8=Integer.class; //获取Integer类的Class对象

       如果想要获得基本数据类型的Class对象,可以使用其所对应的类的TYPE属性,例如,Integer.TYPE可以获得int的Class对象,但是要想获得Integer类的Class对象,必须使用Integer.class。

       除了上面介绍的Class类之外,与反射相关的常用类还有Constructor、Method、Field和Parameter,其中,类Constructor和Method是类Executable的子类。Executable是一个抽象类,该类对象代表可执行类成员。Executable类提供了大量的方法用来获取参数、修饰符或注解等信息。

3. Constructor类

       Constructor类代表某个类中的一个构造方法,通过Class对象的getConstructors()方法可以获得当前运行时类的所有构造方法,每个构造方法对应一个Constructor对象,通过调用Constructor对象的newInstance()方法可以在不使用new运算符的情况下动态地创建对象。Constructor类所包含的主要方法如表2所示。

表2. Constructor类的主要方法
方法功能描述
public T newInstance(Object... initatgs)调用Constructor对象表示的构造方法创建对象,如果未设置参数则表示采用默认无参数的构造方法
public void setAccessible(Boolean flag)如果一个类的构造方法的权限为private,则默认不允许通过反射创建该类的对象,如果先执行该方法,并将入口参数设置为true,则允许创建对象
public String getName()返回构造方法的名称

【例1】下面程序演示了Constructor对象的获取和动态创建对象。

public class ConstructorDemo {
	public static void main(String[] args) throws Exception {
		Class userClass=User.class;//获取User类的对象Class对象
		//通过Class对象的默认构造方法创建对象
		Constructor userCons=userClass.getConstructor(new Class[]{}); 
		Object obj1=userCons.newInstance(new Object[]{});
		//通过Class对象的带参数的构造方法创建对象
		userCons=userClass.getConstructor(new Class[]{int.class, String.class}); 
		Object obj2=userCons.newInstance(new Object[]{1,"fanchao"});
		//将Object对象转换为User对象,然后执行print()方法
		((User)obj1).print();
		((User)obj2).print();
	}
}
class User{
	int id;
	String name;
	//默认构造方法
	public User() {}
	//带参数构造方法
	public User(int id,String name) {
		this.id=id;
		this.name=name;
	}
	public void print() {
		System.out.println("id="+id+",name="+name);
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

执行类ConstructorDemo程序运行结果为:

id=0,name=null

id=1,name=fanchao

4. Method类

       Method类用于封装成员方法的信息,调用Class对象的getMethod()方法或getMethods()方法可以获得当前运行时类的指定方法或所有方法,每个方法对应一个Method对象,通过调用Method对象的invoke()方法可以在不使用“.”运算符的情况下,动态地执行对象的相应方法。Method类所包含的主要方法如表3所示。

表3. Method类的主要方法
方法功能描述
public Class getDeclaringClass()得到一个Class对象
public String getName()获得Method类对象所表示的方法名
public Class[] getParameterTypes()获得Method类对象表示的方法中的参数类型
public Object invoke(Object o, Object… args)调用Method类对象表示的方法,相当于对象o用参数args调用该方法

【例2】下面程序演示了Method对象的获取和动态执行相应方法。

public class MethodDemo {
	public static void main(String[] args) throws Exception {
		Class userClass=User.class;//获取User类的对象Class对象
		//获取User类的默认构造方法创建对象
		Constructor userCons=userClass.getConstructor(new Class[]{}); 
		User user=(User)userCons.newInstance(new Object[]{});
		
		//获取User类的setName()方法,该方法包含一个参数
		Method setNameMethod=userClass.getMethod("setName", new Class[] {String.class});
		//执行user对象的setName()方法,实参为"Fanchao",
		setNameMethod.invoke(user, new Object[] {new String("Fanchao")});
		
		//获取User类的getName()方法,该方法无参数,返回值为String类型
		Method getNameMethod=userClass.getMethod("getName", new Class[] {});
		//执行user对象getName()方法,返回值为一个Object对象,将其转换为String字符串
		Object nameObj=getNameMethod.invoke(user, new Object[] {});
		String name=(String)nameObj;
		System.out.println("name="+name);
	}
}

程序执行结果为:

name=Fanchao

5. Field类

       Field类用于封装成员变量的信息,调用Class对象的getFiled()方法或getFileds()方法可获得当前运行时类的指定成员变量或所有成员变量,每个成员变量对象一个Field对象,通过Field对象的get()方法和set()方法可以为动态地获取和设置对象的成员变量值。Field类所包含的主要方法如表4所示。

表4. Field类的主要方法
方法功能描述
public String getName()获得Filed类对象所表示的变量的名称
public Class<?> getType()获得Filed类对象所表示的变量的类型
public Object get(Object obj)获得Filed类对象所表示的变量的值,其中,obj为该变量所属的对象
public void set(Object obj, Object value)设置Filed类对象所表示的变量的值为value,其中,obj为该变量所属的对象
public Xxx getXxx(Object obj)获得Filed类对象所表示的变量的值,其中,Xxx代表基本类型,obj为该变量所属的对象
public void setXxx(Object obj, Xxx value)设置Filed类对象所表示的变量的值为value,其中,Xxx代表基本类型,obj为该变量所属的对象

【例3】下面程序演示了Field对象的获取以及利用Filed对象动态地设置成员变量值。

public class FieldDemo {
	public static void main(String[] args) throws Exception {
		Class userClass=User.class;//获取User类的对象Class对象
		//获取User类的默认构造方法创建对象
		Constructor userCons=userClass.getConstructor(new Class[]{}); 
		User user=(User)userCons.newInstance(new Object[]{});
		//获取User类的成员变量id
		Field idField=userClass.getDeclaredField("id");
		//给对象user的成员变量id赋值1
		idField.set(user, 1);
		//获取User类的成员变量name
		Field nameField=userClass.getDeclaredField("name");
		//给对象user的成员变量name赋值"Fanchao"
		nameField.set(user, "Fanchao");
		//输出对象user的成员变量id和name的值
		System.out.println(idField.getName()+"="+idField.getInt(user));
		System.out.println(nameField.getName()+"="+(String)nameField.get(user));
	}
}

程序运行结果为:

id=1

name=Fanchao

6. Parameter类

       Parameter类用于封装方法的参数信息,每个Parameter对象代表方法的一个参数。Parameter类所包含的主要方法如表5所示。

表5. Parameter类的主要方法
方法功能描述
public String getName()获得Parameter类对象所表示的参数的名称
public Class getType()获得Parameter类对象所表示的参数的类型
public Type getParameterizedType()获得Parameter类对象所表示的带泛型参数的类型
public boolean isVarArgs()判断Parameter类对象所表示的参数是否为可变参数
public boolean isNamePresent()判断.class文件中是否包含方法的参数名信息

       使用javac.exe命令编译Java源文件时,默认生成的.class字节码文件中不包含方法的形参名信息,因此,调用Parameter对象isNamePresent()方法的返回值为false,调用Parameter对象的getName()方法不能得到我们自己定义的参数名,而是返回argX形式的名称(X是参数的序号,从0开始)。如果希望使用javac.exe命令编译Java源文件时保留形参信息,则需要为编译命令指定-parameters选项。

【例4】下面程序演示了利用反射返回一个类的所有公有方法(包括从父类继承的公有方法)及其参数信息。

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class ParameterDemo {
	public static void main(String[] args) {
		Class userClass=User.class;//获取User类的对象Class对象
		//获取User类的所有公有方法
		Method[] methods=userClass.getMethods();
		System.out.println("类"+userClass.getName()+"包含的公有方法:");
		for(Method method:methods) {
			System.out.println("方法名:"+method.getName()
+", 返回类型:"+method.getReturnType());
			Parameter[] parameters=method.getParameters();
			//检索每个公有方法的每个参数
			for(Parameter parameter:parameters) {
				System.out.println("\t参数名:"+parameter.getName()
+", 参数类型:"+parameter.getType());
			}
			System.out.println("--------------------------------");
		}
	}
}

使用带参数-patameters的javac命令编译上述源文件,程序运行结果为:

charpter7.User包含的公有方法:

    方法名:getName, 返回类型:class java.lang.String

--------------------------------

    方法名:setName, 返回类型:void

        参数名:name, 参数类型:class java.lang.String

--------------------------------

    方法名:getId, 返回类型:int

--------------------------------

    方法名:print, 返回类型:void

--------------------------------

    方法名:setId, 返回类型:void

        参数名:id, 参数类型:int

--------------------------------

    方法名:wait, 返回类型:void

        参数名:arg0, 参数类型:long

        参数名:arg1, 参数类型:int

--------------------------------

    方法名:wait, 返回类型:void

--------------------------------

    方法名:wait, 返回类型:void

        参数名:arg0, 参数类型:long

-------------------------------

    方法名:equals, 返回类型:boolean

        参数名:arg0, 参数类型:class java.lang.Object

--------------------------------

    方法名:toString, 返回类型:class java.lang.String

--------------------------------

    方法名:hashCode, 返回类型:int

--------------------------------

    方法名:getClass, 返回类型:class java.lang.Class

--------------------------------

    方法名:notify, 返回类型:void

--------------------------------

    方法名:notifyAll, 返回类型:void

--------------------------------

输出结果中的中方法wait()equals()toString()hashCode()getClass()notify()notifyAll()是从默认的父类Object中继承而来的。因为Object类的.class字节码文件默认是不包含参数信息的,所以没有输出这些方法的参数信息。

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值