黑马程序员------java 反射Reflection

                                      ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

   java反射是java中相对重要的知识,是java很多框架的基石,没有反射就没有了很多框架。

在java运行时环境中,对于任意一个类,我们都能知道这个类有哪些方法和属性,对于任意一个对象我们也都能调用它的任意一个

方法。这种动态获取类信息以及动态调用对象的方法的功能就来自于java语言的反射(Reflection).

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

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

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

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

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

Reflection是java被视为动态语言的一个关键性质。这个机制允许程序再运行时透过Reflectiion API取得任何一个已知名称的class的

内部信息,包括其modifiers(public,static 等)、superclass、实现之interfaces,也包括fields和methods的所有信息,并可运行时改变fields和methods的所有信息,

也可于运行时改变fields内容或调用methods。

通过reflection我们可以打破java语言的封装性,既可以访问似有的变量方法。


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

- Class类:代表一个类。

- Field类:代表类的成员变量(类的属性)。

- Method类: 代表类的方法。

- Constructor类:代表类的构造方法。

-Array类:提供了动态创建数组,以及访问数据的元素的静态方法。

例如如下获得一个字符串所对应的类的Class对象以及这个类中的所有的方法:

package reflect;

import java.lang.reflect.Method;

public class DumpMethods
{
	public static void main(String[] args) throws Exception
	{
//		Class<?> classType = Class.forName("java.lang.String");//将获得字符串所标示的类的Class对象
		Class<?> classType = Class.forName(args[0]);//将获得传入的字符串对象所标示的类的Class对象
		
		Method[] methods = classType.getDeclaredMethods();//获得Class对象中的所有方法
		
		for(Method me:methods)
		{
			System.out.println(me);
		}
	}
}

输入 java.lang.String

输出:

public int java.lang.String.hashCode()
public boolean java.lang.String.equals(java.lang.Object)
public java.lang.String java.lang.String.toString()
public char java.lang.String.charAt(int)
private static void java.lang.String.checkBounds(byte[],int,int)
public int java.lang.String.codePointAt(int)
public int java.lang.String.codePointBefore(int)
public int java.lang.String.codePointCount(int,int)  ...... java.lang.String类中所有的方法

下面展示了如何简单的获取一个类的Class,获取到类中特定的方法,调用获取的Method对应的方法:

package reflect;

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(4,8));
//		System.out.println(test.echo("123321")); 常规调用方法的写法
		
		/*
		 * 先获取Class对象
		 */
		//Class<?>  classType= Class.forName();//通过Class类的静态方法forname() 传入类的全称可以获取这个类对应的Class对象
		  Class<?>  classType=InvokeTester.class;//通过java内置的语法   类名.class
		  Object invokeTester = classType.newInstance(); //生成对应的类的实例
		  /*
		   * 通过与这个类所对应的Class对象.getMethod("方法名",参数对应Class对象对应的Claa【】数组)
		   */
		  Method addMethod = classType.getMethod("add",new Class[]{int.class,int.class});
		  /*
		   * invoke 表示调用了Method对象对应的目标方法
		   * 参数:第一个Object 对象   标示在那个类上调用
		   * 第二个数组的方式传具体的参数 或者可变参数的方式
		   */
		 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 String[]{"TOM"});
		 
		 System.out.println((String)result1);
	}

}

下面这个例子使用反射实现类的拷贝:

package reflect;


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

public class ReflectTester
{
	
	public Object ReflectCopy(Object obj) throws Exception
	{
		Class<?> classType = Customer.class;
//		Class<?> classType1 = Class.forName("Customer");
//		Class<?> classType2 = new Customer().getClass();//三种获取类的Class方式
		
		Object obj1 = classType.newInstance(); //不带参数的生成实例  Class.newInstance()
		
//		Constructor con = classType.getConstructor(new Class[]{});
//		Object obj2 = con.newInstance(); // 通过先生成constructor类的不带参数的实例  在调用其newInstance()方法生成实例
		
		
//		Constructor con1 = classType.getConstructor(new Class[]{String.class,int.class}); //生成带参数的构造方法的实例
		
//		Object obj3 = con1.newInstance(new Object[]{"TOM",13});  //传入Object类的数组 里面的额值为与类的构造方法对应的参数
		
		Field[] field = classType.getDeclaredFields();
		
		for(Field  fie: field)
		{
			
			String name =fie.getName();
			String firstLetter = name.substring(0,1).toUpperCase();
			String getMethodName ="get"+firstLetter+name.substring(1);//substring(1)截取
			String setMethodName ="set"+firstLetter+name.substring(1);
			
			Method getMethod = classType.getMethod(getMethodName,new Class[]{});
			Method setMethod = classType.getMethod(setMethodName,new Class[]{fie.getType()});
			
			Object value = getMethod.invoke(obj,new Object[]{});
			setMethod.invoke(obj1,new Object[]{value});
		}
	return obj1;
		
	}
	public static void main(String[] args) throws Exception
	{
		ReflectTester test =new ReflectTester();
		Customer cus =new Customer("tom",13);
		cus.setId(1L);
		Customer co =(Customer) test.ReflectCopy(cus);
		
		System.out.println(co.getAge() +"  "+co.getName()+"  "+co.getId());
	}
	
}

class Customer
{
	private String name;
	private int age;
	public Long getId()
	{
		return id;
	}

	public void setId(Long id)
	{
		this.id = id;
	}
	private Long id;
	
	
	public Customer()
	{
		
	}
	
	public Customer(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	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的常用的3中方式:

a、使用Class的静态方法forName  Class.forName("java.lang.String");

b、使用类的.class语法   String.class

c、使用对象的getclass()方法  Class<?> clazz = "aa".getClass();

2、若想通过类的不带参数的构造方法来生成对象,我们有两种方法:

a、通过Class对象的newInstance()方法直接生成: 

  Class<?> clazz = String.class;

 Object obj = clazz.newInstance();

b、先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过Contructor对象的newInstance(方法生成

   Class<?> clazz = Customer.class;

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

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

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

   Class<?> clazz = Customer.class;

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

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

3、Class<T>类提供了几个获取Method的几种方法

      getMethod(String name, Class<?>... parameterTypes)  返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法

      public Method[] getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继      承的那些的类或接口)的公共 member 方法

     public Method getDeclaredMethod(String name,Class<?>... parameterTypes)返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法

     public Method[] getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私       有方法,但不包括继承的方法


java.lang.Array类提供了动态创建和访问数组元素的各种静态方法。例如:下面的例子中

创建了一个长度为10的字符串数组,接着把索引位置为5的元素设为“hello”,然后再读取索引位置为5的元素的值

package reflect;

import java.lang.reflect.Array;

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

反射机制中打破java封装的 setAccessible(boolean f)方法

在java中我们不能再类的外部访问类中的private方法和属性,这是java封装机制。但是通过反射我们可以打破这种常规的机制。

Method 和Field类的SetAccessible(boolean b) 方法可以压制java对访问修饰符的检查.

例如以下连个例子:

 package reflect;

public class PrivateTest
{

	private String sayHello(String name)
	{
		return "hello"+name;
	}
//反射可以打破类的封装
}



package reflect;

import java.lang.reflect.Method;

public class PrivateTest2
{
	public static void main(String[] args) throws Exception
	{
		PrivateTest p = new PrivateTest();
		Class<?> classType = p.getClass();
		
		Method method = classType.getDeclaredMethod("sayHello",new Class[]{String.class});
		method.setAccessible(true);//...压制java对访问修饰符的检查
		
		String str = (String) method.invoke(p,new Object[]{"zhangsan"});
		
		System.out.println(str);
	}

}


package reflect;

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




package reflect;

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

public class Reprivate
{

	public static void main(String[] args) throws Exception
	{	
		Private2 p1 =new Private2();
		
		Class<?> classTy = p1.getClass();
		
		Field field = classTy.getDeclaredField("name");
		
		field.setAccessible(true); //压制JAVA对访问修饰符的检查
		
		field.set(p1,"lisi");
		
		Method method = classTy.getMethod("getName",new Class[]{});
		
		String str = (String) method.invoke(p1,new Object[]{});
		
		System.out.println(str);
		
		
	}

}


总的来说java的反射机制灵活的为我们提供了许多看似不能实现的功能,用它可以解决许多死的机制问题。















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值