JAVA反射

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA反射相关的知识,希望对你有一定的参考价值。

JAVA反射

反射是框架设计的灵魂

(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

一、反射的概述

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
    要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

以上的总结就是什么是反射
????反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
????(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

技术分享图片

二、查看Class类在java中的api详解(1.7的API)

    如何阅读java中的api详见java基础之——String字符串处理
技术分享图片
    Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
    Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
    没有公共的构造方法,方法共有64个太多了。下面用到哪个就详解哪个吧

技术分享图片

三、反射的使用? ?

    反射的使用主要有以下几个方面:
        1、获取class对象的三种方式;
        2、通过反射获取构造方式、使用构造方法;
        3、获取成员变量并调用;
        4、获取成员方法并调用;
        5、反射main方法;
        6、反射方法的其它使用之---通过反射运行配置文件内容;
        7、反射方法的其它使用之---通过反射越过泛型检查。

1、获取Class对象的三种方式:?

    1) 调用对象的getClass()方法,该方法是java.lang.Object中的一个方法;
    2) 通过类的class属性来获取该类对应的class对象,如Person.class将返回Person类对应的class对象?
    3) 使用Class类的forName静态方法:forName(String ?className)? ?
    其中第一种是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取

     例子:

1、Student.java

package?fanshe;
public?class?Student?{
????//?---------------构造方法-------------------
????//?(默认的构造方法)
????Student(String?str)?{
????????System.out.println("默认的构造方法s="?+?str);
????}
????//?无参构造方法
????Student()?{
????????System.out.println("调用了共有、无参构造方法执行了......");
????}
????//?有一个参数的构造方法
????Student(char?name)?{
????????System.out.println("姓名:"?+?name);
????}
????//?有多个参数的构造方法
????Student(String?name,?int?age)?{
????????System.out.println("姓名:"?+?name?+?"年龄:"?+?age);
????}
????//?受保护的构造方法
????protected?Student(boolean?n)?{
????????System.out.println("受保护的构造方法n="?+?n);
????}
????//?私有构造方法
????private?Student(int?age)?{
????????System.out.println("私有的构造方法????年龄:"?+?age);
????}
}

2、Fanshe.java

package?fanshe;
/*
?*?获取class对象
?*/
public?class?Fanshe?{
????public?static?void?main(String[]?args)?{
????????//?1.第一种方式:调用对象的getClass()方法获得class对象
????????Student?stu?=?new?Student();?//?new时,创建了一个student对象、一个class对象
????????Class?stuClass1?=?stu.getClass();//?获得class对象
????????System.out.println(stuClass1.getName());
????????//?第二种方式:通过类的class属性来获得该类对应的class对象
????????Class?stuClass2?=?Student.class;
????????System.out.println(stuClass2?==?stuClass1);//?判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
????????//?第三种方式:使用class类的forname静态方法
????????try?{
????????????Class?stuClass3?=?Class.forName("fanshe.Student");//?注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
????????????System.out.println(stuClass3?==?stuClass2);//?判断三种方式是否获取的是同一个Class对象
????????}?catch?(ClassNotFoundException?e)?{
????????????e.printStackTrace();
????????}
????}
}

上面代码运行结果为:???
    调用了共有、无参构造方法执行了......????
    fanshe.Student????
    true???
    true

注意:在运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。

2、通过反射获取构造方式、使用构造方法

调用方法:
1.获取构造方法:?
1) 批量的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
? ? ?
??2) 获取单个的方法,并调用:
? ?public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
? ?public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

??调用构造方法:? ? Constructor-->newInstance(Object... initargs)
2、newInstance是?Constructor类的方法(管理构造函数的类)? api的解释为:? newInstance(Object... initargs)
??使用此?Constructor?对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用
例子:1)?
package?fanshe;
import?java.lang.reflect.Constructor;
/
?
?通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
??
?
?1.获取构造方法:
???????1).批量的方法:
?
??????????public?Constructor[]?getConstructors():所有"公有的"构造方法
????????????public?Constructor[]?getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
?????
???????2).获取单个的方法,并调用:
?
???????????public?Constructor?getConstructor(Class...?parameterTypes):获取单个的"公有的"构造方法:
????????????public?Constructor?getDeclaredConstructor(Class...?parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
?
??????????????
????????????调用构造方法:
?
???????????Constructor-->newInstance(Object...?initargs)
?*/
public?class?Constructors?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????//?1.加载Class对象
????????Class?clazz?=?Class.forName("fanshe.Student");
????????//?2.获取所有公有构造方法
????????System.out.println("**所有公有构造方法***");
????????Constructor[]?conArray?=?clazz.getConstructors();
????????for?(Constructor?c?:?conArray)?{
????????????System.out.println(c);
????????}
????????System.out.println("****所有的构造方法(包括:私有、受保护、默认、公有)*
");
????????conArray?=?clazz.getDeclaredConstructors();
????????for?(Constructor?c?:?conArray)?{
????????????System.out.println(c);
????????}
????????System.out.println("*获取公有、无参的构造方法***");
????????Constructor?con?=?clazz.getConstructor(null);
????????//?1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
????????//?2>、返回的是描述这个无参构造函数的类对象。
????????System.out.println("con?=?"?+?con);
????????//?调用构造方法
????????Object?obj?=?con.newInstance();
????????//?System.out.println("obj?=?"?+?obj);
????????//?Student?stu?=?(Student)obj;
????????System.out.println("**获取私有构造方法,并调用***");
????????con?=?clazz.getDeclaredConstructor(char.class);
????????System.out.println(con);
????????//?调用构造方法
????????con.setAccessible(true);//?暴力访问(忽略掉访问修饰符)
????????obj?=?con.newInstance(‘男‘);
????}
}

2) Student.java 与之前的Student.java一样
运行结果:? ? **所有公有构造方法***????public fanshe.Student(java.lang.String,int)????public fanshe.Student(char)????public fanshe.Student()????****所有的构造方法(包括:私有、受保护、默认、公有)*????private fanshe.Student(int)????protected fanshe.Student(boolean)????public fanshe.Student(java.lang.String,int)????public fanshe.Student(char)????public fanshe.Student()????fanshe.Student(java.lang.String)????*获取公有、无参的构造方法***????con = public fanshe.Student()????调用了公有、无参构造方法执行了。。。????**获取私有构造方法,并调用***????public fanshe.Student(char)????姓名:男
3、获取成员变量并调用例子:1) Student1.java

package?fanshe;br/>public?class?Student1?{
????public?Student1()?{
????}
????public?String?name;
????protected?int?age;
????char?sex;
????private?String?phoneNum;
[email protected]
????public?String?toString()?{
????????return?"Student1?[name="?+?name?+?",?age="?+?age?+?",?sex="?+?sex?+?",?phoneNum="?+?phoneNum?+?"]";
????}
}

2) Fields.java(测试类)

package?fanshe;
import?java.lang.reflect.Field;
import?java.lang.reflect.InvocationTargetException;
public?class?Fields?{
????public?static?void?main(String[]?args)?throws?NoSuchFieldException,?SecurityException,?InstantiationException,
????????????IllegalAccessException,?IllegalArgumentException,?InvocationTargetException,?NoSuchMethodException?{
????????//?TODO?Auto-generated?method?stub
????????//?1.获取class对象
????????Class?clz?=?Student1.class;
????????//?2.获取所有公有的字段
????????System.out.println("----------获取所有公有的字段----------");
????????Field[]?fieldArray?=?clz.getFields();
????????for?(Field?f?:?fieldArray)?{
????????????System.out.println(f);
????????}
????????//?3.获取所有的字段(包括私有、受保护、默认的)
????????System.out.println("----------获取所有的字段(包括私有、受保护、默认的)----------");
????????fieldArray?=?clz.getDeclaredFields();
????????for?(Field?f?:?fieldArray)?{
????????????System.out.println(f);
????????}
????????//?4.获取公有字段并且调用
????????System.out.println("----------获取公有字段并且调用----------");
????????Field?f?=?clz.getField("name");
????????System.out.println(f);
????????Object?obj?=?clz.getConstructor().newInstance();
????????f.set(obj,?"James");
????????Student1?stu?=?(Student1)?obj;
????????System.out.println(stu.name);
????????//?5.获取私有字段并调用
????????System.out.println("----------获取私有字段并且调用----------");
????????f?=?clz.getDeclaredField("phoneNum");
????????System.out.println(f);
????????f.setAccessible(true);//?暴力反射,解除私有限定
????????f.set(obj,?"18888889999");
????????System.out.println("验证电话:"?+?stu);
????}
}
输出结果为:????----------获取所有公有的字段----------????public java.lang.String fanshe.Student1.name????----------获取所有的字段(包括私有、受保护、默认的)----------????public java.lang.String fanshe.Student1.name????protected int fanshe.Student1.age????char fanshe.Student1.sex????private java.lang.String fanshe.Student1.phoneNum????----------获取公有字段并且调用----------????public java.lang.String fanshe.Student1.name????James????----------获取私有字段并且调用----------????private java.lang.String fanshe.Student1.phoneNum????验证电话:Student1 [name=James, age=0, sex=?, phoneNum=18888889999]由此可见调用字段时:需要传递两个参数:Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
//为字段设置值
f.set(obj, "James");//为Student对象中的name属性赋值--》stu.name = "James"
第一个参数:要传入设置的对象,第二个参数:要传入实参
4、获取成员方法并调用例子:
1) Student2.java
package?fanshe;
public?class?Student2?{
????//?**成员方法***//
????public?void?show1(String?s)?{
????????System.out.println("调用了:公有的,String参数的show1():?s?=?"?+?s);
????}
????protected?void?show2()?{
????????System.out.println("调用了:受保护的,无参的show2()");
????}
????void?show3()?{
????????System.out.println("调用了:默认的,无参的show3()");
????}
????private?String?show4(int?age)?{
????????System.out.println("调用了,私有的,并且有返回值的,int参数的show4():?age?=?"?+?age);
????????return?"abcd";
????}
}

2) MethodClass.java(测试类)package?fanshe;
import?java.lang.reflect.Method;
/
?
?获取成员方法并调用:
??
?
?1.批量的:
??public?Method[]?getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
?
?public?Method[]?getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
??2.获取单个的:
?
?public?Method?getMethod(String?name,Class<?>...?parameterTypes):
?????参数:
?
????name?:?方法名;
?????Class?...?:?形参的Class类型对象
?
????public?Method?getDeclaredMethod(String?name,Class<?>...?parameterTypes)
??
?
????调用方法:
?????Method?-->?public?Object?invoke(Object?obj,Object...?args):
?
????参数说明:
?????obj?:?要调用方法的对象;
?
????args:调用方式时所传递的实参;
):
?*/
public?class?MethodClass?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????//?1.获取Class对象
????????Class?stuClass?=?Class.forName("fanshe.Student2");
????????//?2.获取所有公有方法
????????System.out.println("*获取所有的”公有“方法*****");
????????stuClass.getMethods();
????????Method[]?methodArray?=?stuClass.getMethods();
????????for?(Method?m?:?methodArray)?{
????????????System.out.println(m);
????????}
????????System.out.println("*获取所有的方法,包括私有的*****");
????????methodArray?=?stuClass.getDeclaredMethods();
????????for?(Method?m?:?methodArray)?{
????????????System.out.println(m);
????????}
????????System.out.println("*获取公有的show1()方法*****");
????????Method?m?=?stuClass.getMethod("show1",?String.class);
????????System.out.println(m);
????????//?实例化一个Student对象
????????Object?obj?=?stuClass.getConstructor().newInstance();
????????m.invoke(obj,?"刘德华");
????????System.out.println("***获取私有的show4()方法**");
????????m?=?stuClass.getDeclaredMethod("show4",?int.class);
????????System.out.println(m);
????????m.setAccessible(true);//?解除私有限定
????????Object?result?=?m.invoke(obj,?20);//?需要两个参数,一个是要调用的对象(获取有反射),一个是实参
????????System.out.println("返回值:"?+?result);
????}
}

输出结果为:
????*获取所有的”公有“方法*****????public void fanshe.Student2.show1(java.lang.String)????public final void java.lang.Object.wait() throws java.lang.InterruptedException????public final void java.lang.Object.wait(long,int) 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()????*获取所有的方法,包括私有的*****????public void fanshe.Student2.show1(java.lang.String)????private java.lang.String fanshe.Student2.show4(int)????protected void fanshe.Student2.show2()????void fanshe.Student2.show3()????*获取公有的show1()方法*****????public void fanshe.Student2.show1(java.lang.String)????调用了:公有的,String参数的show1(): s = 刘德华????***获取私有的show4()方法**????private java.lang.String fanshe.Student2.show4(int)????调用了,私有的,并且有返回值的,int参数的show4(): age = 20????返回值:abcd由此可见:m = stuClass.getDeclaredMethod("show4", int.class);//调用制定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);? ?其实这里的成员方法:在模型中有属性一词,就是那些setter()方法和getter()方法。还有字段组成,这些内容在内省中详解
5、反射main方法
例子:1) Student3.javapackage?fanshe;
public?class?Student3?{
????public?static?void?main(String[]?args)?{
????????System.out.println("main方法执行了......");
????}
}2) Main.java(测试类)
package?fanshe;
import?java.lang.reflect.Method;
public?class?Main?{
????public?static?void?main(String[]?args)?{
????????try?{
????????????Class?clz?=?Class.forName("fanshe.Student3");
????????????Method?methodMain?=?clz.getMethod("main",?String[].class);
????????????methodMain.invoke(null,?(Object)?new?String[]?{?"a",?"b",?"c"?});
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
}
输出结果:????main方法执行了......

6、反射方法的其它使用之---通过反射运行配置文件内容例子:
1) Student4.javapackage?fanshe;
public?class?Student4?{
????public?void?show()?{
????????System.out.println("is?show()..........");
????}
}

2) Demo.javapackage?fanshe;
import?java.io.FileReader;
import?java.io.IOException;
import?java.lang.reflect.Method;
import?java.util.Properties;
/
?
?我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
??我们只需要将新类发送给客户端,并修改配置文件即可
?
/
public?class?Demo?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????//?通过反射获取Class对象
????????Class?stuClass?=?Class.forName(getValue("className"));//?"cn.fanshe.Student"
????????//?2获取show()方法
????????Method?m?=?stuClass.getMethod(getValue("methodName"));//?show
????????//?3.调用show()方法
????????m.invoke(stuClass.getConstructor().newInstance());
????}
????//?此方法接收一个key,在配置文件中获取相应的value
????public?static?String?getValue(String?key)?throws?IOException?{
????????Properties?pro?=?new?Properties();//?获取配置文件的对象
????????FileReader?in?=?new?FileReader("pro.txt");//?获取输入流
????????pro.load(in);//?将流加载到配置文件对象中
????????in.close();
????????return?pro.getProperty(key);//?返回根据key获取的value值
????}
}

3) pro.txt(此文件不要放在src下面,直接放在项目下)className=fanshe.Student4
methodName=show

运行结果:????is show()..........
【优势】? ??当我们升级这个系统时,不要Student类,而需要新写一个Student2的类时,这时只需要更改pro.txt的文件内容就可以了。代码就一点不用改动
要替换的student4类:public?class?Student2?{
????public?void?show2(){
????????System.out.println("is?show2()......");
????}
}
只需要将配置文件修改为:className?=?cn.fanshe.Student2
methodName?=?show2
此时的输出结果为:? ? is show2()......

7、反射方法的其它使用之---通过反射越过泛型检查泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的测试类:import?java.lang.reflect.Method;
import?java.lang.reflect.Method;
import?java.util.ArrayList;
/
?通过反射越过泛型检查

?例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
*/
public?class?Demo?{
????public?static?void?main(String[]?args)?throws?Exception{
????????ArrayList<String>?strList?=?new?ArrayList<>();
????????strList.add("aaa");
????????strList.add("bbb");
????????//strList.add(100);
????????//获取ArrayList的Class对象,反向的调用add()方法,添加数据
????????Class?listClass?=?strList.getClass();?//得到?strList?对象的字节码?对象
????????//获取add()方法
????????Method?m?=?listClass.getMethod("add",?Object.class);
????????//调用add()方法
????????m.invoke(strList,?100);
????????//遍历集合
????????for(Object?obj?:?strList){
????????System.out.println(obj);
????}
}
控制台输出:aaa
bbb
100

//反射就总结到这,下面的内省章节也和反射有关,可以算是反射的高级使用吧,如果有兴趣,可以继续查看总结的内省部分。

【参考文献】[1].https://blog.csdn.net/sinat_38259539/article/details/71799078


以上是关于JAVA反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景