Java反射机制详解Class+Field+Method+Constructor

本文详细介绍了Java反射机制,包括Class对象的获取和信息、Field的访问与修改、Method的调用,以及如何通过反射创建新类。反射允许程序在运行时检查类的属性和方法,甚至修改其行为。虽然在日常编程中较少使用,但在AOP和SpringBoot等业务场景中具有重要作用。
摘要由CSDN通过智能技术生成

Java0X1——反射

反射简介

什么是反射,根据wiki的给出的解释:

计算机学中,反射(英语:reflection)是指计算机程序运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

在Java中,简单来说就是在Java程序运行的过程中可以了解到任意的对象的属性和方法。

一般情况下我们可以自己暴露出接口来获取相应的属性,所以初学Java会比较少接触到反射,但是反射机制在一些方面有意想不到的效果。

Class对象

Class对象简介

注意是Class不是class,Class对象是JVM生成的用来加载类的,Java源码编译后会生成字节码文件,JVM就是通过这些.class文件来生成对应的Class类加载到内存中。

注意:每一个类只有一个Class对象

Class对象获取

  • 直接通过类获取 Class test1 = Test.class;
  • 通过实例 Class test2 = t1.getClass(); Class test2 = t1.getClass();
  • 通过完整的类名 Class test3 = Class.forName(“reflection.Test”);
package reflection;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        Class test2 = t1.getClass();
        Class test1 = Test.class;                           //直接通过类获取Class
        Class test2 = t1.getClass();                        //通过实例对象获取Class
        Class test3 = Class.forName("reflection.Test");     //通过类的路径获取
        System.out.println(test1);
        System.out.println(test2);
        System.out.println(test3);
    }
}


class  Test{
    public int id;
    private String name ;
}
output:
class reflection.Test
class reflection.Test
class reflection.Test

Class获取信息

通过Class类就可以获取这个类的一系列信息

方法返回描述
getName()Stirng返回类的完整名称
getSimpleName()String返回类名
getPackage()String获取包名
getSuperclass()Class获取父类
getInterfaces()Class[]获取所有接口
getAnnotations()Class[]获取所有接口
package reflection;


public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        Test t1 = new Test();
        Class test1 = Test.class;                           //直接通过类获取Class
        System.out.println("获取完整名称:"+test1.getName());
        System.out.println("获取类名:"+test1.getSimpleName());
        System.out.println("获取包名:"+test1.getPackage());
        System.out.println("获取父类:"+test1.getSuperclass().getName());
        System.out.println("获取所有的接口:"+test1.getInterfaces()[0].getName());
        System.out.println("获取注解"+test1.getAnnotations()[0].toString());

    }
}


@Deprecated(forRemoval = false)
class  Test extends Father implements InterfaceTest{
    public int id;
    private String name ;
}

class Father{}
interface InterfaceTest{}

output:
获取完整名称:reflection.Test
获取类名:Test
获取包名:package reflection
获取父类:reflection.Father
获取所有的接口:reflection.InterfaceTest
获取注解@java.lang.Deprecated(forRemoval=false, since="")

Field

反射机制还可以获取类的字段和属性

获取Field

主要是通过以下的方法获取

方法返回描述
getField(String name)Field获取对应名称的public属性,包括父类的属性
getFields()Field[]获取所有public属性,包括父类的属性
getDeclareField(String name)Field获取对应名称的属性(只能返回类内声明的属性)
getDeclareFields()Field[]获取所有属性(只能返回类内声明的属性)
package reflection;


import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        Class test = Test.class;
        Field f1 = test.getField("id");
        Field[] f1s = test.getFields();
        Field f2 = test.getDeclaredField("name");
        Field[] f2s = test.getDeclaredFields();

        System.out.println(f1);
        System.out.println("------------");
        for (Field field : f1s) {
            System.out.println(field);
        }
        System.out.println("------------");
        System.out.println(f2);
        System.out.println("------------");
        for (Field field : f2s) {
            System.out.println(field);
        }

    }
}
output:
public int reflection.Test.id
------------
public int reflection.Test.id
------------
private java.lang.String reflection.Test.name
------------
public int reflection.Test.id
private java.lang.String reflection.Test.name

获取以及修改字段的值

根据获取到的Field以及对象的实例获取该实例对应的值

值得注意的是:当访问私有的成员变量的时候需要设置访问的权限field.setAccessible(true)

通过**field.getModifiers()获取属性的修饰符,其返回值是int,再通过Modifier.isPrivate(int)**判断其是否私有

最后通过**field.get(Object)**获取其对于的值

或者通过**field.set(Object obj , Object value)**修改对象的值

package reflection;


import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        Class test = Test.class;
        Test example = new Test(1 , "Java");
        Field[] fields = test.getDeclaredFields();
        /***获取字段的值***/
        /***在获取私有成员变量的值的时候需要获得权限***/

        for (Field field : fields) {
            if(Modifier.isPrivate(field.getModifiers())){
                field.setAccessible(true);
            }
            System.out.println(field.getName()+":"+field.get(example));
        }
        /*** 修改对象私有成员变量的时候同样需要获取权限,但是前面的已经获取过了,无需再次获取***/
        System.out.println("----------------");
        fields[0].set(example , 10);
        fields[1].set(example,"C++");
        for (Field field : fields) {
            System.out.println(field.getName()+":"+field.get(example));
        }
    }
}


class Test{
    public int id;
    private String name;
    public Test(int id, String name) {
        this.id = id;
        this.name = name;
    }
}
	
output:
id:1
name:Java
----------------

After set:
id:10
name:C++

Method

Method获取

对象方法的获取可以参考属性的获取

package reflection;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class test = Test.class;
        Test example = new Test(1 , "Java");
        Method m1 = test.getMethod("getId");
        Method[] m1s = test.getMethods();
        Method m2 = test.getDeclaredMethod("getAverage", int.class, int.class);
        Method[] m2s = test.getDeclaredMethods();

        System.out.println(m1);
        System.out.println("----------");
        for (Method method : m1s) {
            System.out.println(method);
        }
        System.out.println("----------");
        System.out.println(m2);
        System.out.println("----------");
        for (Method method : m2s) {
            System.out.println(method);
        }

    }
}


class Test{
    public int id;
    private String name;
    public Test(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    private int getAverage(int a , int b){
        return a+(b-a)/2;
    }
}
public int reflection.Test.getId()
----------
public java.lang.String reflection.Test.getName()
public int reflection.Test.getId()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
----------
private int reflection.Test.getAverage(int,int)
----------
public java.lang.String reflection.Test.getName()
public int reflection.Test.getId()
private int reflection.Test.getAverage(int,int)

Process finished with exit code 0

可以发现,getMethods()能获取所有public的方法,包括父类继承的方法toString , wait等等,但还值得注意的是构造函数不在其中

值得注意的是,获取Method的时候传入的参数除了函数名称之外,还有多个可变参数描述方法的参数:

test.getDeclaredMethod(“getAverage”, int.class, int.class);

如果是通过**getMethods()**获取的Method因为缺失函数的参数描述,直接invoke会报错

Method调用

获取Method之后只需要通过invoke调用即可,同Field访问私有方法需要获取权限

如果是通过getDeclareMethods()获取的Method因为缺失函数的参数描述,直接invoke会报错,因此需要使用getDeclareMethod(Object obj , Class … params)

package reflection;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class test = Test.class;
        Test example = new Test(1 , "Java");
        Method getAverage = test.getDeclaredMethod("getAverage", int.class, int.class);
        getAverage.setAccessible(true);
        Object ans =  getAverage.invoke(example , Integer.MAX_VALUE ,Integer.MAX_VALUE);
        System.out.println("getAverage(example , Integer.MAX_VALUE ,Integer.MAX_VALUE):"+ans);

    }
}


class Test{
    public int id;
    private String name;
    public Test(int id, String name) {
        this.id = id;
        this.name = name;
    }

    private int getAverage(int a , int b){
        return a+(b-a)/2;
    }
}

output:
getAverage(example , Integer.MAX_VALUE ,Integer.MAX_VALUE):2147483647

通过反射创建新类

newInstance

可以通过newInstance创建构造新类,不过该方法只能调用无参构造,而且在Java9之后该方法已被废弃

package reflection;

public class Main {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class test = Test.class;
        Test example = (Test) test.newInstance();

    }
}

class Test{
    public int id;
    private String name;
}		

getDeclareConstructor

通过getDeclareConstructor获取构造函数再调用newInstance

package reflection;

import java.lang.reflect.InvocationTargetException;

public class Main {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class test = Test.class;
        Test example1 = (Test) test.getDeclaredConstructor(int.class).newInstance(10);
        Test example2 = (Test) test.getDeclaredConstructor(String.class).newInstance("Java");
        Test example3 = (Test) test.getDeclaredConstructor().newInstance();
    }
}


class Test {
    public int id;
    private String name;

    public Test(int id) {
        this.id = id;
        System.out.println("constructor 1");
    }

    public Test(String name) {
        this.name = name;
        System.out.println("constructor 2");
    }

    private Test() {
        System.out.println("constructor 3");
    }
}
output:
constructor 1
constructor 2
constructor 3

可以发现,Constructor和上面的Method十分相似,那么应该还有一个getConstructor函数,这个函数和getDeclareConstructor的区别在于getConstructor无法获取私有构造函数

业务场景

虽然日常的算法题目、小项目很少能用到反射,但是不可否认反射在一些业务场景还是十分有用的,举几个栗子:

  • AOP:面向切面的编程,这个的实现就离不开反射机制,对一些方法进行增强

  • SpringBoot中大量的注解,注解本身是没有意义的,但是可以通过反射让注解变得有意义,通过反射机制获取参数、函数、类等等的注解,再做相应的逻辑处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值