Java-反射详解

139 阅读7分钟

反射的含义

  1. 反射就是把java类中的各种成分映射成一个个的Java对象

  2. Class对象的由来是将class文件读入内存,并为之创建一个Class对象。一个类只产生一个class对象

  3. 反射是框架设计的灵魂,使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)

获取Class对象的三种方式

  1. Object ——> getClass();

  2. 任何数据类型(包括基本数据类型)都有一个“静态”的class属性

  3. 通过Class类的静态方法:forName(String className)(常用)

  • 注意:在运行期间,一个类,只有一个Class对象产生。

  • 三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。

package com.study.notes.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.List;

/**
 * @program: study-notes
 * @description: 作家
 * @author: lzq
 * @create: 2023-06-27 15:38
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {
    //id
    private Long id;
    //姓名
    public String name;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //薪水
    private BigDecimal salary;
    //作品
    private List<Book> books;

    /**************构造方法***************/
    public Author(String name) {
        this.name = name;
    }

    /**************成员方法***************/
    public void show1(String s){
        System.out.println("调用了:公有的,String参数的show1(): s = " + s);
    }
    protected void show2(){
        System.out.println("调用了:受保护的,无参的show2()");
    }
    void show3(){
        System.out.println("调用了:默认的,无参的show3()");
    }
    private String show4(int age){
        System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
        return String.valueOf(age);
    }
}

package com.study.notes.reflect;


import com.study.notes.bean.Author;

/**
 * 获取Class对象的三种方式
 * 1 Object ——> getClass();
 * 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
 * 3 通过Class类的静态方法:forName(String  className)(常用)
 * 注意:在运行期间,一个类,只有一个Class对象产生。
 * 三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
 *
 */
public class ReflectClass {
    public static void main(String[] args) {
        System.out.println("**********************第一种方式获取Class对象*********************************");
        Author author1 = new Author();//这一new 产生一个Author对象,一个Class对象。
        Class authorClass = author1.getClass();//获取Class对象
        System.out.println(authorClass.getName());

        System.out.println("**********************第二种方式获取Class对象*********************************");
        Class authorClass2 = Author.class;
        System.out.println(authorClass == authorClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个

        System.out.println("**********************第三种方式获取Class对象*********************************");
        try {
            Class authorClass3 = Class.forName("com.study.notes.bean.Author");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(authorClass3 == authorClass2);//判断三种方式是否获取的是同一个Class对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;

获取构造方法:

  1. 批量的方法:
  • public Constructor[] getConstructors():所有"公有的"构造方法

  • public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

  1. 获取单个的方法,并调用:
  • public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
  • public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
  • 调用构造方法:Constructor-->newInstance(Object... initargs)
package com.study.notes.reflect;

import java.lang.reflect.Constructor;


/**
 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
 *
 * 1.获取构造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"构造方法
 *           public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
 *
 * 		2).获取单个的方法,并调用:
 * 			public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
 *
 * 			调用构造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */
public class ReflectConstructor {

    public static void main(String[] args) throws Exception {
        System.out.println("************获取Class对象********************");
        Class clazz = Class.forName("com.study.notes.bean.Author");

        System.out.println("**********************所有公有构造方法*********************************");
        Constructor[] conArray = clazz.getConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
        conArray = clazz.getDeclaredConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("*****************获取公有、无参的构造方法*******************************");
        Constructor con = clazz.getConstructor(null);
        //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
        //2>、返回的是描述这个无参构造函数的类对象。
        System.out.println("con = " + con);
        //调用构造方法
        Object obj = con.newInstance();
        System.out.println("obj = " + obj);

        System.out.println("******************获取私有构造方法,并调用*******************************");
        con = clazz.getDeclaredConstructor(String.class);
        System.out.println(con);
        //调用构造方法
        con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
        obj = con.newInstance("男");
        System.out.println("obj = " + obj);
    }

}

获取成员方法并调用

  1. 批量的:
  • public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)

  • public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)

  1. 获取单个的:
  • public Method getMethod(String name,Class<?>... parameterTypes):

  • 参数:

  • name : 方法名;

  • Class ... : 形参的Class类型对象

  • public Method getDeclaredMethod(String name,Class<?>... parameterTypes)

  1. 调用方法:
  • Method --> public Object invoke(Object obj,Object... args):

  • 参数说明:

  • obj : 要调用方法的对象;

  • args:调用方式时所传递的实参;

package com.study.notes.reflect;

import java.lang.reflect.Method;

/**
 * 获取成员方法并调用:
 * 
 * 1.批量的:
 * 		public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 * 		public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					参数:
 * 						name : 方法名;
 * 						Class ... : 形参的Class类型对象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 * 
 * 3.调用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					参数说明:
 * 					obj : 要调用方法的对象;
 * 					args:调用方式时所传递的实参;
 */
public class ReflectMethod {

    public static void main(String[] args) throws Exception {
        System.out.println("************获取Class对象********************");
        Class clazz = Class.forName("com.study.notes.bean.Author");
        
        System.out.println("***************获取所有的”公有“方法*******************");
        clazz.getMethods();
        Method[] methodArray = clazz.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        
        System.out.println("***************获取所有的方法,包括私有的*******************");
        methodArray = clazz.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        
        System.out.println("***************获取公有的show1()方法*******************");
        Method m = clazz.getMethod("show1", String.class);
        System.out.println(m);
        //实例化一个Author对象
        Object obj = clazz.getConstructor().newInstance();
        m.invoke(obj, "刘德华");

        System.out.println("***************获取私有的show4()方法******************");
        m = clazz.getDeclaredMethod("show4", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
        System.out.println("返回值:" + result);
        
    }
}

获取成员变量并调用:

  1. 批量的
  • Field[] getFields():获取所有的"公有字段"

  • Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;

  1. 获取单个的:
  • public Field getField(String fieldName):获取某个"公有的"字段;

  • public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)

  1. 设置字段的值:
  • Field --> public void set(Object obj,Object value):

  • 参数说明:obj:要设置的字段所在的对象;value:要为字段设置的值;

package com.study.notes.reflect;

import com.study.notes.bean.Author;
import java.lang.reflect.Field;

/**
 * 获取成员变量并调用:
 *
 * 1.批量的
 * 		1).Field[] getFields():获取所有的"公有字段"
 * 		2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
 * 2.获取单个的:
 * 		1).public Field getField(String fieldName):获取某个"公有的"字段;
 * 		2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
 *
 * 	 设置字段的值:
 * 		Field --> public void set(Object obj,Object value):
 * 					参数说明:
 * 					1.obj:要设置的字段所在的对象;
 * 					2.value:要为字段设置的值;
 *
 */
public class ReflectField {

    public static void main(String[] args) throws Exception {
        System.out.println("************获取Class对象********************");
        Class clazz = Class.forName("com.study.notes.bean.Author");

        System.out.println("************获取所有公有的字段********************");
        Field[] fieldArray = clazz.getFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }

        System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
        fieldArray = clazz.getDeclaredFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }

        System.out.println("*************获取公有字段并调用***********************************");
        Field f = clazz.getField("name");
        System.out.println(f);
        //获取一个对象
        Object obj = clazz.getConstructor().newInstance();//产生Author对象--》Author author = new Author();
        //为字段设置值
        f.set(obj, "刘德华");//为Author对象中的name属性赋值--》author.name = "刘德华"
        //验证
        Author author = (Author)obj;
        System.out.println("验证姓名:" + author.name);

        System.out.println("**************获取私有字段并调用********************************");
        f = clazz.getDeclaredField("intro");
        System.out.println(f);
        f.setAccessible(true);//暴力反射,解除私有限定
        f.set(obj, "你好");
        System.out.println("简介:" + author);
    }
}