我们知道一个对象在运行时有两种类型,一个是编译类型,一个是运行时类型。在程序运行时,往往是需要发现类和对象的真实的信息的。那么如何获的这种信息呢?

其一,如果我们在编译和运行时都知道类型的具体信息,这时是可以手动将一个对象转换为运行时的类型。

其二,如果我们在编译时无法预知对象和类到底是属于哪些类,那么程序只有依靠运行时的信息来发现对象和类的真实的信息了,这时就必须要用到反射技术。

在谈具体的反射技术之前,我想先回顾下,有关类的加载的一些基本的性质和原理,以方便我们更好地理解,反射的作用和特点。而实际上,一个类如果可以被执行,那么对于JVM来说,它的执行流程为:类的加载、连接、初始化。通过这种方式,才可以获取到一个类的类对象,即java.lang.Class对象,并在此基础上获取到该类的成员变量,方法和构造器等内在的东东。

那么,什么是类的加载呢?类的加载就是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序使用任何类时,系统都会为之建立一个java.lang.Class对象。

同时,这个java.lang.Class对象又有什么特点呢?

1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
4、Class 对象只能由系统建立对象
5、一个类在 JVM 中只会有一个Class实例

同时,我们了解下一个基本的类加载器的结构:

类的连接:连接阶段负责把类的二进制数据合并到JRE中,分为三个步骤:

第一,验证,检验被加载的类是否有正确的内部结构;第二,准备,负责为类的类变量分配内存,并设置默认初始值。第三,解析将类的二进制数据中的符号引用替换成直接引用。

类的初始化:主要对类变量进行初始化。

这样,我们清楚地认识到了一个类的生命周期变化,那么这些也为我们的反射机制带来了铺垫,通过反射要获取一个类的class对象,有三种方式:

(1)使用Class类的forName(String clazzName)静态方法

(2)调用某个类的class属性获取该类对应的Class对象

(3)调用某个类的getClass()方法

通过这三种手段就可以获取到了一个类的Class对象,这样就可以动态获取该对象的实际的信息了。下面通过一个具体的例子说明下,如果通过反射机制,创建一个类的对象,如何通过获取的对象再进一步获取该对象的属性和方法,以及动态为该对象注入新的参数值。

测试实体类:

package reflection;

/**
* @author jiaqing.xu@hand-china.com
* @version 1.0
* @name
* @description
* @date 2017/10/19
*/
public class Person {
public String name;
private int age; public Person(){
System.out.println("无参构造器");
}
public Person(String name,int age){
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} private void privateMethod(){
System.out.println("这是一个私有的方法!");
} public void setName(String name,int age){
System.out.println("getName is:"+name);
System.out.println("getAge is:"+age);
} }

通过反射获取类:

package reflection;

/**
* @author jiaqing.xu@hand-china.com
* @version 1.0
* @name
* @description 通过反射获取类测试
* @date 2017/10/19
*/
public class GetClassTest {
public static void main(String[] args) throws ClassNotFoundException {
//Class是一个描述类的类、对每个类而言只有一个不变的Class与其对应、JRE中只有唯一一个
Class clazz = null; //1 直接通过类名.class方式得到
clazz = Person.class;
//输出:reflection.Person
System.out.println("直接通过类名.class得到:"+clazz); //2 通过对象的getClass()方法获取
Object obj = new Person();
clazz = obj.getClass();
//输出:reflection.Person
System.out.println("通过getClass(): " + clazz); //3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常
clazz = Class.forName("reflection.Person");
//输出:reflection.Person
System.out.println("通过全类名获取: " + clazz);
}
}

获取类的实例:

package reflection;

/**
* @author jiaqing.xu@hand-china.com
* @version 1.0
* @name
* @description 通过反射获取类的实例测试
* @date 2017/10/19
*/
public class GetInstanceTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clazz = Class.forName("reflection.Person");
//使用Class类的newInstance()方法创建类的一个对象
//实际调用的类的那个 无参数的构造器
//一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器
Object obj = clazz.newInstance();
System.out.println(obj.toString());
}
}

获取类所定义的方法:

package reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; /**
* @author jiaqing.xu@hand-china.com
* @version 1.0
* @name
* @description 通过反射获取类所定义的方法的测试
* @date 2017/10/19
*/
public class GetMethodTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//获取clazz
Class clazz = Class.forName("reflection.Person"); //1、得到clazz 对应的类中有哪些方法,不能获取private方法
Method[] methods = clazz.getMethods();
System.out.println("getMethods:");
//遍历方法集合 获取对应的方法名称
for (Method method : methods) {
System.out.print(method.getName() + ", ");
}
System.out.println();
//2、获取所有的方法(包括private方法)
Method[] methods2 = clazz.getDeclaredMethods();
System.out.println("getDeclaredMethods:");
for (Method method : methods2) {
System.out.print(method.getName() + ", ");
} System.out.println();
//3、获取指定的方法
Method method = clazz.getDeclaredMethod("setName", String.class);
System.out.println("method : " + method); Method method2 = clazz.getDeclaredMethod("setName", String.class, int.class);//第一个参数是方法名,后面的是方法里的参数
System.out.println("method2: " + method2); //4、执行方法
Object obj = clazz.newInstance();
//此处注意下该invoke方法,参数为:执行该方法的对象,为该方法传入的参数列表
method2.invoke(obj, "jaycee", 23);
}
}

获取Fileds:

package reflection;

import java.lang.reflect.Field;

/**
* @author jiaqing.xu@hand-china.com
* @version 1.0
* @name
* @description 通过反射获取field测试
* @date 2017/10/19
*/
public class GetFieldTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Class clazz = Class.forName("reflection.Person"); //1、获取字段
//1.1 获取Field的数组,私有字段也能获取
Field[] fields = clazz.getDeclaredFields();
for (Field field: fields) {
System.out.print(field.getName() + ", ");
} //1.2 获取指定名字的Field
Field field = clazz.getDeclaredField("name");
System.out.println("\n获取指定Field名=: " + field.getName()); Person person = new Person("jaycee", 23);
//2、获取指定对象的Field的值
Object val = field.get(person);
System.out.println("获取指定对象字段'name'的Field的值=:" + val); //3、设置指定对象的Field的值
field.set(person, "admin");
System.out.println("设置指定对象字段'name'的Field的值=: " + person.name); //4、若该字段是私有的,需要调用setAccessible(true)方法
Field field2 = clazz.getDeclaredField("age");
field2.setAccessible(true);
System.out.println("获取指定私有字段名=: " + field2.getName());
}
}

获取父类定义的信息:

申明父类:

package reflection.invoke;

/**
* @author jiaqing.xu@hand-china.com
* @version 1.0
* @name
* @description 父类
* @date 2017/10/19
*/
public class Father {
/**
* 父类无参构造函数
*/
public Father(){ }
/**
* 父类私有方法1
*/
private String method(){
return "Father's private method";
}
}

申明子类:

package reflection.invoke;

/**
* @author jiaqing.xu@hand-china.com
* @version 1.0
* @name
* @description 儿子类
* @date 2017/10/19
*/
public class Son extends Father{
/**
* @author jiaqing.xu@hand-china.com
* @date 2017/10/19 10:19
* @param
* @return
* @description 儿子私有方法
*/
private void method1(Integer age){
System.out.println("son's age=:" +age);
}
}

获取父类中定义的方法:

package reflection.invoke;

/**
* @author jiaqing.xu@hand-china.com
* @version 1.0
* @name
* @description 获取父类中定义的私有的方法
* @date 2017/10/19
*/
public class GetSuperClassTest {
public static void main(String[] args) throws ClassNotFoundException {
String className = "reflection.invoke.Son";
Class clazz = Class.forName(className);
//得到父类的Class
Class superClazz = clazz.getSuperclass();
//输出:class reflection.invoke.Father
System.out.println(superClazz);
}
}

总结下反射的优点和缺点:

 优点:
(1)提高程序的灵活性和拓展性,能够在运行时动态地获取类的实例。
(2)和java的动态编译相结合,可以提高更强大的功能。
(3)提前无需硬编码,便可以通过类名获取对应类的实例,进而操作该实例。 缺点:
(1)性能较低,反射是一种接入式的操作需要找到对应的字段和方法,比起直接的代码赋值,要慢的多。
(2)使用反射应该在一个相对安全的环境下进行。
(3)使用时会破坏类的封装性,破坏OOP的整体设计思想。

Java中类加载和反射技术实例的更多相关文章

  1. java反射技术实例

    java反射技术实例​1. [代码][Java]代码     package com.gufengxiachen.java.reflectiontest; public class Person {p ...

  2. 反射那些事儿——Java动态装载和反射技术

    一直以来反射都是只闻其声,却无法将之使用,近日尽心下来学习下,发现了很多精妙之处. Java动态装载和反射技术 一.类的动态装载 1.Java代码编译和执行的整个过程包含了以下三个重要的机制: ● J ...

  3. Java中的类反射

    一.反射的概念 : 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序 ...

  4. java 中类加载器

    jar 运行过程和类加载机制有关,而类加载机制又和我们自定义的类加载器有关,现在我们先来了解一下双亲委派模式. java 中类加载器分为三个: BootstrapClassLoader 负责加载 ${ ...

  5. 第62节:探索Java中的网络编程技术

    前言 感谢! 承蒙关照~ 探索Java中的网络编程技术 网络编程就是io技术和网络技术的结合,网络模型的定义,只要共用网络模型就可以两者连接.网络模型参考. 一座塔有七层,我们需要闯关. 第一层物理层 ...

  6. java中List的用法和实例详解

    java中List的用法和实例详解 List的用法List包括List接口以及List接口的所有实现类.因为List接口实现了Collection接口,所以List接口拥有Collection接口提供 ...

  7. Java中的对象池技术

    java中的对象池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间.对象池其实也就是一个内存 ...

  8. ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗

    1.ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗? :当然是运行期间啊,我自己有个理解误区,改正后如下:编译期间编译器是不去加载类的,只负责编译而 ...

  9. java基础——类加载与反射

    第1章 类加载器 1.1 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. (1)加载 就是指将class文件读入内存,并为之创 ...

随机推荐

  1. PHP识别简单的图片上面的数字(可扩展)

    1.场景 最近在学习图片处理,就是特意把数字生成一个图片,然后再用程序去识别图片的数字.这就有了一下的学习过程. 2.原理分析 2.1 首先是将图片像素化,二值化,然后和字模去对比(需要相对于配置字模 ...

  2. CAS及其ABA问题

    CAS.volatile是JUC包实现同步的基础.Synchronized下的偏向锁.轻量级锁的获取.释放,lock机制下锁的获取.释放,获取失败后线程的入队等操作都是CAS操作锁标志位.state. ...

  3. library not found for -ljpush-ios-3.2.1错误

    很多人在更新pod后报 library not found for -ljpush-ios-3.2.1(举例)错误,这其实是包含版本号类型错误. 究其原因:使用了版本号做库名字,pod升级后 Podf ...

  4. HDU5988 - 2016icpc青岛 - G - Coding Contest 费用流(利用对数化乘为加

    HDU5988 题意: 有n个区域,每个区域有s个人,b份饭.现在告诉你每个区域间的有向路径,每条路有容量和损坏路径的概率.问如何走可以使得路径不被破坏的概率最小.第一个人走某条道路是百分百不会损坏道 ...

  5. POJ-2253-Frogger +最短路小变形

    传送门:http://poj.org/problem?id=2253 参考:https://www.cnblogs.com/lienus/p/4273159.html 题意:给出一个无向图,求一条从 ...

  6. js中的this介绍

    今天跟大家一起简单的来了解一下js中一个有趣的东西,this. 在js中我们用面向对象的思想去编写的时候,各个模块之间的变量就不那么容易获取的到了,当然也可以通过闭包的方式拿到其他函数的变量,如果说每 ...

  7. 自定义属性,innerHTML,outerHTML,dom节点的获取,className,tagName,更改或者是获取css属性

    01.自定义属性 1.自定义属性: 作用:保存数据 通用的自定义属性的前缀 data-属性="属性值" 注:自定义的属性不能通过 元素.属性 取属性值 ,需使用getAttribu ...

  8. 从SpringBoot构建十万博文聊聊Tomcat集群监控

    前言 在十万博文终极架构中,我们使用了Tomcat集群,但这并不能保证系统不会出问题,为了保证系统的稳定运行,我们还需要对 Tomcat 进行有效的运维监控手段,不至于问题出现或者许久一段时间才知道. ...

  9. iphone不支持(格式:2016-02-26 09:12)的格式时间需要转换成:(格式:2016/02/26 09:12)

    function strToTime(str) {return Date.parse(str.replace(/-/g, "/"));} 苹果手机不支持创建这种时间格式 需要转化一 ...

  10. 基于GitLab+Jenkins的DevOps赋能实践

    随着微服务.中台架构的兴起,DevOps也变得非常关键,毕竟是一些基础设施层面的建设,如果搞好了对后面的研发工作会有很大的效率提升.关于DevOps本身的概念,网上已经非常多了,在园子里随便搜索一些都 ...