java之反射的基本介绍
什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为Java的反射机制。
什么是动态呢?动态是相对于静态而言的,主要区别就是二者创建对象的时间不同,静态是在编译时创建对象,动态是在运行期创建对象。
其实在显示业务逻辑中能用到反射的地方很少,一般在工具类中还有aop中使用,但是在基础框架的设计中应用比较多。
反射的优缺点
优点:运行期进行类型的判断,动态加载,提高了代码的灵活度
缺点:性能时反射的最大的缺点,反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。并且很多地方是没法优化或者优化很有限。
业务可读性不好,且在出现问题时排错成本很高。
适用场景
- 逆向代码 ,例如反编译
- 与注解相结合的框架 例如Retrofit
- 单纯的反射机制应用框架 例如EventBus 2.x
- 动态生成类框架 例如Gson
- 编码阶段不知道需要实例化的类名是哪个,需要在runtime从配置文件中加载
- 在runtime阶段,需要临时访问类的某个私有属性
四个基本类
- Field:反射的属性
- Constractor:构造函数
- Method:方法
- Class:类的字节码对象
反射的应用场景
实例:对账工具类
说明:比较两个集合中有差异数据的对象,最后返回差异数据的集合,并列出差异的文字描述。
注意点:
1.如何确定两个集合哪两个对象进行比较,这个需要根据具体业务来定。此例中是根绝借款订单号和期数来确定两个集合中的哪两个对象进行比较
2.如何确定对象中的哪些字段参与比较,是根据自定义注解。即字段上有这个自定义注解此字段就参与比较
代码:
使用反射构建对账工具类代码:
/**
* 构建差异数据
* <P>
* 1.比较的实体里面必须包含exInfo字段,用于存储比较后的文字信息
* 2.自定义注解ContrastTitle,此注解主要用于区分哪些字段需要比较
*
* @param csvDatas csv文件数据 通常就是外来数据
* @param localDatas 本地数据
* @return
*/
public <T> List<T> buildDifferentialData(List<T> csvDatas, List<T> localDatas){
List<T> differentialDatas = new ArrayList<>();
try{
for (int i=0;i<csvDatas.size() ;i++) {
boolean isEqually=false;
T csvDataObj=csvDatas.get(i);
// 获取对象的属性(这个属性是为了存放比对结果得备注属性)
Field exInfoField = csvDataObj.getClass().getDeclaredField("exInfo");
// 对象的属性的访问权限设置为可访问
exInfoField.setAccessible(true); for (T localData:localDatas){
if(csvDataObj.equals(localData)){
isEqually=true;
StringBuffer sb1= compareTwoClass(csvDataObj, localData);
if(!sb1.toString().equals("")){
// 设置此属性的值
exInfoField.set(csvDataObj,sb1.append("不一致").toString());
differentialDatas.add(csvDataObj);
differentialDatas.add(localData);
}
localDatas.remove(localData);
break;
}
}
if(!isEqually){
//csvDatas中有独有的
// 设置此属性的值
exInfoField.set(csvDataObj,"合作方独有的数据");
differentialDatas.add(csvDataObj);
}
}
for(T localDataPeculiar:localDatas){
Field exInfoField = localDataPeculiar.getClass().getDeclaredField("exInfo");
// 对象的属性的访问权限设置为可访问
exInfoField.setAccessible(true);
// 设置此属性的值
exInfoField.set(localDataPeculiar,"自己独有的数据");
differentialDatas.add(localDataPeculiar);
}
}catch (Exception e){ }
return differentialDatas;
}
/**
* 比较两个对象的属性值是否一致
*
* @param class1
* @param class2
* @return
* @throws ClassNotFoundException
* @throws IllegalAccessException
*/
public StringBuffer compareTwoClass(Object class1,Object class2) throws ClassNotFoundException, IllegalAccessException {
StringBuffer exInfo=new StringBuffer("");
//获取对象的class
Class<?> clazz1 = class1.getClass();
Class<?> clazz2 = class2.getClass();
//获取对象的属性列表
Field[] csvDataFieldArr = clazz1.getDeclaredFields();
Field[] localDataFieldArr = clazz2.getDeclaredFields();
//遍历属性列表csvDataFieldArr
for (int i = 0; i < csvDataFieldArr.length; i++) {
Field csvDatafield= csvDataFieldArr[i]; //遍历属性列表localDataFieldArr
for (int j = 0; j < localDataFieldArr.length; j++) {
Field localDataField= localDataFieldArr[j];
//设置能够访问私有属性
csvDatafield.setAccessible(true);
localDataField.setAccessible(true);
//如果csvDatafield属性名与localDataField属性名相同
if (csvDatafield.getName().equals(localDataField.getName())) {
//判断是否有对账用的注解,即判断此属性是否需要对账
boolean fieldHasAnno = csvDatafield.isAnnotationPresent(ContrastTitle.class);
if(fieldHasAnno) {
//如果csvDatafield属性值与localDataField属性值内容不相同
if (!compareTwo(csvDatafield.get(class1), localDataField.get(class2))) {
ContrastTitle fieldAnno = csvDatafield.getAnnotation(ContrastTitle.class);
//获取注解属性的值,用于拼装差异数据的备注
String value = fieldAnno.value();
exInfo.append(value + ",");
}
}
break;
}
}
}
return exInfo;
} /**
* 比较属性值是否相同
*
* @param object1
* @param object2
* @return
*/
private boolean compareTwo(Object object1,Object object2){
if(object1==null&&object2==null){
return true;
}
if((object1==null&&object2!=null)||(object1!=null&&object2==null)){
return false;
}
if(object1.equals(object2)){
return true;
}
return false;
}
自定义注解:
import java.lang.annotation.*; /**
* 对账中需要对账的字段需要添加这个注解
*
* @author wanglinan 2018年11月08日
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ContrastTitle { String value(); }
对掌集合中的实体对象:
import com.zhongan.insf.athena.insure.account.annotations.ContrastTitle;
import com.zhongan.insf.common.annotation.ExcelTitle;
import lombok.Getter;
import lombok.Setter; import java.math.BigDecimal; /**
*
* Title: RepayPlanDetailFileVO
* Description: 还款计划对账文件转换
* @author duanws
* @date 2018年8月17日
*/
@Getter
@Setter
public class RepayPlanDetailFileVO { /**
* 借款申请单号
*/
@ExcelTitle(value = "借款申请单号")
private String loanOrderId; @ExcelTitle(value = "应还本金")
@ContrastTitle(value = "应还本金")
private BigDecimal repayAmount; @ExcelTitle(value = "应还利息")
@ContrastTitle(value = "应还利息")
private BigDecimal repayInterest; @ExcelTitle(value = "当前期数")
@ContrastTitle(value = "当前期数")
private Integer repayNum; @ExcelTitle(value = "约定还款日")
private String repayDate; @ExcelTitle(value = "备注")
private String exInfo; @Override
public boolean equals(Object object) {
RepayPlanDetailFileVO vo=(RepayPlanDetailFileVO)object;
//通过借款订单号和当前期数确定比对对象
if(vo.getLoanOrderId().equals(loanOrderId)&&vo.getRepayNum()==repayNum){
return true;
}
return false;
} @Override
public String toString() {
return "RepayPlanDetailFileVO{" +
"loanOrderId='" + loanOrderId + '\'' +
", repayAmount=" + repayAmount +
", repayInterest=" + repayInterest +
", repayNum=" + repayNum +
", repayDate='" + repayDate + '\'' +
", exInfo='" + exInfo + '\'' +
'}';
}
}
另一种给属性设置值的方法
Field exInfo = repayPlanDetailFileVO.getClass().getDeclaredField("exInfo");
Class<?> type = exInfo.getType();
Method setReadOnly = repayPlanDetailFileVO.getClass().getMethod("setExInfo", type);
String s ="test2";
setReadOnly.invoke(repayPlanDetailFileVO,s);
System.out.println(repayPlanDetailFileVO.getExInfo());
两个对象中相同属性值的copy
private static Object constructObject(Object fromObject,Object toObject,String[] fields) throws Exception{ // 数据源的class
Class fromClass = fromObject.getClass();
// 目标的class
Class toClass = toObject.getClass(); for (String field : fields){
try{
// 获取fromClass的Field
Field fromDeclaredField = fromClass.getDeclaredField(field);
fromDeclaredField.setAccessible(true); // 从fromClass中获取属性的值
Object value = fromDeclaredField.get(fromObject); // 获取toClass的Field
Field toDeclaredField = toClass.getDeclaredField(field);
toDeclaredField.setAccessible(true); // 将fromClass中该属性的值设置给toClass中的该属性
toDeclaredField.set(toObject, value); }catch (NoSuchFieldException e){
System.out.println(field+"属性不存在");
e.printStackTrace();
}
} // 如果没有传递属性过来,那么默认对比from和to中的属性,存在的进行赋值操作
if(fields.length == 0){
Field[] fromDeclaredFields = fromClass.getDeclaredFields();
Field[] toDeclaredFields = toClass.getDeclaredFields();
List<String> fromList = new ArrayList<String>();
List<String> toList = new ArrayList<String>(); // 取出from中所有field
for (Field field : fromDeclaredFields){
field.setAccessible(true);
fromList.add(field.getName());
} // 取出to中所有field
for (Field field : toDeclaredFields){
field.setAccessible(true);
toList.add(field.getName());
} // 循环from属性list
for (String name : fromList){ // to中是否包含该属性
if(toList.contains(name)){ // 包含先进行取值
Field fromDeclaredField = fromClass.getDeclaredField(name);
fromDeclaredField.setAccessible(true);
Object value = fromDeclaredField.get(fromObject); // 进行赋值操作
Field toDeclaredField = toClass.getDeclaredField(name);
toDeclaredField.setAccessible(true);
toDeclaredField.set(toObject, value);
}
}
} return toObject;
}
}
JDBC数据库的链接
public class ConnectionJDBC { /**
* @param args
*/
//驱动程序就是之前在classpath中配置的JDBC的驱动程序的JAR 包中
public static final String DBDRIVER = "com.mysql.jdbc.Driver";
//连接地址是由各个数据库生产商单独提供的,所以需要单独记住
public static final String DBURL = "jdbc:mysql://localhost:3306/test";
//连接数据库的用户名
public static final String DBUSER = "root";
//连接数据库的密码
public static final String DBPASS = ""; public static void main(String[] args) throws Exception {
Connection con = null; //表示数据库的连接对象
Class.forName(DBDRIVER); //1、使用CLASS 类加载驱动程序 ,反射机制的体现
con = DriverManager.getConnection(DBURL,DBUSER,DBPASS); //2、连接数据库
System.out.println(con);
con.close(); // 3、关闭数据库
}
模拟spring加载Xml配置文件
public class BeanFactory {
private Map<String, Object> beanMap = new HashMap<String, Object>();
/**
* bean工厂的初始化.
* @param xml xml配置文件
*/
public void init(String xml) {
try {
//读取指定的配置文件
SAXReader reader = new SAXReader();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//从class目录下获取指定的xml文件
InputStream ins = classLoader.getResourceAsStream(xml);
Document doc = reader.read(ins);
Element root = doc.getRootElement();
Element foo; //遍历bean
for (Iterator i = root.elementIterator("bean"); i.hasNext();) {
foo = (Element) i.next();
//获取bean的属性id和class
Attribute id = foo.attribute("id");
Attribute cls = foo.attribute("class"); //利用Java反射机制,通过class的名称获取Class对象
Class bean = Class.forName(cls.getText()); //获取对应class的信息
java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
//获取其属性描述
java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
//设置值的方法
Method mSet = null;
//创建一个对象
Object obj = bean.newInstance(); //遍历该bean的property属性
for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {
Element foo2 = (Element) ite.next();
//获取该property的name属性
Attribute name = foo2.attribute("name");
String value = null; //获取该property的子元素value的值
for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {
Element node = (Element) ite1.next();
value = node.getText();
break;
} for (int k = 0; k < pd.length; k++) {
if (pd[k].getName().equalsIgnoreCase(name.getText())) {
mSet = pd[k].getWriteMethod();
//利用Java的反射极致调用对象的某个set方法,并将值设置进去
mSet.invoke(obj, value);
}
}
} //将对象放入beanMap中,其中key为id值,value为对象
beanMap.put(id.getText(), obj);
}
} catch (Exception e) {
System.out.println(e.toString());
}
} //other codes
}
学习链接
java之反射的基本介绍的更多相关文章
- Java 类反射机制分析
Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...
- 反射学习1、反射机制的介绍和基本的API的使用
关于动态语言: 一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”.从这个观点看,Perl,Python,Ruby是动态语言,C+ ...
- Java的反射机制和动态代理
介绍Java注解的时候,多次提到了Java的反射API.与javax.lang.model不同的是,通过反射API可以获取程序在运行时刻的内部结构.反射API中提供的动态代理也是非常强大的功能,可以原 ...
- java梳理-反射
本文属于面试题梳理系列:问题:java反射类的訪问私有方法与普通方法相比,须要多处理什么? 之前梳理类载入的时候,介绍到初始化的时机之中的一个:用java.lang.reflect包的方法对类进行反 ...
- 如何利用缓存机制实现JAVA类反射性能提升30倍
一次性能提高30倍的JAVA类反射性能优化实践 文章来源:宜信技术学院 & 宜信支付结算团队技术分享第4期-支付结算部支付研发团队高级工程师陶红<JAVA类反射技术&优化> ...
- Java的反射机理
Java反射是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的全部属性和方法等,调用方法以及访问属性,而且不需要提前在编译期知道运行的对象是什么 ...
- JAVA的反射理解
1----------------------------反射的概念----------------------------------------------- JAVA的反射机制是在运行状态中,对 ...
- java的反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...
- iOS运行时编程(Runtime Programming)和Java的反射机制对比
运行时进行编程,类似Java的反射.运行时编程和Java反射的对比如下: 1.相同点 都可以实现的功能:获取类信息.属性设置获取.类的动态加载(NSClassFromString(@“clas ...
随机推荐
- 【iCore4 双核心板_FPGA】例程八:乘法器实验——乘法器使用
实验现象: 程序运行时,绿色led闪烁(目前,具体的乘法器调用请参考iCore3乘法器例程) 核心代码: module multiplier_ctrl( input clk_25m, input rs ...
- (诊断)No module named MySQLdb
启动Keystone同步数据库时提示: -- :: TRACE keystone File , in dbapi -- :: TRACE keystone return __import__('MyS ...
- AD域部署使用bginfo软件
实验网络拓扑图: 实验目标: bginfo收集信息服务器通过bginfo软件收集每个域客户端信息录入到SQL server 2008数据库 bginfo软件官网下载地址: https://docs.m ...
- HAVANA 团队简介
在Ensembl 下载的gtf 文件中,会有一部分来源自 HAVANA havana 的全称叫做 human and vertebrate analysis and annotation, 是sag ...
- Spark学习笔记——构建分类模型
Spark中常见的三种分类模型:线性模型.决策树和朴素贝叶斯模型. 线性模型,简单而且相对容易扩展到非常大的数据集:线性模型又可以分成:1.逻辑回归:2.线性支持向量机 决策树是一个强大的非线性技术, ...
- C# Winform 防止MDI子窗体重复打开
可以在MDI主窗体中添加以下方法. //防止打开多个窗体 private bool ShowChildrenForm(string p_ChildrenFormText) { int i; //依次检 ...
- gradle教程 [原创](eclipse/ADT下 非插件 非Android Studio/AS)纯手打 第二篇:gradle简单实战
一个bug 一个脚印的叫你们用gradle. 1介于网络上的很多资料都是老的 不适用与现在的新版本gradle 尤其是有些gradle方法改名了老的用不了 2介于网上都是粘贴复制并且零碎我很蛋疼啊,走 ...
- Ubuntu中apt与apt-get命令的区别
https://blog.csdn.net/taotongning/article/details/82320472
- 08策略模式Strategy
一.什么是策略模式 Strategy模式也叫策略模式是行为模式之一, 它对一系列的算法加以封装,为所有算法定义一 个抽象的算法接口,并通过继承该抽象算法接口 对所有的算法加以封装和实现,具体的算法选择 ...
- 【中间件安全】Tomcat 安全加固规范
1. 适用情况 适用于使用Tomcat进行部署的Web网站. 2. 技能要求 熟悉Tomcat配置操作,能够利用Tomcat进行建站,并能针对站点使用Tomcat进行安全加固. 3. 前置条件 1.根 ...