`
greemranqq
  • 浏览: 966176 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

反射(一)----原理机制和基本运用

阅读更多

   JAVA 反射的应用还是比较多,这里会对反射的一些原理进行介绍,然后我们才知道如何使用和优化。至

 

于反射的使用介绍,这里就不在过多叙述了,API 上都介绍得有。

   要了解JAVA 反射的原理,我们还得对类在虚拟机中的一些知识做简要介绍...

 

一、类文件的结构:

   1.1 虚拟机加载Class文件过程:

       在JVM 类加载机制的博客里面我介绍过整体流程,这里仅仅介绍加载时相关部分。

       在我们启动一个类,或者其他方式加载一个类的时候,会通过类的全限定名获取该类的二进制流,然

 

后将字节流所代表的的静态存储结构转化成方法区的运行时数据结构,然后会生成一个代表该类的

 

java.lang.Class 对象,作为在方法区这个类的访问入口。也就是说只要完成了这一步骤,那么通过这个入

 

口我们就可以访问里面的存储好的数据结构信息了。而且动态加载的时候,会先进行查找,该类是否存在,

 

存在了就不会再加载了,保持一份。

 

    class 文件是一组以8位字节为基础单位的二进制流,各个数据项目按严格的顺序紧凑的排列在Class文

 

件中,里面的信息主要描述以下信息:

    1.版本号:主版本号和次版本号

 

    2.常量池:主要存放字面量(Literal)和符号引用(references)

      2.1 字面量:文本字符串、final 类型的常量值 等

      2.2 符号引用:

          a.类和接口的全限定名字

          b.字段描述和描述符

          c.方法的名称和描述

    

    3.访问标志:

      a.是类还是接口

      b.是否是public 等类型

      c.是否是abstract ,是否被声明为final 等标志

    

    4.类索引、父类索引和接口索引集合

      a.类索引:确定这个类的全限定名

      b.父类索引:确定父类的全限定名

      c.接口索引集合:作为入口,作为一个计数器

 

    5.字段表集合:

      包括信息有字段作用域(public,private等修饰符)、是实例变量还是类变量(static)、可变性 (final)、并发可见性(volatile)、可否被序列化(transient)等信息

    

    6.方法集合:

      包括访问标志、名称索引、描述符索引、属性表集合。

 

    7.其他:包括属性表集合、Code 属性(指令) 等其他这里暂时不过多介绍,详细请看虚拟机的书籍。

 

 

 

 

二、反射概念:

    通过上面简单的介绍,相信大家了解了我们的Class 文件在加载到JVM 里面之后,实际存放的信息有很

 

多,而且上面介绍的都是大家有一定了解的,比如 方法 、属性 等等,那么反射是什么呢?

    所谓反射是JAVA 语言允许在 运行时拥有一种自审的能力,也就是说JVM 允许代码在运行期间可以获得

 

类的内部信息,简单的说我们可以在程序运行期间获得刚才我们介绍的类里面的信息。

 

    2.1 反射的常用方法:

        a.forName(String className) : 

          返回与带有给定字符串名的类或接口相关联的 Class 对象。

 

        b.forName(String name, boolean initialize, ClassLoader loader) :

          使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。

 

        c.getAnnotation(Class<A> annotationClass) 

          如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

 

        d.getAnnotations() 

          返回此元素上存在的所有注释。

 

        e.getConstructor(Class<?>... parameterTypes) 

          返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

 

        f.getDeclaredField(String name) 

          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。

    

        g.getDeclaredMethod(String name, Class<?>... parameterTypes) 

          返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

 

 

这里我就不累赘了,仅仅引入性的介绍几个,更多的可以去看看API

 

 

三、反射的基本使用:这里我们写一个简单类,模拟常见的功能。

   

 public class Test {
// 构造方法
public Test() {
System.out.println("无参数构造");
}
public Test(String str) {
System.out.println("有参构造:"+str);
}
public  void test(){
System.out.println("普通测试方法:");
}
public static void staticTest(){
System.out.println("静态测试方法");
}
// 基本属性
private Integer number;
public String name = "张三";
    }

 

 

  

    1.类加载:

 

    public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.Test");
// 这里加载类,会调toString 方法, 打印类型,和getName 方法
// 同时发现类已经存在,说明加载成功
// 这里常常我们用来动态加载一个类,比如再获得JDBC 连接的时候,
// 我们可以动态的获得不通厂商的连接:Class.forName("xxx.oracle/mysql")
System.out.println("类信息:"+c);
// 同时这里可以直接这只加载器实现:Class.forName(name, initialize, loader) 
}

 

 

    2 获得方法并且调用:

 

     public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.Test");
// 这个是获得指定方法,我们先获得普通方法
Method m = c.getMethod("test");
// 这里执行会失败了,因为我们们的类没进行实例化
// m.invoke(c);
// 这样就不会,这里使用了默认的构造
m.invoke(c.newInstance());
// 但是静态方法不用实例化就可以
Method m2 = c.getMethod("staticTest");
m2.invoke(c);
// 当然我们还能获得所有的方法信息
// 获得该类所有方法
c.getDeclaredMethods();
// 获得包括继承的类的所有方法
c.getMethods();
}

 

  

      3.获得字段信息:

public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.Test");
// 私有字段是无法通过公有方式访问
// Field f1 = c.getField("number");
// public 字段是可以直接访问
Field f = c.getField("name");
System.out.println(f);


// 这是赋值,必须先获得对象实例,可以为字段赋值
Object o = c.newInstance();
f.set(o, "2");
System.out.println(f.get(o));
// 其他方法,和获取method 差不多
}

 

关于构造器,权限等这些就可以看API,这里仅仅介绍。

     

      4.其他应用:

        反射运用得很多,比如我们熟悉的hibernate、spring、以及其他的orm 框架都需要用到。

        这里我们模拟hibernate 的实现,来完成对象的保存操作。

        // 这是我们模拟的实体bean

// 这是我们模拟的实体bean
public class Bean {
	private Integer id;
	private String name;
	private String password;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

 

 

 

 

import java.lang.reflect.Field;
import java.lang.reflect.Method;

// 这是我们的测试
public class Test {
	public static void main(String[] args) {
		// 1. 首先我们创建一个bean,模拟从前端获取的数据
		Test t = new Test();
		Bean bean = t.getBean();
		// 2.生成我们需要的SQL 并设值
		t.save(bean);
	}
	
	private Bean getBean(){
		// 模拟用反射实现
		Bean bean = null;
		try {
			Class c = Class.forName("Bean");
			bean = (Bean) c.newInstance();
			// 私有字段无法访问,我们通过方法赋值
			Method m1 = c.getDeclaredMethod("setId",Integer.class);
			Method m2 = c.getDeclaredMethod("setName",String.class);
			Method m3 = c.getDeclaredMethod("setPassword",String.class);
			m1.invoke(bean, 1);
			m2.invoke(bean, "admin");
			m3.invoke(bean, "123456");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bean;
	}
	
	// 假设我们的表  就是 BEAN
	private void save(Bean bean){
		Field[] fields = Bean.class.getDeclaredFields();
		StringBuffer sb = new StringBuffer("INSERT INTO BEAN VALUES");
		sb.append(getInsertStr(fields.length));
		// 这里我们可以看到SQL 已经生成
		System.out.println(sb);
		// 这里就是我们的JDBC 根据字段名字赋值 的操作了。
		// 当然hibernate 写得肯定会复杂很多,但是基本原理不变
		
	}
	
	private String getInsertStr(int fields){
		StringBuffer sb = new StringBuffer("(");
		for(int i = 0;i<fields;i++){
			sb.append("?,");
		}
		sb.delete(sb.length()-1,sb.length());
		sb.append(")");
		return sb.toString();
	}
}

 

在spring 里面,在介绍过生成代理类,也就是AOP 的地方也用过,这里也就不多说了。

 

小结:

     1.这里介绍了反射的基本运用,以及一些相关原理的东西,还没真正深入

     2.反射给了我们很大的灵活,但是同时很多错误只能到运行期间才能发现,使用要多注意。

     3.反射提供灵活的同时,也牺牲了性能,在JDK1.6+ 版本,反射的一般调用,比直接调用慢2倍左右。

       性能这一块后面再研究,再招优化的方案。

0
0
分享到:
评论

相关推荐

    asp.net知识库

    利用反射实现ASP.NET控件和数据实体之间的双向绑定,并且在客户端自动验证输入的内容是否合法 asp.net报表解决方法 SQLDMO类的使用 SQL过程自动C#封装,支持从表到基本存储过程生成 使用SQLDMO控制 SQL Server 使用SQL...

    基于Java设计一个web服务器【100011614】

    在本次实习中,我学会了 javaWeb 的基本原理和运用,现在我基本上可以对java中 web项目进行编写.学会了 java的反射机制,会运用了 Java进行相应的处理更加的理解 Java动态编译,并且会使用 javac的简单命令进行编译4....

    Spring面试题

    3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。 4. hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。 2. ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

     16.4 多对一和一对一关联的检索策略  16.4.1 迫切左外连接检索(fetch属性为“join”)  16.4.2 延迟检索(lazy属性为默认值“proxy”)  16.4.3 无代理延迟检索(lazy属性为“no-proxy”)  16.4.4 立即检索...

    超爽的自学课件(java)

    1) 第1章:对象入门 这一章是对面向对象的程序设计(OOP)的一个综述,其中包括对“什么是对象”之类的基本问题的回答,并讲述了接口与实现、抽象与封装、消息与函数、继承与合成以及非常重要的多形性的概念。...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part4

     16.4 多对一和一对一关联的检索策略  16.4.1 迫切左外连接检索(fetch属性为“join”)  16.4.2 延迟检索(lazy属性为默认值“proxy”)  16.4.3 无代理延迟检索(lazy属性为“no-proxy”)  16.4.4 立即检索...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part3

     16.4 多对一和一对一关联的检索策略  16.4.1 迫切左外连接检索(fetch属性为“join”)  16.4.2 延迟检索(lazy属性为默认值“proxy”)  16.4.3 无代理延迟检索(lazy属性为“no-proxy”)  16.4.4 立即检索...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part1.rar

     16.4 多对一和一对一关联的检索策略  16.4.1 迫切左外连接检索(fetch属性为“join”)  16.4.2 延迟检索(lazy属性为默认值“proxy”)  16.4.3 无代理延迟检索(lazy属性为“no-proxy”)  16.4.4 立即检索...

    c#学习方法

    3. .Net 框架原理讲解:多维数组、可变参数 Params、类的实例化内存分配机制等。4. 深入学习对象类型:里氏替换原则(LSP)、类的属性极其本质特性、IS ,AS 关键字、深入学习字符串理论、枚举类型以及适用场合。5. ...

    Java面试宝典2020修订版V1.0.1.doc

    41、什么是java反射机制? 84 42、hashmap的底层实现 85 43、什么是java内存泄漏,怎么预防? 85 七、框架部分 85 1、谈谈你对Struts2的理解。 85 2、谈谈你对Hibernate的理解。 86 3、你对Spring的理解。 87 4、...

    基于.NET平台GUI自动化测试框架的设计

    分析了录制回放技术的基本原理和缺陷,运用WIN32API和.NET反射机制,设计了一个改进的轻量级GUI自动化测试框架,解决了当前测试自动化中出现的一些棘手问题。

Global site tag (gtag.js) - Google Analytics