反射(二)——反射机制

            反射机制

  在一个已有的java程序中,随着需求的变更,如果我们需要扩充或者改进程序,一般来说修改源代码不是一件好事。而如果我们可以重新定义新的类,并且在原来程序的基础上进行扩充,那我们就不用再为修改复杂的源代码而烦恼。换言之,在不改变原来类的源代码的情况下能不能定义新的,再保存原有功能的基础上进行扩展呢?

  幸运的是,java的反射机制,很好的解决了这个问题。新定义的类经过编译以字节码文件产生,形成class文件(字节码文件)。java反射技术将该class文件打开,进行解剖,获取内部数据结构。由于应用程序不能进行更改,但程序运行时可以指定class文件的内部数据,对class文件的内部数据进行获取和处理。对于应用程序来说,不需要知道我们定义新的可扩展的类,只需要知道新的类的字节码交给它,它就会用反射技术来处理。JAVA反射机制是在运行状态中,对任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个属性和方法;这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。需要注意的是,要想解剖一个类必须要先获取该类的class文件对象。而解剖使用的就是java.lang.Class类中的方法,该类的放有关方法可参考JAVAAPI官方文档。竟然我们可以对程序进行扩展,那我们自己定义的类能不能“乱写”、瞎搞呢?程序为了更加安全的使用新扩展的类,反射机制对其新的类进行一种特殊的接口约束。所以使用反射机制是安全的。

  在任何一门面向对象的编程语言中,对象一定是真实存在的事物,而类是事物特征的抽象描述。java中的java.lang.Class类是用于描述类对象的类。Class类的实例表示正在运行的java应用程序中的类和接口。使用反射机制都能获取到任意一个类的所有属性和方法。那怎么样才能获取到Class文件对象的方法呢?那下面将介绍获取Class文件对象的三中方法。为了更好形象的描述以下的内容,我们创建了一个Good类。

    public class Goods {
	
	    public String goodsName; //商品名称
	    public double price;  //价格
	
	    /*static {
		    System.out.println("静态代码块");
	    }*/
	
	    //无参构造器
	    public Goods() {
		
	    }
	
	    //有参构造器
	    public Goods(String goodsName,double price) {
		    this.goodsName = goodsName;
		    this.price = price;
	    }
	
	    public void goods() {
		    System.out.println("商品总揽");
	    }
	
	    public void info(String gname, double gprice) {
		    System.out.println(gname+"的价格是:"+gprice);
	    }
	
    }

  

  

方法一:对象获取

  使用某个类的对象直接调用getClass()方法,该方法返回一个Class对象。如果我们此时打印Class对象的返回值,会发现打印出来的其实就是某个类的全名。比如:

    Goods good = new Goods();
    Class c = good.getClass();
    System.out.println(c);  //输出类的全名

  

方法二:类名获取

  每个类型(包括基本类型和引用类型),JVM都会赋予该类型一个静态属性,而属性的名字就叫class。我们在自己写的代码中是无法看见这个属性的,但是我们用类名去调用的时候,这个属性是确实存在的。相信我们都曾经在静态同步锁中遇见过它,所有的类型都有class属性,如double.class等。

    Class c = Goods.class;
    System.out.println(c);  //输出类的全名

  

方法三:Class类的静态方法获取

  使用静态方法forName(String className)进行获取,使用该方法的好处在于类名可以使用字符串,既然是字符串,那在使用时就会提高它的灵活性。特别要注意的是className必须使用类的全名。在使用该方法时,所在的本地方法必须抛出异常,如果嫌麻烦可以直接抛出Exception 

    Class c = Class.forName("com.gzhxtc.win.Goods");
    System.out.println(c);  //输出类的全名

  

  在类的加载器那里为类创建class文件的时候,class文件对象是唯一的吗?为了证明这个问题,我们对同一类分别创建了两个对象,然后比较两个对象的引用和内用是否相同,而实验的结果告诉我们这的确是同一个对象。

    Class c1 = Class.forName("com.gzhxtc.win.Goods");
    Class c2 = Class.forName("com.gzhxtc.win.Goods");
    //判断两个对象的引用是否想等,结果为:true
    System.out.println(c1 == c2); 
    //判断两个对象的内容是否想等,结果为:true
    System.out.println(c1.equals(c2)); 

  

  前面介绍了获取Class文件对象的三中方法,这三种方法各有千秋,但用的最多的是第三种方法——Class类的静态方法获取。该方法由于一个字符串的传输使得程序更加灵活。现在有了这三种获取Class文件对象的方法,我们接下来便切入正题,在程序中如何获取类中的成员属性和方法呢?下面将为类中各种变量的获取进行介绍。

 

一、反射获取构造方法

1. 无参数构造器  

    /*
    * 获取类中所有public修饰的构造器
    * 在Class类中有很多供我们使用的方法,一般用的比较多的是getConstructors()方法,
    * 该方法可以获取class文件对象中的所有被public修饰的构造方法。它的返回值是
    * Constructor[]数组,即接受多个构造器。Constructor是描述构造方法的类。
    */
    Class c = Class.forName("com.gzhxtc.win.Goods");
    Constructor[] cons = c.getConstructors();
    for (Constructor constructor : cons) {
	System.out.println(constructor); 
    }
    //输出构造器的全名:
    //public com.gzhxtc.win.Goods()
    //public com.gzhxtc.win.Goods(java.lang.String,java.lang.String)

    /*
    * 获取指定的public修饰的构造器
    */
    Class c = Class.forName("com.gzhxtc.win.Goods");
    //newInstance()方法这里因为是无参构造器,所以不传参数
    Object obj = c.newInstance();
    //如果要想让obj对象能使用类中的方法,必须强制转换类型
    Goods good = (Goods)obj;
    //接下来good就可以调用Goods中的方法了
    good.goods();
    System.out.println();//输出:商品总揽

  

2.有参数构造器   

    /*
    * 获取有参的public修饰的构造器,需要传参数的class文件对象
    */
    Class c = Class.forName("com.gzhxtc.win.Goods");
    //获取有参构造器
    Constructor constructor = c.getConstructor(String.class, double.class);
    Object obj = constructor.newInstance("电热棒",99.99);
    Goods good = (Goods)obj;
    System.out.println(good.goodsName);  //输出:电热棒
    System.out.println(good.price);      //输出:99.99

  

二、反射获取类的成员变量 

    /*
    * 使用getFields()方法,获取class文件中所有public的成员变量
    * 在导包的时候导的是lang包里面的,千万别导错
    */
    Class c = Class.forName("com.gzhxtc.win.Goods");
    Field[] fileds = c.getFields();
    for (Field field : fileds) {
	System.out.println(field);
    }
    //获取指定的public的成员变量
    Field field = c.getField("goodsName");
    Object obj = c.newInstance();  //运行
    //修改成员变量,参数必须要有对象的支持和修改后的值
    field.set(obj, "电饭煲");
    Goods good = (Goods)obj;
    System.out.println(good.goodsName); //输出:电饭煲

  

  三、反射获取成员方法

1.无参public的成员方法 

    /*
    * 使用getMethons()获取的class文件中的所有public方法(包括继承的)
    */
    Class c =Class.forName("com.gzhxtc.win.Goods");
    Method[] methods = c.getMethods();
    for (Method method : methods) {
	    System.out.println(method);
    }

  

  

2. 有参public的成员方法 

    /*
    * 获取指定的成员方法
    */
    Class c =Class.forName("com.gzhxtc.win.Goods");	
    Object obj = c.newInstance();  //运行
    Method method = c.getMethod("info",String.class,double.class);
    //调用Method类的invoke
    method.invoke(obj, "电冰箱", 2999.0);  //输出:电冰箱的价格是:2999.0

  

  以上是反射技术的基础,关于反射技术的更多方法请参考java的API1.8官方文档。

 

转载于:https://www.cnblogs.com/Lynnblog/p/8962165.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值