您的位置:首页 > 职场人生

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

2014-09-24 21:58 423 查看
------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的反射机制灵活的为我们提供了许多看似不能实现的功能,用它可以解决许多死的机制问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: