学到spring框架的时候,发现反射思想很重要,故特此写下此文,以加深理解。
文章目录
- 1:反射概述
- 2:Class对象特点
- 3:反射的使用
- 1:获取类对象
- 2 利用反射机制创建对象
- 3: 获取成员变量并使用
- 4: 获取成员方法并使用
- 5: 获取main方法并使用
- 4 : 关于反射的用法举例
- 1:通过反射运行配置文件内容
- 2:通过反射越过泛型检查
1:反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
实际上,我们创建的每一个类也都是对象,即类本身是java.lang.Class类的实例对象。这个实例对象称之为类对象,也就是Class对象。那么,Class对象又是什么对象呢?
2:Class对象特点
下图是Class类的api(图片来自于Java基础之—反射(非常重要))
![在这里插入图片描述](https://img-blog.csdnimg.cn/20181029101808836.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpbGlsdW5p,size_27,color_FFFFFF,t_70)
从图中可以得出以下几点:
- Class 类的实例对象表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有很多的实例,每个类都有唯一的Class对象。
- Class 类没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机自动构造的。也就是说我们不需要创建,JVM已经帮我们创建了。
- Class 对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法
3:反射的使用
假设我们现在有一个Hero类
package pojo;
public class Hero {
public String name;
public float hp;
public float armor;
public int moveSpeed;
}
1:获取类对象
获取类对象有3种方式
Class.forName()
(常用)Hero.class
new Hero().getClass()
在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样。(此处准确是在ClassLoader下,只有一个类对象)
示例:
package pojo;
public class ObjectTest {
public static void main(String[] args) {
String className = "pogo.Hero";
try {
Class pClass1 = Class.forName(className);
Class pClass2 = Hero.class;
Class pClass3 = new Hero().getClass();
System.out.println(pClass1==pClass2);
System.out.println(pClass1==pClass3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
三种方式中,常用第一种,第二种需要导入类的包,依赖太强,不导包就抛编译错误。第三种对象都有了还要反射干什么。一般都第一种,一个字符串可以传入也可写在配置文件中等多种方法。
2 利用反射机制创建对象
基本步骤
与传统的通过new 来获取对象的方式不同反射机制,反射会先拿到Hero的“类对象”,然后通过类对象获取“构造器对象”再通过构造器对象创建一个对象,具体步骤:
1.获取类对象 Class class = Class.forName("pojo.Hero");
2.获取构造器对象 Constructor con = clazz.getConstructor(形参.class);
3 获取对象 Hero hero =con.newInstance(实参);
上面是最简单的获取方法,当Hero的构造方法不是无参构造方法时,获取构造器对象略有不同,见下面测试:
构造方法不同时,获取构造器对象的方法
示例:
- Hero类添加6种构造方法
Hero(String str){
System.out.println("(默认)的构造方法 s = " + str);
}
public Hero(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
public Hero(char name){
System.out.println("姓名:" + name);
}
public Hero(String name ,float hp){
System.out.println("姓名:"+name+"血量:"+ hp);
}
protected Hero(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
private Hero(float hp){
System.out.println("私有的构造方法 血量:"+ hp);
}
- 通过反射机制获取对象
package test;
public class ConstructorTest {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("pojo.Hero");
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);
System.out.println("con = " + con);
Object obj = con.newInstance();
System.out.println("******************获取私有构造方法,并调用*******************************");
con = clazz.getDeclaredConstructor(float.class);
System.out.println(con);
con.setAccessible(true);
obj = con.newInstance(100);
}
}
输出:
**********************所有公有构造方法*********************************
public pojo.Hero(java.lang.String,float)
public pojo.Hero(char)
public pojo.Hero()
************所有的构造方法(包括:私有、受保护、默认、公有)***************
private pojo.Hero(float)
protected pojo.Hero(boolean)
public pojo.Hero(java.lang.String,float)
public pojo.Hero(char)
public pojo.Hero()
pojo.Hero(java.lang.String)
*****************获取公有、无参的构造方法*******************************
con = public pojo.Hero()
调用了公有、无参构造方法执行了。。。
******************获取私有构造方法,并调用*******************************
private pojo.Hero(float)
私有的构造方法 血量:100.0
总结:
1.获取构造器对象方法:
1).批量的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2).获取单个的方法:
public Constructor getConstructor(Class… parameterTypes): 获取单个的"公有的"构造方法
public Constructor getDeclaredConstructor(Class…parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
3: 获取成员变量并使用
基本步骤
1.获取HeroPlus类的对象 new方法/第2章中的方法
h
2. 获取属性 Field f1 = h.getDeclaredField("属性名")
3. 修改属性 f1.set(h,实参)
,注意这里的h是对象,不是类对象
示例:
- 新增HeroPlus类
package pojo;
public class HeroPlus {
public String name;
public float hp;
public int damage;
public int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroPlus(){
}
public HeroPlus(String string) {
name =string;
}
@Override
public String toString() {
return "Hero [name=" + name + "]";
}
public boolean isDead() {
return false;
}
public void attackHero(HeroPlus h2) {
System.out.println(this.name+ " 正在攻击 " + h2.getName());
}
}
- 获取属性并修改
package test;
public class ParaTest {
public static void main(String[] args) {
HeroPlus h =new HeroPlus();
h.name = "garen";
try {
Field f1= h.getClass().getDeclaredField("name");
f1.set(h, "teemo");
System.out.println(h.name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
补充:
getField和getDeclaredField的区别
getField 只能获取public的,包括从父类继承来的字段。
getDeclaredField 可以获取本类所有的字段,包括private的,但是 不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))
4: 获取成员方法并使用
- 获取HeroPlus类的对象
h
- 获取成员方法:
public Method getMethod(String name ,Class<?>… parameterTypes):获取"公有方法";(包含了父类的方法也包含Object类)
public Method getDeclaredMethods(String name ,Class<?>… parameterTypes) :获取成员方法,包括私有的(不包括继承的)
参数解释:
name : 方法名;
Class … : 形参的Class类型对象 - 调用方法
Method --> public Object invoke(Object obj,Object… args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参;
示例:
package test;
public class MethodTest {
public static void main(String[] args) {
HeroPlus h = new HeroPlus();
try {
Method m = h.getClass().getMethod("setName", String.class);
m.invoke(h, "盖伦");
System.out.println(h.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
5: 获取main方法并使用
示例:
- HeroPlus 新增main方法
public static void main(String[] args) {
System.out.println("执行main方法");
}
- 通过下面步骤获取main方法
package test;
public class MainTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("pojo.HeroPlus");
Method methodMain = clazz.getMethod("main", String[].class);
methodMain.invoke(null, (Object)new String[]{"a","b","c"});
} catch (Exception e) {
e.printStackTrace();
}
}
}
4 : 关于反射的用法举例
反射非常强大,但是从上面的记录来看,反而觉得还不如直接调用方法来的直接和方便。
通常来说,需要在学习了Spring 的依赖注入,反转控制之后,才会对反射有更好的理解,所以先这里举两个例子,来演示一下反射的一种实际运用。
1:通过反射运行配置文件内容
- 首先准备两个业务类
package service;
public class Service1 {
public void doService1(){
System.out.println("业务方法1");
}
}
package service;
public class Service2 {
public void doService2(){
System.out.println("业务方法2");
}
}
- 当需要从第一个业务方法切换到第二个业务方法的时候,使用非反射方式,必须修改代码,并且重新编译运行,才可以达到效果
package service;
public class CommonTest {
public static void main(String[] args) {
new Service2().doService2();
}
}
- 使用反射方式则方便很多
- 首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。
里面存放的是类的名称,和要调用的方法名。首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。里面存放的是类的名称,和要调用的方法名。 - 在测试类Test中,首先取出类名称和方法名,然后通过反射去调用这个方法。
- 当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。
示例:
spring.txt内容
class=reflection.Service1
method=doService1
测试类
package service;
public class ReflectTest {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) throws Exception {
File springConfigFile = new File("H:\\eclpise-workspace\\reflect-demo\\src\\spring.txt");
Properties springConfig= new Properties();
springConfig.load(new FileInputStream(springConfigFile));
String className = (String) springConfig.get("class");
String methodName = (String) springConfig.get("method");
Class clazz = Class.forName(className);
Method m = clazz.getMethod(methodName);
Constructor c = clazz.getConstructor();
Object service = c.newInstance();
m.invoke(service);
}
}
2:通过反射越过泛型检查
泛型是在编译期间起作用的。在编译后的.class文件中是没有泛型的。所有比如T或者E类型啊,本质都是通过Object处理的。所以可以通过使用反射来越过泛型。
示例:
package test;
public class GenericityTest {
public static void main(String[] args) throws Exception{
ArrayList<String> list = new ArrayList<>();
list.add("this");
list.add("is");
Class listClass = list.getClass();
Method m = listClass.getMethod("add", Object.class);
m.invoke(list, 5);
for(Object obj : list){
System.out.println(obj);
}
}
}
GitHub源码:java反射
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)