前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >5.7(1) 反射

5.7(1) 反射

作者头像
Mister24
发布2018-05-14 10:28:56
6010
发布2018-05-14 10:28:56
举报
文章被收录于专栏:java初学java初学

5.7 反射

反射(reflection library)提供了动态操作java代码程序的方法,这项功能被大量应用于JavaBean中,使用反射,在设计或运行添加新类的时候,能够快速地应用开发工具动态查找新添加类的能力。

  能够分析类能力的程序叫做反射(reflective)。

  •   在运行中分析类的能力;
  •       在运行中查看对象;
  •       实现通用的数组操作代码;
  •       利用Method对象。

5.7.1 Class类

在程序运行期间,java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类,JVM利用运行时类型信息选择相应的方法执行。

  可以通过专门的Java类访问这些信息,保存这些信息的类被称为Class,Object类中的getClass()方法将会返回一个Class类型的实例。

代码语言:javascript
复制
Employee e;
...
Class cl = e.getClass();

  如同用一个Employee对象表示一个特定的雇员属性一样,一个Class对象将表示一个特定类的属性。最常用的Class方法是getName。这个方法返回类的名字。

代码语言:javascript
复制
System.out.println(e.getClass().getName() + " " + e.getName());

  如果e是一个雇员,会打印出:

代码语言:javascript
复制
Employee Harry Hacker

  如果e是一个经理,会打印出:

代码语言:javascript
复制
Manager Harry Hacker

  如果类在一个包里,包的名字也作为类名的一部分:

代码语言:javascript
复制
Date d = new Date();
Class cl = d.getClass();
String name = cl.getName();

  也可以调用静态方法forName获得类名对应的Class对象。

代码语言:javascript
复制
String className = "java.util.Date";
Class cl = Class.forName(className);

  如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法。这个方法只有在className是类名或接口名的时候才能执行。否则,forName方法将会抛出一个checkedexception异常(已检查异常)。无论何时使用这个方法,都应该提供一个异常处理器(execption handler)。

获得Class类对象的第三种方法非常简单,如果T是任意的java类型,T.class将代表匹配的类对象。例如:

代码语言:javascript
复制
Class cl1 = Date.class;
Class cl2 = int.class;
Class cl3 = Double[].class;

  一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类,例如,int不是类,但int.class是一个Class类型的对象。

  JVM为每个类管理一个Class对象,因此,可以利用==运算符实现两个类对象比较的操作。例如:

代码语言:javascript
复制
if(e.getClass() == Employe.class)
    ...

  还有一个很有用的方法newInstance(),可以用来快速地创建一个类的实例。例如,

代码语言:javascript
复制
e.getClass().newInstance();

  创建一个与e具有相同类型的实例,newInstance方法调用默认的构造器(没有参数的构造器)初始化新创建的对象。如果这个类没有默认的构造器,就会抛出一个异常。

  将forName域newInstance配合起来使用,可以根据存储在字符串中的类名创建一个对象。

代码语言:javascript
复制
String s = "java.util.Date";
Object m = Class.forName(s).newInstance();

5.7.3 利用反射分析类的能力

java.lang.reflect中包含三个类Filed、Method和Constructor分别用于描述类的域、方法和构造器。

       Filed类:    getDeclaredFileds()

            getName(返回项目的名称)、getType(返回域所属的类型)、getModifiers(返回一个整型数值,描述修饰符的情况)

   Method类:   getDeclaredMethods()

getName(返回项目的名称)、getModifiers(返回一个整型数值,描述修饰符的情况)、getReturnType(返回值类型)、                       getParameterTypes(返回参数类型)

Constructors类:  getDeclaredConstructors()

            getName(返回项目的名称)、 getParameterTypes(返回参数类型)

代码语言:javascript
复制
package reflect_5_13;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Scanner;

public class ReflectionTest {
	public static void main(String args[])
	{
		String name;
		if(args.length > 0)
		{
			name = args[0];
		}
		else
		{
			Scanner in = new Scanner(System.in);
			System.out.println("Enter class name(e.g.java.util.Date): ");
			name = in.next();
		}
		
		try
		{
			//首先用cl保存获取的name.class
			Class cl = Class.forName(name);
			//获得超类supercl
			Class supercl = cl.getSuperclass();
			//获取cl的修饰语(public/private/final...)
			String modifiers = Modifier.toString(cl.getModifiers());
			if(modifiers.length() > 0)
				System.out.print(modifiers + " ");
			System.out.print("class " + name);
			if(supercl != null && supercl != Object.class)
				System.out.print("extends " + supercl.getName());
			
			System.out.print("\n{\n");
			printConstructors(cl);
			System.out.println();
			printMethods(cl);
			System.out.println();
			printFields(cl);
			System.out.println("}");
			
			
		}catch(ClassNotFoundException e)
		{
			e.printStackTrace();
		}
		System.exit(0);		
	}
	
	/*
	 * 输出所有的构造器
	 */

	public static void printConstructors(Class cl)
	{
		Constructor[] constructors = cl.getDeclaredConstructors();
		for(Constructor c:constructors)
		{
			//构造器的名字
			String name = c.getName();
			System.out.print(" ");
			//构造器的修饰符
			String modifiers = Modifier.toString(c.getModifiers());
			if(modifiers.length() > 0)
			{
				System.out.print(modifiers + " ");
			}
			System.out.print(name + "(");
			//构造器的参数类型
			Class[] paramTypes = c.getParameterTypes();
			for(int j = 0; j < paramTypes.length; j++)
			{
				if(j > 0)
				{
					System.out.print(", ");
				}
				System.out.print(paramTypes[j].getName());	
			}
			System.out.println(");");
		}
	}
	
	/*
	 * 输出所有的方法
	 */
	public static void printMethods(Class cl)
	{
		Method[] methods = cl.getDeclaredMethods();
		for(Method m:methods)
		{
			//方法的获取返回类型
			Class retType = m.getReturnType();
			//方法的名称
			String name = m.getName();
			
			System.out.print(" ");
			
			//方法的修饰符
			String modifiers = Modifier.toString(m.getModifiers());
			if(modifiers.length() > 0)
			{
				System.out.print(modifiers + " ");
			}
			System.out.print(retType.getName() + " " + name + "(");
			
			//将参数类型打印出来
			Class[] paramTypes = m.getParameterTypes();
			for(int j = 0; j < paramTypes.length; j++)
			{
				if(j > 0)
				{
					System.out.print(", ");
				}
				System.out.print(paramTypes[j].getName());
			}
			System.out.println(");");	
		}
	}
	/*
	 * 输出所有的域
	 */
	public static void printFields(Class cl)
	{
		Field[] fields = cl.getDeclaredFields();
		
		for(Field f:fields)
		{
			//域的类型
			Class type = f.getType();
			//域的名称
			String name = f.getName();
			System.out.print(" ");
			//域的修饰符
			String modifiers = Modifier.toString(f.getModifiers());
			if(modifiers.length() > 0)
			{
				System.out.print(modifiers + " ");
				System.out.println(type.getName() + " " + name + " ");
			}
		}
	}
}

输出结果:

代码语言:javascript
复制
Enter class name(e.g.java.util.Date): 
java.util.Date
public class java.util.Date
{
 public java.util.Date(int, int, int, int, int, int);
 public java.util.Date(java.lang.String);
 public java.util.Date();
 public java.util.Date(long);
 public java.util.Date(int, int, int);
 public java.util.Date(int, int, int, int, int);

 public boolean after(java.util.Date);
 public boolean before(java.util.Date);
 public boolean equals(java.lang.Object);
 public java.lang.String toString();
 public int hashCode();
 public java.lang.Object clone();
 public int compareTo(java.util.Date);
 public volatile int compareTo(java.lang.Object);
 private void readObject(java.io.ObjectInputStream);
 private void writeObject(java.io.ObjectOutputStream);
 private final sun.util.calendar.BaseCalendar$Date normalize();
 private final sun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date);
 public static long parse(java.lang.String);
 public static long UTC(int, int, int, int, int, int);
 public int getDate();
 private static final java.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String);
 private final sun.util.calendar.BaseCalendar$Date getCalendarDate();
 private static final sun.util.calendar.BaseCalendar getCalendarSystem(long);
 private static final sun.util.calendar.BaseCalendar getCalendarSystem(int);
 private static final sun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date);
 public int getDay();
 public int getHours();
 private static final synchronized sun.util.calendar.BaseCalendar getJulianCalendar();
 static final long getMillisOf(java.util.Date);
 public int getMinutes();
 public int getMonth();
 public int getSeconds();
 private final long getTimeImpl();
 public int getTimezoneOffset();
 public int getYear();
 public void setDate(int);
 public void setHours(int);
 public void setMinutes(int);
 public void setMonth(int);
 public void setSeconds(int);
 public void setYear(int);
 public java.lang.String toGMTString();
 public java.time.Instant toInstant();
 public java.lang.String toLocaleString();
 public void setTime(long);
 public static java.util.Date from(java.time.Instant);
 public long getTime();

 private static final sun.util.calendar.BaseCalendar gcal 
 private static sun.util.calendar.BaseCalendar jcal 
 private transient long fastTime 
 private transient sun.util.calendar.BaseCalendar$Date cdate 
 private static int defaultCenturyStart 
 private static final long serialVersionUID 
 private static final [Ljava.lang.String; wtb 
 private static final [I ttb 
}

构造器:名称、修饰符、参数类型

方法:名称、修饰符、参数类型、返回类型

域:名称、类型

  其他的API还需要详细的了解。

5.7.4 在运行时使用反射分析对象

查看任意对象的数据域名称和类型:

1、获得对于的Class对象;

2、通过Class对象调用getDeclaredFields。

在编写程序的时候,如果知道想要查看的域名和类型,查看指定的域是一件很容易的事情,而利用反射机制可以查看在编译时还不清楚的对象域。

  查看对象域的关键方法是Field类中get方法。如果f是一个Field类型的对象(例如,通过getDeclaredFields得到的对象),obj是某个包含f域的类的对象,f.get(obj)将返回一个对象,其值为obj域的当前值。

代码语言:javascript
复制
Eployee harry = new Employee("Harry Hacker", 35000, 10, 1, 1989);
Class cl = harry.getClass();
Field f = cl.getDeclaredField("name");
Object v = f.get(harry);

  实际上这段代码有问题,由于name是一个私有域,所以get方法将会抛出一个IllegalAccessException。只有利用get方法才能得到可访问域的值,除非拥有访问权限,否则Java安全机制只允许查看任意对象有那些域,而不允许读取它们的值。

  反射机制的默认行为受限于java的访问控制,然而,如果一个java程序没有收到安全管理器的控制,就可以覆盖访问控制,为了达到这个目的,需要调用Field、Method或Constructor对象的setAccessible方法。例如:

代码语言:javascript
复制
f.setAccessible(true);

  setAccessible方法是AccessibleObject类中的一个方法,它是Field、Method和Constructor类的公共超类。这个特性是为调试、持久化存储和相似机制提供的。

  get方法还有一个需要解决的问题,nama是一个String,因此它作为Object返回没有任何问题。但是,假设我们想要查看salary域,它属于double类型,而java中数值类型不是对象。要想解决这个问题,可以使用Fields类中的getDouble方法,也可以调用get方法,此时,反射机制将会自动将这个域值打包到对应的对象包装器中,这里将打包成Double。

  调用f.get(obj, value)可以将obj对象的f域设置为新值。

  使用getDeclaredFields获得所有的数据域,然后使用setAccessible将所有的域设置为可访问的,对于每个域,获得了名字和值。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-03-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档