首先先讲解一下动态语言和非动态语言:
动态语言:在程序运行期间,允许改变程序结构或者变量类型 例如:Perl, Python,Ruby
与之相对的是静态语言,C++,Java,C#
在动态语言中允许 var a=1; a=”aaaa”; a=false;
反射机制(Reflection)基本概念
java 的反射机制:动态获取类的信息以及动态调用对象的方法。
对于任意一个对象都够知道这个类具有哪些属性和方法
对于任意一个对象,能够调用它的任意一个方法。
java 中有一个非常突出的动态相关机制,反射机制reflection(被视为准动态语言的一个关键性质),
java程序可以加载一个运行时才得知名称的 class, 获悉其完整的构造(不包括method定义),获取其内部信息,并生成实例化对象,或对其fileds设值,或唤起其methods
这种看透class的能力被称为内省(introspection),它与reflection 是常被提起的两个术语
这种反射机制可以获取filed和methods的所有信息,并可于运行时改变fileds的内容或者调用Methods
java的反射机制提供了这样的功能:都是运行时的事情
1. 运行时判断任意一个对象所属的类
2. 运行时构造任意一个类的对象
3. 运行时判断任意一个类具有的属性和方法
4. 运行时调用任意一个对象的方法
反射机制 java.lang.reflect包中 :
/*
* Class类 对应一个类 ,在java.lang包下,其他全在java.lang,reflect包下
* Filed类, 对应类成员变量
* Method类 对应类成员方法
* Constructor类 对应类的构造方法
* Array类 提供了动态创建数组,以及访问数组的元素的静态方法
* java中不管产生了多少对象,都会对应于唯一的同一个 .class
*/
// 获取String类
Class<?> clas = Class.forName("java.lang.String");
// 获取String类的所有方法
Method[] method=clas.getDeclaredMethods();
// 获取String类的所有成员变量
Field[] filed = clas.getDeclaredFields();
// 获取String类的所有构造方法
Constructor[] con = clas.getDeclaredConstructors();
如何通过反射机制实例化一个对象,并调用其成员方法
下面是例子,详细介绍了步骤
public class ReflectTest{
public int add(int a, int b, int c){
return a+b+c;
}
public static void main(String[] args) throws Exception {
// 如何实现 实例化一个ReflexTest对象,并调用其add方法
// 第一种方法,new
// ReflectTest reflect = new ReflectTest();
// reflect.add(1,2,3);
// 第二种方法, 通过反射机制实现
//第一步:首先获取类的信息
Class<?> classType = ReflectTest.class;
// 第二步: 通过类信息实例化一个对象,调用newInstance() 方法
Object instance = classType.newInstance();
// 第三步,获取Method方法,利用getMethod方法,获取类的特定成员方法,
Method addMethod = classType.getMethod("add", new Class[] {int.class,int.class,int.class});
// Method addMethod = classType.getMethod("add", int.class, int.class, int.class); //上面一句的等价写法,后面一个变量为可变参数,离散输入或者以数组方式输入都可以
// public Method getMethod(String name, Class<?>... parameterTypes) 第一个参数为方法名,第二个为方法里面的变量的类的信息,为什么需要类的信息,因为java中允许重载的,所以 需要方法名,参数个数,参数类型才可以完全确定一个方法
// 第四步: 利用获取的方法的invoke()方法,调用类的成员方法,第一个参数为获取的实例,第二个参数为具体的传入的对象,注意的是返回值的类型是Object类型,使用的时候需要进行一步强制类型转换
Object result = addMethod.invoke(instance, new Object[]{1, 2, 3});
//Object result = addMethod.invoke(instance, 1, 2, 3); // 上面一句的等价写法
//public Object invoke(Object obj, Object... args) 后面一个变量为可变参数,离散输入或者以数组方式输入都可以
System.out.println((Integer)result);
}
}
要想使用反射,首先需要获得待处理类或对象所对应的 Class对象。
获取某个类或某个对象所对应的 Class对象的常用的 3种方式:
a) 使用Class类的静态方法 forName:Class.forName(“java.lang.String”);
b) 使用类的.class语法:String.class;
c) 使用对象的 getClass()方法:String s = “aa”; Class
public class ReflectTest2 {
public static void main(String[] args) throws Exception {
ReflectTest2 r1 = new ReflectTest2();
Person p1 = new Person(1,"wang", 20);
Person p2 = (Person) r1.copy(p1);
System.out.println(p2.getId()+p2.getName()+p2.getAge());
}
public Object copy(Object obj) throws Exception{
// 首先获取类的信息,并实例化一个对象
Class<?> classType = obj.getClass();
Object instance = classType.newInstance();
// newInstance() 方法生成一个对象,是调用无参数的构造方法,
// Object instance = classType.newInstance();
// 下面的两句等价于上面的一句,
// Constructor con = classType.getConstructor(new Class[]{});
// instance = con.newInstance( new Objec[]{});
// 但是因为Class的newInstance只能处理没有参数的情况,所以不合适所有的情况,Constrator因此提供了多个函数的构造方法
// 采用Constrator 实现,首先获得其构造函数,多个参数的
// Constructor conn = classType.getConstructor(new Class[]{int.class,String.class,int.class});
// Object instance2 = conn.newInstance(new Object[]{1,"wang",18});
// 获取所有的成员变量
Field[] field = classType.getDeclaredFields();
for( Field f : field ){
// 首先获取成员变量的名字
String s = f.getName();
// 要获取成员变量的值,得通过它的get方法,所以获取其get方法
String firstLetter = s.substring(0,1).toUpperCase(); // substring 截取字符串,toUpperCase 将字符串大写
// 首先得到get成员方法的名字,然后再得到get方法
String getMethod ="get"+ firstLetter + s.substring(1);
Method getMeth = classType.getMethod(getMethod, new Class[]{});
// 获取其set方法
String setMethod ="set"+ firstLetter + s.substring(1);
Method setMeth = classType.getMethod(setMethod, new Class[]{f.getType()});
// 从要复制的对象中取出成员变量的值,然后再复制到要复制的那个对象中去,通过set方法赋值
Object o = getMeth.invoke(obj, new Object[]{});
setMeth.invoke(instance, new Object[]{o});
}
return instance;
}
}
class Person{
private int id;
private String name;
private int age;
public Person() {
// TODO Auto-generated constructor stub
}
public Person(int id, String name, int age) {
// TODO Auto-generated constructor stub
this.id=id;
this.name=name;
this.age=age;
}
public int getId() {
return id;
}
public void setId(int 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;
}
}
需要注意的一点:
Integer.TYPE 返回的是 int,
而Integer .class返回的是 Integer 类所对应的Class对象。
定义Array数组
public class ReflectTest3 {
Class<?> classType = String.class;
// 申请一个10个类型为classType的数组
Object obj = Array.newInstance(classType,10);
// 获取数组中下标为5的 classType 类型的对象
Object obj2 = Array.get(obj,5);
int[] dimension = new int[]{5,10,15};
// 这里其实是定义了一个三维数组
Object array = Array.newInstance(Integer.TYPE, dimension);
int [][][] arraycast = (int[][][]) array;
重点:如何利用反射机制调用对象的私有方法,访问对象的私有成员变量
public class ReflectTest4 {
public static void main(String[] args) throws Exception{
// 使用类的私有成员方法
SayHello reflect = new SayHello();
Class<?> classType = reflect.getClass();
// 两种获取Method的方法,但是是有区别的
// Method method1 = classType.getMethod("sayHello", new Class[]{String.class}); // 返回的方法必须是public的
Method method2 = classType.getDeclaredMethod("sayHello", new Class[]{String.class}); // 返回的方法没有要求
method2.setAccessible(true); // 压制java的访问控制检查
// 直接使用会报错的,因为是调用的私有的方法,java进行访问检查, 因此之前需要加上上面一句,压制他的访问检查
method2.invoke(reflect, "world"); //helloworld
method2.invoke(reflect, new Object[]{"world2"}); //helloworld2
// 后面学的一些框架 如 hebirnate等都是使用了此框架
// 修改私有成员变量的值
SayHello sayHello2 = new SayHello();
Class<?> classType2 = sayHello2.getClass();
Field field = classType2.getDeclaredField("name");
field.setAccessible(true);
field.set(sayHello2, "lisi");
System.out.println(sayHello2.getName()); // lisi
}
}
class SayHello{
private String name = "zhangsan";
public String getName(){
return name;
}
private void sayHello(String s){
System.out.println("hello"+s);
}
}
在获取对象的类的信息时,特别需要注意的一点
Class<?> classType = Integer.TYPE; // 得到的是 int
Class<?> classType = Integer.class; // 得到的是Integer
// 同理可知,其他的其中原生数据类型的包装类也是同样的原理
//实际开发的时候很少用反射,但是如果自己开发框架,肯定会用到