Java反射

本文深入探讨Java反射机制,包括Class类的使用、方法、成员变量和构造器的反射,以及Java类加载机制。通过实例演示如何获取类信息、方法反射操作,并讨论反射与泛型的关系。

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

 反射是Java的一个特点,也是使原本为静态语言的Java,多了那么一些灵活性,在理解各个框架源码以及组件内容的时候是一个不错的知识点,比如注解,这是一个非常常见,又很好使的玩意,之前也有简单的学习---Java 注解 基础、Java 注解 实践
从主要以下几点开始学习
  • Class类的使用
  • 方法的反射
  • 成员变量的反射
  • 构造器的反射
  • Java类加载机制
  •  
Class类 和 面向对象
在面向对象的环境中,万事万物皆对象,但也总有例外,Java中有两个不属于对象,一个是普通数据类型,一个是静态的成员
普通数据类型有封装类的弥补,静态的属于类,那么类是不是对象呢,类是对象,是java.lang.Class类的实例对象,看文字还比较容易理解,中文说出来就比较绕口, 英文: there is a class named Class
一个普通的类的实例对象表示

 
[AppleScript]  纯文本查看  复制代码
?
1
2
3
4
5
6
public class Coo {
     / / Doo的实例对象 以doo表示
     Doo doo = new Doo ( ) ;
}
 
class Doo { }
那么一个Class类的实例对象,有三种表示方式,但不能是new Class,因为下面源码中也解释为什么
[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
/*
 * Private constructor. Only the Java Virtual Machine creates Class objects.
 * This constructor is not used and prevents the default constructor being
 * generated.
 */
private Class(ClassLoader loader) {
    // Initialize final field for classLoader.  The initialization value of non-null
    // prevents future JIT optimizations from assuming this final field is null.
    classLoader = loader;
}
上面是Class类中的一个构造器,是私有的,而且注释说只有JVM创建Class对象,在以前的Java版本中你可能会看到一个无参的构造器,没关系,那你的构造器也绝对是私有,不能直接创建,在上面中出现了一个JIT编译的关键词,有兴趣的小伙伴可以研究扩展
任何类都是Class的实例对象,下面三种方式:
[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Coo {
 
    //Doo的实例对象 以doo表示
    Doo doo=new Doo();
 
    //①可以看出Doo类有一个隐含的静态成员变量class
    Class first=Doo.class;
 
    //②已知类的对象,通过getClass获取
    Class second=doo.getClass();
 
    //重理解一次:doo代表Doo类的实例对象,first、second代表的是Class的实例对象
    //这个Class的实例对象又证明说Doo这个类本身是一个实例对象的存在
    //一本正经的胡说八道,那么给一个官方给出的说法是这样的 :first、second表示了Doo类的类 类型(class type)
    //所有东西都是对象,类也是对象,是Class的实例对象,这个对象称为该类的类类型
    //就可以分析到,Doo的对象是doo,Doo的类类型是Class的对象first、second
    //不管哪种表达方式表示Doo的类类型,一个类只可能是Class类的一个实例对象,所以first == second
 
    //③需要异常处理,参数为类的全称"com.cloud.eureka.Doo"
    Class third=null;
 
    {
        try {
            third = Class.forName("com.cloud.eureka.Doo");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
 
    //依然存在 first == second == third
    //由此可见,我们可以通过类的类类型创建该类的对象,通过first、second、third创建
    //需要异常处理,是谁的类的类类型对象,创建的对象就是谁,需要强转
    //newInstance前提需要无参构造方法
    {
        try {
            Doo dooFirst= (Doo) first.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
 
class Doo{}
Java 动态加载类信息
三种表示Class的实例对象中,第三种具有很好的动态加载类③
  • 可以表示类的类类型,还可以动态加载类
  • 区分编译、运行
  • 编译时加载类属于静态加载类
  • 运行时加载类属于动态加载类
很多时候,大家都是通过工具(IDEA、eclipse等)进行办公或者学习,编译和运行都是由工具来辅助完成的,那么我们需要知道编译、运行的区别
1.2..3...好,我们得到了编译、运行知识的技能
只要是在类里面用到的,都隐含class,对应的类的类类型,如下:
[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
public class Coo {
 
    Class c0=int.class;
    Class c1=String.class;
    Class c2=Double.class;
    Class c3=void.class;
     
    // package不是在类里面的,error
    // Class c4=package.class;
}
 
在Doo类中,写个方法
[Java]  纯文本查看  复制代码
?
1
2
3
4
5
6
class Doo{
     public static void staticVoidMethod(Object o){
         //传递的是什么类型,就是什么类型
         Class co=o.getClass();
     }
}
o传递的是什么对象,co就是该类的类类型,那么底层怎么实现的,可能会比较复杂,贴一份源码
public final native Class<?> getClass();
这是一个native声明的一个方法,称为本地方法,Java中有一项技术JNR,使用Java声明,C语言实现,Java 中调用...一堆,有兴趣的可以了解了解,效果就是上面说的,返回类的类类型
下面是简单的通过Class获取类的信息:
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Doo {
     public static void staticVoidMethod(Object o) {
         //传递的是什么类型,就是什么类型
         Class co = o.getClass();
 
         System.out.println( "类的全名称:" + co.getName());
         System.out.println( "类的名字:" + co.getSimpleName());
 
         //Method类,方法对象
         //一个成员方法 就是 一个Method对象
         //getMethods 获取所有public的方法,其中包括父类继承的函数
         Method[] allMethods = co.getMethods();
 
         //getDeclaredMethods获取该类自己声明的方法
         Method[] thisMethods = co.getDeclaredMethods();
 
         for (Method method : allMethods) {
             //method.getReturnType()得到的是类的类类型
             //比如返回值是String,那么得到的是String.class的类类型,通过getName获取名称
             System.out.println( "返回类型:" + method.getReturnType().getName());
 
             System.out.println( "方法名称:" + method.getName());
 
             //获取参数类型
             Class[] parameterTypes = method.getParameterTypes();
             for (Class c : parameterTypes) {
                 System.out.println( "参数类型:" + c.getName());
             }
             System.out.println( "====================================" );
         }
 
         //成员变量 =》对象
         //属于java.lang.reflect.Field
         //Field封装了关于成员变量的操作
         //getFields获取所有public的成员变量
         Field[] field=co.getFields();
         //得到自己声明的成员变量
         Field[] declaredFields=co.getDeclaredFields();
         for (Field fields:field) {
             System.out.println( "成员变量类型" +fields.getType());
             System.out.println( "成员变量名称" +fields.getName());
         }
     }
}
简单来一个main方法,加入一个String类
[Java]  纯文本查看  复制代码
?
1
2
3
4
5
6
public class Coo {
     public static void main(String[] args) {
         String hello= new String();
         Doo.staticVoidMethod(hello);
     }
}
控制台打印 , 所有String内的方法信息:
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
类的全名称:java.lang.String[/font][/align]类的名字:String
返回类型: boolean
方法名称:equals
参数类型:java.lang.Object
====================================
返回类型:java.lang.String
方法名称:toString
====================================
返回类型: int
方法名称:hashCode
====================================
返回类型: int
方法名称:compareTo
参数类型:java.lang.Object
====================================
//......
 
可以总结出来,getDeclaredXXX()方法都是获取自己声明的内容,包括成员变量,构造器,方法等等,直接的getXXX()方法部分会获取所有内容包括父类的内容,另外数组是一个特殊的存在,打印的是“0]”差不多的样子,在JVM对数组的存储方式也比较VIP,有兴趣的可以理解扩展
方法的反射
上面有获取所有的方法的示例,下面来学习如何获取某一个方法以及方法的反射操作
①方法的名称和方法的参数列表可以唯一定位某一个方法
②method.invoke(对象,参数列表)
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class MethodReflect {
     //获取getMethod方法,获取①号
     public static void main(String[] args) {
         MethodDemo demo = new MethodDemo();
         //1.获取类信息
         Class c0 = demo.getClass();
 
         //2.获取方法
         try {
             //第一种写法
             Method method1 = c0.getDeclaredMethod( "getMethod" , new Class[]{String. class , String. class });
             //第二种写法
             Method method2 = c0.getDeclaredMethod( "getMethod" , String. class , String. class );
 
             //平时正常的调用方法: demo.getMethod(str0,str1)
             //现在使用method1来调用--public Object invoke(Object obj, Object... args)
             //第一个参数是调用的类,第二个参数是可用可无,按定义的方法来录入(str0,str1)
             //invoke的方法如果有返回值,则返回Object的值,void的返回值为null
             try {
                 Object object1 = method1.invoke(demo, new Object[]{ "hello" , " world" });
                 Object object2 = method2.invoke(demo, "hello" , " world" );
             } catch (IllegalAccessException e) {
                 e.printStackTrace();
             } catch (InvocationTargetException e) {
                 e.printStackTrace();
             }
 
         } catch (NoSuchMethodException e) {
             e.printStackTrace();
         }
 
     }
 
}
 
class MethodDemo {
     //①
     public void getMethod(String a, String b) {
         System.out.println( "concat: " + a + b);
     }
 
     //②
     public void getMethod( int a, int b) {
         System.out.println( "sum: " + a + b);
     }
}

反射和泛型
泛型不说了,非常的常用,比如list,map等等,约定类型,不多做解释,直接先来一个操作,比对List和List<String>是否相等
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
public class Coo {
     public static void main(String[] args) {
         //无泛型
         List list0= new ArrayList();
         //String泛型
         List<String> list1= new ArrayList<>();
         list1.add( "hello" );
 
         Class c0=list0.getClass();
         Class c1=list1.getClass();
         //输出
         System.out.println(c0==c1);
     }
}
输出的结果是true,  编译后的class文件也可以当成字节码,说明反射的操作都是编译之后的操作,而且返回true说明编译之后list的泛型被抹去了,去泛型化的,得到Java的泛型是一种规范,只在编译时有效,跳过编译编译就无效了,为了验证这一点,刚好可以使用反射来做一个验证
[Java]  纯文本查看  复制代码
?
1
2
3
4
5
//获取list1<String> 中的 add方法 ,向里面加一个int类型的值
Method method=c1.getMethod( "add" ,Object. class );
method.invoke(list1, 100 );
 
System.out.println(list1.size());
 
list1的大小改变了,说明添加成功了,也验证了Java泛型只在编译期有效,运行时则去泛型化,如果去遍历这个list1是会报类型转化异常的
反射的用处有很多,比如工具类,源码理解,注解解析等等,再例如excel导出导入这样的操作,网上也有非常多的poi操作案例,也可以用反射+注解的方式非常简洁的实现; 例如spring源码中很多的注解@Autowired、@SpringCloudApplication、@Service...等等很多很多
 
 



  •  

1.png (50.37 KB, 下载次数: 0)

 

1.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值