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

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

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

在谈具体的反射技术之前,我想先回顾下,有关类的加载的一些基本的性质和原理,以方便我们更好地理解,反射的作用和特点。而实际上,一个类如果可以被执行,那么对于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. dotnet core各rpc组件的性能测试

    一般rpc通讯组件都具有高性特性,因为大部分rpc都是基于二进制和连接复用的特点,相对于HTTP(2.0以下的版本)来说有着很大的性能优势,非常适合服务间通讯交互.本文针对了dotnet core平台 ...

  2. tesseract-ocr下载

    参考:http://www.tuicool.com/articles/E3MNziM 1.下载地址:https://sourceforge.net/projects/tesseract-ocr/fil ...

  3. CodeForces 628 D Magic Numbers 数位DP

    Magic Numbers 题意: 题意比较难读:首先对于一个串来说, 如果他是d-串, 那么他的第偶数个字符都是是d,第奇数个字符都不是d. 然后求[L, R]里面的多少个数是d-串,且是m的倍数. ...

  4. lightoj 1032 - Fast Bit Calculations(数位dp)

    A bit is a binary digit, taking a logical value of either 1 or 0 (also referred to as "true&quo ...

  5. NOIP 2016 蚯蚓 题解

    一道有趣的题目,首先想到合并果子,然而发现会超时,我们可以发现首先拿出来的切掉后比后拿出来切掉后还是还长,即满足单调递增,故建立三个队列即可. 代码 #include<bits/stdc++.h ...

  6. JavaScript数组方法速查,32个数组的常用方法和属性

    JavaScript数组方法速查手册极简版 http://30ke.cn/doc/js-array-method JavaScript数组方法速查手册极简版中共收了32个数组的常用方法和属性,并根据方 ...

  7. 013 turtle程序语法元素分析

    目录 一.概述 二.库引用与import 2.1 库引用 2.2 使用from和import保留字共同完成库引用 2.3 两种库引用方法比较 2.4 使用import和as保留字共同完成库引用 三.t ...

  8. Android实现广告页图片无限轮播

    一.概述 对于一个联网的Android应用, 首页广告无限轮播基本已经成为标配了. 那么它是怎么实现的呢? 有几种实现方式呢? 二.无限轮播的实现 1.最常规的手段是用 ViewPager来实现 2. ...

  9. Java抽象类构造方法

    java中抽象类的子类的构造方法会隐含父类的无参构造方法. package com.zempty.abstractclass; public class AbstractDemo01 { public ...

  10. Elastic Stack 笔记(三)Kibana5.6 安装

    博客地址:http://www.moonxy.com 一.前言 Kibana 是 Elastic Stack 公司推出的一个针对 Elasticsearch 的开源分析及可视化平台,可以搜索.查看存放 ...