反射的作用与原理。 如何提高反射效率?
1.定义: 反射机制是在运行时, 对于任意一个类, 都能够知道这个类的所
有属性和方法; 对于任意一个对象, 都能够调用它的任意一个方法。 在 java 中,
只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。
2.反射机制主要提供了以下功能: 在运行时判定任意一个对象所属的类;
在运行时创建对象; 在运行时判定任意一个类所具有的成员变量和方法; 在运
行时调用任意一个对象的方法; 生成动态代理。
3.哪里用到反射机制?
jdbc 中有一行代码:
Class.forName('com.mysql.jdbc.Driver.class');//加载 MySql 的驱动类。 这就
是反射, 现在很多框架都用到反射机制, hibernate, struts 都是用反射机制实
现的。
4.反射的实现方式
在 Java 中实现反射最重要的一步, 也是第一步就是获取 Class 对象, 得到
Class 对象后可以通过该对象调用相应的方法来获取该类中的属性、方法以及调
用该类中的方法。
有 4 种方法可以得到 Class 对象:
1.Class.forName(“类的路径” );
2.类名.class。
3.对象名.getClass()。
4.如果是基本类型的包装类, 则可以通过调用包装类的 Type 属性来获得
该包装类的 Class 对象。
例如: Class<?> clazz = Integer.TYPE;
4.实现 Java 反射的类
1)Class: 它表示正在运行的 Java 应用程序中的类和接口。
2)Field: 提供有关类或接口的属性信息, 以及对它的动态访问权限。
3)Constructor: 提供关于类的单个构造方法的信息以及对它的访问权限
4)Method: 提供关于类或接口中某个方法信息。
注意:Class类是Java反射中最重要的一个功能类,所有获取对象的信息(包
括: 方法/属性/构造方法/访问权限)都需要它来实现。
5.反射机制的优缺点?
优点:
(1) 能够运行时动态获取类的实例, 大大提高程序的灵活性。
(2) 与 Java 动态编译相结合, 可以实现无比强大的功能。
缺点:
(1) 使用反射的性能较低。 java 反射是要解析字节码, 将内存中的对象进
行解析。
解决方案:
1.由于 JDK 的安全检查耗时较多, 所以通过 setAccessible(true)的方式关闭安全检查
来(取消对访问控制修饰符的检查) 提升反射速度。
2.需要多次动态创建一个类的实例的时候, 有缓存的写法会比没有缓存要快很多:
3.ReflectASM 工具类 , 通过字节码生成的方式加快反射速度。
(2) 使用反射相对来说不安全, 破坏了类的封装性, 可以通过反射获取这个
类的私有方法和属性。
- 反射机制(reflection)
- 编程语言的分类
动态语言:在程序运行时,动态的改变变量的类型,为变量增加,删除属性及方法;
var a=4;
a="hello wold";
a=[4,5,76,78];
a=new Date();
a=new Object();
a.name="james";
非动态语言:
java并不是动态语言;
- 反射技术;
java不是动态语言,但通过反射技术可以实现动态语言的部分特征;
- 什么是反射技术?
在程序运行时,动态分析类的能力就是反射;
具体来说;
1.运行时,可以根据类名(String)加载这个类;
Class.forName("com.mysql.jdbc.Driver");
2.可以获得某个类的信息(元数据),例如类名,属性,方法,父类等;
3.可以动态调用一个对象的所有方法(包括private)
- Class类;
Class这个类代表加载到java虚拟机中的类实例,每个类只加载一次,因此每个只有一个唯一的实现,通过这个类实例,我们可以获得关于这个类型的所有元数据;
- 如何获得Class实例呢?
- Class c= Book.class;
- Class c=Class.forName("com.oracle.vo.Book");
- Class c=obj.getClass();
- Class中提供的常用方法;
getName():获得类的全名(包括包名)
getSimpleName():只获得类名
getSuperclass():获得父类
getInterfaces():获得实现的接口;
isXXX():是否是某种类型的实例(Array,Enum,Interface)
getComponentType():如果类实例是数组,得到数组中元素的类型;
- Field:
属性的元数据的封装;
获得Field的方法;
Field[] getFields():获得所有公共的属性(自定义和继承的)
Field getField(String name):根据属性名来获得指定的公共属性;
Field[] getDeclaredFields():获得所有自定义的属性;
Field[] getDeclaredField();根据属性名来获得自定义的
- Constructor:构造器
Class Book{
public Book(String name,int price){
}
}
Book b=new Book();
1.开辟空间;
2.调用构造方法;
3.将对象的引用返回b;
- 如何获得构造方法?
getConstructors();获得所有的公共的构造方法;
getConstructor(Class...args);根据参数列表获得指定的公共的构造方法
getDeclaredConstructors();获得所有的构造方法;
getDeclaredConstructor(Class...args);
- 作业:
定义一个单模式,并使用反射来创建多个对象;
- 关于反射中构造方法的最常用场景 ;
- 调用private的构造方法;
- 调用默认的公共的构造方法来动态创建对象,它是使用Class.newInstance()来简化我们的代码;
- dao的查询;
sql,class ,将查询出来的记录转换成对应的对象;
- Method方法
method表示类中定义的一个方法;
修改符
返回值类型;
方法名;
参数;
如何用method;
被调用 ; invoke();
- 如何获得method;
getMethod(String name,Class...obj):指定公共
getMethods();所有公共
getDeclaredMethod(String name,class...obj);指定的方法
getDeclaredMethods();获得全部方法
- mthod中常用的方法;
getReturnType():获得返回值类型 Class
getModifiers():获得访问修饰符的组合(int)
getParameterTypes:获得参数类型的数组 Class[]
getName():获得方法名
setAccessible():设置方法是否为可访问
invoke(Object targer,Object...params):调用这个方法,
target:目标对象(哪个对象的方法)
params: 实参列表
- Annotation
注解;
简单来说,就是给编译器看的一些注释;
作用:在不改变类结构的情况下,每元数据(Class,Field,Constructor,Methos等)增加一些补充信息,这些补充信息可以在程序运行时获得;
- Annotaion(注解)分为两种
JDK提供的:
@Override:重写
@Deprecated:过期,不建议使用
@SuppressWarnings:压制警告
自定义的:
- 自定义注解
/**
* 定义一个注解,用来修饰属性;注解一直保留到运行阶段;
* @author Administrator
*
*/
@Retention(RUNTIME)
@Target(FIELD)
public @interface PrimaryKey {
}
@Retention(RUNTIME)
@Target(TYPE)
public @interface Table {
String value();
}
- 通过反射操作注解信息;
Annotation getAnnotation(Class):获得指定类型的注解,如果存在,则返回注解实例,不存在则返回null;
//如果当前的类上有Table注解,则获得此类型的注解;
Table t=(Table)c.getAnnotation(Table.class);
boolean isAnnotationPresent(Class) :当前的元数据中是否使用了某种类型的注解;
例如:isAnnotationPresent(PrimaryKey.class)
彻底理解jdbc为什么用反射创建驱动程序对象
2016年09月13日 00:55:00 weixin_34082177 阅读数:73
1.class.forName(mysql),这样更换数据库时,不需要更改程序代码,程序不需要重新编译就能运行。
因为反射是动态编译的,程序运行期间生成指定类的对象,
这样就可以程序运行期间生成不同的数据库驱动程序对象。省去了固定写死一个数据库驱动对象(通过new 构造函数的方法),造成每更换一个数据库,要重新编译代码的问题。
2.ioc容器中生成对象时,也是根据xml配置信息中的类型,反射生成对象的。并把这些对象存储在hashmap中,供程序调用的
3.hibernate/mybatis这种持久层框架中,进行数据持久化时,通过反射生成sql语句
JDBC为什么使用反射加载驱动
2016年08月30日 17:04:30 tenlee 阅读数:2818
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tenlee/article/details/52371088
一直不太明白在使用JDBC的时候用Class.forName("com.mysql.jdbc.Driver")
去加载驱动,就谷歌了一下,得到如下答案:
前言
JDBC源码地址
还有
在解释具体原因之前先简单看下Class.forName做了什么。假设一个类以前从来没有被装进内存过,Class.forName(String className)这个方法会做以下几件事情:
1、装载。将字节码读入内存,并产生一个与之对应的java.lang.Class类对
2、连接。这一步会验证字节码,为static变量分配内存,并赋默认值(0或null),并可选的解析符号引用(这里不理解没关系)
3、初始化。为类的static变量赋初始值,假如有static int a = 1;这个将a赋值为1的操作就是这个时候做的。除此之外,还要调用类的static块。(这一步是要点)
Class.forName(String className)方法会将这三步都做掉,
基本原因
首先,上面一段代码的主要作用,是在运行期以反射的方式来检查JDBC驱动的主类com.mysql.jdbc.Driver
是否存在,若不存则表示运行环境中没有这个驱动,进入catch段。如果你确定一定以及肯定它会存在,可以直接写成
import
com.mysql.jdbc.Driver;
- 1
效果基本是一样的(只是在编译期及运行期要都保证此类存在classpath中)
所以,以反射形式加载的一个好处是当驱动jar包不存在时,我们可以做更多的操作。(要知道,在很久很久以前,jdbc驱动一般都是放在运行环境的classpath中的,如tomcat/lib)
另一个原因
如果你是一个有追求的程序员,那么另外一个很重要的原因是解耦。
首先要明白JDBC是Java的一种规范,通俗一点说就是JDK在java.sql.*
下提供了一系列的接口(interface),但没有提供任何实现(也就是类)。 所以任何人都可以在接口规范之下写自己的JDBC实现(如MySQL)。而若调用者也只调用接口上的方法(如我们),那么当未来有任何变更需要时(例如要从MySQL迁移至Oracle),则理论上不需要对代码做任何修改就能直接切换(可惜SQL语法没能统一规范)
这意味着什么?意味着你的代码中不应该引用任何与实现相关的东西,你的代码只知道java.sql.*
,而不应该知道com.mysql.*
或是com.oracle.*
,以避免或减少未来切换数据源时对代码的变更。
注意,我们使用的所有其他API包括Connection/Statement/ResultSet
等都是java.sql.*
的东西,甚至com.mysql.jdbc.Driver
类也是:
package com.mysql.jdbc;
public class Driver
...implements java.sql.Driver {
...}
- 1
- 2
因此,直接import com.mysql.jdbc.Driver;
违反了开闭原则(OCP,对扩展开放,对修改关闭)。(有人说我用反射也必须要修改代码呀,事实上你可以将类名字符串存储至.properties文件,和数据库用户名密码放在一起,就像Hibernate做的那样)
一般项目里都会有数据库配置文件,其中就有一条是driverName="com.mysql.jdbc.Driver",代码会根据配置文件加载驱动。
当你想换数据库时只要修改driverName的值就可以了,代码就不需要修改
你在代码里直接new 个mysql的Driver,那换数据库时不是还要修改代码中你new 的Driver
首先你要知道com.mysql.jdbc.Driver的作用是什么,加载这个类实际是注册了对应的jdbc driver,不同的数据库厂商driver是不同的简单理解是类名不同,写死在代码里面不灵活,所以提取出来到配置文件里了。
因为反射好用,
为什么好用呢,因为你给个类地址它就能new 一个对象出来,动态的
最重要的是地址是字符串,是一个变量,不是一个具体对象,也就是说它脱离了代码,增加了相当大的灵活性。这里的灵性性是对对象的处理。
个人理解,当你用久了面向对象编程,对一条条规定用烦的时候,你会觉得反射这个机制相当于打破了这些规定,什么对象方法,想怎么弄就怎么弄,想怎么改就怎么改。
反正好用,不过性能上不太好,所以用的都是关键的地方