使用JAVA反射的利与弊

在Java的20周年的纪念日的日子里,让我们来重新温习下Java里面的高级知识,Java肯定希望大家了解她,要不然你跟她天天相濡以沫了这么长时间,让她知道你竟然不了解她,不在乎她,那么她该有多伤心呢,所以我们不应该做一个负心汉,更不应该做一个忘恩负义的人,她教会了你生存的技能,所以我们也应该将她发扬光大!

Java的核心技能有如下几项: 
(1)JVM的调优 
(2)类加载器 
(3)反射 
(4)动态编译 
(5)动态代理 
(6)注解 
(7)多线程 
(8)IO,NIO,Socket,Channel等网络编程 
除了JAVA的基础,面向对象的思想外,这些既是java里面核心技术,也是面试时候,面试官经常爱问的几个知识,了解,熟悉和掌握他们的重要性不言而喻,今天就先来谈谈反射。

反射给java提供了,运行时获取一个类实例的可能,这一点非常灵活,你仅仅传一个类的全包名路径,就能通过反射,来获取对应的类实例,我们一般会用Class类,来调用这个被反射的Objcet类下的,构造方法,属性,或方法等,反射在一些开源框架里用的非常之多,Spring,Struts,Hibnerate,MyBatics都有它的影子,反射虽然很灵活,能够使得写的代码,变的大幅精简,所以在用的时候,一定要注意具体的应用场景,反射的优缺点如下:

优点:

(1)能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。 
(2)与Java动态编译相结合,可以实现无比强大的功能

缺点: 
(1)使用反射的性能较低 
(2)使用反射相对来说不安全 
(3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性

任何事物,都有两面性,反射的优点,也同是就是它的缺点,所以,没有好与坏,只有最合适的场景,一阴一阳,才是天道平衡的条件。

下面来看个,使用java反射,来自动封装数据库对应的表的例子,初学java的人都会给每个实体类建立一个Dao对象,来专门操作这个对象对应的表,这样做没错,很好,是分层,分工明确的一个表现,但是如果有几十个实体类,那么这种重复增删改查的工作,就会大大增加,散仙初入门的时候也有如此的感受,虽然我们可以通过,抽象类和接口,使用适配器的设计模式来简化重复的代码,但是不可避免的就是类的臃肿了,下面看看如何使用反射来搞定这么多实体类的重复的增删改查的代码: 
使用前提: 
(1)每一个实体类都会对应一个数据库表 
(2)每个表的列,与对应的实体类的属性名是一样的 
(3)实体类要提供基本的get或set方法

实体类如下:

Java代码  

  1. package com.qin.model;
  2. public class Dog {
  3. private int id;
  4. private String name;
  5. private String type;
  6. private String color;
  7. private int weight;
  8. public int getId() {
  9. return id;
  10. }
  11. public void setId(int id) {
  12. this.id = id;
  13. }
  14. public String getName() {
  15. return name;
  16. }
  17. public void setName(String name) {
  18. this.name = name;
  19. }
  20. public String getType() {
  21. return type;
  22. }
  23. public void setType(String type) {
  24. this.type = type;
  25. }
  26. public String getColor() {
  27. return color;
  28. }
  29. public void setColor(String color) {
  30. this.color = color;
  31. }
  32. public int getWeight() {
  33. return weight;
  34. }
  35. public void setWeight(int weight) {
  36. this.weight = weight;
  37. }
  38. public Dog() {
  39. // TODO Auto-generated constructor stub
  40. }
  41. public Dog(int id, String name, String type, String color, int weight) {
  42. super();
  43. this.id = id;
  44. this.name = name;
  45. this.type = type;
  46. this.color = color;
  47. this.weight = weight;
  48. }
  49. @Override
  50. public String toString() {
  51. return "Dog [id=" + id + ", name=" + name + ", type=" + type + ", color="
  52. + color + ", weight=" + weight + "]";
  53. }
  54. }

Java代码  

  1. package com.qin.model;
  2. public class Person {
  3. private int id;
  4. private String name;
  5. private int age;
  6. private String address;
  7. public int getId() {
  8. return id;
  9. }
  10. public void setId(int id) {
  11. this.id = id;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public int getAge() {
  20. return age;
  21. }
  22. public void setAge(int age) {
  23. this.age = age;
  24. }
  25. public String getAddress() {
  26. return address;
  27. }
  28. public void setAddress(String address) {
  29. this.address = address;
  30. }
  31. public Person() {
  32. // TODO Auto-generated constructor stub
  33. }
  34. public Person(int id, String name, int age, String address) {
  35. super();
  36. this.id = id;
  37. this.name = name;
  38. this.age = age;
  39. this.address = address;
  40. }
  41. @Override
  42. public String toString() {
  43. return "Person [id=" + id + ", name=" + name + ", age=" + age
  44. + ", address=" + address + "]";
  45. }
  46. }

Java代码  

  1. package com.qin.db;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. /**
  7. * 数据库连接的
  8. * 测试类
  9. * @author qindongliang
  10. *
  11. *
  12. * **/
  13. public class ConnectionFactory {
  14. public static Connection getCon()throws Exception{
  15. Class.forName("com.mysql.jdbc.Driver");
  16. //加上字符串编码指定,防止乱码
  17. Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/rate?characterEncoding=utf8", "root", "qin");
  18. return connection;
  19. }
  20. public static void main(String[] args) throws Exception {
  21. Class.forName("com.mysql.jdbc.Driver");
  22. Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/rate", "root", "qin");
  23. System.out.println(connection);
  24. connection.close();
  25. }
  26. }

Java代码  

  1. package com.qin.commons;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.Method;
  4. import java.sql.Connection;
  5. import java.sql.PreparedStatement;
  6. import java.sql.ResultSet;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. import com.qin.db.ConnectionFactory;
  10. import com.qin.model.Dog;
  11. import com.qin.model.Person;
  12. /***
  13. * 反射自动查询和封装的类
  14. *@author qindongliang
  15. *
  16. * */
  17. public class CommonSupport {
  18. /**
  19. * @param obj需要保存的对象
  20. * @param string 保存对象的sql语句
  21. * */
  22. public static String createSqlByObject(Object obj){
  23. StringBuffer sb=new StringBuffer("insert into ");
  24. //得到对象的类
  25. Class c=obj.getClass();
  26. //得到对象中的所有方法
  27. Method[] ms=c.getMethods();
  28. //得到对象中所有的属性,虽然在这个里面就能获取所有的字段名,但不建议这么用,破坏类的封装性
  29. Field[]  fs=c.getDeclaredFields();
  30. //得到对象类的名字
  31. String cname=c.getName();
  32. System.out.println("类名字: "+cname);
  33. //表名字
  34. String tableName=cname.split("\\.")[cname.split("\\.").length-1];
  35. System.out.println("表名字: "+tableName);
  36. //追加表名和(左边的符号
  37. sb.append(tableName).append(" (");
  38. //存放列名的集合
  39. List<String> columns=new ArrayList<String>();
  40. //存放值的集合
  41. List values=new ArrayList();
  42. //遍历方法
  43. for(Method m:ms){
  44. String methodName=m.getName();//获取每一个方法名
  45. //只得到具有get方法的属性,getClass除外
  46. if(methodName.startsWith("get")&&!methodName.startsWith("getClass")){
  47. //System.out.println("属性名:"+methodName);
  48. String fieldName = methodName.substring(3, methodName.length());
  49. //               System.out.println("字段名:"+fieldName);
  50. columns.add(fieldName);//将列名添加到列名的集合里
  51. try{
  52. Object value=m.invoke(obj, null);
  53. //System.out.println("执行方法返回的值:"+value);
  54. if(value instanceof String){
  55. //                       System.out.println("字符串类型字段值:"+value);
  56. values.add("‘"+value+"‘");//加上两个单引号,代表是字符串类型的
  57. }else{
  58. //                       System.out.println("数值类型字段值:"+value);
  59. values.add(value);//数值类型的则直接添加
  60. }
  61. }catch(Exception e){
  62. e.printStackTrace();
  63. }
  64. }
  65. }
  66. for(int i=0;i<columns.size();i++){
  67. String column=columns.get(i);
  68. Object value=values.get(i);
  69. System.out.println("列名:"+column+" 值:  "+value);
  70. }
  71. //拼接列名
  72. for(int i=0;i<columns.size();i++){
  73. if(i==columns.size()-1){
  74. sb.append(columns.get(i)).append(" ) ");
  75. }else{
  76. sb.append(columns.get(i)).append(" , ");
  77. }
  78. }
  79. System.out.println(" 拼接列名后的sql:"+sb.toString());
  80. sb.append(" values ( ");
  81. //拼接值
  82. for(int i=0;i<values.size();i++){
  83. if(i==values.size()-1){
  84. sb.append(values.get(i)).append(" ) ");
  85. }else{
  86. sb.append(values.get(i)).append(" , ");
  87. }
  88. }
  89. System.out.println(" 拼接值后的sql:"+sb.toString());
  90. //返回组装的sql语句
  91. return sb.toString();
  92. }
  93. /**
  94. * 将对象保存在数据库中
  95. * @param obj 保存的对象
  96. * **/
  97. public static void addOne(Object obj){
  98. try {
  99. Connection con=ConnectionFactory.getCon();
  100. String sql=createSqlByObject(obj);
  101. PreparedStatement ps=con.prepareStatement(sql);
  102. int result=ps.executeUpdate();
  103. if(result==1){
  104. System.out.println("保存成功!");
  105. }else{
  106. System.out.println("保存失败!");
  107. }
  108. ps.close();
  109. con.close();
  110. } catch (Exception e) {
  111. // TODO Auto-generated catch block
  112. e.printStackTrace();
  113. }
  114. }
  115. /**
  116. * 根据类名字和一个查询条件
  117. * 自动封装一个Bean对象
  118. * @param columnName 列名
  119. * @param value 列值
  120. * @return {@link Object}
  121. *
  122. * */
  123. public static Object getOneObject(String className,String columnName,String value){
  124. String tableName=className.split("\\.")[className.split("\\.").length-1];
  125. System.out.println("表名字: "+tableName);
  126. //根据类名来创建对象
  127. Class c=null;
  128. try{
  129. c=Class.forName(className);//反射生成一个类实例
  130. }catch(Exception e){
  131. e.printStackTrace();
  132. }
  133. //拼接sql语句
  134. StringBuffer sb=new StringBuffer();
  135. sb.append("select * from ")
  136. .append(tableName)
  137. .append(" where ")
  138. .append(columnName).append(" = ").append("‘").append(value).append("‘");
  139. String querySql=sb.toString();
  140. System.out.println("查询的sql语句为:"+querySql);
  141. Object obj=null;
  142. try{
  143. Connection con=ConnectionFactory.getCon();//得到一个数据库连接
  144. PreparedStatement ps=con.prepareStatement(querySql);//预编译语句
  145. ResultSet rs=ps.executeQuery();//执行查询
  146. //得到对象的所有的方法
  147. Method ms[]=c.getMethods();
  148. if(rs.next()){
  149. //生成一个实例
  150. obj=c.newInstance();
  151. for(Method m:ms){
  152. String mName=m.getName();
  153. if(mName.startsWith("set")){
  154. //根据方法名字自动提取表中对应的列名
  155. String cname = mName.substring(3, mName.length());
  156. //打印set的方法名
  157. // System.out.println(cname);
  158. //得到方法的参数类型
  159. Class[] params=m.getParameterTypes();
  160. //                    for(Class cp : params){
  161. //                        System.out.println(cp.toString());
  162. //                    }
  163. //如果参数是String类型,则从结果集中,按照列名取到的值,进行set
  164. //从params[0]的第一个值,能得到该数的参数类型
  165. if(params[0]==String.class){//
  166. m.invoke(obj, rs.getString(cname));
  167. //如果判断出来是int形,则使用int
  168. }else if(params[0]==int.class){
  169. m.invoke(obj, rs.getInt(cname));
  170. }
  171. }
  172. }
  173. }else{
  174. System.out.println("请注意:"+columnName+"="+value+"的条件,没有查询到数据!!");
  175. }
  176. rs.close();
  177. ps.close();
  178. con.close();
  179. }catch(Exception e){
  180. e.printStackTrace();
  181. }
  182. return obj;
  183. }
  184. public static void main(String[] args) throws Exception{
  185. //====================添加======================
  186. Dog d=new Dog(21, "小不点", "藏獒", "灰色", 25);
  187. Person p=new Person(6, "大象hadoop", 10, "家住Apache基金组织");
  188. //createSqlByObject(d);
  189. //addOne(d);给dog表添加一条数据
  190. //addOne(p);//给person表添加一条数据
  191. //=======================查询=======================
  192. //强制转换为原始类
  193. //    Dog d1=(Dog)getOneObject("com.qin.model.Dog", "id", "1");
  194. //    System.out.println(d1);
  195. Person d1=(Person)getOneObject("com.qin.model.Person", "id", "1");
  196. //Person d1=(Person)getOneObject("com.qin.model.Person", "name", "王婷");
  197. System.out.println(d1);
  198. }
  199. }

代码量是非常的少的,而且具有通用型,如果再有10个这个实体类,我们代码根本不用任何改动,只需要传入不同的实体类名字即可,当然这一点和Hibernate的自动化ORM非常接近了,在Hibnerate里,可以自动通过表生成类,也可以通过类生成数据库的表,原理其实就是利用了反射的特性,帮我们做了大量的重复工作,当然Hibnerate提供了更多的特性,也这只是一个简单的例子,具体的应用场景中,我们也需要因地制宜,否则,则为适得其反!

最后,大家来一起喊一句: 
JAVA ,我爱你 !

时间: 2024-10-16 15:06:10

使用JAVA反射的利与弊的相关文章

Android-Java-synchronized同步锁机制&amp;利与弊

synchronized同步锁机制 定义锁??的方式一: package android.java.thread09; public class Test implements Runnable { @Override public void run() { /** * 定义一个锁??,这个锁是Jav内部要去判断用的,属于隐士的 看不到的 * Java内部的实现属于 ??锁机制 */ Object lock = new Object(); synchronized (lock) { /** *

Java反射

1. 介绍 反射是一种能够在程序运行时动态访问.修改某个类中任意属性和方法的机制. 具体:对于任意一个类,都能够知道这个类的所有属性和方法对于任意一个对象,都能够调用它的任意一个方法和属性 在运行时,当加载完类之后,JVM在堆内存中会自动产生一个Class类型的对象,这个对象包含了完整的类的结构信息 这个Class对象就像一面镜子,透过这个镜子看到类的结构 那么,如何得到这个Class对象呢?以下可否 Class c = new Class(); 答案是不行的,因为Class的构造函数定义为私有

Java 反射详解

反射反射,程序员的快乐,今天你快乐了吗?如果你不快乐,没关系,接下来让你快乐起来! 一.什么是反射? 通过百度百科我们可以知道,Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:并且能改变它的属性.而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言.从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C

java反射机制(一)—— 利用反射机制实例化对象

一.Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载.探知.使用编译期间完全未知的classes.换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods.(度娘文库是这么说的) 二.这篇文章主要介绍一下通过反射机制去实例化一个类的对象,然后调用其方法.本文主要介绍两种方式,第一种就是通过构造函数来实例化,第二种就是通过Cl

java 反射 详解

本文来自:blog.csdn.net/ljphhj JAVA反射机制:   通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们. 理论的东东太多也没用,下面我们看看实践 Demo - Demo: package cn.lee.demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import

【java】java反射机制,动态获取对象的属性和对应的参数值,并属性按照字典序排序,Field.setAccessible()方法的说明【可用于微信支付 签名生成】

方法1:通过get()方法获取属性值 package com.sxd.test.controller; public class FirstCa{ private Integer num; private String name; private Boolean flag; public Integer getNum() { return num; } public void setNum(Integer num) { this.num = num; } public String getNam

java 反射类的理解与应用

本文主要解析的类是: ClassLodaer,Class,Field,Method,Constructor. 本文的目标很简单,只是对这些常用的反射类进行简单解释.对这些类中常用方法进行介绍. JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制.Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在

Java 反射机制

使用 Java 反射机制可以在运行时期检查 Java 类的信息,检查 Java 类的信息往往是你在使用 Java 反射机制的时候所做的第一件事情,通过获取类的信息你可以获取以下相关的内容: Class 对象 类名 修饰符 包信息 父类 实现的接口 构造器 方法 变量 注解 除了上述这些内容,还有很多的信息你可以通过反射机制获得,如果你想要知道全部的信息你可以查看相应的文档 JavaDoc for java.lang.Class 里面有详尽的描述. 在本节中我们会简短的涉及上述所提及的信息,上述的

Java 反射,开发框架必备技能

通过反射技术我们将上面的统一资源定位付(URL) 映射到Class 相当于 class: news method: list parameter: 2 差不多就是下面样子 class News{ public String list(String catagory_id){ ... ... } } 我们只需要在框架核心中分析 url 然后调用对应的方法下载,于此同时将参数传递过去. Class<?> cls = Class.forName("cn.netkiller.reflect.