zoukankan      html  css  js  c++  java
  • Java 反射机制

    1. 反射介绍

      Java反射机制是在运行状态中,对于任意一个类,能够获取类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,可以动态获取信息以及动态调用对象的方法。

      Class类

      Class是一个类,封装了当前对象所对应的类的信息。一个类中有属性,方法,构造器等。比如有一个Person类,一个Order类,一个Book类,这些都是不同的类,可以用Class描述类,它有类名,属性,方法,构造器等。Class是用来描述类的类。

      当我们获取Class类后,就可以通过Class类获取类对象,获取类中的属性和方法。

    2. 获取Class对象的三种方式

      (1)Class.forName()  静态方法,返回类,参数是完整的包名;

      (2)创建一个对象,对象调用getClass(),Object类中继承的方法;

      (3)任何一个类型都有class属性;

    public class ReflectTest01{
        public static void main(String[] args){
            Class c1 = null;
            Class c2 = null;
            //第一种
            try{
                c1 = Class.forName("java.lang.String");  //获取String类型类对象
                c2 = Class.forName("java.util.Date");    //获取Date类型类对象
                Class c3 = Class.forName("java.lang.Integer");    //获取Integer类型类对象
                Class c4 = Class.forName("java.lang.System");   
                System.out.println(c1);
                System.out.println(c2);
            }catch(ClassNotFoundException e){
                e.printStackTrace();
            }
            
            //第二种
            String s = "abc";
            System.out.println(s.getClass() == c1);  //true
            Date d = new Date();
            System.out.println(d.getClass() == c2);  //true
            
            //第三种
            Class x = String.class;
            Class y = Date.class;
            Class z = int.class;
            System.out.println(x == c1);    //true
        }
    }

      使用反射创建对象

    public class User{
        public User(){
            System.out.println("无参数构造方法");
        }
        public User(String s){
            System.out.println("有参数构造方法");
        }
    }
    
    public class ReflectTest02{
        public static void main(String[] args){
            //1. 传统创建对象的方法
            User u = new User();
            System.out.println(u);
            
            //2. 使用反射创建对象;
            try{
                Class c = Class.forName("com.reflect.test.User");
                Object o = c.newInstance();  //调用无参构造方法,如果没有无参构造方法,就会抛出异常;
                System.out.println(o);
            }catch(Exception e){
                e.printStackTrace();
            }
            
            
        }
    }

      

      使用反射可以更加灵活的实例化对象

    // classinfo.properties
    className=com.reflect.test.User
    
    public class ReflectTest03{
        public static void main(String[] args){
            try{
                FileReader fr = new FileReader("C:\Users\THINK\Desktop\JavaTest\ReflectTest\classinfo.properties");
                Properties p = new Properties();
                p.load(fr);
                fr.close();
            
                String classname = p.getProperty("className");
                Class c = Class.forName(classname);
                Object o = c.newInstance();
                System.out.println(o);
            }catch(Exception e){
                e.printStackTrace();
            }
            
        }
    }

      使用属性配置文件,只需要修改属性配置文件的value值,就可以实例化。

      

      关于forName()方法的执行时机

      forName()方法执行时类加载时执行,执行类的静态代码块

    package com.reflect.test;
    
    public class MyClass{
        static{
            System.out.println("类的静态代码块执行了");
        }
    }
    
    public class ReflectTest04{
        public static void main(String[] args){
            try{
                Class.forName("com.reflect.test.MyClass");  //静态代码块是在类加载时执行
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    
    //运行结果
    //类的静态代码块执行了

    3. 获取文件绝对路径

      文件必须从根路径下开始(即使用IDEA创建项目后,从src路径下开始)

      currentThread()  获取当前线程对象

      getContextClassLoader()  获取当前线程的类加载器对象

      getResource()  获取资源,默认从根路径下开始加载资源

    public class FilePathTest{
        public static void main(String[] args){
            String path = Thread.currentThread().getContextClassLoader().getResource("classinfo.properties").getPath();
            System.out.println(path); ///C:/Users/THINK/Desktop/JavaTest/ReflectTest/classinfo.properties
            
            String path1 = Thread.currentThread().getContextClassLoader().getResource("com/reflect/test/MyClass.class").getPath();
            System.out.println(path1); ///C:/Users/THINK/Desktop/JavaTest/ReflectTest/com/reflect/test/MyClass.class
        }
    }

      

      以文件绝对路径形式读取文件然后加载流

    public class IoPropertiesTest{
        public static void main(String[] args) throws Exception{
            //第一种:读取后转化为字符流
            String path = Thread.currentThread().getContextClassLoader().getResource("com/reflect/test/db.properties").getPath();
            FileReader fr = new FileReader(path);
            System.out.println(path);
            
            //第二种:直接读取字节流
            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/reflect/test/db.properties");
            Properties p = new Properties();
            p.load(is);
            is.close();
            
            String user = p.getProperty("user");
            System.out.println(user);
        }
    }

      使用资源绑定器读取文件

      注意:属性配置文件不能带后缀(.properites);属性文件路径从根路径下开始;

    public class ResourceBundleTest{
        public static void main(String[] args){
            ResourceBundle rb = ResourceBundle.getBundle("com/reflect/test/db");
            String user = rb.getString("user");
            System.out.println(user);
        }
    }

    4. JVM类加载机制

      类加载是用来把类(class)装载到JVM的。JVM定义的类加载器:

      Bootstap Classloader:引导类加载器,用C++编写,是JVM自带的类加载器,负责Java平台核心库,用来装载核心库,该加载器无法直接获取。

      Extension Classloader:扩展类加载器,负责jdk home/lib/ext目录下的jar包装入工作库。

      System Classloader:系统类加载器,负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作库。

      类加载自底而上检查类是否已加载,如果当前类库有父类库会优先检查父类库是否加载,所以类加载顺序是自顶向下加载类。

    public class String{
        static{
            System.out.println("静态代码块在类加载时执行");
        }
        
        public static void main(String[] args){
            String str = new String();
        }
    }
    
    /*
    运行结果:
    错误: 在类 String 中找不到 main 方法, 请将 main 方法定义为:
       public static void main(String[] args)
    否则 JavaFX 应用程序类必须扩展javafx.application.Application
    
    */

      依次加载类,在系统类加载器中没有找到String中的main方法。

    5. 获取类的属性Field

      getName()  获取属性名,方法名

      getFields()  获取public修饰的属性

      getDeclaredFields()  获取所有属性

      getModifiers()  获取属性修饰符,返回对应修饰符id(返回int类型)

      Modifiers.toString()  对应id转换为修饰符(返回String类型)

      getType()  获取属性类型

    package com.reflect.test;
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    
    public class ReflectTest05{
        public static void main(String[] args) throws Exception{
            Class studentclass = Class.forName("com.reflect.test.Student");
            String studentclassname = studentclass.getName();  //获取完整类名
            System.out.println("完整类名:" + studentclassname);
            
            Field[] fields = studentclass.getFields(); //只能获取public修饰的属性
            System.out.println(fields.length);  //1
            Field field= fields[0];
            System.out.println(field.getName());  //id
            
            Field[] fs = studentclass.getDeclaredFields();  //获取所有属性
            System.out.println(fs.length);  //5
            
            for(Field fi : fs){
                int i = fi.getModifiers();   //获取属性修饰符,返回对应修饰符id
                String modifiey = Modifier.toString(i);  //id转换为对应修饰符
                System.out.println(i + " : " + modifiey);
                
                Class fieldtype = fi.getType();  //获取属性类型
                System.out.println(fieldtype.getName());
                
                System.out.println(fi.getName());  //获取属性名
                    
            }
        
        }
    }

      反射机制获取类的属性(反编译)

    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    
    public class ReflectTest06{
        public static void main(String[] args) throws Exception{
            StringBuffer sb = new StringBuffer();
            Class dateclass = Class.forName("java.util.Date");
            sb.append(Modifier.toString(dateclass.getModifiers()) + " class " + dateclass.getSimpleName() + " {
    ");
            Field[] fields = dateclass.getDeclaredFields();
            for(Field field : fields){
                sb.append("	");
                sb.append(Modifier.toString(field.getModifiers()));
                sb.append(" ");
                sb.append(field.getType().getSimpleName());
                sb.append(" ");
                sb.append(field.getName());
                sb.append(";
    ");
            }
            sb.append("}");
            System.out.println(sb);
        }
    }

    6. 可变长度参数

      参数是可变长度参数时:

      (1)可以不传参数,可以传1个参数,也可以传多个参数;也可以直接传入一个数组;

      (2)如果参数有多个,可以长度参数必须时最后一个;

    public class ArgTest{
        public static void main(String[] args){
            m1();
            m1(3);
            m1(1,2);
            m2("a","c","d");
            String[] str = new String[]{"a","c","d"};
            m2(str);
        }
        
        public static void m1(int... args){
            System.out.println("m1方法被执行了");
        }
        
        public static void m2(String... args){
            for(int i=0; i<args.length; i++){
                System.out.println(args[i]);
            }
        }
        
        //public static void m3(String... args, int a){}  //错误:varargs 参数必须是最后一个参数
        
        public static void m4(int a, String... args){}
    }

    7. 访问对象属性

    public class ReflectTest07{
        public static void main(String[] args) throws Exception{
            Class studentclass = Class.forName("com.reflect.test.Student");
            Object obj = studentclass.newInstance();
            Field idfield = studentclass.getDeclaredField("id");  //获取id属性
            idfield.set(obj,23);   //设置id值为23,这样只能给public属性赋值
            System.out.println(idfield.get(obj));
            
            Field agefiled = studentclass.getDeclaredField("age");
            agefiled.setAccessible(true);  //设置私有属性,要先设置可以访问
            agefiled.set(obj,34);
            System.out.println(agefiled.get(obj));
        }
    }

    8. 反射获取类方法

      例1:获取普通类中的方法

    public class ReflectTest08{
        public static void main(String[] args) throws Exception{
            Class userclass = Class.forName("com.reflect.test.UserService");
            Method[] usermethod = userclass.getDeclaredMethods();
            
            for(Method method : usermethod){
                System.out.print(Modifier.toString(method.getModifiers()));  //获取方法修饰符
                System.out.print(" ");
                System.out.print(method.getReturnType());  //获取方法返回值类型
                System.out.print(" ");
                System.out.print(method.getName());  //获取方法名
                System.out.println();
                
                Class[] parameterTypes = method.getParameterTypes();
                for(Class parameterType : parameterTypes){
                    System.out.println(parameterType.getSimpleName());
                }
            }
        }
    } 
    public class UserService{
        public boolean login(String username,String password){
            if("admin".equals(username) && ("1243").equals(password)){
                return true;
            }
            return false;
        }
        
        public void logout(){
            System.out.println("系统自动退出");
        }
    }

      例2:获取String类中的方法

    public class ReflectTest09{
        public static void main(String[] args) throws Exception{
            Class stringclass = Class.forName("java.lang.String");
            StringBuffer sb = new StringBuffer();
            sb.append(Modifier.toString(stringclass.getModifiers()) + " class " + stringclass.getSimpleName() + " {
    ");
            Method[] stringmethod = stringclass.getDeclaredMethods();
            for(Method method : stringmethod){
                sb.append("	");
                sb.append(Modifier.toString(method.getModifiers()));
                sb.append(" ");
                sb.append(method.getReturnType().getSimpleName());
                sb.append(" ");
                sb.append(method.getName());
                sb.append(" ");
                sb.append("(");
                Class[] stringpara = method.getParameterTypes();
                for(Class para : stringpara){
                    sb.append(para.getSimpleName());
                    sb.append(",");
                }
                if(sb.toString().endsWith(",")){
                    sb.deleteCharAt(sb.length() - 1);
                }
                sb.append("){}
    ");
            }
            sb.append("}");
            System.out.println(sb);
        }
    }

    9. 反射获取类中构造方法

    public class ReflectTest10{
        public static void main(String[] args) throws Exception{
            Class stringclass = Class.forName("java.lang.String");
            StringBuffer sb = new StringBuffer();
            sb.append(Modifier.toString(stringclass.getModifiers()) + " class " + stringclass.getSimpleName() + " {
    ");
            Constructor[] stringcon = stringclass.getDeclaredConstructors();
            for(Constructor con : stringcon){
                sb.append("	");
                sb.append(Modifier.toString(con.getModifiers()));
                sb.append(" ");
                sb.append(stringclass.getSimpleName());
                sb.append("(");
                Class[] conpara = con.getParameterTypes();
                for(Class para : conpara){
                    sb.append(para.getSimpleName());
                    sb.append(",");
                }
                if(conpara.length > 0){
                    sb.deleteCharAt(sb.length() - 1);
                }
                sb.append("){}
    ");
            }
            sb.append("}");
            System.out.println(sb);
        }
    }

    10 反射获取构造方法,创建对象

    public class Vip{
        public int no;
        public String name;
        public String email;
        public boolean sex;
        
        public Vip(){System.out.println("无参数构造方法");}
        
        public Vip(int no){this.no = no;}
        
        public Vip(int no, String name){
            this.no = no;
            this.name = name;
        }
        
        public Vip(int no, String name, String email){
            this.no = no;
            this.name = name;
            this.email = email;
        }
        
        public Vip(int no, String name, String email, boolean sex){
            this.no = no;
            this.name = name;
            this.email = email;
            this.sex = sex;
        }
        
        public String toString(){
            return "vip no" + no + "name" + name;
        }
    }
    public class ReflectTest11{
        public static void main(String[] args) throws Exception{
            //1. 传统创建对象方法
            Vip v1 = new Vip();
            Vip v2 = new Vip(1,"zhang","zhang@11.com",true);
            
            //2. 反射创建对象
            Class vipclass = Class.forName("com.reflect.test.Vip");
            Object o = vipclass.newInstance();  //调用无参构造方法
            System.out.println(o);
            
            Constructor con = vipclass.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);  
            Object o1 = con.newInstance(2,"zhang2","zhang2@11.com",true);  //调用有参数构造方法
            System.out.println(o1);
            
            Constructor noparacon = vipclass.getDeclaredConstructor();
            Object o2 = noparacon.newInstance();  //调用无参数构造方法
            System.out.println(o2);
        }
    }

    11. 获取父类和接口

    public class ReflectTest12{
        public static void main(String[] args) throws Exception{
            Class stringclass = Class.forName("java.lang.String");
            
            //获取父类
            Class superclass = stringclass.getSuperclass();
            System.out.println(superclass.getName());
            
            //获取接口(一个类可能有多个接口)
            Class[] stringinterface = stringclass.getInterfaces();
            for(Class si : stringinterface){
                System.out.println(si.getName());
            }
        }
    }

    12. 反射机制的应用

      反射可以在运行时动态获取类中的属性和方法。

        (1)准备两个业务类

    package service;
    
    public class Service1{
        public void doService1(){
            System.out.println("业务方法1");
        }
    }
    package service;
    
    public class Service2{
        public void doService2(){
            System.out.println("业务方法2");
        }
    }

      在没有反射时,当我们要从业务1切换到业务2时,我们必须修改代码重新编译运行,才可以达到效果

    package service;
    
    public class CommonTest{
        public static void main(String[] args){
            //new Service1().doService1();
            new Service2().doService2();
        }
    }

      (2)使用反射方法则可以方便很多

      准备一个配置文件,里面配置类名和方法名

    #example.properties
    
    class=service.Service2
    method=doService2

      测试类

    public class ReflectCommonTest{
        public static void main(String[] args) throws Exception{
            ResourceBundle rb = ResourceBundle.getBundle("service/example");
            String servicemethod = rb.getString("method");
            String serviceclass = rb.getString("class");
            System.out.println(servicemethod);
            System.out.println(serviceclass);
            
            Class c1 = Class.forName(serviceclass);
            Method m1 = c1.getDeclaredMethod(servicemethod);
            Constructor con1 = c1.getDeclaredConstructor();
            Object o1 = con1.newInstance();
            m1.invoke(o1);  //调用对象指定方法
            
            
        }
    }

      当我们从业务2切换业务1时,我们只需要修改配置文件中的class和method,对于测试类不需要修改一行代码,也不需要重新编译,就可以运行,完成业务切换。

  • 相关阅读:
    centos8下安装Dlib
    如何在CentOS7上安装桌面环境
    PIL、Pillow安装使用方法
    Linux -ls-列出文件的详细信息
    ValueError: Object arrays cannot be loaded when allow_pickle=False
    AttributeError: module 'keras.backend' has no attribute 'set_image_dim_ordering'
    CentOS安装CMake
    ValueError: Object arrays cannot be loaded when allow_pickle=False
    Keras requires TensorFlow 2.2 or higher
    importError: libSM.so.6: cannot open shared object file: No such file or directory
  • 原文地址:https://www.cnblogs.com/homle/p/15364367.html
Copyright © 2011-2022 走看看