Java 反射理解

1. 反射的概述:

    反射(Reflection)允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息,反射可以打破面向对象的封装特性,调用类中 private 属性的方法。

    程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。反射的核心是JVM在运行时才动态加载类或调用方法/访问属性。Java反射框架主要提供以下功能:

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

2. 反射的使用

    1、获得Class对象

方法有三种
(1)使用Class类的forName静态方法:

Class<?> classType = Class.forName(String className);
//在JDBC开发中常用此方法加载数据库驱动:
 Class.forName(driver);

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.getDeclaredMethods();
		
		for(Method method : methods)
		{
			System.out.println(method);
		}
	}
}

(2)使用类的.class语法:String.class;直接获取某一个对象的class:

Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;

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
	{
		// InvokeTester test = new InvokeTester();
		// System.out.println(test.add(1, 2));
		// System.out.println(test.echo("tom"));

		Class<?> classType = InvokeTester.class;    //获取待操作的类的 class 对象 对于每一个类只要在类的名字后面打一个点,就可以获得该类的 class 对象

		Object invokeTester = classType.newInstance();  //获得 class 类所对应的类的实例

		// System.out.println(invokeTester instanceof InvokeTester);    

		Method addMethod = classType.getMethod("add", new Class[] { int.class, int.class });    //通过反射调用方法,要想调用方法就要获得与方法所对应的 Method 对象。
        //关键点在这上下两句,当需要获得一个 Method 类型的对象的时候,需要的是抽象的概念,所以传递一个 Class 类型的数组,用以存放待调用方法的参数,
        //而需要调用这个方法的时候就需要具体的信息,所以就传入一个具体的 Object 类型的数组用于存放参数。并且这两个数组的长度是一样的。
        //并且两个是有联系的,上面的数组要求的参数是 int 类型的,那么下面调用方法的时候参数的数组中也必须是存放两个具体的 int 类型的参数。
		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 result2 = echoMethod.invoke(invokeTester, new Object[]{"tom"});
		
		System.out.println((String)result2);
	}
}


(3)调用某个对象的getClass()方法,比如:

StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();

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

public class ReflectTester
{
	// 该方法实现对Customer对象的拷贝操作
	public Object copy(Object object) throws Exception
	{
		Class<?> classType = object.getClass();     //先获得 class 对象
        
//      System.out.println(classType.getName());    //这样的话就仅是返回一个类的全称。。
//      System.out.println(classType);  //要是这样的话就是返回一个类的类型以及类的全称。

        /**
        Constructor cons = classType.getConstructor(new Class[]{String.class, int.class});
        Object obj = cons.newInstance(new Object[]{"hello", 3});
        //以上两行代码等价于下面一行,虽然如此,但是下面的方法却也只能是在构造方法不接收参数的情况下能行,要是构造方法接收参数的话就不可以了。
        //上面的方法却是两种情况都是可以适用的。
        Object obj2 = classType.newInstance();
        */                                                  

		Object objectCopy = classType.getConstructor(new Class[] {})
				.newInstance(new Object[] {});      
        //获得 Constructor 对象,然后 new 出一个新的实例。

        
        //获得对象的所有成员变量
		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);     //获得 get 方法名字
			String setMethodName = "set" + firstLetter + name.substring(1);     //获得 set 方法名字

			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 customer = new Customer("Tom", 20);
		customer.setId(1L);

		ReflectTester test = new ReflectTester();

		Customer customer2 = (Customer) test.copy(customer);

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

/**
*   程序执行流程:重点是 copy() 方法的内容
*       首先获得 class 对象,然后调用 Class 类里面的方法获得构造方法,并且使用构造方法生成一个实例。
*       然后还是通过 Class 类中的 getDeclaredFields() 方法获得所有的成员变量,
*       之后把这些成员变量通过 for-Each 循环遍历得到每个方法的方法名,然后通过变化大小写和增加 get,set 来将其配出正确的方法名,
*       而后再通过反射,调用上面已经准备好的方法。
*/

    2. 判断是否为某个类的实例

一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断是否为某个类的实例,它是一个Native方法:

public native boolean isInstance(Object obj);

    3. 创建实例

通过反射来生成对象主要有两种方式。

(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

Class<?> c = String.class;
Object str = c.newInstance();

(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

    4、获取方法

获取某个Class对象的方法集合,主要有以下几个方法:

getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

	public Method[] getDeclaredMethods() throws SecurityException

getMethods()方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

    public Method[] getMethods() throws SecurityException

getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象

    public Method getMethod(String name, Class<?>... parameterTypes)

实例:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class test1 {
	public static void test() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
	        Class<?> c = methodClass.class;
	        Object object = c.newInstance();
	        Method[] methods = c.getMethods();
	        Method[] declaredMethods = c.getDeclaredMethods();
	        //获取methodClass类的add方法
	        Method method = c.getMethod("add", int.class, int.class);
	        //getMethods()方法获取的所有方法
	        System.out.println("getMethods获取的方法:");
	        for(Method m:methods)
	            System.out.println(m);
	        //getDeclaredMethods()方法获取的所有方法
	        System.out.println("getDeclaredMethods获取的方法:");
	        for(Method m:declaredMethods)
	            System.out.println(m);
	    }
    }
class methodClass {
    public final int fuck = 3;
    public int add(int a,int b) {
        return a+b;
    }
    public int sub(int a,int b) {
        return a+b;
    }
}

运行结果:

//getMethods获取的方法:
public int org.ScZyhSoft.common.methodClass.add(int,int)
public int org.ScZyhSoft.common.methodClass.sub(int,int)
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 final native void java.lang.Object.wait(long) 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()
getDeclaredMethods获取的方法:
public int org.ScZyhSoft.common.methodClass.add(int,int)
public int org.ScZyhSoft.common.methodClass.sub(int,int)

    5、获取构造器信息

获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

    public T newInstance(Object ... initargs)
//根据传入的参数来调用对应的Constructor创建对象实例

    6、获取类的成员变量(字段)信息

getFiled: 访问公有的成员变量
getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量
getFileds和getDeclaredFields

    7、调用方法

我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为:

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException

使用举例:

public class test1 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> klass = methodClass.class;
        //创建methodClass的实例
        Object obj = klass.newInstance();
        //获取methodClass类的add方法
        Method method = klass.getMethod("add",int.class,int.class);
        //调用method对应的方法 => add(1,4)
        Object result = method.invoke(obj,1,4);
        System.out.println(result);
    }
}
class methodClass {
    public final int fuck = 3;
    public int add(int a,int b) {
        return a+b;
    }
    public int sub(int a,int b) {
        return a+b;
    }
}

    8、利用反射创建数组

Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:

public static Object newInstance(Class<?> componentType, int length)
        throws NegativeArraySizeException {
        return newArray(componentType, length);
    }

猜你喜欢

转载自my.oschina.net/ChinaHaoYuFei/blog/1791485