黑马程序员技术交流社区

标题: 【黑马程序员杭州】深度解析java中的反射 [打印本页]

作者: 小江哥    时间: 2017-11-21 16:00
标题: 【黑马程序员杭州】深度解析java中的反射
本帖最后由 小江哥 于 2017-11-21 16:00 编辑

1.在研究反射之前,我们先看看类加载器的相关内容,他是反射的基石.
1.1什么是类加载器
   类加载器是负责将.class文件加载到内在中,并为之生成对应的Class对象,也就是字节码文件对象。
1.2类加载器有哪些?(我们来看代码)
[Java] 纯文本查看 复制代码
/*
* 演示:类加载的种类:
* 获取类的类加载器:
*          ClassLoader getClassLoader()返回该类的类加载器。
* 获取类加载器的父亲:
*          loader.getParent()返回委托的父类加载器
*/
public class ClassLoaderDemo01 {
        public static void main(String[] args) {
                // 获取字节码:
                ClassLoader loader = ClassLoaderDemo01.class.getClassLoader();
                System.out.println(loader);// sun.misc.Launcher$AppClassLoader@f52d950
               
                // 获取父类加载器:
                ClassLoader parent = loader.getParent();
                System.out.println(parent);// sun.misc.Launcher$ExtClassLoader@f52d950
               
                // 获取爷爷加载器
                ClassLoader grandpa = parent.getParent();
                System.out.println(grandpa);// null ,因为BootStrap是顶级类加载器,不是用Java写的。看不到。
               
                ClassLoader loader2 = System.class.getClassLoader();
                System.out.println(loader2);// null
        }
}
有代码运行结果,我们知道了,一共有三个类加载器.
1.3什么是全盘委托机制

我们自己定义了一个类

而系统的rt.jar中也有一个一模一样的类:
我们编译发现没有任何问题调用的是当前自定义类
结果运行报错

我们在编译的时候没有加载类此时编译器就认为我们当前用的是自定义类当我们正真运行的时候就会调用类加载器根据全盘负责委托机制委托父类去加载最终BootStrap在自己的底盘找到了自己的MimeType所以加载的是系统的类不是我们自定义的运行的时候肯定没有show方法所以报错
1.4.自定义类加载器
我们写了自定义类加载器加载我们的类

结果运行发现又报错了

等号的左侧是直接引用MimeType所以就会全盘负责委托机制APPClassLoader加载再去找爷爷于是加载的就是系统的MimeType
等号的右侧我们自己写的类加载器自己加载自己的类肯定是我们定义的MimeType内存中就出现了两份MimeType肯定不是一个类报类型转换异常就很正常了!!注意两个不同的类加载器如果加载了同一个class文件也会在内存中生成两个字节码这样就会出问题!!
1.5类加载器的注意事项:
2.反射
反射的基石 Class对象Class中包含了类中的所有信息:类名称类修饰符类所在包 ;类中的字段 Field ; 类中的构造函数Constructor ;类中的成员方法Method
反射:就是通过Class对象获取Class中的所有信息,比如FieldConstructorMethod。获取之后有什么用?怎么用?这是反射要学习的要点。
2.1获取Class对象的方式
[JavaFX] 纯文本查看 复制代码
/*
* 演示:三种获取Class的方式:
* 方式1:类名.class
* 方式2:对象.getClass()
* 方式3:Class.forName(String classname)必须传类的全名!!
*/
public class ReflectDemo01 {
        public static void main(String[] args) throws ClassNotFoundException {
                // 方式1:类名.class
                Class c1 = String.class;
                System.out.println(c1.getSimpleName());
                System.out.println(c1.getName());
               
                // 方式2:对象.getClass()
                Class c2 = "".getClass();
                System.out.println(c2.getSimpleName());
                System.out.println(c2.getName());
               
                // 方式3:Class.forName(String classname)
                Class c3 = Class.forName("java.lang.String");
                System.out.println(c3.getSimpleName());
                System.out.println(c3.getName());
               
                System.out.println(c1 == c2);// true
                System.out.println(c1 == c3);// true
               
                System.out.println(int.class);
                System.out.println(void.class);
        }
}

首先我们先定义一个学生类
[Java] 纯文本查看 复制代码
package cn.itcast.reflect;

@SuppressWarnings("all")
public class Student {
        private String name;
        public int age;
        private String addr;
        
        public Student() {
                super();
        }
        
        public Student(String name, int age, String addr) {
                super();
                this.name = name;
                this.age = age;
                this.addr = addr;
        }
        
        private static void show(){
                System.out.println("show ... run");
        }
        
        public void show(String desc){
                System.out.println(name + " :" + desc);
        }
        
        @Override
        public String toString() {
                return "Student [name=" + name + ", age=" + age + ", addr=" + addr + "]";
        }
}
2.2反射获取构造函数
[Java] 纯文本查看 复制代码
/*
* 演示:反射获取构造函数:
*         Constructor[] getConstructors() 获取所有公共的构造函数
*         Constructor[] getDeclaredConstructors() 获取所有声明的构造函数
*         Constructor getConstructor(Class ... parameterTypes)获取指定的公共构造函数
*         Constructor getDeclaredConstructor(Class ... parameterTypes) 获取指定的声明的构造函数
* 创建对象:
*         T newInstance(Object... initargs)   使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例
*
* 如果一个类有公共的空参构造,此时,你就可以直接用字节码文件对象.newInstance()来创建对象
*/
public class ReflectDemo02 {
        public static void main(String[] args) throws Exception {
                Class clazz = Student.class;
                // Constructor[] getConstructors() 获取所有公共的构造函数
                // Constructor[] cons = clazz.getConstructors();
                // for (Constructor con : cons) {
                // System.out.println(con);// public cn.itcast.reflect.Student()
                // }
                // Constructor[] getDeclaredConstructors() 获取所有声明的构造函数
                // Constructor[] cons = clazz.getDeclaredConstructors();
                // for (Constructor con : cons) {
                // System.out.println(con);
                // }

                // Constructor getConstructor(Class ... parameterTypes)获取指定的公共构造函数
                Constructor con = clazz.getConstructor();
                System.out.println(con);// public cn.itcast.reflect.Student()

                // Constructor getDeclaredConstructor(Class ... parameterTypes)
                // 获取指定的声明的构造函数
                // 获取私有的private Student(String name, int age, String addr)
                Constructor con2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
                System.out.println(con2);

                // T newInstance(Object... initargs) 使用此 Constructor
                // 对象表示的构造方法来创建该构造方法的声明类的新实例,
                // 空参构造创建对象
                Student s1 = (Student) con.newInstance();
                System.out.println(s1);

                // 有参构造
                // 暴力访问:
                con2.setAccessible(true);
                Student s2 = (Student) con2.newInstance("小江哥", 21, "杭州");
                System.out.println(s2);
               
                // 通过字节码文件对象,直接生产本类对象
                Object obj = clazz.newInstance();
                System.out.println(obj);
        }
}

2.3反射获取成员变量
[Java] 纯文本查看 复制代码
/*
* 演示:反射获取成员变量:
*         Field[] getFields()获取所有公共字段
*         Field[] getDeclaredFields() 获取所有声明字段
*         Field getField(String name) 获取指定的公共字段
*         Field getDeclaredField(String name) 获取指定任意字段
* 获取字段值或设置字段值
*         set(Object obj, Object value)将obj对象上此 Field 表示的字段设置为指定的值
*         Object get(Object obj)获取指定对象上当前字段的值
*/
public class ReflectDemo03 {
        public static void main(String[] args) throws Exception {
                // 我们要反射的对象
                Student s = new Student("小江哥", 21, "杭州");
                // 获取字节码
                Class clazz = s.getClass();
               
                // Field[] getFields()获取所有公共字段
//                Field[] fields = clazz.getFields();
//                for (Field f : fields) {
//                        System.out.println(f);// public int cn.itcast.reflect.Student.age
//                }
               
                // Field[] getDeclaredFields() 获取所有声明字段
//                Field[] fields = clazz.getDeclaredFields();
//                for (Field f : fields) {
//                        System.out.println(f);
//                }
               
                // Field getField(String name) 获取指定的公共字段
                Field f1 = clazz.getField("age");
                System.out.println(f1);// public int cn.itcast.reflect.Student.age
               
                // Field getDeclaredField(String name) 获取指定任意字段
                Field f2 = clazz.getDeclaredField("addr");
                System.out.println(f2);// private java.lang.String cn.itcast.reflect.Student.addr
               
                // 获取字段值:
                int age = (int) f1.get(s);
                System.out.println(age);// 21
               
                // 修改字段值
                f2.setAccessible(true);
                f2.set(s, "北京");
                System.out.println(s);// Student [name=小江哥, age=21, addr=北京]
        }
}

2.4反射获取成员方法
[Java] 纯文本查看 复制代码
/*
* 演示:反射获取成员方法:
*         Method[] getMethods() 获取所有公共成员方法,包含继承自父类的
*         Method[] getDeclaredMethods() 获取所有声明的方法,只要自己声明的方法。
*         Method getMethod(String methodName, Class ... parameterTypes) 获取指定的公共方法
*         Method getDeclaredMethod(String methodName, Class ... parameterTypes) 获取指定的声明方法
* 调用方法
*         Object invoke(Object obj, Object... args) 调用指定对象obj的当前method表示的方法
*/
public class ReflectDemo04 {
        public static void main(String[] args) throws Exception {
                Student s = new Student("小江哥", 21, "杭州");
                Class clazz = s.getClass();
                // Method[] getMethods() 获取所有公共成员方法
//                Method[] methods = clazz.getMethods();
//                for (Method m : methods) {
//                        System.out.println(m);
//                }
                // Method[] getDeclaredMethods() 获取所有声明的方法
//                Method[] methods = clazz.getDeclaredMethods();
//                for (Method m : methods) {
//                        System.out.println(m);
//                }
               
                // Method getMethod(String methodName, Class ... parameterTypes) 获取指定的公共方法
                // public void show(String desc)
                Method m = clazz.getMethod("show", String.class);
                System.out.println(m);// public void cn.itcast.reflect.Student.show(java.lang.String)
               
                // Method getDeclaredMethod(String methodName, Class ... parameterTypes) 获取指定的声明方法
                Method m2 = clazz.getDeclaredMethod("show");
                System.out.println(m2);// private static void cn.itcast.reflect.Student.show()
               
                // 举例:我们调用show(String desc)
                // 非反射调用:
                s.show("一个技术宅!");
                // 反射调用
                m.invoke(s, "一个胸怀宽广的人!");
                // Object invoke(Object obj, Object... args) 调用指定对象obj的当前method表示的方法
               
                // 演示:无参静态方法        private static void show()
                m2.setAccessible(true);
                m2.invoke(null);
               
                // 带返回值方法
                Method m3 = clazz.getMethod("toString");
                String str = (String) m3.invoke(s);
                System.out.println(str);
        }
}

2.5实战
用反射模拟BeanUtilspopulate方法
[Java] 纯文本查看 复制代码
/*
* 演示:用反射模拟一个BeanUtils的populate方法
*/
public class ReflectDemo05 {
        public static void main(String[] args) throws Exception {
                Student s = new Student();
                Map<String,Object> m = new HashMap<>();
                m.put("name", "小江哥");
                m.put("age", 21);
                m.put("addr", "杭州");
               
                myPopulate(s, m);
               
                System.out.println(s);// Student [name=小江哥, age=21, addr=杭州]
        }
        /*
         * 定义一个功能:实现把一个Map中的属性,设置到一个对象中
         */
        public static void myPopulate(Object o, Map<String,Object> m) throws Exception{
                // 获取对象的字节码文件对象
                Class<?> clazz = o.getClass();
                // 反射获取所有成员变量数组
                Field[] fields = clazz.getDeclaredFields();
                // 循环遍历,取出每个成员
                for (Field f : fields) {
                        // 获取成员的名字
                        String name = f.getName();
                        // 从map中根据键找值
                        Object value = m.get(name);
                        if( value != null){
                                // 把这个值设置给当前字段
                                f.setAccessible(true);
                                f.set(o, value);
                        }
                }
        }
}

     最后相信大家对反射和类加载机制有了一定的了解,我们下次再见.




作者: wheat    时间: 2017-11-21 16:01
厉害啊

作者: 小年青    时间: 2017-11-21 16:03
很有用很有用
作者: 猫先森    时间: 2017-11-21 16:03
哇哇哇哇,涨知识!
作者: huangjinchao.ar    时间: 2017-11-21 16:04
哇塞,干货干货
作者: 咸鱼666    时间: 2017-11-21 16:04
很不错,非产有用
作者: 张艺凡老师    时间: 2017-11-21 16:05
厉害厉害
作者: 采姑娘的小蘑菇    时间: 2017-11-21 16:05
干货干货
作者: PHP老曹    时间: 2017-11-21 16:07
正在学习JAVA,你的帖子受用的很,加油!
作者: 鱼丸儿    时间: 2017-11-21 16:09
果然有用
作者: 小浙姐姐    时间: 2017-11-21 16:10
干货6666666
作者: 陈君    时间: 2017-11-21 16:11
干货6666

作者: 许言    时间: 2017-11-21 16:11
顶顶顶顶顶
作者: cj1234    时间: 2017-11-21 16:12
6666

作者: swallow25715902    时间: 2017-11-21 16:12
不明觉厉!
作者: 慧-wh    时间: 2017-11-21 16:14
厉害啊,必须顶!666
作者: nhm    时间: 2017-11-21 16:17
666666666666
作者: fangmifang618    时间: 2017-11-21 16:19
感觉很厉害啊
作者: 黑马6666    时间: 2017-11-21 16:33
厉害了我的哥
作者: 秋子菇凉    时间: 2017-11-21 16:59
不明觉厉!加油!
作者: 程代明    时间: 2017-11-21 17:46
very good !
作者: 阿布乙    时间: 2017-11-21 18:05
这个很赞啊!厉害厉害
作者: battlexing    时间: 2017-11-22 04:11
markmarkmark

作者: 奔跑地小少女    时间: 2017-11-22 14:35
不明觉厉
作者: 刘华莎    时间: 2017-11-22 15:52
棒棒哒
作者: fanbuer    时间: 2017-11-22 16:22
不错不错,学到了^_^
作者: 小核桃仁    时间: 2017-11-22 16:29
厉害厉害




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2