反射

【看张老师的视频笔记和总结】

1、反射的基础–Class类

   各个Java类属于同一个事物,描述这类事物的java类名就是Class。Class类代表java类,其各个实例对象分别对应个各类在内存中的字节码。

(1)字节码

   一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码。

   不同类的字节码是不一致的,其在内存中的内容也是不同的。这一个一个的空间分别用一个一个对象表示,这些对象具有相同的类型,这个类型就是Class。

(2)如何得到各个字节码对应的实例对象(Class类型)

    * 类名.class  , 如 System.class
    * 对象.getClass()  ,如 new Date().getClass()
    * Class.forName(“类名”) , 如 Class.forName("java.util.Date")
        public class ResflectTest {
            public static void main(String[] args) throws Exception{
                // TODO Auto-generated method stub
                String str1 = "abc";
                Class cls1 = str1.getClass();
                Class cls2= String.class;
                Class cls3 = Class.forName("java.lang.String");

                System.out.println(cls1==cls2);
                System.out.println(cls2==cls3);

                System.out.println(cls1.isPrimitive()); //cls1的类型是基本类型吗
                System.out.println(int.class.isPrimitive()); //字节码是否是基本类型
                System.out.println(int.class==Integer.class);
                //int字节码的类型与Integer字节码的类型
                System.out.println(int.class == Integer.TYPE); //
                System.out.println(int [].class.isPrimitive());
            }
        }

(3)九个预定义Class实例对象

    数组属于一个Class类,所有具有相同元素类型和维数的数组共享一个Class对象。基本的java类型(primitive Java types)[int,short,long,float,double,boolean,char,byte]和void类型也表示为Class对象。
    如,int.class  ,Integer.class   ,Integer.TYPE

    数组类型的Class实例对象:int [].class   ; Class.isArray

2、反射(Reflection)

    java中反射是指,我们可以在运行时加载、探知、使用编译期间完全未知的类。也即,java程序可以加载一个运行时才得知名称的类,获悉其完整构造,并生成其对象实体、或对其变量设值、或调用其方法。这种“看透类”的能力被称为Introspection(内省、内观、反省)。JavaBean是Reflection的实际应用之一。 

反射就是把java类中的各种成分映射成为相应的java类。


(1)构造方法的反射应用

    Class对象可以获得该类里成分包括方法(由Method对象表示)、构造器(由Construction对象表示)、Field(由Field对象表示),程序可以通过Method对象来执行队形的方法,通过Construction对象来调用对应的构造器创建对象,通过Field对象直接访问并修改对象的属性值。

Construction类代表某个类中的一个构造方法

    A、得到某类中所有的构造方法
        Constructor [] constructors = Class.forName("java.lang.String").getConstrutors();
    B、  得到某一个构造方法
        Constructor constructor = Class.forName("java.lang.String").getConstrutors
                                  (StringBuffer.class);//获得方法时要用到类型
    C、创建实例对象
        通常方式:String str = new String(new StringBuffer("abc"));

        反射方式:
        Constructor constructor = String.class.getConstructor(StringBuffer.class);
        String str = (String)constructor.newInstance(new StringBuffer("abc"));
        //调用获得的方法时,要用到上面相同类型的实例对象
    D、Class.newInstance()方法
        String obj = (String)Class.forName("java.lang.String").newInstance();
        //该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

(2)成员变量的反射(Field类)

    Field类代表某个字节码中的变量,不代表某个对象中的变量。即,Field对象对应的是类上面的成员变量,不是对象的的成员变量。如下示:
    fieldY代表的是x的定义,不是具体的x变量。
        private int x;
        public int y;


        ReflectPoint pt1 = new ReflectPoint(3,5);
        Field fieldY = pt1.getClass().getField("y");
        //getField()方法只能读取公有的成员变量。(即看不到除去公有的成员变量)
        System.out.println(fieldY.get(pt1));

        Field fieldX = pt1.getClass().getDeclaredField("x");  
        //getDeclaredField()方法可以读取任意的成员变量
        fieldX.setAccessible(true);  
        /*因为x设置为私有的,虽然上步getDeclaredField("x")成功编译,
        *但是在读取时出现private,即只能看见,但是使用不了。
        *这时使用setAccessible(true);设置反射的对象可以读取。(暴力反射)
        */
        System.out.println(fieldX.get(pt1));
    例子:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改为“a”。
        public static void main(String[] args) throws Exception{
            ReflectPoint pt1 = new ReflectPoint(3,5);
            changeStringValue(pt1);
            System.out.println(pt1);
        }
        public static void changeStringValue(Object obj) throws Exception
        {
            Field[] field1 = obj.getClass().getFields();
            for (Field field:field1)
            {
                if(field.getType() == String.class)
                //同一份字节码,即是相同的内存地址的值,则使用==进行比较
                {
                    String oldValue = (String)field.get(obj);
                    String  newValue = oldValue.replace('b', 'a');
                    field.set(obj, newValue);
                }
            }
        }
        public class ReflectPoint {
            private int x;
            public int y; //定义为共有的,才能在ReflectTest中调用“y”

            public String str1= "cat";
            public String str2= "bobcat";
            public String str3= "catball";

            public ReflectPoint(int x, int y) {
                super();
                this.x = x;
                this.y = y;
            }
            @Override
            public String toString()
            {
                return str1 + ":" +str2 + ":" +str3;
            }
        }

(3)成员方法的反射

    Method类:代表某个类中的一个成员方法。
    (方法中的参数类型用Class对象表示)

    如:Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
        System.out.println(charAt.invoke(str,1));//反射方式
        System.out.println(str.charAt(1));//通常方式
    【如果传递给Method对象的invoke()方法的第一个参数是null,则说明该Method对象对应的是一个静态方法】
        //成员方法的反射
        Method methodcharAt = String.class.getMethod("charAt", int.class) ;
        System.out.println(methodcharAt.invoke(str1, 1));
        System.out.println(methodcharAt.invoke(str1, new Object[] {2}));

        //int i[] = new int [] {1,2,3};
        //String str[] = new String [] {new String("abc"),new String("123")};
        //Object shuzu = new Object[] {new String("abc"),2};

[对接收数组参数的成员方法的反射]

    通过反射的方式调用有数组参数的方法时(main方法),invoke方法怎样传递参数?

    public Object invoke(Object obj,Object... args)
    public Object invoke(Object obj,Object [] args)
    可知,invoke方法需要Object类型的参数,整个数组作为一个参数。
即,invoke(Object obj,new Object [] {数组})。
        /*静态代码的方式直接调用main方法
         * TestArguments.main(new String[] {"123","abc"});
         * class TestArguments{
            public static void main(String [] args)
            {
                for(String arg:args)
                {
                    System.out.println(arg);
                }
            }

           }
         */
        String startingClassName = args[0];
        Method mainMethod  = Class.forName(startingClassName).getMethod("main", String[].class);
        //mainMethod.invoke(null, new Object[]{new String[] {"123","abc"}});
        //将new String[] {"123","abc"}作为Object数组的一个元素,这样符合invoke的构造方法参数传递。
        mainMethod.invoke(null, (Object)new String[] {"123","abc"});
        //invoke需要Object类型的数组参数,但是new String[] {"123","abc"}是字符串数组,所以进行强制类型转换即可。

(4)数组的反射

    前边提及到,具有相同元素和维度数目的数组是一个Class。

数组与Object的关系与反射类型

    Object是所有类的超类。
    JAVA中只有基本类型不是对象,但是String、数组是对象,都是从Object类中扩展而来。

    int [][]可以转化为Object[]是因为,int[]是Object对象,则int[][]转换为Object[]时,相当于其元素是int[]。

    int[]不能强制转换成Object[],但是可以转换为Object类。是因为int是基本数据类型,不是对象,也即不是Object对象,int[]是基本数据类型int的一维数组,是由Object类扩展而来。

    String是对象,也是Object类型的对象,则String[]可强转成Object[]
    String [] a4 = new String[]{"a","b","c"};
    printObject(a4);
    printObject("xyz");
    private static void printObject(Object obj) {
        // TODO Auto-generated method stub
        Class clazz = obj.getClass();
        if(clazz.isArray())
        {
            int len = Array.getLength(obj);
            for(int i=0;i< len;i++)
            {
                System.out.println(Array.get(obj, i));
            }
        }else{
            System.out.println(obj);
        }
    }
    怎样得到数组中元素的类型?
    【只能得到某个元素的具体类型:args[1].getClass().getName();例如Object[]整个数组的元素类型可以是{“123”,123}等。】

3、反射的作用(实现框架功能)

        //Collection collection = new ArrayList();使用通常ArrayList
        //使用反射达到ArrayList的功能,此处将类名保存为一个文件,这样不用在程序中修改类名。
        InputStream ips= new FileInputStream("config.properties"); 
        Properties pros = new Properties();
        pros.load(ips);
        ips.close();
        String className = pros.getProperty("className");
        Collection collections =(Collection) Class.forName(className).newInstance();

        //Collection collections = new HashSet();要实现HashSet功能时,将文件中的类名改为HashSet即可
        ReflectPoint pt1 = new ReflectPoint(3,3);
        ReflectPoint pt2 = new ReflectPoint(5,5);
        ReflectPoint pt3 = new ReflectPoint(3,3);

        collections.add(pt1);
        collections.add(pt2);
        collections.add(pt3);
        collections.add(pt1);
        System.out.println(collections.size());
    这里的文件路径是相对路径,在实际中不是这样存放的。一种是设置为绝对路径;
    一种是getRealPath();//得到总的目录在硬盘中对应的位置,最后拼接上自己内部的地址,就是文件的完整地址.InputStream ips= ReflectTest2.class.getResourceAsStream("resouces/config.properties");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值