Java反射-再次认识
近期的学习发如今非常多方面。基础知识掌握的还非常不坚固,所以对于架构、知识点等属于那种问啥啥知道,做啥啥不出来的那种类型。前些日子。老师一直在抓基础,做什么都要从最简单的demo開始,仅仅有懂了原理之后再去用一些高深的东西如框架等才会理解的更深刻。如今首先须要理解的就是基本上每一个Java框架都在用的反射技术。
要想理解反射,首先得了解类的载入过程。看下图:
我们的源码经过编译之后变成字节码,然后在JVM中执行时通过类载入器载入字节码在内存中生成Class类对象。这个Class类对象内包括有field对象(类的成员变量生成)、constructor对象(类的构造方法生成)和method对象(类的方法生成)。
当我们拿到一个类或者对象的时候就能够通过反射对它们进行操作,以下再来看反射:
- 什么是反射
-
Java反射主要是指程序能够訪问、检測和改动它本身状态或行为的一种能力。是Java被视为动态(或准动态)语言的一个关键性质。这个机制同意程序在执行时透过Reflection APIs取得不论什么一个已知名称的class的内部信息。包含其modifiers(诸如public, static 等)、superclass(比如Object)、实现之interfaces(比如Cloneable),也包含fields和methods的全部信息,并可于执行时改变fields内容或唤起methods。Java反射机制容许程序在执行时载入、探知、使用编译期间全然未知的classes。换言之,Java能够载入一个执行时才得知名称的class。获得其完整结构。
-
- 反射用在哪儿
- 多用于框架和组件,写出复用性高的通用程序。
- 如struts的form仅仅要有了form对象和 property名字就能够利用反射给property赋值和取值 对这类操作 一个方法就能够搞定。假设hibernate不用字段进行反射映射 那么每一个HQL的编译和结果处理 将无法进行等等。
- 多用于框架和组件,写出复用性高的通用程序。
- 怎么用
- 针对我们所知的不同情况分别有3种方法获取Class字节码对象
- 当已知类名的时候,通过 “类名.class”获得
- 当已知对象的时候。通过 “对象.getClass”获得
- 当已知包含包名在内的完整类名(如果为String格式)的时候,可通过 “Class.forName(String)”获得
- 获取Class字节码对象之后能够构造对象实例、获取对象中的属性对象、方法对象和构造函数对象
- 获取以上须要的各种对象之后就能够操作它们。进行增删改查等操作了。
- 针对我们所知的不同情况分别有3种方法获取Class字节码对象
- 长处与缺点是什么
- 长处
为什么要用反射机制?直接创建对象不就能够了吗,这就涉及到了动态与静态的概念,静态编译:在编译时确定类型,绑定对象,即通过。动态编译:执行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用。用以减少类之间的藕合性。一句话,反射机制的长处就是能够实现动态创建对象和编译,体现出非常大的灵活性,特别是在J2EE的开发中 它的灵活性就表现的十分明显。比方,一个大型的软件,不可能一次就把把它设计的非常完美,当这个程序编译后,公布了,当发现须要更新某些功能时,我们不可能要用户把曾经的卸载,再又一次安装新的版本号,假如这种话。这个软件肯定是没有多少人用的。採用静态的话,须要把整个程序又一次编译一次才干够实现功能的更新,而採用反射机制的话,它就能够不用卸载,仅仅须要在执行时才动态的创建和编译,就能够实现该功能。
- 缺点
它的缺点是对性能有影响。使用反射基本上是一种解释操作。我们能够告诉JVM。我们希望做什么而且它满足我们的要求。这类操作总是慢于仅仅直接运行同样的操作。
- 样例
- 获取Class字节码对象的方法
<span style="font-size:18px;"><span style="font-size:14px;">/** * 获取字节码Class对象的方法 * @throws ClassNotFoundException */ @Test public void demo1() throws ClassNotFoundException{ //获取Class对象三种方式 //1.已知类 Class c1 = ReflectTest.class; //2.已知对象 Object o = new ReflectTest(); Class c2 = o.getClass(); //3.未知类和对象,知道完整类名 String className = "com.lc.reflect.ReflectTest"; Class c3 = Class.forName(className); System.out.println(c1); System.out.println(c2); System.out.println(c3); }</span></span>
- 操作构造方法
<span style="font-size:18px;"><span style="font-size:14px;">/** * 获取构造方法练习 * @throws Exception */ @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void demo2() throws Exception{ //获取的Person类字节码对象 String className = "com.lc.reflect.Person"; Class c = Class.forName(className); //通过字节码对象获得全部的构造方法 Constructor[] constructors = c.getConstructors(); for (int i = 0; i < constructors.length; i++) { System.out.println(constructors[i]); } //获取指定的构造方法 Constructor constructorDefault = c.getConstructor(); System.out.println(constructorDefault); //Sring.class为String的字节码对象 Constructor constructor = c.getConstructor(String.class); //带參数类型为String的构造方法 System.out.println(constructor); //创建对象实例的正常写法: Person person1 = new Person(); Person person2 = new Person("lc"); //使用反射构造Person对象的实例 Person reflectPerson1 = (Person)constructorDefault.newInstance(); //无參构造方法 Person reflectPerson1_1 = (Person)c.newInstance(); //通过Class对象直接newInstance。将会默认调用目标类无參构造方法 Person reflectPerson2 = (Person)constructor.newInstance("lc");//參数为String类型的构造方法 }</span></span>
- 操作成员变量
<span style="font-size:18px;"><span style="font-size:14px;">/** * 使用反射操作类成员变量的练习 */ @SuppressWarnings("rawtypes") @Test public void demo3() throws Exception{ //面向对象的写法是对象调用属性,而反射就正好相反了。。 Person p = new Person("lc"); System.out.println("对象调用属性的写法=====>:"+p.getName()); //使用反射操作类成员变量 --Field类 //1.必须获得目标类的字节码对象 Class c = Class.forName("com.lc.reflect.Person"); //2.操作成员实例变量name--获得name代表Field对象 Field[] f1 = c.getFields(); //获取全部public成员变量。包含父类继承 for (int i = 0; i < f1.length; i++) { System.out.println(f1[i]); } Field[] f2 = c.getDeclaredFields(); //获取当前类定义的全部成员。包含private for (int i = 0; i < f2.length; i++) { System.out.println(f2[i]); } //获得name成员变量 Field field = c.getDeclaredField("name"); //当前field是private //设置private变量能够訪问 field.setAccessible(true); //获得p对象指定name属性值 Object value = field.get(p); //相当于p.getName(); System.out.println("反射操作成员变量的写法=====>"+value); } /** * 使用反射改变成员变量的值(包含私有) * @throws Exception */ @Test public void demo4() throws Exception{ Person p = new Person(); //调用p对象中setName设置name的值 //1.获取字节码对象 Class c = Class.forName("com.lc.reflect.Person"); //2.操作setName获得setName对象反射对象的Method对象 //String类型參数setName方法 Method setName = c.getDeclaredMethod("setName", String.class); //调用p对象中setName setName.invoke(p, "sky"); //相当于p.setName("sky"); //3.读取name的值getName方法 Method getName = c.getDeclaredMethod("getName"); Object name = getName.invoke(p); //相当于p.getName(); System.out.println("反射获取成员变量的值======>"+name); }</span></span>
- 操作普通方法
<span style="font-size:18px;"><span style="font-size:14px;">/** * 操作方法对象 * @throws Exception */ @Test public void demo5() throws Exception{ //已知String类型完整类名---获得字节码对象 String className = "com.lc.reflect.Person"; Class c = Class.forName(className); //已知Class对象。构造实例 Object obj = c.newInstance(); //调用无參构造方法 //获得字节码对象中指定属性和方法 //获得name属性 Field f = c.getDeclaredField("name"); //获得setName方法 Method setName = c.getDeclaredMethod("setName", String.class); //改动属性的值,运行对应方法 f.setAccessible(true); f.set(obj, "sky"); setName.invoke(obj, "sky_lc"); //以上代码等价于以下的代码 Person p = new Person(); //p.name = "sky"; p.setName("sky_lc"); }</span></span>
Java语言反射提供一种动态链接程序组件的多功能方法。它同意程序创建和控制不论什么类的对象,无需提前硬编码目标类。这些特性使得反射特别适用于创建以很普通的方式与对象协作的库。Java reflection 很实用,它使类和数据结构能按名称动态检索相关信息,并同意在执行着的程序中操作这些信息。Java 的这一特性很强大,而且是其他一些经常使用语言,如 C、C++、Fortran 或者 Pascal 等都不具备的。
因为用于字段和方法接入时反射要远慢于直接代码,反射在性能上会有所影响,但性能问题的程度取决于程序中是怎样使用反射的。假设它作为程序执行中相对非常少涉及的部分。缓慢的性能将不会是一个问题。即使測试中最坏情况下的计时图显示的反射操作仅仅耗用几微秒。
仅反射在性能关键的应用的核心逻辑中使用时性能问题才变得至关重要。
所以。合理的使用反射将大大提高我们程序的通用性和复用性。