java反射的学习,并实现一个小案例:从配置文件获取类和方法,并执行方法

从B站上学习了java反射的相关知识,做个笔记记录一下,基础概念不再赘述,只记录一些重点

一、获取Class对象的方式

1、获取Class对象的三种方式对应着java代码在计算机中的三个阶段

(1)【Source源代码阶段】 Class.forName("全类名"):将字节码文件加载进内存,返回Class对象 。多用于配置文件,将类名定义在配置文件中。读取文件,加载类。

(2)【Class类对象阶段】 类名.class:通过类名的属性class获取。 多用于参数的传递

(3)【Runtime运行时阶段】对象.getClass():getClass()方法是定义在Objec类中的方法 。 多用于对象的获取字节码的方式

结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。

2、测试三种获取方法

@Test
public void reflect1() throws ClassNotFoundException {
    //方式一:Class.forName("全类名");
    Class cls1 = Class.forName("com.test.domain.User");   //自定义实体类
    System.out.println("cls1 = " + cls1);

    //方式二:类名.class
    Class cls2 = User.class;
    System.out.println("cls2 = " + cls2);

    //方式三:对象.getClass();
    User user= new User();        
    Class cls3 = user.getClass();
    System.out.println("cls3 = " + cls3);

    // == 比较三个对象
    System.out.println("cls1 == cls2 : " + (cls1 == cls2));    //true
    System.out.println("cls1 == cls3 : " + (cls1 == cls3));    //true
    //结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。
}

 二、Class对象功能

1、获取功能,只列举一些常用的

1、获取成员变量

Field[] getFields() :获取所有public修饰的成员变量

Field getField(String name) 获取指定名称的 public修饰的成员变量

Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

Field getDeclaredField(String name)

2、获取成员方法 

Method[] getMethods()

Method getMethod(String name, 类... parameterTypes)

Method[] getDeclaredMethods()

Method getDeclaredMethod(String name, 类... parameterTypes)  

3、获取构造方法 

Constructor[] getConstructors()

Constructor getConstructor(类... parameterTypes)

Constructor[] getDeclaredConstructors()

Constructor getDeclaredConstructor(类... parameterTypes)

4、获取全类名

String getName()

 2、Field,成员变量

  • (1)设置值 void set(Object obj, Object value)
  • (2)获取值 get(Object obj)
  • (3)忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射

注意:操作private修饰的变量,需要添加下面代码,否则set和get会报错

代码示例:公共代码User类

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
    public String name;
    private String sex;
    private int age;
    public String desc;
    
    public void eat(){
        System.out.println("eat...");
    }
    
    public void eat(String food){
        System.out.println("eat"+food);
    }
}

 2.1 获取类的成员变量

public void test01() throws Exception{
    Class clazz = Class.forName("com.ang.reflection.domain.User");

    //获取到public的字段
    Field[] fieldsPublic = clazz.getFields();
    Arrays.stream(fieldsPublic).forEach(f->{
        System.out.println(f);
    });

    //获取指定的public字段
    Field fieldPublic = clazz.getField("name");
    System.out.println("-----------------------------");
    System.out.println(fieldPublic);

    //获取所有的任意修饰符的字段
    Field[] fieldAll = clazz.getDeclaredFields();
    System.out.println("-----------------------------");
    Arrays.stream(fieldAll).forEach(f->{
        System.out.println(f.toString());
    });

    //获取指定的任意修饰符的字段
    Field fieldAny = clazz.getDeclaredField("age");
    System.out.println("-----------------------------");
    System.out.println(fieldAny);
}

打印结果: 

java反射的学习,并实现一个小案例:从配置文件获取类和方法,并执行方法_第1张图片

2.2 操作类的成员变量

/**
 * 获取并设置成员变量的值
 */
public void test02() throws Exception {
    User user = getUser();
    Class clazz = User.class;

    //获取user对象中name的值
    Field name = clazz.getDeclaredField("sex");

    //sex是私有变量,直接get/set会报错,需要下面一行代码
    name.setAccessible(false);

    //get
    Object value1 = name.get(user);
    System.out.println(value1.toString());
    //set
    name.set(user,"女");
    System.out.println(user.getName());
}

 打印结果:

3、Constructor,构造方法

创建对象:T newInstance(Object... initargs)

注意:如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

3.1 无参构造

/**
 * 无参构造
 */
public void test01() throws Exception {
    Class clazz = Class.forName("com.ang.reflection.domain.User");

    //获取无参构造
    Constructor constructor = clazz.getConstructor();
    System.out.println(constructor);
    System.out.println("---------------------");

    //使用无参构造创建对象
    Object user = constructor.newInstance();
    System.out.println(user);

    //也可以直接使用反射的对象来创建实例
    Object user1 = clazz.newInstance();
    System.out.println(user1);
}

 打印结果:

java反射的学习,并实现一个小案例:从配置文件获取类和方法,并执行方法_第2张图片

 3.2 有参构造

/**
 * 有参构造
 */
public void test02() throws Exception{
    Class clazz = Class.forName("com.ang.reflection.domain.User");

    Constructor[] constructors = clazz.getConstructors();
    Arrays.stream(constructors).forEach(c->{
        System.out.println(c);
    });

    System.out.println("------------------------");

    //获取有参构造方法
    Constructor constructor = clazz.getConstructor(String.class,String.class,int.class,String.class);
    System.out.println(constructor);

    //构造对象
    Object user = constructor.newInstance("李四","男",20,"喜欢打游戏");
    System.out.println(user);
}

打印结果:

java反射的学习,并实现一个小案例:从配置文件获取类和方法,并执行方法_第3张图片

3.3 对于getDeclaredConstructor方法和getDeclaredConstructors方法

对于多出个Declared关键词的两个方法,与不带这个词的两个方法的对比。与之前3.2叙述的一样,getDeclaredConstructor方法可以获取到任何访问权限的构造器,而getConstructor方法只能获取public修饰的构造器。具体不再测试。此外在构造器的对象内也有setAccessible(true);方法,并设置成true就可以操作了。

4、Method,成员方法 

  • 执行方法:Object invoke(Object obj, Object... args)
  • 获取方法名称:String getName();

 4.1 获取所有public成员方法

/**
 * 获取成员方法
 */
public void test01() throws Exception {
    Class clazz = Class.forName("com.ang.reflection.domain.User");

    Method[] methods = clazz.getMethods();
    Arrays.stream(methods).forEach(m->{
        System.out.println(m);
        System.out.println(m.getName());
        System.out.println("--------------------------");
    });
}

4.2 无参方法 

/**
 * 无参方法
 */
public void test02() throws Exception{
    Class clazz = Class.forName("com.ang.reflection.domain.User");
    Object user = new User();

    Method method = clazz.getMethod("eat");
    method.invoke(user);
}

打印结果: 

4.3 有参方法 

/**
 * 有参构造
 */
public void test03() throws Exception{
    Class clazz = Class.forName("com.ang.reflection.domain.User");
    Object user = new User();

    Method method = clazz.getMethod("eat", String.class);
    method.invoke(user, " Apple");
}

打印结果:

三、小案例

实现一个功能,从配置文件中,根据配置的不同类名和方法名,通过反射来运行类中的方法 

 1、步骤

(1)将需要创建的对象的全类名和需要执行的方法定义在配置文件中

(2)在程序中加载读取配置文件

(3)使用反射技术来加载类文件进内存

(4)创建对象

(5)执行方法

 2、代码实现

2.1 实体类User,可以有其他的类

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
    public String name;
    private String sex;
    private int age;
    public String desc;

    public void eat(){
        System.out.println("eat...");
    }
    public void eat(String food){
        System.out.println("eat"+food);
    }
}

2.2 配置文件:reflection.properties,可以配置其他的类和方法 

className = com.ang.reflection.domain.User
methodName = eat

2.3 编写测试方法

/**
 * 一个小案例,从配置文件读取class和method,实现方法的执行
 */
public class DemoTest {
    public static void main(String[] args) throws Exception{
        //获取类加载器
        ClassLoader classLoader = DemoTest.class.getClassLoader();
        //加载文件
        InputStream is = classLoader.getResourceAsStream("reflection.properties");
        //获取值
        Properties properties = new Properties();
        properties.load(is);

        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //反射获取class
        Class clazz = Class.forName(className);
        //反射获取实例
        Object object = clazz.newInstance();
        //反射获取无参方法
        Method method1 = clazz.getDeclaredMethod(methodName);
        method1.invoke(object);

        //反射获取有参方法
        Method method2 = clazz.getDeclaredMethod(methodName, String.class);
        method2.invoke(object, " orange");
    }
}

打印结果:

你可能感兴趣的:(java,java,java反射)