------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的反射机制灵活的为我们提供了许多看似不能实现的功能,用它可以解决许多死的机制问题。