反射的作用

 
开门见山地说说反射的作用
 
1.为我们提供了全面的分析类信息的能力
2.动态加载
 
我理解的“反射”的意义
(仅个人理解哈)
 
我理解的java反射机制就是: 提供一套完善而强大的API“反射“类的结构。
打个比方,反射机制就像是一面镜子,而类就像是一个在照着镜子的人。
镜子(反射机制)照出(反射)了人的全貌(类的全方位的信息,例如方法,成员变量和构造器等的相关信息)
为什么要照镜子? 因为不照镜子看不清楚自己的全貌,“镜子”就是为了解决这个问题出现的为我们提供全面分析类的能力
 

 
好吧,我知道这听起来还是很模糊,让我们一步一步来

类也是对象

在java里有一句话:万物皆对象, 即使是int等基本类型,虽然本质上不是对象,但行为却也和对象密切相关(基本包装类型和自动装箱)
 
所以有一个可能完全打破我们常规思维的论断是: 类也是对象
 

“类”对象和“类”类型

好吧,其实说“ 类也是对象”并不太好应该说,java中每个类都有一个与之对应的“类”对象(Class对象),这个“类”对象由jvm生成,并保存了对应类的相关信息。例如,假设我们的java文件涉及三个类:a类,b类和c类,那么编译的时候就会对应生成a类的“类”对象,a类的“类”对象,a类的“类”对象,分别用于保存和a,b,c类对应的信息
 
我们的思维是这样的: 一个对象必然有一个与之对应的类,因为只有类才能实例化对象啊
 
那么,“类对象”的“上面”,应该还有一个类才对!这个“类之上的类”,就是java.lang.Class,它是所有“类”对象的类(这样说可能听起来很拗口)
 
我们这样声明一个“类”对象,假设这个类对象(Class对象)是a——
Class a
我们称a属于“类”类型(Class类型)
 
所以我们可以其实可以将java中的对象分为两种
 
1. 实例对象
2. Class对象
 
所以我们今天要讲的第一个内容是:  有别于平时使用的实例对象的——Class对象

取得Class对象的三种方式

 
我们假设有这么一个类叫MyClass:
public class MyClass {  }
那么取得该类对应Class对象的方法有三种:
 
一. 通过“类名.class”的方式取得
Class classInstance= MyClass.class;

二. 通过类创建的实例对象的getClass方法取得

MyClass myClass = new MyClass();
Class classInstance = myClass.getClass();

三.通过Class类的静态方法forName方法取得参数是带包名的完整的类名

Class classInstance = Class.forName("mypackage.MyClass");

 

【注意】
 
1.运行forName时候可能会因为找不到包名而抛出已检查异常ClassNotFoundException,所以我们需要将其包裹在try-catch语句中:
try {
  Class classInstance = Class.forName("mypackage.MyClass");
} catch (ClassNotFoundException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}
2.上面三种方法取得的对象都是相同的,所以效果上等价
 

利用反射API全面分析类的信息——方法,成员变量,构造器

 
我们上面提到,反射的一大作用是用于分析类的结构,或者说用于分析和这个类有关的所有信息。而这些信息就是类的基本的组成: 方法,成员变量和构造器
 
事实上,和我们上面所介绍的Class类和Class对象相似的是,一个类中的方法,成员变量和构造器也分别对应着一个对象
 
1.每个方法都对应有一个保存和该方法有关信息的Method对象, 这个对象所属的类是java.lang.reflect.Method;
2.每个成员变量都对应有一个保存和该变量有关信息的Field对象,这个对象所属的类是 java.lang.reflect.Field
3. 每个构造器都对应有一个保存和该构造器有关信息的Constructor对象,这个对象所属的类是java.lang.reflect.Constructor
 
方法,成员变量和构造器是附属于某一个类的,正因如此,我们应该先取得某一个类对应的Class对象,其次才考虑如何取得 Method/Field/Constructor对象
 
 

我们可以通过一系列的方法,从一个类的Class对象中取得对应的Method对象,Field对象和Constructor对象
 
假设c是一个类的Class对象:
 
通过 c.getDeclaredMethods()可取得这个类中所有声明方法对应的Method对象组成的数组
通过 c.getDeclaredFields()可取得这个类中所有声明的成员变量对应的Field对象组成的数组
通过 c.getConstructors(); 可取得这个类中所有构造函数所对应的Constructor对象所组成的数组
 
在下面的示例中,我们将遍历某一个类中方法,成员变量和构造器的名称:
 
MyClass.java:
public class MyClass {
  private int value; //成员变量
  public MyClass (int value) { this.value = value;   } //构造函数
  public int getValue() {  return value; } //方法1
  public void setValue(int value) {  this.value = value;  } //方法2
}
Test.java:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
public class Test {
public static void printClassMessage (Object obj) {
Class c = obj.getClass(); // 获取obj所属类的Class对象
Method [] methods = c.getDeclaredMethods(); // 获取方法对象列表
System.out.println("遍历MyClass类里的所有方法的名称:");
for(int i =; i<methods.length; i++) {
System.out.println(methods[i].getName());
}
Field [] fields = c.getDeclaredFields();   // 获取成员变量对象列表
System.out.println("遍历MyClass类里的所有成员变量的名称:");
for(int i =; i<fields.length; i++) {
System.out.println(fields[i].getName());
}
 
Constructor [] constructors = c.getConstructors();  // 获取构造函数对象列表
System.out.println("遍历MyClass类里的所有构造函数的名称:");
for(int i =; i<constructors.length; i++) {
System.out.println(constructors[i].getName());
}
}
    public static void main(String [] args) {
    MyClass myClass = new MyClass(); // 创建一个MyClass对象
    printClassMessage(myClass);  // 打印这个对象所属类的相关信息
  }
}
运行结果:
遍历MyClass类里的所有方法的名称:
getValue
setValue
遍历MyClass类里的所有成员变量的名称:
value
遍历MyClass类里的所有构造函数的名称:
mypackage.MyClass

上面的例子仅仅是作为一个展示,Method/Field/Constructor对象的API当然不仅限于getName这样获取名称的简单操作,所以接下来我将分别介绍更具体的反射API

 

利用反射API分析类中方法信息

getMethods和getDeclaredMethods方法

 
getMethods和getDeclaredMethods的区别在于:
 
getMethods取得的method对应的方法包括从父类中继承的那一部分,而
getDeclaredMethods取得的method对应的方法不包括从父类中继承的那一部分
 
例如上面通过打印getDeclaredMethods打印的MyClass的方法信息:
getValue
setValue
 让我们看看通过getMethods打印又会取得什么结果:
 
Test.java:
import java.lang.reflect.Method;
 
public class Test {
public static void printMethodsMessage (Object obj) {
   Class c = obj.getClass();
    Method [] methods = c.getMethods();
    for (Method method : methods) {
System.out.println(method.getName());
  }
}
   
public static void main(String [] args) {
    MyClass myClass = new MyClass();
    printMethodsMessage(myClass);
  }
}
运行结果:
getValue
setValue
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
让我们思考一下: 一个方法有哪些“信息”值得(或需要)我们去分析呢?
 
主要是两部分:
1. 返回值
2. 方法参数
 
你可能猜的出来, Method对象已经提供了一套API去获取这些信息了,让我们来看看:
 
 
 

通过method.getReturnType()获取方法返回值对应的Class对象

import java.lang.reflect.Method;
 
public class Test {
public static void printMethodsMessage (Object obj) {
   Class c = obj.getClass();  // 取得obj所属类对应的Class对象
   Method [] methods = c.getDeclaredMethods(); // 取得obj所属类中方法对应的Method对象组成的数组
   for (Method method : methods) { // 遍历Method对象
    String name = method.getName();   // 取得方法名
    Class returnClass = method.getReturnType(); // 获取方法返回值对应的Class对象
    String returnName = returnClass.getName();  //获取返回值所属类的类名——也即返回值类型
System.out.println(name + "方法的返回值类型是" + returnName);
  }
}
  public static void main(String [] args) {
  MyClass myClass = new MyClass();
  printMethodsMessage(myClass);
}
}
运行结果:
getValue方法的返回值类型是int
setValue方法的返回值类型是void
 
通过method.getReturnType(),我们取得了该方法的返回值对应的Class对象(哈哈,绕了一圈后还是回到Class对象上了)
然后通过Class对象调用getName方法就取得了返回值所属的类的名称,也即返回值类型
 

通过method.getParameterTypes()获取方法各参数的Class对象组成的数组

 
MyClass.java:
public class MyClass {
  public void method1 (int a, long b) {   };
  public void method2 (float a, double b) {   };
  public void method3 (String str) {   };
}
Test.java:
public class Test {
public static void printMethodsMessage (Object obj) {
   Class c = obj.getClass();  // 取得obj所属类对应的Class对象
    Method [] methods = c.getDeclaredMethods(); // 取得obj所属类中方法对应的Method对象组成的数组
    for (Method method : methods) { // 遍历Method对象
    String methodName = method.getName();   // 取得方法名
    String paramsStr = "";  // 用于存放某个方法参数类型列表的字符串
    Class [] paramsClasses = method.getParameterTypes();
    for (Class pc: paramsClasses) {
String paramStr = pc.getName(); // 获取当前参数类型
paramsStr+=paramStr + "  ";
}
    System.out.println(methodName+ "方法的所有参数的类型列表:" + paramsStr);
  }
}
  public static void main(String [] args) {
    MyClass myClass = new MyClass();
    printMethodsMessage(myClass);
  }
}
 
运行结果:
method2方法的参数类型列表:float  double 
method1方法的参数类型列表:int  long 
method3方法的参数类型列表:java.lang.String 

利用反射API分析类中成员变量信息

 

获取成员变量类型对应的的Class对象

 
 
读取成员变量的值
 
MyClass.java:
public class MyClass {
  private int number = ;
  private String name ="彭湖湾";
}
Test.java:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
public class Test {
public static void printFieldsMessage (Object obj) {
   Class c = obj.getClass();  // 取得obj所属类对应的Class对象
    try {
Field field = c.getDeclaredField("name");  // 取得名称为name的field对象
field.setAccessible(true); // 这一步很重要!!!设置为true才能访问私有成员变量name的值!
String nameValue = (String) field.get(obj); // 获取obj中name成员变量的值
System.out.println("MyClass类中name成员变量的值为:" + nameValue);  // 输出
   } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
   }
}
   
public static void main(String [] args) {
  MyClass myClass = new MyClass();
    printFieldsMessage(myClass);
  }
}
运行结果:
MyClass类中name成员变量的值为:彭湖湾

通过getType方法读取成员变量类型的Class对象

 
这里只展现比较关键的两段代码:
Field field = class1.getDeclaredField(number");
System.out.print(field.getType().getName());
运行结果:
int
【注意】:因为java权限的原因,直接读取私有成员变量的值是非法的(加了field.setAccessible(true)后就可以了),但仍可以直接读取私有成员变量的类型
 

利用反射API分析类中构造器信息

 
分析构造函数的时候,其实思路大体上和分析方法时候一致,关键在于获取参数所属类的Class对象
 
区别在于:
 
1. 获取该类声明的构造器用的是getDeclaredConstructors方法而不是getDeclaredMethods方法
2. 构造函数没有返回值,所以不需要分析返回值(我似乎说了废话....)
 
废话不多说了,看下面的例子:
 
MyClass.java:
public class MyClass {
  public MyClass(int a, String str){}
}
Test.java:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
public class Test {
public static void printContructorsMessage (Object obj) {
   Class c = obj.getClass();  // 取得obj所属类对应的Class对象
    Constructor [] constructors = c.getDeclaredConstructors();
    for (Constructor constructor : constructors) {
    Class [] paramsClasses =  constructor.getParameterTypes();
    String paramsStr = "";
    for (Class pc : paramsClasses) {
    String paramStr = pc.getName();
    paramsStr+=paramStr + "  ";
}
    System.out.println("构造函数的所有参数的类型列表:" + paramsStr);
}
}
 public static void main(String [] args) {
    MyClass myClass = new MyClass(, "彭湖湾");
    printContructorsMessage(myClass);
 }
}

 

运行结果:
构造函数的所有参数的类型列表:int  java.lang.String 

利用反射动态加载类,并用该类创建实例对象

 
动态加载类
 
我们用普通的方式使用一个类的时候,类是静态加载
而使用Class.forName("XXX")这种方式,则属于动态加载一个类
 
静态加载的类在编译的时候就能确定该类是否存在,但动态加载一个类的时候却无法在编译阶段确定是否存在该类,而是在运行时候才能够确定是否有这个类,所以要捕捉可能发生的异常
 
我们可以从eclipse的使用上有个相对直观的了解:
 
eclipse在保存的时候是可以自动编译的,SO
 
例如我们如果直接使用一个本就不存在的类NotExistClass的时候
 

保存后,在编译阶段就能够发现:“诶? 好像没有这个类哦!”
报的错误是:
 
NotExistClass cannot be resolved to a type
但是如果我们用Class.forName("XXX")动态加载一个类呢?
我们发现,保存后,在编译阶段已经不能发现这个错误了,对应的是要捕捉可能发生的异常
 
 

 
用该动态加载的类创建实例对象
 
Class对象有一个newInstance方法,我们可以用它来创建实例对象
Class classInstance = Class.forName("mypackage.MyClass");
MyClass myClass = (MyClass) classInstance.newInstance();
 
不过要注意的是,因为newInstance返回的是一个Object,所以要做强制类型转换,将其变成MyClass类型
 
捕捉可能产生的异常后:
public class Test {
   public static void main(String [] args) {
    try {
Class classInstance = Class.forName("mypackage.MyClass");
MyClass myClass = (MyClass) classInstance.newInstance();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
   }
}
 
全文的总结
 
如果您已经看到这里了,那么请允许我在用多那么一点罗嗦的文字做个总结:
 
总结
 
1.反射为我们提供了全面的分析类信息的能力,例如类的方法,成员变量和构造器等的相关信息,反射能够让我们很方便的获取这些信息, 而实现这个获取过程的关键是取得类的Class对象,然后根据Class对象取得相应的Method对象,Field对象和Constructor对象,再分别根据各自的API取得信息。
2.反射还为我们提供动态加载类的能力
 
一些细节
1. API中getDeclaredXXX和getXXX的区别在于前者只获取本类声明的XXX(如成员变量或方法),而不获取超类中继承的XXX, 后者相反
2. API中, getXXXs(注意后面的s)返回的是一个数组, 而对应的 getXXX("键")按键获取一个值(这个时候因为可能报已检查异常所以要用try-catch语句包裹)
3. 私有成员变量是不能直接获取到值的!因为java本身的保护机制,允许你取得私有成员变量的类型,但是不允许直接获取值,所以要对对应的field对象调用field.setAccessible(true) 放开权限
 
最后
反射的API其实还有一堆...但是抱歉的是没办法一一给大家介绍,只能当作个“引子”给大家尝个鲜,另外,本人java小白,有诸多不当之处,还望大家指出,谢谢大家。
 
【完】
 

参考资料:
《java核心技术 卷1》—— Cay S. Horstmann, Gary Cornell

 

【java】java反射初探 ——“当类也照起镜子”的更多相关文章

  1. 【java】java反射初探 ——“当类也学会照镜子”

    反射的作用   开门见山地说说反射的作用   1.为我们提供了全面的分析类信息的能力 2.动态加载类   我理解的“反射”的意义 (仅个人理解哈)   我理解的java反射机制就是: 提供一套完善而强 ...

  2. Java反射初探 ——“当类也学会照镜子”

    反射的作用 开门见山地说说反射的作用   1.为我们提供了全面的分析类信息的能力 2.动态加载类   我理解的“反射”的意义 (仅个人理解哈)   我理解的java反射机制就是: 提供一套完善而强大的 ...

  3. java通过反射取得一个类的完整结构

    首先我们在person包中新建一个Person.java: package person; import sex.Sex; public class Person{ private String na ...

  4. java利用反射打印出类的结构

    1 输入一个类名:java.lang.String将打印出String类定义的结构,例如: public final class java.lang.String { public java.lang ...

  5. Java通过反射机制修改类中的私有属性的值

    首先创建一个类包含一个私有属性: class PrivateField{ private String username = "Jason"; } 通过反射机制修改username ...

  6. Java关于反射

    反射的概念:JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java ...

  7. 七分钟理解 Java 的反射 API

    像java一样,一种具有反射功能的语言.允许开发人员在运行时检查类型.方法.字段.注解等,并在程序运行时决定是否使用. 为此,Java的反射API提供类,类,字段,构造函数,方法,注释和其他. 使用它 ...

  8. java 深入技术七(类的结构和反射)

    1.java反射(reflect) java 类的结构 java反射机制就是把java类按结构分解,每一部分对应特定的反射类 java反射机制允许运行时加载,探知和使用在编译期间完全未知的classe ...

  9. java中的反射机制,以及如何通过反射获取一个类的构造方法 ,成员变量,方法,详细。。

    首先先说一下类的加载,流程.只有明确了类这个对象的存在才可以更好的理解反射的原因,以及反射的机制. 一.  类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三 ...

随机推荐

  1. 【Centos7】hostnamectl 设置主机名

    Centos7中提供了设置主机名的工具 hostnamectl hostname有三种状态 static(永久) transient(瞬态) pretty (灵活) 查看主机名状态 [oracle@h ...

  2. css中滚动条样式的设置

    参数说明: 1.overflow-y : 设置当对象的内容超过其指定高度时如何管理内容:overflow-x : 设置当对象的内容超过其指定宽度时如何管理内容. 参数: visible:扩大面积以显示 ...

  3. LF模式是个坑,ZeroIce中间件让你体会这个痛

    LF模式是个坑,一个小小的失误就可能使你的网络处理瘫痪,Ice就很好地展现了出来,换句话说,Ice中间件或是LF模式就是一个坑,如果你一不小心. LF模式的官方论文中,论述了此模式用于高性能网络并发模 ...

  4. 004-谈一谈lock和synchronized

    这两个关键字都是用来对线程进行同步操作的. 参考疯狂java讲义16.5节 线程的同步. (完全答反了...)

  5. python+selenium自动化软件测试(第9章) :Logging模块

    9.1 Logging模块 什么是日志记录?记录是跟踪运行时发生的事件的一种手段.该软件的开发人员将记录调用添加到其代码中,以指示某些事件已发生.事件由描述性消息描述,该消息可以可选地包含可变数据(即 ...

  6. Mybatis --- 映射文件、参数处理、参数值的获取、select元素

    映射文件:指导着MyBatis如何进行数据库增删改查, 有着非常重要的意义:   - cache   命名空间的二级缓存配置 - cache-ref   其他命名空间缓存配置的引用. - result ...

  7. html5 响应式布局(媒体查询)

    响应式布局        响应式布局,简而言之,就是一个网站能够兼容多个终端--而不是为每个终端做一个特定的版本.这个概念是为解决移动互联网浏览而诞生的.        响应式布局可以为不同终端的用户 ...

  8. Freemarker的使用方法

    1. Freemarker概念 1.1 什么是freemarker FreeMarker是一个用Java语言编写的模板引擎,它基于模板来生成文本输出.FreeMarker与Web容器无关,即在Web运 ...

  9. linux为用户配置java环境变量

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt226 一. 解压安装jdk 在shell终端下进入jdk-6u14-linu ...

  10. Servlet和JSP生命周期概述

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt374 Servlet生命周期分为三个阶段: 1,初始化阶段  调用init( ...