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,对于测试类不需要修改一行代码,也不需要重新编译,就可以运行,完成业务切换。

原文地址:https://www.cnblogs.com/homle/p/15364367.html