引言:
相信大家在初学JDBC时都用过这样一行代码, Class.forName("com.mysql.jdbc.Driver");这行代码的作用是:加载数据库驱动类到jvm中,并执行初始化。但是我们一般只会去关注mysql、sqlserver还是oracle,对class.forName()的原理不太了解,本文就谈谈这个我们“最熟悉的陌生人”——java反射机制。
测试
一 什么是反射?
Java反射机制是在运行状态中(动态),对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
从程序运行结果看反射:
正常方式:引入需要的"包.类"名称->通过new实例化->取得实例化对象 ;
反射方式:实例化对象->getClass()方法->得到完整的”包.类“名称。
二 反射机制的作用?
Case1: 假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。但利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
Case2: 大家都用过eclipse。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候。一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。
现在很多开源框架都用到反射机制,hibernate、struts都是用反射机制实现的。 Java的反射机制体现了Java的灵活性,反射机制是构建框架技术的基础所在。灵活掌握Java反射机制,对学习框架技术有很大的帮助。
简而言之,Java反射机制的作用有二:
(1)反编译,即.class->.java。
(2)通过反射机制访问java对象的属性,方法,构造方法等。
三 反射的优点与缺点
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念。
1.静态编译:在编译时确定类型,绑定对象,即通过。
2.动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,降低类之间的藕合性。
优点:实现动态创建对象和编译,体现出很大的灵活性。
特别是在J2EE的开发中,它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
缺点:是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
四 具体的功能实现(4步)
4.1获取类 有3种方法可以实现,我们来获取Employee类
//第一种方式:
Class c1 = Class.forName("Employee");
//第二种方式:
//java中每个类型都有class 属性.
Class c2 = Employee.class;
//第三种方式:
//java语言中任何一个java对象都有getClass 方法
Employeee = new Employee();
Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)
4.2创建对象 获取类以后我们来创建它的对象,利用newInstance
Class c =Class.forName("Employee");
//创建此Class 对象所表示的类的一个新实例
Objecto = c.newInstance(); //调用了Employee的无参数构造方法.
4.3获取属性
分为所有的属性和指定的属性:
4.3.1获取所有属性
Class c = Class.forName("java.lang.Integer"); //获取整个类
Field[] fs = c.getDeclaredFields(); //获取所有的属性
StringBuffer sb = new StringBuffer(); //定义可变长的字符串,用来存储属性
sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n");
//通过追加的方法,将每个属性拼接到此字符串中
for(Field field:fs){
sb.append("\t");//空格
sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如public,static等等
sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字
sb.append(field.getName()+";\n");//属性的名字+回车
}
sb.append("}");
System.out.println(sb);
4.3.2 获取特定的属性
public static void main(String[] args) throws Exception{
//以前的方式:
/*
User u = new User();
u.age = 12; //set
System.out.println(u.age); //get
*/
//获取类
Class c = Class.forName("User");
//获取id属性
Field idF = c.getDeclaredField("id");
//实例化这个类赋给o
Object o = c.newInstance();
//打破封装
idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。
//给o对象的id属性赋值"110"
idF.set(o, "110"); //set
//get
System.out.println(idF.get(o));
}
4.4
获取方法 和构造方法,不再详细描述,只来看一下关键字:
方法关键字 | 含义 |
getDeclaredMethods() | 获取所有的方法 |
getReturnType() | 获得方法的放回类型 |
getParameterTypes() | 获得方法的传入参数类型 |
getDeclaredMethod("方法名",参数类型.class,……) | 获得特定的方法 |
|
|
构造方法关键字 | 含义 |
getDeclaredConstructors() | 获取所有的构造方法 |
getDeclaredConstructor(参数类型.class,……) | 获取特定的构造方法 |
|
|
父类和父接口 | 含义 |
getSuperclass() | 获取某类的父类 |
getInterfaces() | 获取某类实现的接口 |
这样我们就可以获得类的各种内容,进行了反编译。对于 JAVA 这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。
综上,通过学习JAVA反射,并灵活的运用它,能够使我们的代码更加灵活,但是它也有它的缺点,就是运用它会使我们的软件的性能降低,复杂度增加,具体问题具体分析。
本文参考文献链接:
1--http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html
2--http://blog.csdn.net/liujiahan629629/article/details/18013523
3--http://www.cnblogs.com/gulvzhe/archive/2012/01/27/2330001.html
4--http://blog.csdn.net/ritterliu/article/details/7764849