java反射详解(二)_基本概念及应用

2019-03-17 10:04|来源: 领悟书生

反射的基本概念

反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。

一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。


构造方法的反射应用

//new String(new  StringBuffer("abc"))
//得到某一个构造方法
Constructor constructor= String.class
             .getConstructor(StringBuffer.class);
//创建实例对象
String str =  (String)constructor
              .newInstance(new StringBuffer("abc"));
System.out.println(str.charAt(2));

Class.newInstance()方法:

l  例子:String obj= (String)Class.forName("java.lang.String").newInstance();

l  该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

l  该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。


成员变量的反射

Field代表某个类中的一个成员变量

先来定义一个类

package com.naxsu.reflect;
public class Point {
    private int x;
    public int y;
    public Point(int x, int y) {
       super();
       this.x = x;
       this.y = y;
    }
}

测试:

@Test
public void test3() throws Exception{
    Point p = new Point(3, 5);
    //getField:只能获取public类型的成员变量
    Field fieldy = p.getClass().getField("y");
    //field不是对象身上的变量,而是类上,要用它去取某个对象上对应的值
    System.out.println(fieldy.get(p));
    //getDeclaredField获取该类对应的成员变量
    Field fieldx =  p.getClass().getDeclaredField("x");
    //允许获取private成员变量的值
    fieldx.setAccessible(true);
    System.out.println(fieldx.get(p));
}

案例:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"

Point类中增加三个成员变量,并覆盖他的toString方法:

public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "naxsu";
@Override
public String toString() {
    return "Point [str1=" + str1 + ", str2=" + 
           str2 + ",  str3=" + str3 + "]";
}
@Test
public void test3() throws Exception{
    Point p = new Point(3, 5);
    changeStringValue(p);
    System.out.println(p);
}
     
private static void changeStringValue(Object obj) 
    throws Exception{
    Field[] fields=obj.getClass().getFields();
    for(Field  field:fields){
       //字解码的比较要用==进行比较
       if(field.getType()==String.class){
           String oldValue =  (String)field.get(obj);
           String newValue = oldValue.replace('b', 'a');
           field.set(obj, newValue);
       }
    }
}

输出:Point [str1=aall,  str2=aasketaall, str3=naxsu]


成员方法的反射

Method代表某个类中的一个成员方法

@Test
public void test4() throws Exception{
    String str = "abc";
    //str.charAt(1);
    //得到类中的某一个方法
    Method methodCharAt= String.class.getMethod("charAt", int.class);
    //invoke:调用方法
    System.out.println(methodCharAt.invoke(str,  1));
}

注:

如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!

jdk1.4和jdk1.5的invoke方法的区别:

l  Jdk1.5:public Object invoke(Object obj,Object... args)

l  Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为charAt.invoke(“str”, new Object[]{1})形式。

System.out.println(methodCharAt.invoke(str,  new Object[]{1}));

对接收数组参数的成员方法进行反射

用反射方式执行某个类中的main方法

public class ReflectTest {
    @Test
    public void test5() throws Exception{
       String startingClassName="com.naxsu.reflect.TestArguments";
       Method mainMethod = Class.forName(startingClassName)
              .getMethod("main", String[].class);
       mainMethod.invoke(null, 
              new Object[]{new String[]{"111","222","3333"}});
    }
}
  
class TestArguments{
    public static void main(String[]  args) {
       for (String arg:  args) {
           System.out.println(arg);
       }
    }
}

启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。

解决办法:

1.        mainMethod.invoke(null,newObject[]{new String[]{"xxx"}});

2.        mainMethod.invoke(null,(Object)newString[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了



本文链接:java反射详解(二)_基本概念及应用,由huangyineng原创,转载请注明出处

相关问答

更多
  • 什么是操作系统 操作系统是一个大型的软件系统,其功能复杂,体系庞大。从不同的角度看的结果也不同,正是“横看成岭侧成峰”,下面我们通过最典型的两个角度来分析一下。 1.从程序员的角度看 正如前面所说的,如果没有操作系统,程序员在开发软件的时候就必须陷入复杂的硬件实现细节。程序员并不想涉足这个可怕的领域,而且大量的精力花费在这个重复的、没有创造性的工作上也使得程序员无法集中精力放在更具有创造性的程序设计工作中去。程序员需要的是一种简单的,高度抽象的可以与之打交道的设备。 将硬件细节与程序员隔离开来,这当然就是操 ...
  • 爬虫的关键,是爬取允许爬取的数据,有效利用。比如搜索引擎还是要遵守 robots.txt 的。反爬就是网站不想让你爬,用随机高匿代理之类的解决反爬技术很成熟,但是有点抢劫的性质。爬虫的关键(或者难点)是如何不被封 IP ,至于怎么抓取页面 httpclient 跟其他组件都差不多
  • 在Java运行时刻,能否知道一个类的属性方法并调用改动之?对于任意一个对象,能否知道他的所属类,并调用他的方法?答案是肯定的。这种动态的获取信息及动态调用方法的机制在Java中称为“反射”(reflection)。 Java反射机制主要提供以下功能: 在运行时判断任意一个对象所属的类; 在运行时构造任意一个类的对象; 在运行时判断任意一个类所具有的成员变量和方法; 在运行时调用任意一个对象的方法。 Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Refl ...
  • Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中(注意是运行时,而非编译时)的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来,能够通过字符串形式的类名称(包括完整包名)反射性地创建类的实例,能够动态执行类方法等。 JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。 ...
  • 1.建立数据库database,建立数据表student。 字段: sid 学号 文本 sname 姓名 文本 ssex 性别 文本(2字节) saddress 地址 文本 stel 电话 文本 2.类代码 /****************************************************** 使用: 设置数据库路径、sql语句、最大返回行数,使用getHTMLTable()返回html表格形式的字符串 executeSQL(String,String)执行SQL语句返回一个对象,请 ...
  • 公钥基础设施PKI   作者:xxx123123 文章来源:本站原创 点击数:29 更新时间:2005-8-29   一、 PKI概述   企业生意成功与否在很大程度上取决于该企业是否拥有一个安全可靠的网络系统。目前大多数企业的IT管理人员都为其企业的网络系统采取了某种形式的加密和认证方案。许多企业的网络管理人员正在利用Web向企业提供安全的Internet商务、虚拟专用网络(VPN)以及远程认证服务,以使其远地雇员拥有对企业网络的存取能力。然而,当前的大多数安全技术(例如用户名和口令、一次性口令以及双向鉴 ...
  • 在做软件架构设计时,根据不同的抽象层次可分为三种不同层次的模式:架构模式(Architectural Pattern)、设计模式(Design Pattern)、代码模式(Coding Pattern)。 架构模式是一个系统的高层次策略,涉及到大尺度的组件以及整体性质和力学。架构模式的好坏可以影响到总体布局和框架性结构。 设计模式是中等尺度的结构策略。这些中等尺度的结构实现了一些大尺度组件的行为和它们之间的关系。模式的好坏不会影响到系统的总体布局和总体框架。设计模式定义出子系统或组件的微观结构。 代码模式( ...
  • 栈,是一种先进后出的数据结构, 队列,是一种先进先出的数据结构, 栈,有一个指针,指向栈顶元素 队列,有两个指针,一个指向队尾,一个指向队首 栈,只能从顶进,从顶出 队列,从队尾进,从队首出 栈,像一个木桶,我们往里面放面包,我们只能从桶口处往里面放,然后再从桶口处往外取,这就形成了栈的先进后出的特性。 队列,像一个管子,我们从管子的屁股处往里面塞糖豆,肯定是最先塞进去的糖豆先从管子的头处掉出去,这就形成了队列的先进先出的特性。 这是我能达到的最精简的程度了,希望你能看懂。^_^
  • 是数学上的一类特殊函数的总称。一般贝塞尔函数是下列常微分方程(一般称为贝塞尔方程)的标准解函数: 这类方程的解是无法用初等函数系统地表示的。 贝塞尔函数的具体形式随上述方程中任意实数 变化而变化(相应地, 被称为其对应贝塞尔函数的阶数)。实际应用中最常见的情形为 是整数 ,对应解称为n阶贝塞尔函数。  尽管在上述微分方程中, 本身的正负号不改变方程的形式,但实际应用中仍习惯针对 和 定义两种不同的贝塞尔函数(这样做能带来好处,比如消除了函数在 点的不光滑性)。