它是反射框架的灵魂,spring mvc的底层是通过反射机制的xml配置实现的。本文将通过实例详细讲解Java中的反射机制,感兴趣的朋友可以向边肖学习。
目录
一、什么是反思二。反思的原则。反思的利与弊。反射的用途。反射的基本用途
一、什么是反射
(1)1)Java反射机制的核心是动态加载类,获取程序运行时类的详细信息,从而操作类或对象的属性和方法。本质上,JVM获取类对象,然后通过类对象进行反编译,从而获取对象的各种信息。
(2)Java是一种编译然后运行的语言。程序中的对象类型是在编译时确定的,而有些类可能需要在程序运行时动态加载。这些类没有被加载到JVM中,因为它们以前没有被使用过。通过反射,您可以动态地创建对象,并在运行时调用它们的属性,而无需事先知道在编译时运行的对象是谁。
二、反射的原理
下图显示了该类的正常加载过程、反射原理和类对象:
类对象的起源是读取。类文件放到内存中,并为它创建一个类对象。
三、反射的优缺点
1.优点:我们可以在运行时获取类的各种内容,并进行反编译。对于Java这种先编译后运行的语言,我们可以轻松地创建灵活的代码。这些代码可以在运行时组装,组件之间不需要链接源代码,更容易实现面向对象。
2.缺点:(1)反射会消耗一定的系统资源,所以如果不需要动态创建对象,那么就不需要使用反射;
(2)反射调用方法时可以忽略权限检查,因此可能破坏封装,导致安全问题。
四、反射的用途
1.反编译:同学们。Java语言(一种计算机语言,尤用于创建网站)
2.通过反射机制访问java对象的属性、方法和构造方法。
3.我们在使用IDE的时候,比如Ecplise,当我们输入一个对象或者类,想要调用它的属性和方法的时候,只要点号一按,编译器就会自动列出它的属性或者方法。这里使用了反射。
4.反射最重要的用途是开发各种通用框架。例如,配置了许多框架(Spring)(例如,通过XML文件配置了Bean)。为了保证框架的通用性,可能需要根据配置文件加载不同的类或对象,调用不同的方法。此时,它们必须使用反射,并在运行时动态加载所需的加载对象。
5.例如,在使用Strut2框架的开发过程中,我们通常在struts.xml中配置动作,比如
操作名称='登录'类='组织。“sczyhsoft . test . action . simpleloginaction”方法=“execute”
结果/商店/商店索引. JSP/结果
结果名称='error'login.jsp/result
/操作
例如,当我们请求登录时。动作,那么StrutsPrepareAndExecuteFilter会解析struts.xml文件,从动作中找出动作名login,根据class属性创建一个SimpleLoginAction的实例,用Invoke方法调用execute方法。这个过程离不开反思。配置文件与动作建立了映射关系。当视图层发出请求时,请求会被strutsprepareendexecutefilter拦截,然后strutsprepareendexecutefilter会动态创建一个Action实例。
比如加载数据库驱动的时候,也会用到反射。
class . forname(' com . MySQL . JDBC . driver ');//动态加载mysql驱动
五、反射机制常用的类
Java . lang . class;
Java . lang . reflect . constructor;
Java . lang . reflect . field;
Java.lang.reflect .方法;
Java . lang . reflect . modifier;
六、反射的基本使用
1.获取该类有三种主要方式:
(1)Object - getClass
(2)任何数据类型(包括基本数据类型)都有一个“静态”的类属性。
(3)类的静态方法:forName(字符串类名)(最常用)
包凡舍;
公共课范舍{
公共静态void main(String[] args) {
//获取类对象的第一种方法
学生stu1=新学生();//这个新的产生一个学生对象和一个类对象。
class stu class=stu 1 . getclass();//获取类对象
system . out . println(stu class . getname());
//获取类对象的第二种方法
class stu class 2=student . class;
system . out . println(stu class==stu class 2);//判断第一种方式得到的类对象与第二种方式得到的类对象是否相同。
//获取类对象的第三种方式
尝试{
class stu class 3=class . forname(' fanshe。学生’);//注意,这个字符串必须是真实路径,即包含包名、包名和类名的类路径。
system . out . println(stu class 3==stu class 2);//判断三种方法得到的类对象是否相同。
} catch(ClassNotFoundException e){
e . printstacktrace();
}
}
}
注意,运行时,一个类只生成一个类对象,所以打印出来的结果都是真的;
在这三种方法中,第三种是常用的。为什么第一个对象可用的时候要反射?第二个需要导入类包,依赖性太强。如果不引导包,就会抛出编译错误。一般用第三种方法。可以在配置文件中传递或写入字符串。
2.判断是否是一个类的例子:
通常,我们使用instanceof关键字来确定它是否是一个类的实例。同时我们也可以在反射中使用类对象的isInstance()方法来判断什么时候是类的实例,是原生方法。
公共本机布尔值isInstance(对象obj);
3.创建实例:通过反射生成对象有两种主要方式:
(1)使用类对象的newInstance()方法创建类对象对应类的实例。
班级?c=String.class
object str=c . new instance();
(2)先通过类对象获取指定的构造函数对象,然后调用构造函数对象的newInstance()方法创建对象。此方法可以用指定的构造函数构造类的实例。
//获取字符串的类对象
班级?str=String.class
//通过类对象获取指定的构造函数对象
constructor constructor=c . get constructor(string . class);
//根据构造函数创建一个实例:
object obj=constructor . new instance(" hello reflection ");
4.通过反射获得构造方法并使用:
(1)批量采集的方法:
public constructor[]get constructors():所有“公共”构造函数
public constructor[]getDeclaredConstructors():获取所有构造方法(包括私有、受保护、默认、公共)
(2)单次采集的方法,并调用:
公共构造函数get构造函数(类.参数类型):获取单个“公共”构造函数:
公共构造函数GetDeclaredConstructor(类.parametertypes):获取“构造函数”可以是私有的、受保护的、默认的和公共的。
(3)调用施工方法:
构造函数- newInstance(对象.initargs)
NewInstance是构造函数类(管理构造函数的类)的一个方法
api被解释为:newInstance(Object.initargs),它使用此构造函数对象表示的构造函数创建此构造函数的声明类的新实例,并用指定的初始化参数初始化该实例。
它的返回值是T类型,所以newInstance是一个新的实例对象,创建构造函数的声明类并调用它。
示例:
学生类:有六种构造方法。
包凡舍;
公共课学生{
//-
//(默认构造方法)
学生(字符串字符串){
System.out.println('(默认)构造方法s=' str);
}
//无参数构造方法
公共学生(){
'调用公共的、无参数的构造函数来执行。');
}
//只有一个参数的构造方法
公立学生(char name){
system . out . println(' name:' name ');
}
//多参数构造方法
公共学生(字符串名称,整数){
system . out . println(' name:' name ' age:' age);//这个的执行效率有问题,后面会解决。
}
//受保护的构造方法
受保护的学生(布尔n){
System.out.println('受保护的构造方法n=' n);
}
//私有构造方法
私人学生(年龄限制){
System.out.println('私有的构造方法年龄:'年龄);
}
}
测试类:
包凡舍;
导入Java。郎。反思。建造师;
/*
* 通过班级对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
*公共构造函数[] getConstructors():所有'公有的'构造方法
公共构造函数[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
*公共构造函数getConstructor(类.参数类型):获取单个的'公有的'构造方法:
*公共构造函数getDeclaredConstructor(类.参数类型):获取'某个构造方法'可以是私有的,或受保护、默认、公有;
* 3).调用构造方法:
*构造函数- newInstance(对象.initargs)
*/
公共类构造函数{
公共静态void main(String[] args)引发异常{
//1.加载班级对象
class clazz=class。forname(' fanshe .学生');
//2.获取所有公有构造方法
系统。出去。println(' * * * * * * * * * * * * * * * * * * *)所有公有构造方法*********************************');
构造函数[]con array=clazz。获取构造函数();
对于(构造函数c : conArray){
系统。出去。println(c);
}
系统。出去。println(' * * * * * * * * * * * * *)所有的构造方法(包括:私有、受保护、默认、公有)***************');
con array=clazz。getdeclaredconstructors();
对于(构造函数c : conArray){
系统。出去。println(c);
}
系统。出去。println(' * * * * * * * * * * * * * * * * * '获取公有、无参的构造方法*******************************');
构造函数con=clazz。获取构造函数(null);
//1、因为是无参的构造方法所以类型是一个空,不写也可以:这里需要的是一个参数的类型,切记是类型
//2、返回的是描述这个无参构造函数的类对象。
系统。出去。println(' con=' con);
//调用构造方法
object obj=con . new实例();
//系统。出去。println(' obj=' obj);
//Student stu=(Student)obj;
系统。出去。println(' * * * * * * * * * * * * * * * * * * *)获取私有构造方法,并调用*******************************');
con=clazz。getdeclaredconstructor(char。类);
系统。出去。println(con);
//调用构造方法
con . set accessible(真);//暴力访问(忽略掉访问修饰符)
obj=con.newInstance('男');
}
}
控制台输出:
**********************所有公有构造方法*********************************
公范社Student(java.lang.String,int)
公范社。学生(字符)
公范社。学生()
************所有的构造方法(包括:私有、受保护、默认、公有)***************
二等兵樊舍。学生(整数)
保护了樊舍。学生(布尔型)
公范社Student(java.lang.String,int)
公范社。学生(字符)
公范社。学生()
樊舍。学生(java.lang.String)
*****************获取公有、无参的构造方法*******************************
con=公范社。学生()
调用了公有、无参构造方法执行了。
******************获取私有构造方法,并调用*******************************
公范社。学生(字符)
姓名:男
5、获取成员变量并调用:
学生类:
包fanshe.field
公共课学生{
公共学生(){
}
//**********字段*************//
公共字符串名称;
受保护的年龄;
迷人的性爱;
私有字符串电话号码
@覆盖
公共字符串toString() {
返回'学生[姓名='姓名,年龄='年龄,性别='性别
,电话号码='电话号码']';
}
}
测试类:
包fanshe.field
导入Java。郎。反思。场;
/*
* 获取成员变量并调用:
*
* 1.批量的
* 1).Field[] getFields():获取所有的'公有字段'
* 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
* 2.获取单个的:
* 1).公共字段getField(字符串字段名):获取某个'公有的'字段;
* 2).公共字段getDeclaredField(字符串字段名):获取某个字段(可以是私有的)
*
* 设置字段的值:
* Field - public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2 .价值:要为字段设置的值;
*/
公共类字段{
公共静态void main(String[] args)引发异常{
//1.获取班级对象
class stu class=class。forname(' fanshe。字段。学生’);
//2.获取字段
系统。出去。println(' * * * * * * * * * * * * *)获取所有公有的字段********************');
field[]field array=stu class。获取字段();
对于(字段f : fieldArray){
系统。出去。println(f);
}
系统。出去。println(' * * * * * * * * * * * * *)获取所有的字段(包括私有、受保护、默认的)********************');
field array=stu class。getdeclaredfields();
对于(字段f : fieldArray){
系统。出去。println(f);
}
系统。出去。println(' * * * * * * * * * * * * *)获取公有字段**并调用***********************************');
字段f=stu类。getfield(“name”);
系统。出去。println(f);
//获取一个对象
对象obj=stu类。获取构造函数().新实例();//产生学生对象-》学生stu=新生();
//为字段设置值
f.set(obj,刘德华');//为学生对象中的名字属性赋值- 》stu.name='刘德华'
//验证
学生斯图=(学生obj
System.out.println('验证姓名:'斯图。姓名);
系统。出去。println(' * * * * * * * * * * * * * * *)获取私有字段****并调用********************************');
f=stu类。getdeclaredfield(“phoneNum”);
系统。出去。println(f);
f。设置可访问性(true);//暴力反射,解除私有限定
f.set(obj,' 188889999 ');
System.out.println('验证电话:'斯图);
}
}
控制台输出:
************获取所有公有的字段********************
公共Java。郎。弦乐反串。场。学生。名字
************获取所有的字段(包括私有、受保护、默认的)********************
公共Java。郎。弦乐反串。场。学生。名字
受保护的int fanshe.field.Student.age
char fanshe.field.Student.sex
私有Java。郎。弦乐反串。场。学生。电话号码
*************获取公有字段**并调用***********************************
公共Java。郎。弦乐反串。场。学生。名字
验证姓名:刘德华
**************获取私有字段****并调用********************************
私有Java。郎。弦乐反串。场。学生。电话号码
验证电话:学生[姓名=刘德华,年龄=0,性别=
6、获取成员方法并调用:
学生类:
包fanshe.method
公共课学生{
//**************成员方法***************//
公共void show1(字符串s){
System.out.println('调用了:公有的,字符串参数的show 1():s=' s);
}
受保护的void show2(){
System.out.println('调用了:受保护的,无参的show 2()');
}
void show3(){
System.out.println('调用了:默认的,无参的show 3()');
}
私有字符串显示4(整数){
System.out.println('调用了,私有的,并且有返回值的,int参数的show 4():age=' age);
返回ABCD ';
}
}
测试类:
包fanshe.method
导入Java。郎。反思。方法;
/*
* 获取成员方法并调用:
*
* 1.批量的:
*公共方法[] getMethods():获取所有'公有方法;(包含了父类的方法也包含目标类)
*公共方法[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
*公共方法获取方法(字符串名,类?参数类型):
* 参数:
*名称:方法名;
*类别.形参的班级类型对象
*公共方法getDeclaredMethod(字符串名,类?参数类型)
*
* 调用方法:
*方法-公共对象调用(对象obj,对象.参数):
* 参数说明:
*目标:要调用方法的对象;
*参数:调用方式时所传递的实参;
):
*/
公共类方法类别{
公共静态void main(String[] args)引发异常{
//1.获取班级对象
class stu class=class。forname(' fanshe。方法。学生’);
//2.获取所有公有方法
系统。出去。println(' * * * * * * * * * * * * * * *)获取所有的"公有"方法*******************');
斯图课。get方法();
方法[]方法数组=stu类。get方法();
for(Method m : methodArray){
系统。出去。println(m);
}
系统。出去。println(' * * * * * * * * * * * * * * *)获取所有的方法,包括私有的*******************');
方法数组=stu类。getdeclaredmethods();
for(Method m : methodArray){
系统。出去。println(m);
}
系统。出去。println(' * * * * * * * * * * * * * * *)获取公有的展示1()方法*******************');
方法m=stuClass.getMethod('show1 ',string。类);
系统。出去。println(m);
//实例化一个学生对象
对象obj=stu类。获取构造函数().新实例();
调用(obj,刘德华');
系统。出去。println(' * * * * * * * * * * * * * * *)获取私有的展示4()方法******************');
m=stu类。getdeclaredmethod(' show 4 ',int。类);
系统。出去。println(m);
m。设置可访问性(true);//解除私有限定
Object result=m.invoke(obj,20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println('返回值:'结果);
}
}
控制台输出:
***************获取所有的"公有"方法*******************
公空樊舍。方法。学生。展示1(Java。郎。字符串)
public final void Java。郎。对象。wait(long,int)抛出Java . lang . interrupted异常
公共最终原生void Java。郎。对象。等待(长时间)抛出Java . lang . interrupted异常
public final void Java。郎。对象。等待()抛出Java . lang . interrupted异常
公共布尔型Java。郎。对象。等于(Java。郎。对象)
公共Java。郎。string Java。郎。对象。tostring()
公共本机int Java。郎。对象。哈希码()
public final native Java。郎。Java类。郎。对象。获取类()
公共最终原生void Java。郎。对象。通知()
公共最终原生void Java。郎。对象。notifyall()
***************获取所有的方法,包括私有的*******************
公空樊舍。方法。学生。展示1(Java。郎。字符串)
私人Java。郎。弦乐反串。方法。学生。显示4(整数)
受保护的虚空凡舍。方法。学生。显示2()
虚空凡舍。方法。学生。显示3()
***************获取公有的展示1()方法*******************
公空樊舍。方法。学生。展示1(Java。郎。字符串)
调用了:公有的,字符串参数的show1(): s=刘德华
***************获取私有的展示4()方法******************
私人Java。郎。弦乐反串。方法。学生。显示4(整数)
调用了,私有的,并且有返回值的,int参数的show4():年龄=20
返回值:abcd
7、反射主要的方法:
学生类:
包fanshe.main
公共课学生{
公共静态void main(String[] args) {
System.out.println('main方法执行了。');
}
}
测试类:
包fanshe.main
导入Java。郎。反思。方法;
/**
* 获取学生类的主要的方法、不要与当前的主要的方法搞混了
*/
公共类主要{
公共静态void main(String[] args) {
尝试{
//1、获取学生对象的字节码
class clazz=class。forname(' fanshe。主要的。学生’);
//2、获取主要的方法
方法方法main=clazz。get方法(' main ',String[].类);//第一个参数:方法名称,第二个参数:方法形参的类型,
//3、调用主要的方法
//methodMain.invoke(null,new String[]{'a ',' b ',' c ' });
//第一个参数,对象类型,因为方法是静电静态的,所以为空可以,第二个参数是线数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
//这里拆的时候将新字符串[]{'a ',' b ',' c'}拆成3个对象。所以需要将它强转。
methodMain.invoke(null,(Object)new String[]{'a ',' b ',' c ' });//方式一
//methodMain.invoke(null,new Object[]{new String[]{'a ',' b ',' c ' } });//方式二
} catch(异常e) {
e。printstacktrace();
}
}
}
控制台输出:
主要的方法执行了。
8、利用反射创建数值:
数组在Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)里是比较特殊的一种类型,它可以赋值给一个对象引用。
公共静态void testArray()引发ClassNotFoundException {
班级?cls=类。forname(' Java。郎。string’);
对象数组=数组。新实例(cls,25);
//往数组里添加内容
Array.set(array,0,' golang ');
Array.set(array,1,' Java ');
Array.set(array,2,' python ');
Array.set(array,3,' Scala ');
Array.set(array,4,' clo jure ');
//获取某一项的内容
系统。出去。println(数组。get(array,3));
}
9、反射方法的其他使用-通过反射运行配置文件内容:
学生类:
公共课学生{
公共void show(){
系统。出去。println(' is show()');
}
}
配置文件以文本文件(文本文件)文件为例子:
className=cn.fanshe.Student
方法名=显示
测试类:
导入Java。io。filenotfoundexception
导入Java。io。filereader
导入Java。io。io异常;
导入Java。郎。反思。方法;
导入Java。util。属性;
/*
* 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
* 我们只需要将新类发送给客户端,并修改配置文件即可
*/
公开课演示{
公共静态void main(String[] args)引发异常{
//通过反射获取班级对象
class stu class=class。forname(getValue('类名'));//'cn.fanshe.Student '
//2获取显示()方法
方法m=stu类。get方法(getValue('方法名'));//显示
//3.调用显示()方法
m。调用(stu类。获取构造函数().new instance());
}
//此方法接收一个钥匙,在配置文件中获取相应的价值
公共静态字符串getValue(字符串键)引发IOException{
Properties pro=new Properties();//获取配置文件的对象
文件阅读器in=新文件阅读器(' pro。txt’);//获取输入流
专业负载(英寸);//将流加载到配置文件对象中
英寸close();
返回pro。getproperty(key);//返回根据键获取的价值值
}
}
控制台输出:
正在显示()
需求:
当我们升级这个系统时,不要学生类,而需要新写一个学生2的类时,这时只需要更改专业版的文件内容就可以了。代码就一点不用改动。
公共课学生2 {
public void show2(){
系统。出去。println(' is show 2()');
}
}
配置文件更改为:
className=cn.fanshe.Student2
方法名称=显示2
10、反射方法的其他使用-通过反射越过泛型检查:
泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的
测试类:
导入Java。郎。反思。方法;
导入Java。util。ArrayList
/*
* 通过反射越过泛型检查
* 例如:有一个线泛型的集合,怎样能向这个集合中添加一个整数类型的值?
*/
公开课演示{
公共静态void main(String[] args)引发异常{
ArrayList字符串strList=new ArrayList();
strlist。add(' AAA ');
strlist。add(' BBB ');
//strlist。添加(100);
//获取ArrayList的类对象,反向调用add()方法,添加数据。
class list class=strlist . getclass();//获取strList对象的字节码对象
//获取add()方法
method m=list class . get method(' add ',object . class);
//调用add()方法
m.invoke(strList,100);
//遍历集合
for(Object obj : strList){
system . out . println(obj);
}
}
}
控制台输出:
美国汽车协会
血脑屏障
100
关于Java 10分钟掌握反射机制原理的文章到此为止。有关Java反射机制的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望你以后能支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。