• 反射机制(草稿)


    反射机制作用:
    通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件)
    通过反射机制可以操作代码片段。

    反射机制的相关类在java.lang.reflect.*包下

    反射机制相关的重要的类:

    java.lang.Class 代表整个字节码,代表一个类型,代表整个类

    java.lang.reflect.Method 代表字节码中的方法字节码,代表类中的方法

    java.lang.reflect.Constructor 代表字节码中的构造方法字节码,代表类中的构造方法

    java.lang.reflect.Field代表字节码中的属性字节码,代表类中的成员变量

    public class User{
    	//Field
    	int no;
    	//Constructor
    	public User(int no){this.no=no}
    	//Method
    	public int getNo(){
    	return no;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    /*
    * 操作一个类的字节码,需要获得这个类的字节码。获取java.lang.Class实例
    * 有三种方式
    * 1.Class.forName()
    *   1)静态方法
    *   2)方法的参数是一个字符串
    *   3)完整类名必须带有包名,java.lang不能省略
    * */
    public class Test01 {
        public static void main(String[] args) {
            Class c1=null;
            try {
                 c1=Class.forName("java.lang.String");//c1代表string.class文件,c1代表string类型
                Class c2=Class.forName("java.lang.Date");//c2代表Date类型
                Class c3=Class.forName("java.lang.Integer");
                Class c4=Class.forName("java.lang.System");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        //方式2.java中任何一个对象都有一个方法getClass()
    
            String s="abc";
            Class x=s.getClass();//x代表string.class字节码文件,x代表string类型
            System.out.println(c1==x);//true(==判断的是对象的内存地址,这两个变量中保存的内存地址是一样的,都指向方法区中的字节码)
    
        //方式3.java语言中任何一种类型,包括基本数据类型,都有.class属性。
            Class y=String.class;//y代表string类型
            Class k= Date.class;//k代表Date类型
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    public class Test01 {
        public static void main(String[] args) {
    
            try {
                //通过反射机制获取Class,通过Class来实例化对象
                Class c=Class.forName("reflect.User");//c代表User类型
                //newInstance()这个方法会调用User这个类的无参构造方法,完成对象的创建。
                Object obj= c.newInstance();
                System.out.println(obj);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    /*验证反射机制的灵活性
    *   java代码写一遍,在不改变java源代码的基础上,可以做到不同对象的实例化(properties文件里的对象类型随便改,反射代码不用变),非常灵活。
    * 符合OCP开闭原则,对扩展开放,对修改关闭
    * */
    public class Test01 {
        public static void main(String[] args) throws Exception {
        //通过IO流读取classinfo.properties文件
            FileReader reader=new FileReader("JavaBase/classinfo.properties");
            //创建属性类对象Map
            Properties pro=new Properties();//key values都是string
            //加载
            pro.load(reader);
            //关闭流
            reader.close();
    
            //通过key获取value
            String className=pro.getProperty("className");
            //System.out.println(className);//结果:reflect.User
    
            //通过反射机制实例化对象
            Class c=Class.forName(className);
            Object obj=c.newInstance();
            System.out.println(obj);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    /*
    * 研究Class.forName()发生了什么
    * 如果只希望一个类的静态代码块执行,其他代码一律不执行。
    * 可以使用Class.forName("完整类名");
    * 这个方法的执行会导致类加载,静态代码块执行
    * */
    public class Test01 {
        public static void main(String[] args)  {
            try {
                //Class.forName()这个方法的执行会导致:类加载
                Class.forName("reflect.MyClass");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    class MyClass{
        //静态代码块在类加载时执行且只执行一次
        static{
            System.out.println("MyClass类的静态代码块执行了");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    /*
    研究文件路径
    该文件必须在类路径下(类路径:在src下的就是在类路径下)
    src是类的根路径
    * */
    public class Test01 {
        public static void main(String[] args) throws Exception {
            /*
            * Thread.currentThread()当前线程对象
            *getContextClassLoader()是线程对象的方法,可以获取到当前线程的类加载器对象
            * getResource()[获取资源]这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
            * */
        String path=Thread.currentThread().getContextClassLoader().getResource("classinfo.properties").getPath();
        //采用以上方式可以拿到一个文件的绝对路径
            ///C:/Users/user/IdeaProjects/test/out/production/JavaBase/classinfo.properties
        System.out.println(path);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    public class Test01 {
        public static void main(String[] args) throws Exception {
            //获取文件绝对路径
            //方式1
    /*        String path = Thread.currentThread().getContextClassLoader()
                    .getResource("classinfo.properties").getPath();
            FileReader reader=new FileReader(path);*/
            //方式2 直接以流的形式传送(不需要直接路径)
            InputStream reader=Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo.properties");
            Properties pro=new Properties();
            pro.load(reader);
            reader.close();
            //通过key 获取value
            String className=pro.getProperty("className");
            System.out.println(className);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    /*
    * java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容
    * 使用以下方式的时候,属性配置文件必须放在类路径下
    * */
    public class Test01 {
        public static void main(String[] args) {
        //资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下,文件扩展名必须是properties
            //并且在写路径的时候,路径后面的扩展名不能写
            ResourceBundle bundle=ResourceBundle.getBundle("classinfo");
    
            String className=bundle.getString("className");
            System.out.println(className);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    关于JDK中自带的类加载器
    什么是类加载器?
    启动类加载器
    扩展类加载器
    应用类加载器

    假设有这样一段代码:
    string s=“abc”;

    代码在开始执行之前,会将所需要类全部加载到JVM中。
    通过类加载器加载,看到以上代码类加载器会找string.class文件,找到就加载,那么是怎么进行加载的呢?

    首先通过“启动类加载器”加载。
    注意:启动类加载器加载JDK最核心的类库

    如果通过“启动类加载器”加载不到的时候
    会通过“扩展类加载器”加载。

    如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载。
    注意:应用类加载器专门加载:classpath中的jar包(class文件。)

    java中为了保证类加载的安全,使用双亲委派机制。
    优先从启动类加载器中加载,称为<父>
    父中无法加载到,再从扩展类加载器中加载,
    称为<母>。双亲委派,如果都加载不到,才会考虑从应用加载器中加载。

    分割一下

    /*反射Student类中所有Field*/
    public class Test01 {
        public static void main(String[] args) throws ClassNotFoundException {
            //获取整个类
            Class studentClass=Class.forName("reflect.Student");
            String className=studentClass.getName();
            System.out.println("完整类名"+className);
    
            String simpleName=studentClass.getSimpleName();
            System.out.println("简类名"+simpleName);
            //获取类中所有public修饰的Field
            Field[] fields=studentClass.getFields();
            System.out.println(fields.length);//
            //取出这个field
            Field f=fields[0];
            //取出这个field的名字
            String fieldName=f.getName();
            System.out.println(fieldName);
    
            //获取所有的Field
            Field[] fs=studentClass.getDeclaredFields();
            System.out.println(fs.length);//4
            //遍历
            for(Field field:fs){
                //获取属性修饰符列表
                int i=field.getModifiers();
                System.out.println(i);//返回的修饰符是一个数字,每个数字是修饰符的代号
                //可将代号转换成字符串
                String modifierString= Modifier.toString(i);
                System.out.println(modifierString);
                //获取属性的类型
                Class fieldType=field.getType();
               //完整类型名
                // String fName=fieldType.getName();
                //简单类型名
                String fName=fieldType.getSimpleName();
                System.out.println(fName);
                //获取属性的名字
                System.out.println(field.getName());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    //反射属性Field
    public class Student {
        //Field翻译为字段,就是属性/成员
        //4个Field分别采用了不同的访问控制权限修饰符
        public int no;//field对象
        private String name;
        protected int age;
        boolean sex;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    反编译

    /*通过反射机制,反编译一个类的属性Field*/
    public class Test01 {
        public static void main(String[] args) throws ClassNotFoundException {
        //创建这个是为了拼接字符串
            StringBuilder s=new StringBuilder();
    
            Class studentClass=Class.forName("java.lang.Thread");
    
            s.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n");
            Field[] fields=studentClass.getDeclaredFields();
            for(Field field:fields){
                s.append("\t");
                s.append(Modifier.toString(field.getModifiers()));
                s.append(" ");
                s.append(field.getType().getSimpleName());
                s.append(" ");
                s.append(field.getName());
                s.append(";\n");
            }
            s.append(")");
            System.out.println(s);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    /*
    * 怎样通过反射机制访问一个java对象的属性?
    * 给属性赋值set
    * 获取属性的值get
    * */
    public class Test01 {
        public static void main(String[] args) throws Exception {
           //不使用反射机制访问对象的属性
            Student s=new Student();
           //给对象赋值
            s.no=111;
            //读属性值
            //两个要素
            System.out.println(s.no);
    
            //使用反射机制访问对象的属性(set get)
            Class studentClass=Class.forName("reflect.Student");
            Object obj=studentClass.newInstance();//obj就是student对象
            //获取no属性(根据属性的名称来获取field)
            Field noField=studentClass.getDeclaredField("no");
    
            //给obj对象(Student对象)的no属性赋值
            /*
            * 三要素:对象 属性 值
            * */
            noField.set(obj,22);
            //读取属性的值
            //两个要素:获取obj对象的no属性的值
            noField.get(obj);
    
            /*
            * 如果想要访问私有属性,可以使用打破封装的方式
            * */
            Field nameField=studentClass.getDeclaredField("name");
            //打破封装(这就是反射机制的缺点,会给不法分子留下机会)
            nameField.setAccessible(true);
            //给name属性赋值
            nameField.set(obj,"jackson");
            //获取name属性的值
            System.out.println(nameField.get(obj));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    /*
    * 可变长度参数
    * 语法是:类型...
    1.可变长度参数要求参数个数:0~N个
    2.可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有一个
    3.可变长度参数可以当做一个数组来看待
     */
    public class Test01 {
        public static void main(String[] args) throws Exception {
           m();
           m(10);
           m3("abc","def","ghjm");
           String[] strs={"asfd","dsfcdv"};
           //也可以传一个数组
           m3(strs);
        }
        public static void m(int...args){
            System.out.println("执行m方法");
        }
        //必须在最后,只能有一个
        public static void m2(int a,String...args){}
    
        public static void m3(String... args){
            //args有length属性,说明args是一个数组
            //可将可变长度参数视为一个数组
            for (int i = 0; i < args.length; i++) {
                System.out.println(args[i]);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    /*
    * 反射Method
    * */
    public class Test01 {
        public static void main(String[] args) throws ClassNotFoundException {
            //获取类
            Class userServiceClass=Class.forName("reflect.UserService");
    
            //获取所有的Method(包括私有)
            Method[] methods=userServiceClass.getDeclaredMethods();
            System.out.println(methods.length);//2
    
            //遍历Method
            for(Method method:methods){
                //获取修饰符列表
                System.out.println(Modifier.toString(method.getModifiers()));
                //获取方法的返回值类型
                System.out.println(method.getReturnType().getSimpleName());
                //获取方法名
                System.out.println(method.getName());
                //方法的修饰符列表(一个方法的参数可能会有多个
                Class[] parameterTypes=method.getParameterTypes();
                for(Class parameterType:parameterTypes){
                    System.out.println(parameterType.getSimpleName());
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    /*
    * 用户业务类
    * */
    public class UserService {
        /***
         * 登录方法
         * @param name 用户名
         * @param password 密码
         * @return TRUE表示登陆成功,FALSE表示登陆失败
         */
        public boolean login(String name,String password){
            if("admin".equals(name)&&"123".equals(password)){
                return true;
            }
            return false;
        }
        /**
         * 退出登录的方法
         */
        public void logout(){
            System.out.println("系统已安全退出");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    /*
    * 通过反射机制调用一个对象的方法
    * 反射机制让代码更具通用性。
    * */
    public class Test01 {
        public static void main(String[] args) throws Exception {
            Class userServiceClass=Class.forName("reflect.UserService");
            //不用反射机制,调用方法
            //创建对象
            UserService userService=new UserService();
            //调用方法
            boolean loginSuccess=userService.login("admin","123");
            System.out.println(loginSuccess?"登陆成功":"登陆失败");
    
            /*******************************************************/
            //创建对象
            Object obj=userServiceClass.newInstance();
            //获取Method
            Method loginMethod=userServiceClass.getDeclaredMethod("login",String.class,String.class);
            //调用方法(要素:对象 方法名 实参列表 返回值)
            //invoke-----调用
            /**
             * loginMethod 方法
             * obj 对象
             * "admin","123"实参
             * retValue返回值
             */
            Object retValue=loginMethod.invoke(obj,"admin","123");
            System.out.println(retValue);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    【游戏建模全流程】Maya风格化模型制作教程
    SpringMVC源码分析(三)HandlerExceptionResolver启动和异常处理源码分析
    期货平盘(期货大单压盘)
    .net项目部署Docker
    IDEA 关闭SpringBoot启动Logo/图标
    手把手写深度学习(15):在Hugging Face上构建自己的语料库
    在仿真环境中运行lio-sam
    【公众号开发】图像文字识别 · 模板消息推送 · 素材管理 · 带参数二维码的生成与事件的处理
    Nacos 小bug: application.properties配置未生效,导致端口未生效
    这么多年,Android 虚拟机到底干了些什么?
  • 原文地址:https://blog.csdn.net/misakisaigao/article/details/127816624