-
- Class类:代表一个类。
- Field 类:代表类的成员变量(成员变量也称为类的属性)。
- Method类:代表类的方法。
- Constructor 类:代表类的构造方法。
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法
- Proxy : 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
一、Java的反射机制
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
Java 反射机制主要提供了以下功能:
在运行时构造任意一个类的对象。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时判断任意一个对象所属的类。
在运行时调用任意一个对象的方法
动态语言:
程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods
尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语
二、Java Reflecation API
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
代码一:下面来看一段程序,从控制台输入完整类名,程序会打印出该类的所有方法
import java.util.Scanner;
import java.lang.reflect.Method;
public class DumpMethod {
public static void main(String[] args) {
//获取控制台读取的类
Scanner input = new Scanner(System.in);
String className = input.nextLine();
try {
//加载并初始化 控制台输入的指定类 泛型?是所有泛型类的父类
Class<?> classType = Class.forName(className);
//获得类的所有方法
Method methods[] = classType.getDeclaredMethods();
for(int i=0;i<methods.length;i++) {
System.out.println(methods[i]);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
forName
public static Class<?> forName(String className)
throws ClassNotFoundException返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:
Class.forName(className, true, currentLoader)
其中 currentLoader 表示此类的定义类加载器。
例如,以下代码片段返回 java.lang.Thread 类的运行时 Class 描述符。
Class t = Class.forName("java.lang.Thread")
调用 forName("X") 将导致名为 X 的类被初始化。
参数:
className - 所需类的完全限定名。
返回:
具有指定名的类的 Class 对象。
抛出:
LinkageError - 如果链接失败
ExceptionInInitializerError - 如果此方法所激发的初始化失败
ClassNotFoundException - 如果找不到该类
代码二:
ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object 同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将它返回
import java.lang.reflect.Field;
import java.lang.reflect.Method;
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;
}
}
显示结果:
class org.cgz.reflection.Customer
id:12
name:Tom
age:23
Copy information:12 Tom 23
public class ReflectTester {
public static void main(String[] args) throws Exception {
Customer customer = new Customer("Tom",23);
customer.setId(new Long(12));
Customer customerCopy = (Customer) new ReflectTester().copy(customer);
System.out.println("Copy information:" + customerCopy.getId() + " " + customerCopy.getName() + " "+ customerCopy.getAge());
}
public Object copy(Object object) throws Exception {
//获得对象的类型
Class<?> classType = object.getClass();
System.out.println(classType);
//通过获取Class对象的无参构造,创建一个新的对象
Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
//获得该对象层次的所有属性
Field fields[] = classType.getDeclaredFields();
for(int i=0;i<fields.length;i++) {
Field field = fields[i];
//获得field属性名
String fiedlName = field.getName();
//获得属性名第一个字母的大写形式
String fiestLetter = fiedlName.substring(0, 1).toUpperCase();
//获得setter方法名
String setMethodName = "set" + fiestLetter + fiedlName.substring(1);
//获得getter方法
String getMethodName = "get" + fiestLetter + fiedlName.substring(1);
//获得属性对应的setter和getter方法
Method getMethod = classType.getMethod(getMethodName, new Class[]{});
Method setMethod = classType.getMethod(setMethodName, field.getType());
Object value = getMethod.invoke(object, new Object[]{});
System.out.println(fiedlName+":"+value);
//调用拷贝对象的setter方法
setMethod.invoke(objectCopy, new Object[]{value});
}
return objectCopy;
}
}
Java.lang.Class Class类是Reflection API中的核心类
1.获取目标类型的Class对象
(1). 引用类型 获取Class对象的三种方式
|++调用静态方法Class.forName() (使用此种方式 类名必须完整,即使在类中导入了目标类所在的包,仍然需要全名)
|++Class.forName("org.cgz.Person")
|++Class.forName("oracle.jdbc.driver.OracleDriver")
|++调用Object类中定义的getClass()方法
|++Person p = new Person()
|++Class c1 = p.getClass()
|++Class c2="Hello World".getClass()
|++使用.class表达式(不是方法也不是属性 只是一个约定的表达式,在编译阶段就需要知道类名,即*.class的*必须是合法的类名,否则编译 无法通过)
|++Class c1 = String.class
|++Class c2 = org.cgz.Person.class
|++Class c3 = oracle.jdbc.driver.OracleDriver.class
(2).基本数据类型 及void
|++使用.class表达式
|++Class c1 = int.class
|++Class c2 = double.class
|++Class c3 = void.class
|++调用相应的封装类的.Type属性 注:Type对包装类有效,且使用Type后返回的是拆箱后的基本类型 基本类型没有type属性
|++Class c1 = Integer.TYPE public static final Class<Integer> TYPE 表示基本类型 int 的 Class 实例。
|++Class c2 = Double.TYPE
...
|++Class c3 = Void.TYPE
public class test {
public static void main(String[] args) throws Exception{
String className = "java.lang.String";
Class<?> c = Class.forName(className);
System.out.println(c);
System.out.println(c.getClass());
//System.out.println(c.class); *.class在编译阶段就必须知道类名,否则编译无法通过
System.out.println(Integer.TYPE);
System.out.println(Integer.class);
System.out.println(int.class);
System.out.println(Void.class);
System.out.println(Void.TYPE);
}
}
运行结果:
class java.lang.String
class java.lang.Class
Int
Java.lang.Integer
int
class java.lang.Void
void
2. Class类的常用方法
(1). public static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class
(2). Public String getName() 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称
Array相关
(3). public Class<?> getComponentType(Class … parameterTypes)
返回表示数组组件类型的Class 就是取得 数组元素的类型
Constructor相关
(4). public Constructor<?> getConstructor(Class<?> … parameterTypes)
返回该Class对象指定的公共的构造方法,parameterType是Class对象的一个数组,这些Class对象按声明顺序标识构造器的形式参数类型 一般用new Class[] {String.class , Integer.class}表示 注:参数列表中int如果写成 Integer编译不能通过,或使用Integer.TYPE
例:在Person类中有一构造方法 参数是String ,int类型
Class c = Class.forName("org.cgz.reflection.Person");
Constructor cstr = c.getConstructor(new Class[] {String.class , int.class})
(5). public Constructor[] getConstructors()
获得该类对象的所有公共的构造方法,如果此类是数组.Void,基本类型 则返回长度0的数组
(6). public Constructor<?> getDeclaredConstructor(Class<?> … parameterTypes)
表示该Class对象表示的类或接口的指定的构造方法
(7). public Constructor[] getDeclaredConstructors()
返回该Class对象表示的类或接口的所有构造方法,而不仅仅是公共的构造器.
如果此 Class 对象表示一个接口、一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组.
Field相关
(8). Public Field getDeclaredField(String name)
返回此Class对象所表示的名称为name的Field对象.注:此方法不返回数组类型的length
属性.
(9). public Field[] getDeclaredFields() 返回该Class对象表示的类或接口所声明的所有字段,但不包括继承字段.返回的元素没有排序.
(10). Public Field getField(String name)
要反映的字段由下面的算法确定。设 C 为此对象所表示的类:
1.如果 C 声明一个带有指定名的公共字段,则它就是要反映的字段。
2.如果在第 1 步中没有找到任何字段,则该算法被递归地应用于 C 的每一个直接超接口。直接超接口按其声明顺序进行搜索。
3.如果在第 1、2 两步没有找到任何字段,且 C 有一个超类 S,则在 S 上递归调用该算法。如果 C 没有超类,则抛出NoSuchFieldException
(11). Public Field[] getFields()
返回该Class对象表示的类或接口的所有可访问的公共字段
特别地,如果该 Class 对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果该 Class 对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段。该方法不反映数组类的隐式长度字段。用户代码应使用 Array 类的方法来操作数组.
Method相关
(12). public Method getDeclaredMethod(String name,Class parameterTypes)
返回一个Method对象,name表示该方法的名称,parameterTypes表示该方法的参数类型列表,是一个Class对象数组 new Class[] {}
(13). Public Method[] getDeclaredMethods()
返回该Class对象表示的类或接口的所有方法,但不包括继承的方法
(14). Public Method getMethod(String name,Class parameterTypes)
返回一个Method对象,它是该Class表示的类或接口的公共方法,包括其继承的方法
(15). public Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。数组类返回从 Object 类继承的所有(公共)member 方法
Modifier 相关
(16). public int getModifiers()
返回此类Class对象表示的接口或类的以整数编码的Java修饰符,他们必须通过 Modifier类的toString(int modifiers)方法来解析
Int mo = c.getModifiers(); String modifierName = Modifier.toString(mo);
Instance
(17). Public T newInstance()
创建该Class对象的一个新的实例,注:不带参数,也就是只能使用Class对象的无参构造来实例化对象
如果想要调用带参数的构造方法,需要通过Constructor类的newInstance()方法来实现
Class<? > c = Class.forName("org.cgz.Person"); //获得类的Class对象
Constructor cons = c.getConstructor(new Class[]{String.class,int.class}); //通过Class对象的getConstructor(Class…parameterTypes)获得构造器
Object obj = cons.newInstance(new Object[] {"张三",22})//通过Constructor对象的newInstance(Object..initargs)方法新建该类的实例化对象
Java Reflecation API
3. java . Lang . Reflect . Constructor类常用方法
(1). Public String getName()
以字符串形式返回此构造方法的名称。它总是与构造方法的声明类的全名.
(2). Public Class<?>[] getParameterTypes()
返回该构造方法的形参的类型,如果是无参构造,则返回一个长度为0的数组
(3). Public int getModifiers()
以整数形式返回此Constructor对象所表示的构造方法的修饰符,必须要用Modifier类的toString(int mo)方法解析
(4). Public T newInstance(Object … initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。如果所需的访问检查和参数检查获得成功并且实例化继续进行,这时构造方法的声明类尚未初始化,则初始化这个类
initargs表示的是实参
Constructor.newInstance( new Object[] { "张三" , 22 } )或Constructor.newInstance( "张三",22 );
(5). Public String toString()
(6). Public boolean equals(Object obj)
4.java . Lang . Reflect . Field类的常用方法
(1). Public String getName()
返回此Field对象表示的字段的名称
(2). Public Class<?> getType()
返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型.
(3). Public int getModifiers()
以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。应该使用 Modifier 类对这些修饰符进行解码
(4). Public Object get(Object obj)
返回指定对象上的此Field对象表示的值,如果该值是一个基本类型值,则自动将其包装在一个对象中
Obj:从中提取字段值的对象
(5). Public Xxx getXxx(Object obj)
获得 基本 类型或另一个通过扩展转换可以转换为 基本 类型的基本类型的静态或实例字段的值
Xxx: int double float long….
(6). Public void set(Object obj, Object value)
Obj:该字段所属的指定对象,value:为指定对象中的此Field对象表示的字段赋予的新值
(7). Public void setXxx(Object obj, Xxx value) 和set()方法类似
(8). Public void equals(Object obj)
(9). Public String toString()
返回一个描述此 Field 的字符串 例如:private int java.io.FileDescriptor.fd
5.java . lang . reflect . Method类常用方法
(1). Public String getName()
以 String 形式返回此 Method 对象表示的方法名称
(2). Public int getModifiers()
以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。应该使用 Modifier 类对修饰符进行解码
(3). Public Class<?> getReturnType()
返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型
(4). Public Class<?> getParameterType()
按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。如果基础方法不带参数,则返回长度为 0 的数组
(5). Public Object invoke(Object obj , Object … args)
对带有指定参数的指定对象调用由此 Method 对象表示的基础方法
Obj:调用Method对象表示方法的对象
args:方法所需要的参数
6.java.lang.reflect .Array
(1).public Object get(Object array, int index)
返回指定数组上的的指定下标的数组元素的值
(2). Public static Object newInstance(Class<?> componentType, int length)
创建一个具有指定数组组件类型,指定长度的数组
(3). Public void set(Object array, int index , Object value)
将指定数组对象中的指定索引的元素的值设置为value
(4). Public static Double getDouble(Object array, int index)
以Double形式返回指定数组中指定索引的元素的值
(5). Public static void setDouble(Object array, int index, Double d)
为指定数组的指定索引的元素设置一指定的Double类型的值d
7.java.lang.reflect.Modifier
三、实例代码
1.通过Java的反射机制调用并执行方法 主要是Method类的演示实例
public class InvokeTester {
public int add(int param1, int param2) {
return param1 + param2;
}
public String show(String msg) {
return "show: " + msg;
}
public static void main(String[] args) throws Exception {
Class<?> classType = InvokeTester.class;
Object invokeTester = classType.newInstance();
Method addMethod = classType.getDeclaredMethod("add", new Class[] {int.class, int.class });
Method showMethod = classType.getDeclaredMethod("show",new Class[] { String.class });
int sum = (Integer) addMethod.invoke(invokeTester, new Object[] {new Integer(2), new Integer(30) });
String msg = (String) showMethod.invoke(invokeTester,new Object[] { "反射机制" });
System.out.println(sum);
System.out.println(msg);
}
}
打印结果:
32
show: 反射机制
2.Array类的简单使用
public class ArrayTest1 {
public static void main(String[] args) throws Exception{
Class<?> classType = Class.forName("java.lang.String");
//通过Array类的newInstance()方法创建一个长度为10的字符串数组
Object array = Array.newInstance(classType, 10);
//Array array = (Array)Array.newInstance(classType, 10);
//将数组索引为5的元素值设置为你好
Array.set(array, 5,"你好");
//获取数组索引为5的元素的值
String msg = (String)Array.get(array, 5);
System.out.println(msg);
}
}