黑马程序员_java 反射

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

反射的基石-->Class类
    每个类加载之后,系统会为该类生成一个对应的Class(字节码文件)对象,通过Class对象就可以访问到
    JVM中的这个类。
获得Class对象的方式:
    1:通过对象的getClass()方法
        举例:
            Person p = new Person();
            Class c = p.getClass();
    2:调用某个类的class属性
        举例:
            Class c = Person.class;
    3:使用Class类的forName(String className)静态方法,该方法需要传入某个类的全限定名
        举例:
                Class c = Class.forName("cn.itcast.Person");
        演示说明:
            package cn.itcast;
            /*
             * Description:
             *     该类测试通过3种方式得到的字节码文件对象是否是同一对象
             * */
            public class ClassTest {
                public static void main(String[] args) throws ClassNotFoundException {
                    Person p = new Person();
                    Class c1 = p.getClass();
                    Class c2 = Person.class;
                    Class c3 = Class.forName("cn.itcast.Person");
                    System.out.println(c1 == c2); //true
                    System.out.println(c2 == c3); //true
                }
            }
        通过结果可以看出,这三种方式得到的字节码文件对象,是同一个对象。
获取构造器的方法:
    Constructor<T> getConstructor(Class<?>... parameterTypes)
        返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
    Constructor<?>[] getConstructors()
        返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的
        所有公共构造方法。
    Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
    Constructor<?>[] getDeclaredConstructors()
        返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有
        构造方法。
    演示:
        package com.itheima;
        /*
         * Person类仅用于演示反射的不同效果
         */
        public class Person {
            private String name;
            int age;
            public String address;
            public static final sex;

            public Person() {
            }

            private Person(String name) {
                this.name = name;
            }

            public Person(String name, int age) {
                this.name = name;
                this.age = age;
            }

            public void show() {
                System.out.println("show");
            }

            public void method(String name) {
                System.out.println("show " + name);
            }

            private String function(String name, int age) {
                return name + "***" + age + "***" + "function";
            }

            private void hello() {
                System.out.println("hello");
            }
            @Override
            public String toString() {
                return name + "***" + age;
            }
        }
        package com.itheima.reflect;
        import java.lang.reflect.Constructor;
        public class ConstructorTest {
            public static void main(String[] args) throws Exception {
                //创建字节码文件对象
                Class c = Class.forName("com.itheima.Person");
                //获取所有的公共构造方法
                Constructor[] cons = c.getConstructors();
                System.out.println("Person类的公有构造方法:");
                //遍历
                for(Constructor conItem : cons)
                    System.out.println(conItem);
                System.out.println("*******************************************");
                //获取所有的构造方法,包括私有
                Constructor[] allCons = c.getDeclaredConstructors();
                System.out.println("Person类的所有构造方法");
                for(Constructor conItem : allCons)
                    System.out.println(conItem);
                System.out.println("*******************************************");
                //获取某一个构造方法
                Constructor con = c.getDeclaredConstructor(String.class);
                //setAccessible(boolean flag)方法,可以暴力访问非公有的构造方法
                con.setAccessible(true);
                //newInstance() 创建此 Class 对象所表示的类的一个新实例。
                Object obj = con.newInstance("zhangSan");
                System.out.println(obj);
            }
        }
        运行结果:
            Person类的公有构造方法:
            public com.itheima.Person(java.lang.String,int)
            public com.itheima.Person()
            *******************************************
            Person类的所有构造方法
            private com.itheima.Person(java.lang.String)
            public com.itheima.Person(java.lang.String,int)
            public com.itheima.Person()
            *******************************************
            zhangSan***0
获取Class对象的Method方法:
    Method getMethod(String name, Class<?>... parameterTypes)
        返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
    Method[] getMethods()
        返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或
        接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的
        公共 member 方法。
    Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
    Method[] getDeclaredMethods()
        返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有
        方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
    演示:
        package com.itheima.reflect;

        import java.lang.reflect.Constructor;
        import java.lang.reflect.Method;
        /*
         * 步骤:
         * 1:获取字节码文件对象
         * 2:获取指定构造器,并调用newInstance()创建对象
         * 3:根据方法名获取Method对象
         * 4:调用方法访问该方法,如果该方法是非公有访问权限,需在访问之前调用setAccessble(boolean flag),
         *      并设为true
         * Object invoke(Object obj, Object... args)
         *     对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
         * */
        public class MethodTest {
            public static void main(String[] args) throws Exception {
                //获取字节码文件对象
                Class c = Class.forName("com.itheima.Person");
                //获取一个构造器,创建对象
                Constructor con = c.getDeclaredConstructor(String.class, int.class);
                Object obj = con.newInstance("Lisi", 24);
                //获取方法名为function的私有方法
                Method m1 = c.getDeclaredMethod("function", String.class, int.class);
                //私有的成员要使用暴力访问
                m1.setAccessible(true);
                //使用invoke()方法调用字节码文件对象的方法
                System.out.println(m1.invoke(obj, "WangWu", 32));
                //获取方法名为method的公有方法
                Method m2 = c.getMethod("method", String.class);
                //调用方法
                m2.invoke(obj, "ZhaoLiu");
            }
        }
        运行结果:
            WangWu***32***function
            show ZhaoLiu
获取Class对象的Field的方法:
    Field getField(String name)
        返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
    Field[] getFields()
        返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的
        所有可访问公共字段。
    Field getDeclaredField(String name)
        返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
    Field[] getDeclaredFields()
        返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的
        所有字段。
    演示:
        package com.itheima.reflect;
        import java.lang.reflect.Field;
        /*
         * 访问属性的步骤:
         * 1:获取字节码文件对象
         * 2:获取构造器,调用newInstance()方法创建对象
         * 3:获取属性名
         * 4:调用Field类的方法对属性进行操作
         *   a:Object get(Object obj)  返回指定对象上此 Field 表示的字段的值。
         *   b:void set(Object obj, Object value)
         *           将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
         *   如果访问非公有属性时,要使用setAccessble(boolean flag)方法,设置为true
         * */
        public class FieldTest {
            public static void main(String[] args) throws Exception {
                //获取字节码文件对象
                Class c = Class.forName("com.itheima.Person");
                //使用Person类的无参构造器创建对象
                Object obj = c.getConstructor().newInstance();
                //获取所有的属性对象
                Field[] fs = c.getDeclaredFields();
                for(Field item : fs)
                    System.out.println(item);
                //根据属性名,获取Field对象
                Field f = c.getDeclaredField("sex");
                //通过get()方法获取属性值
                //f.set(obj,"女");不能改变final修饰的属性值,否则运行时引发异常
                System.out.println(f.get(obj));
                Field f1 = c.getDeclaredField("age");
                f1.setAccessible(true);
                //给指定属性设置属性值
                f1.set(obj, 32);
                //获取属性值
                System.out.println(f1.get(obj));
            }
        }
        运行结果:
            private java.lang.String com.itheima.Person.name
            int com.itheima.Person.age
            public static final java.lang.String com.itheima.Person.sex
            public java.lang.String com.itheima.Person.address
            男
            32
    反射运用体现:
        package com.itheima.reflect;
        import java.lang.reflect.Method;
        import java.util.ArrayList;
        /*
         * 通过反射向集合添加元素
         * */
        public class ArrayListDemo {
            public static void main(String[] args) throws Exception {
                ArrayList<String> al = new ArrayList<String>();
                al.add("haha");
                //al.add(3);因为指定了集合只能添加String类型的元素,所以编译失败
                Class c = al.getClass();
                //把add()方法可以接收的类型改为Object类型,就可以添加任意类型的元素
                Method m = c.getMethod("add", Object.class);
                //向集合添加整形的元素
                m.invoke(al, 3);
                System.out.println(al);
            }
        }
        运行结果:[haha, 3]
使用反射操作数组:
    在java.util.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过
    使用Array来动态的操作数组。
    常用方法:
    static Object newInstance(Class<?> componentType, int length)
            创建一个具有指定的组件类型和长度的新数组。
    static Object get(Object array, int index) 返回指定数组对象中索引组件的值。操作引用数据类型
    static xxx getXxx(Object array, int index) 返回指定数组对象中索引组件的值。
            其中xxx是各种基本数据类型。
    static void set(Object array, int index, Object value)
            将指定数组对象中索引组件的值设置为指定的新值。操作引用数据类型
    static void setXxx(Object array, int index, xxx value)
            将指定数组对象中索引组件的值设置为指定的新值。其中xxx是基本数据类型
        演示:
            package com.itheima.reflect;
            import java.lang.reflect.Array;
            public class ArrayTest {
                public static void main(String[] args) {
                    //创建一个长度为5的String类型的数组
                    Object obj = Array.newInstance(String.class, 5);
                    //向数组添加元素
                    Array.set(obj, 0, "zhangSan");
                    Array.set(obj, 1, "liSi");
                    Array.set(obj, 2, "wangWu");
                    //调用get()方法,取出数组中的元素
                    Object str1 = Array.get(obj, 0);
                    Object str2 = Array.get(obj, 2);
                    System.out.println(str1);
                    System.out.println(str2);
                }
            }
        运行结果:
            zhangSan
            wangWu
使用反射获取泛型信息:
    通过指定类对应的Class对象,可以获得类中包含的Field,获得Field对象后,就可以很容易的
    获取该Field的数据类型:
        Class<?> a = f.getType();
    但这种方式只对普通类型的Field有效。如果该Field的类型是有泛型限制的类型,则不能准确的
    获得该Field的泛型参数。
    为了获得Field的泛型类型,应该使用getGenericType()方法,返回值类型Type
        如:Type t = f.getGenericType();
    Type是Java编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、
    类型变量和基本类型。
    然后将Type对象强制类型准换为ParameterizedType对象,ParameterizedType代表被参数化的类型,
    也就是增加了泛型限定的类型。它提供了两个方法:
        getRawType():返回没有泛型信息的原始类型
        getActualTypeArguments():返回泛型参数的类型。
        案例体现一:
            package com.itheima.generic;
            import java.lang.reflect.Field;
            import java.lang.reflect.ParameterizedType;
            import java.lang.reflect.Type;
            import java.util.ArrayList;
            import java.util.Map;

            public class GenericFieldTest {
                private ArrayList<String> al;
                private Map<String, Integer> m;
                public static void main(String[] args) throws Exception {
                    Class c = GenericFieldTest.class;
                    Field f = c.getDeclaredField("al");
                    //直接使用getType()方法,来查看al的类型,但这种方法不能
                    //查看到泛型的类型
                    System.out.println(f.getType());
                    //通过getGenericType()方法,获取Field实例f的泛型类型
                    Type t = f.getGenericType();
                    //强制类型转换
                    ParameterizedType pt = (ParameterizedType)t;
                    System.out.println(pt);
                    //获取Map的Field对象
                    f = c.getDeclaredField("m");
                    //获取f的泛型类型
                    Type t1 = f.getGenericType();
                    //强制类型转换
                    pt = (ParameterizedType)t1;
                    Type[] arg = pt.getActualTypeArguments();
                    for(Type item: arg)
                        System.out.println(item);
                }
            }
        运行结果:
            class java.util.ArrayList
            java.util.ArrayList<java.lang.String>
            class java.lang.String
            class java.lang.Integer
        案例体现二:
            package com.itheima.generic;
            import java.lang.reflect.Method;
            import java.lang.reflect.ParameterizedType;
            import java.lang.reflect.Type;
            import java.util.ArrayList;
            public class GenericTest {
                public static void main(String[] args) throws Exception
                {
                    ArrayList<String> al = new ArrayList<String>();
                    Method m = GenericTest.class.getMethod("test", ArrayList.class);
                    Type[] t = m.getGenericParameterTypes();
                    ParameterizedType pt = (ParameterizedType)t[0];
                    System.out.println(pt.getRawType());
                    System.out.println(pt.getActualTypeArguments()[0]);
                }

                public static void test(ArrayList<String> al)
                {   
                }
            }
        运行结果:
            class java.util.ArrayList
            class java.lang.String

反射的作用-->实现框架功能
    框架和框架要解决的核心问题:
        把框架比做成为装修的房子,用户需要给房子做装修。
    框架与工具类的区别:
        工具类被用户类调用,用户类被框架调用。因为框架先存在,无法知道要调用的类,所以
        无法通过new关键字来创建对象,因此要用反射来做
    案例体现:
        package com.itheima.reflect;
        import java.io.FileInputStream;
        import java.io.InputStream;
        import java.util.Collection;
        import java.util.Properties;
        import com.itheima.Person;
        /*
         * 通过反射向集合添加元素
         * 配置文件中的内容:className = java.util.HashSet
         * 1:配置文件 -->创建输入流对象-->创建Properties对象-->使用load()方法
         *    把文件加载到集合中-->通过getProperties()方法获取配置信息
         * 2:根据获得的配置信息,并通过newInstance()方法,创建集合对象
         * 3:向集合添加元素
         */
        public class ReflectDemo {
            public static void main(String[] args) throws Exception {
                InputStream is = new FileInputStream("config.properties");
                Properties prop = new Properties();
                prop.load(is);
                is.close();
                String className = prop.getProperty("className");
                //创建集合对象
                Collection coll = (Collection)Class.forName(className).newInstance();
                coll.add(new Person("zhangSan", 23));
                coll.add(new Person("liSi", 21));
                coll.add(new Person("wangWu", 26));
                coll.add(new Person("liSi", 21));
                System.out.println(coll);
            }
        }
        好处:如果想使用不同的集合来盛装元素,那么就可以不用改源码,直接改配置文件中的信息
JavaBean:
    JavaBean是java的特殊类,主要用于传递数据信息,这种java类中的方法主要用于访问私有字段,
    且方法名符合命名规范,JavaBean的实例对象通常称之为"值对象"。
JavaBean的应用:
    1、在javaEE开发中,经常要使用javaBean,很多环境都要求按JavaBean方式操作
    2、Java提供了对javaBean操作的类-->PropertyDescriptor 描述JavaBean
       通过一对存储器方法导出的一个属性。
构造方法摘要
    PropertyDescriptor(String propertyName, Class<?> beanClass)
        通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个
        PropertyDescriptor。

常用方法:
    Method getReadMethod() 获得应该用于读取属性值的方法。
    Method getWriteMethod() 获得应该用于写入属性值的方法。
    演示:
        package com.itheima.reflect;
        /*
         * 创建一个JavaBean类
         * */
        public class Description {
            private String name;
            private int age;
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public int getAge() {
                return age;
            }
            public void setAge(int age) {
                this.age = age;
            }
        }
        package com.itheima.reflect;
        import java.beans.PropertyDescriptor;
        import java.lang.reflect.Method;
        /*
         * 步骤:
         *  1:根据属性名和所属的字节码文件对象创建PropertyDescription对象
         *  2:调用PropertyDescription对象的方法,获取Method对象
         *  3:通过Method对象的invoke()方法来调用字节码文件对象的方法
         * */

        public class BeanDemo {
            public static void main(String[] args) throws Exception {
                Description d = new Description();
                //定义变量,保持属性名
                String propName = "name";
                //根据属性名和所属的字节码文件对象创建PropertyDescription对象
                PropertyDescriptor pd = new PropertyDescriptor(propName, d.getClass());
                //通过PropertyDescription的getReadMethod()方法,获得Method对象。
                Method m = pd.getReadMethod();
                //通过Method对象的invoke()方法来调用字节码文件对象的方法
                System.out.println(m.invoke(d));
                m = pd.getWriteMethod();
                m.invoke(d, "zhangSan");
                m = pd.getReadMethod();
                System.out.println(m.invoke(d));
            }
        }
        运行结果:
            null
            zhangSan

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值