Java基础:反射机制(Reflection)总结

  ------- android培训java培训、期待与您交流! ----------

 

       反射在java中有非常重大的意义,它是一种动态的相关机制,可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods具体定义),并生成其对象实体、或对其fields设值、或使用其 methods。反射是框架的基础,而框架能大大节省java程序的开发成本,是日后工作中经常会碰到的技术。所以,学习好反射的重要性就不言而喻了。

 

一、概念

 

       java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。反射的动态特性极大地体现了java的灵活性。

       很多人刚接触反射的时候,可能觉得不大好理解,那是因为对反射的过程了解得不够清楚。我们要知道,反射都是通过传递class来间接完成目的。

       比如想通过反射调用一个类的构造方法来创建对象,那么就要先得到那个类的字节码对象(class)。而类的字节码对象是属于Class类,Class类有提供获取class对象中包括构造方法,成员方法,以及字段所属类(Constructor,Methods,Field)的方法。那么我们只要使用字节码对象调用Class中的getConstructor()方法,即可获取对象中的构造方法的所属类,最后通过构造方法的所属类获取构造方法。当我们最终获取构造方法后,就能成功创建对象。

       如果文字表达的不够清楚,可以看以下图解:

 

二、反射机制的优点与缺点

 

       首先要弄清楚为什么要用反射,这涉及到了动态与静态的概念。

       静态编译:在编译时确定类型,绑定对象,即通过。
    动态编译:运行时确定类型,绑定对象。

       反射的优点就是:它的动态创建对象和编译,最大限度发挥了java的灵活性,体现了多态的应用,又可以降低类之间的藕合性。

       它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM虚拟机,我们希望做什么并且它满足我们的要求,但这类操作总是慢于只直接执行相同的操作。

 

 

三、使用方法

 

       首先需要了解一下,java.lang.reflect包中的一些类来的含义。

       常用的类有:
    Class:包含某个类的全部信息,包括属性、方法、构造方法等。(java.lang包)
    Construtor:代表类中的构造方法。
    Method:代表类中的方法。
    Field:代表类中的属性。

 

       步骤(结合图解):
       (1)取得对象的class字节码对象。
       根据字符串全类名:Class cls=Class.forName(String className);
       根据对象:Class cls=obj.getClass(); //获取当前对象所属的类
       根据类:Class cls=Person.class; 


    (2)取得对象成员所属的类
    取得所有字段的所属类的数组:Field[] field=cls.getDeclaredFields();
    取得所有公共字段的所属类的数组:Field[] field=cls.getFields();   
       取得指定的字段的所属类:Field fields=cls.getDeclaredField (String name);
       取得指定的公共字段的所属类:Field fields=cls.getField (String name);

       取得所有方法的所属类的数组:Method[] methods=cls.getDeclaredMethods();
    取得指定方法的所属类:Method method = cls.getJMethod();

       取得所有构造方法的所属类的数组:Constructor[]con = cls.getConstructors();
    取得指定构造方法的所属类:Constructor con = cls.getConstructor(Class... parameterTypes);

 

    (3)使用对象成员所属类获取对象成员
    获取指定字段:Object objField = field.get();

    获取指定方法:ObjectobjMethod = method.invoke(Object obj,Object... args);

    获取指定构造方法:ObjectobjCon = con.newInstance(Object... initargs) ;

 

 

四、应用

 

     1.例子一(通过反射获取类的构造方法):(以下例子为了方便阅读,异常都做抛处理)

 

import java.lang.reflect.Constructor;

public class ReflectTest {

    public static void main(String[] args) throws Exception {
//        通过反射获取一个类的构造方法。注意反射比较消耗资源,会影响程序性能。
        Constructor<String> cons = String.class.getConstructor(StringBuffer.class);
//        也可通过获取到的构造方法反过来获取构造方法所属的类。
        Class<String> cls4 = cons.getDeclaringClass();
        System.out.println(cls4);
    }
}   

         运行结果:

       class java.lang.String

 

       2.例子二(通过反射获取类中字段、改变字段的值):

 

public class Student {
    private int high;
    public int wight;
    public String str1 = "ball";
    public String str2 = "basketball";
    public String str3 = "hello";
    
    public ReflectPiont(int high, int wight) {
        super();
        this.high = high;
        this.wight = wight;
    }
}

 

 

import java.lang.reflect.Field;

import com.gzjzone.jul262015.ReflectPiont;

public class ReflectTest {

    public static void main(String[] args) throws Exception {
        Student s1 = new Student(3, 5);
//        getField方法返回的不是具体的变量y,而是一个类,通过这个类调用get方法才能获取具体对象上变量。
        Field fieldW = s1.getClass().getField("wight");
        System.out.println(fieldW.get(s1));
        
//        因为s1上的x变量是私有的,所以要用getDeclaredField,只要是声明的变量都能提取成类。
        Field fieldH = s1.getClass().getDeclaredField("high");
//        同理s1上的x变量是私有的,所以只能通过暴力反射的方法获取x。
        fieldH.setAccessible(true);
        System.out.println(fieldH.get(s1));
        
        changeStringValue(s1);
        System.out.println(s1);
    }
    
    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);
            }
        }
    }
}

 

       运行结果:

       5
    3
    aall:aasketaall:hello

 

       3.例子三(通过反射,获取类中的方法):

 

import java.lang.reflect.Method;

public class ReflectTest {

    public static void main(String[] args) throws Exception {
//        使用反射调用main方法。
//        先假设本类运行时会接收到TestArguments的名称。
        String className = args[0];
        Method mainMethod = Class.forName(className).getMethod("main", String[].class);
//        在jdk1.4中invoke方法的参数列表是(Object,Object[]),invoke方法接收到Object[]后会拆开。
//        而jdk1.5以后invoke方法的参数列表是可变参数列表(Object,...arg)。
//        因为1.5是兼容1.4的,所以如果把String[]传进invoke方法,会被拆开,这不符合要求。
//        解决方法为:1.把String[]作为一个元素装进一个Object[]中;2.把String[]强制转成Object。
//        如果invoke方法第一个参数为null,表示调用的是静态方法。
        mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
    }
}

class TestArguments{
    public static void main(String[] args){
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

 

       运行结果:

       111
    222
    333

 

       4.例子四(通过反射,获取数据元素):

 

import java.lang.reflect.Array;

public class ReflectTest {

    public static void main(String[] args) throws Exception {
//        通过反射获取数组,Array。
        int[] arr = {1,2,3};
        printObject(arr);
        printObject("xyz");
    }

    private static void printObject(Object obj) {
        if (obj.getClass().isArray()) {
            int len = Array.getLength(obj);
            for (int x = 0; x < len; x++) {
                System.out.println(Array.get(obj, x));
            }
        }
        else{
            System.out.println(obj);
        }
    }
}

 

       运行结果为:

       1
    2
    3
    xyz

 

       5.例子五(基础框架):

 

public class Student {
    private int high;
    public int wight;

    public ReflectPiont(int high, int wight) {
        super();
        this.high = high;
        this.wight = wight;
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + x;
        result = prime * result + y;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ReflectPiont other = (ReflectPiont) obj;
        if (x != other.x)
            return false;
        if (y != other.y)
            return false;
        return true;
    }

    @Override
    public String toString(){
        return str1+":"+str2+":"+str3;
    }    
}

 

 

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        
//        运用框架的方式创建对象,好处就是可以先做框架,后再创建对象对应类,从而增强程序的适用性。
//        框架完成后,创建对象是,只要先通过配置文件的方式获取对象对应的类名即可。
        InputStream ips = new FileInputStream("config.properties");    //内容为className=java.util.HashSet
        Properties pro = new Properties();
        pro.load(ips);
        ips.close();
        String className = pro.getProperty("className");
        //Class类中本身也有newInstance()方法,它实际是调用Constructor类中的newInstance(),只能获取类的空参数构造函数。
        Collection col = (Collection)Class.forName(className).newInstance();
        
        Student s1 = new Student(2, 4);
        Student s2 = new Student(3, 6);
        Student s3 = new Student(2, 4);
        
        col.add(s1);
        col.add(s1);
        col.add(s2);
        col.add(s3);
        
/*        注意,在hashSet集合中,因为y的值有参与hashCode值的运算,所以修改y后,
        pt1指向的对象内存hashCode地址已变,而pt1指向的还是原来对象的地址。
        最后删除的是pt1指向的原对象地址,而变更后的对象地址则还存在,造成内存溢出。*/
        pt1.y = 6;
        col.remove(pt1);
        
        System.out.println(col.size());
    }
}

 

       运行结果为:

       2

 

       以上就是本人现阶段所学习到的反射知识总结,若以后对反射有更加深的见解或建议,再继续完善文章。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值