Java的动态性--反射

4 篇文章 0 订阅

反射可以说是Java基础当中相当重要的知识了,因为反射为Java语言实现了动态性,同时也为Spring、Struts2、Hibernate、Mybatis、SpringMVC等框架提供了实现的可能性。众所周知,Java不同于那些动态语言,如Javascript、Ruby、Python,Java是一门静态语言,但为了实现动态性,Java提供了一个包:java.lang.reflect,这个包为Java实现动态性提供了基础,即反射,所有与反射有关的类都处于该包下。

一、类的加载

要搞清楚反射机制,首先得知道在Java中类的加载机制。我们编写的.java文件首先被编译器编译成一个字节码文件(即.class文件),然后在程序运行时,虚拟机JVM的类加载器会将此字节码文件加载到内存的方法区中(一种特殊的堆),在该方法区中包含了类的运行时数据,同时还会在该方法区中生成一个对应的java.lang.Class对象,该对象时作为外部访问该对象的入口。(注意:一个类只会被虚拟机加载一次



二、反射的核心对象--Class

java.lang.Class类时整个反射技术中最重要的对象,所有的其他反射对象都是通过Class类得到到,因此可以说Class是整个反射的源头。从上图中我们也可以看到,Class对象是外部访问该类对象的入口,在虚拟机加载完每一个类的同时都会对应生成一个相应的Class对象,它就像每个类的镜子一样,可以从该Class中获取到对应类的所有结构。既然Class是所有反射的源头,那该如何获取到该Class对象呢?

三、如何获取到Class对象

获得类所对应的Class对象有通常有四种方法,这里为了便于举例说明,就先定义了一个Student类:

package com.tiantang.reflection;

public class Student {
	// 私有属性
	private String name;
	//公有的成员变量
	public int age;

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Student() {

	}

	public Student(String name) {
		this.name = name;
	}

}

下面通过获取Student类所对应的Class对象来举例说明,如何获取Class对象

(1)利用类名.class获得

例如:

(2)利用Class类中的静态方法Class.forName(str)

形参str指的是你所要获取Class所对应的类的全名

例如:

(3)利用对象.getClass()方法

每一个类都是Object的子类,而Object类中有一个getClass()方法,可以通过该方法来得到类所对应的Class对象

例如:先创建一个Student对象:

然后通过对象.getClass()方法得到Class对象:

(4)通过类加载器获得

在这里我们先创建一个TestClass类,我们在TestClass类中获得Student类所对应的Class对象

例如:先获取到类加载器:

然后利用类加载器的loadClass(str)方法获得:

loadClass()方法中的形参str指的是你所要获取Class所对应的类的全名。

以上通过四种方式分别获得了Student类所对应的Class对象(clazz,clazz2,clazz3,clazz4),由于虚拟机对每个类只会加载一次,因此这四个Class对象时同一个对象,读者可以分别打印一下这四个对象的hash值。


四、通过Class对象来获得Student类的完整结构

Class类中提供了相应的方法来供我们获取到类的完整结构,例如获得类的属性、方法、构造器、注解、类名、类全名、权限修饰符、方法的返回值类型等等。

(1)获得类的属性

Class类中提供了四个方法,都可以来获得类的属性。

①:Field:getField(String name):通过属性名来获取属性,返回的是一个Field类型的对象

②:Field[]:getFields():该方法获得的是本类以及父类的所有权限为public的属性对象,返回值是一个Field类型的数组

③:Field:getDeclaredField(String name):通过属性名获取本类中定义的属性(自己定义的属性,不论是public还是private),返回值是一个Field对象。

④:Fields[]:getDeclaredFields()::获取本类中所有自己定义的属性(自己定义的属性,不论是public还是private),返回值是一个Field类型的数组。

通过以上四个方法,可以获取到类中的属性,这四个方法大致可以分为两组,要注意其中的区别。

Field对象:从Field对象中我们可义得到属性名(getName()),属性的权限修饰符(getModifiers(),该方法返回的是一个int型的值,如果要将其变为public,private,protected等字符串,则需要用如下方法:Modifier.toString(int)),同时还可以获得属性的值,给属性赋值,属性的注解等等,Field提供了一些列对属性的操作的方法。读者可以阅读jdk的源码或者相关的API去了解。

(2)获得类的方法

和获得属性类似,Class类也提供了四个方法来获得类的方法。

①:Method:getMethod(String name):根据方法名获得方法,返回的是Method对象;

②:Method[]:getMethods:返回的是本类及父类中所有权限为public的方法所组成的数组;

③:Method:getDeclaredMethod(String name):根据方法名获得本类中自己定义的方法

④:Method[]:getDeclaredMethods:获得本类中自己定义的方法所组成的数组。

同样这四个方法可以分为两组,在使用时应该注意他们的区别。

Method对象:该对象表示的是方法所对应的类,通过该对象,可以得到方法的注解,方法名,修饰符,返回值类型,以及调用invoke方法来调用对象中的方法。读者可以阅读jdk的源码或者相关的API去了解。

(3)获得类的构造器

与(1)(2)类似,同样是四个方法,建议读者多去看看源码,笔者就不一一列举了,因为这与上面都是重复的,读者只要理解了其中之一,就能明白其他的。

(4)获取父类以及实现的接口

①:获得父类:getSuperclass();

②:获得带泛型化的父类:getGenericSuperclass();

③:获得实现的接口:getInterfaces()以及getGenericInterfaces()

这里笔者重点解释一下父类和泛型化的父类,以及如何获取父类的泛型化参数

我们先定义一个People类,让People类带泛型:

然后再定义一个Student类,并让Student实现People类,并让泛型具体化:

测试代码:

打印结果:

从结果中发现:getSuperclass()获得的是不带泛型的父类,而getGenericSuperclass()得到父类带上了泛型,这就是两者的区别。

那什么是获得父类的泛型化参数呢?

从上面的例子中,我们知道在Student继承People是,让泛型T具体化了,即具体泛型是String,获得父类的泛型化参数就是获得该String类对象。

那应该如何获得呢?


这段代码十分重要,以后会经常用到,用到的最多指出就是在JDBC中,Dao具体操作哪一类对象时就必须通过反射获得,不过这段代码基本是套路,读者如果无法理解的话,也可以收藏下来,多敲几次。

(5)获得注解

Class类中提供了一些方法来获取类上的注解,如:getAnnotation(Class)和getAnnotations(),前者返回的是Annotation对象,或者返回的是Annotation[]数组。得到Annotation对象后,读者就可以得到注解的具体信息。笔者会在下一篇博客(文章名:利用反射和注解模拟ORM框架中的自动建表功能)里写一个利用反射获得注解,然后拼接sql语句的练习,有点类似ORM框架里的通过反射和JavaBean的结构自动创建表。

五、反射的应用

关于反射的应用,用到的地方实在是太多了,笔者就不细说了,读者可以去看看这篇文章( 设计模式之代理模式),以及笔者后续的一篇文章( 利用反射和注解模拟ORM框架中的自动建表功能

六、总结

关于反射的基本内容差不多就这么多了,当然,笔者在这里介绍的知识一点皮毛,不过足以入门反射了,想要学的更深,还得不断的读源码以及实战,希望读者也能继续加油,笔者也会继续深入理解反射以及更多关于类加载的过程。如果想走的远,个人认为就必须得把反射学得彻底,这样我们才能更好的理解框架,甚至尝试自己去写框架。


  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
兼容Java,iOS,Android三端的AES-128-ECB加密算法,附三端Demo Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems公司于1995年5月正式发布。它的设计目标是“一次编写,到处运行(Write Once, Run Anywhere)”,这意味着开发者可以使用Java编写应用程序,并在支持Java的任何平台上无需重新编译即可运行,这得益于其独特的跨平台性,通过Java虚拟机(JVM)实现不同操作系统上的兼容。 Java的特点包括: 面向对象:Java全面支持面向对象的特性,如封装、继承和多态,使得代码更易于维护和扩展。 安全:Java提供了丰富的安全特性,如禁止指针运算、自动内存管理和异常处理机制,以减少程序错误和恶意攻击的可能性。 可移植性:Java字节码可以在所有安装了JVM的设备上执行,从服务器到嵌入式系统,再到移动设备和桌面应用。 健壮性与高性能:Java通过垃圾回收机制确保内存的有效管理,同时也能通过JIT编译器优化来提升运行时性能。 标准库丰富:Java拥有庞大的类库,如Java SE(Java Standard Edition)包含基础API,用于开发通用应用程序;Java EE(Java Enterprise Edition)提供企业级服务,如Web服务、EJB等;而Java ME(Java Micro Edition)则针对小型设备和嵌入式系统。 社区活跃:Java有着全球范围内庞大的开发者社区和开源项目,持续推动技术进步和创新。 多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的角色,是现代软件开发不可或缺的重要工具之一。
Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems公司于1995年5月正式发布。它的设计目标是“一次编写,到处运行(Write Once, Run Anywhere)”,这意味着开发者可以使用Java编写应用程序,并在支持Java的任何平台上无需重新编译即可运行,这得益于其独特的跨平台性,通过Java虚拟机(JVM)实现不同操作系统上的兼容。 Java的特点包括: 面向对象:Java全面支持面向对象的特性,如封装、继承和多态,使得代码更易于维护和扩展。 安全:Java提供了丰富的安全特性,如禁止指针运算、自动内存管理和异常处理机制,以减少程序错误和恶意攻击的可能性。 可移植性:Java字节码可以在所有安装了JVM的设备上执行,从服务器到嵌入式系统,再到移动设备和桌面应用。 健壮性与高性能:Java通过垃圾回收机制确保内存的有效管理,同时也能通过JIT编译器优化来提升运行时性能。 标准库丰富:Java拥有庞大的类库,如Java SE(Java Standard Edition)包含基础API,用于开发通用应用程序;Java EE(Java Enterprise Edition)提供企业级服务,如Web服务、EJB等;而Java ME(Java Micro Edition)则针对小型设备和嵌入式系统。 社区活跃:Java有着全球范围内庞大的开发者社区和开源项目,持续推动技术进步和创新。 多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的角色,是现代软件开发不可或缺的重要工具之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值