java--反射

本文详细介绍了Java反射机制,包括Type、Field、Constructor、Method和Class的相关操作,如访问非public字段、调用构造函数和方法,以及如何处理反射中的异常。还探讨了泛型在反射中的应用,并提供了相关示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Type

Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。

Field

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

常用方法

getDeclaredAnnotations()//获取该字段的所有注解,返回的是数组。 前提是该注解是运行时的注解java.lang.annotation.RetentionPolicy.RUNTIME
getModifiers()//获取该字段的访问修饰符
getType()//返回的是该字段的类型
getGenericType()//获取该字段的泛型
get(Object o)//返回是该类某个对象的字段值
set(Object o)// 给某个对象,设置值
    public String name="laoqiang";
    public int age = 12;
    public List<String> list = new ArrayList<String>();
    public static void main(String[] args) {
        new Test321().getExample();
    }
    Field[] fields = Test321.class.getFields();
        Field f1 = null;
        try {
            f1 = Test321.class.getField("list");//获取某个指定的字段
        } catch (NoSuchFieldException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (SecurityException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        System.out.println(f1.getGenericType());//返回该字段的泛型
        for(Field f:fields) {
            try {
                System.out.println("字段的名字:"+f.getName()+"字段的修饰符:"+f.getModifiers()+"值:"+f.get(new Test321())+"字段的类型"+f.getType());
            } catch (IllegalArgumentException | IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            Test321 t = new Test321();
            Field f = Test321.class.getDeclaredField("age");
            f.set(t,34);
            System.out.println(t.age);
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
反射非 public 的变量导致的 NoSuchFieldException

这里写图片描述

你就这么记,带有declare的,都可以访问自身类所有你申明的(修饰符可以是public、private、默认、protected)方法、属性、构造函数。不带declare的,(如果有继承)可以访问父类public修饰的属性和方法,以及自身的public修饰的属性、方法(即自身的除了public 修饰的,其他都不可以访问)

经常上面的可以看出,我们无法直接通过上述的方法去获得父类除了public 之外的方法,那有办法吗,我们间接获取就好了

c.getSuperclass();

有了父类的class,在根据上面的两种申明方法,想获取什么都可以。

修改或访问变量导致的 IllegalAccessException

当你想要获取或者修改 不可修改(final)的变量时,会导致IllegalAccessException。我们可以使用 AccessibleObject.setAccessible() 方法告诉安全机制,这个变量可以访问。

使用 setAccessible(true) 方法前也需要注意,这可能会导致意想不到的后果,比如:在运行时虽然你通过反射修改了变量 a 的值,但其他部分可能还在使用原来的值。(多线程使用,需要注意)

Constructor

该类中包含了构造器的信息。

关于该类的方法,在这里就不在叙说了,你可以自己查看Api,和Field有很多类似的。

如何通过反射调用构造函数
 Constructor c2 = c.getDeclaredConstructor(String.class,int.class);//,这里的参数,是你构造函数中参数的类型类,获取了某种参数类型的构造函数,需要注意,如果你的构造函数中,有基本类型的参数,你在这,也要写基本类型,不要多次一举,反而会报错,找不到对应的构造方法
             Test322 t = (Test322) c2.newInstance("laoqiang",21);//传入真正的参数
Method

该类包含方法的信息。

关于该类的方法,在这里就不在叙说了,你可以自己查看Api,和Field有很多类似的。

    Type[] types = m.getGenericParameterTypes();//获取方法的参数类型
            for(Type t:types) {
                System.out.println(t.getTypeName());
            }
如何利用反射执行方法
Test322 tt = (Test322) c.newInstance();
            Method m = c.getMethod("show",String.class,int.class);//传入方法名,光方法名是不能完全确定某个方法,所以还是需要参数类型去确定,这里也是证明了,重载的方法,主要靠参数类型,个数去判断哦,触类旁空,才能成长。
            m.invoke(tt,"laoqiang",23);//传入真正的参数,以及执行该方法的对象
Class (和反射相关的)

该类在java.lang包下。

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。(接口也是当做类处理的) 该Class对象存放着所有关于该对象的运行时信息。

forName(String name)// 返回与带有给定字符串名的类或接口相关联的 Class 对象。
getAnnotations()//返回所有注解
getDeclaredConstructors()//返回所有申明的构造器
getDeclaredFields()//返回所有申明的字段
getDeclaredMethods()//返回所有申明的方法
getInterfaces()//获取该类实现的接口
getModifiers()//获取该类或者接口的修饰符
获取正在加载的class
  • 调用运行时类的本身的.class来获取
 Class  c = Test322.class;
  • 调用调用运行时类的对象来获取
     Test322 t  =new Test322();
         Class c = t.getClass();
  • 通过类的全称类名去获取
 Class c = null;
        try {
            c = Class.forName("com.example.test.Test322");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  • 通过类的加载器去获取
ClassLoader c1 = this.getClass().getClassLoader();
        Class c = null;
        try {
            c = c1.loadClass("com.example.test.Test322");//需要全限的包名
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

通过正在运行的class对象,去获取它的方法、属性、构造函数。

 Method[] methods  = c.getDeclaredMethods();
         for(Method m:methods) {
             System.out.println(m.getName());
         }
         Constructor[] constructors = c.getDeclaredConstructors();
         for(Constructor cc:constructors) {
             System.out.println(cc.getName());
         }
         Field[] fields =c.getDeclaredFields();
         for(Field f:fields) {
             System.out.println(f.getName());
         }

上述4中获取Class方法,使用==来判断的话(返回是true),他们都是同一个对象,也就是说每一个类型的对象,他们的Class对象只有一个。

java 编译类型
  • 静态编译:在编译期间确定类型和绑定对象,绑定对象通过使用new关键字,在编译期确定类型的应用是方法重载。
  • 动态编译:在运行期间确定类型和绑定对象,绑定对象通过newInstance方法,反射就是一种动态编译,以及多态,方法重写,都应用到。
newInstance

该方法是Class类中一个方法,可以返回当前类的一个实例。

        Test322 t = null;
        try {
            t = (Test322) c.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

使用该方法,实际上就是调用反射类的无参构造,所以,你一定要保证有无参构造,否则创建实例会报异常。

操作另外一个类中private属性、方法

我们知道getDeclaredXxx和getXxx,我们可以在某个类中,想获取啥,就获取啥,但是我们,经常的需求,就是在另外一个类中,是可以获取private修饰的属性、方法的信息,但是操作private修饰的属性、方法,会报错,该怎么办?

java.lang.IllegalAccessException: Class com.example.test.Test322 can not access a member of class com.example.test.Test326 with modifiers "private"

setAccessible方法可以帮助我们去越过jvm的访问检查。

使用Field类、Method类 & Constructor类对象的setAccessible,即可完成我们的需求。

注意:在自身类中反射操作(就是赋值呀)修饰、方法,是不需要加setAccessible方法,这个只是针对于在不同类中操作私有属性、方法。

在不同类之间,获取Class的信息,还是根据getDeclareXxx和getXxx一样的,这个不变化。

反射获取泛型

我们都知道泛型是只存在于编译期的,在运行期是被擦除,反射的功能很强大,可以在运行期去获取泛型。

ParameterizedType

该接口ParameterizedType 表示参数化类型,如 Collection。
所以我们可以通过 它取获取泛型类型

try {
                Type t = c.getDeclaredField("list").getGenericType();//这个获取参数类型,相当于List
                System.out.println(((ParameterizedType)t).getActualTypeArguments()[0]);//进一步获取List的泛型参数
            } catch (NoSuchFieldException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

参考学习
参考学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值