Java内省用法,反射与内省 --- 应该知道的常见用法

目录

反射 (Reflect)

反射的原理

使用反射操作属性

使用反射操作构造函数

无参数构造方法

有参数构造方法

使用反射操作方法

实例方法

静态方法

Modifier解析修饰符

内省 (Introspector)

JavaBean 的规范

MethodUtils

ConstructorUtils

PropertyUtils

BeanUtils

ConvertUtils

反射和内省的区别

反射 (Reflect)

在框架开发中,都是基于配置文件开发的,在配置文件中配置了类,可以通过读取配置文件中的类名,然后通过反射得到类中的所有内容,或是让类中的某个方法来执行。

也就是说,反射是在运行时获取一个类的所有信息,可以获取到 .class 的任何定义的信息(包括成员 变量,成员方法,构造器等)可以操纵类的字段、方法、构造器等部分。

反射的原理

得到 class 文件

把 java 文件保存到本地硬盘,得到 .java 文件

编译 java 文件,得到 .class 文件

JVM 把 .class 文件加载到内存中,class 文件在内存中使用 Class 类表示

通过 class 文件得到 Class 类,可以通过以下 3 种方式获得 Class 类

通过成员变量获得: 类名.class

通过具体对象获得: 对象.getClass()

通过 Class 的静态方法获取: Class.forName("classFilePath")

通过 Class 类获取 class 文件中的内容,包括:成员变量,构造方法,普通方法,它们都可以用相应的类表示:

成员方法:Field

构造方法:Constructor

普通方法:Method

使用反射操作属性

public void test3() {

try {

Class c2 = Class.forName("cn.itcast.test09.Person"); // 得到Class类

Person p11 = (Person) c2.newInstance(); // 得到Person类的对象,返回

Field[] fields = c2.getDeclaredFields(); // 得到所有的属性,返回一个Field数组

Field f1 = c2.getDeclaredField("name"); // 通过属性名称得到特定属性,参数是属性的名称

// 如果操作的是私有的属性,不让操作,可以通过setAccessible(true)操作私有属性

f1.setAccessible(true);

f1.set(p11, "wangwu"); // 设置name值,相当于p.name = "wangwu";

System.out.println(f1.get(p11)); // 相当于 p.name

}catch(Exception e) {

e.printStackTrace();

}

}

使用反射操作构造函数(创建类的实例)

无参数构造方法

通过 Class 对象的 newInstance() 方法创建。

public void test1() throws Exception {

Class c3 = Class.forName("cn.itcast.test09.Person");

// 无参数的构造方法就是直接使用newInstance()方法

Person p = (Person) c3.newInstance();

p.setName("zhangsan");

System.out.println(p.getName());

}

有参数构造方法

不能再通过 Class 对象的 newInstance() 方法创建了,要先得到要调用的构造函数的 Consturctor 对象,然后通过 Constructor 对象的 newInstance() 方法创建。

public void test2() throws Exception {

Class c1 = Class.forName("cn.itcast.test09.Person");

// 获取所有的构造方法

Constructor[] css = c1.getConstructors();

// 获取特定的构造方法:传递是有参数的构造方法里面参数类型,类型使用class的形式传递

Constructor cs = c1.getConstructor(String.class, String.class);

// 通过有参数的构造方法创建Person实例,而不是通过Class的对象

Person p1 = (Person) cs.newInstance("lisi","100");

System.out.println(p1.getId()+" "+p1.getName());

}

使用反射操作方法

实例方法

public void test4() throws Exception {

Class c4 = Class.forName("cn.itcast.test09.Person");

Person p4 = (Person) c4.newInstance();

// 得到所有的普通方法

Method[] mds = c4.getDeclaredMethods();

// 得到特定的普通方法,传递两个参数:第一个参数:方法名称;第二个参数:方法里面参数的类型

Method m1 = c4.getDeclaredMethod("setName", String.class);

// 使用invoke执行方法,传递两个参数:第一个参数:person实例;第二个参数:设置的值

// 在这里要传入person对象的原因是:我们需要知道到底是哪一个对象的setName方法执行了

// 如果要操作的是私有的方法 ,需要 m1.setAccessible(true);

m1.invoke(p4, "niuqi");

System.out.println(p4.getName());

}

静态方法

静态方法调用方式是 类名.方法名,不需要类的实例,所以使用反射操作静态方法的时候,也是不需要实例的,在 invoke 方法的第一个参数传入 null 即可: m1.invoke(null, "niuqi");。

例如,我们利用反射操作一个主方法 main 方法(在学习《深入理解Java虚拟机》时遇到了这个用法:

Class clazz = ...

try {

Method method = clazz.getMethod("main", new Class[] { String[].class});

method.invoke(null, new String[] {null});

} catch (Throwable e){

e.printStackTrace();

}

Modifier解析修饰符

利用反射我们可以获得类的属性、构造器、方法等,那么如何判断类或变量、方法的修饰符呢?

Java针对类、成员变量、方法,有很多修饰符,例如public、private、static、final、synchronized、abstract等,这些修饰符用来控制访问权限或其他特性。

通过查看源码,我们发现,Field、Method、Constructor 都直接或间接实现了 Member 接口,看下Member接口:Member 表示一个类中的成员,包括成员变量、方法、构造方法三种实现。

Java 文档:

Interface Member

所有已知实现类:

Constructor , Executable , Field , 方法

Member是一个反映关于单个成员(字段或方法)或构造函数的标识信息的接口。

Member接口有个方法:

int getModifiers() 作为整数返回由此 Member所表示的成员或构造方法的 Java语言修饰符。

我们想要得到的应该是一个表示修饰符的字符串,却得到的是一个整数,那么一个整数表示怎样的修饰符或者这个整数怎么转化为修饰符呢?

在这里,需要用到java.lang.reflect.Modifier这个类。Modifier提供了很多静态方法。

static String toString(int mod) 返回描述指定修饰符中的访问修饰符标志的字符串。

static boolean isPrivate(int mod) 如果整数参数包含 private修饰符,则返回 true , false返回false。

static boolean isProtected(int mod) 如果整数参数包含 protected修饰符,则返回 true , false false。

static boolean isPublic(int mod) 如果整数参数包含 public修饰符,则返回 true , false false。

static boolean isStatic(int mod) 如果整数参数包含 static修饰符,则返回 true , false false。

如 public static String toString(int mod) 就可以输出该整数对应的所有的修饰符。

public static boolean isPublic(int mod) 就可以判断该整数对应的是不是包含public修饰符。

我们再来看下 Modifier 源码:

public static final int PUBLIC = 0x00000001;

public static final int PRIVATE = 0x00000002;

public static final int PROTECTED = 0x00000004;

public static final int STATIC = 0x00000008;

public static final int FINAL = 0x00000010;

public static final int SYNCHRONIZED = 0x00000020;

public static final int VOLATILE = 0x00000040;

public static final int TRANSIENT = 0x00000080;

public static final int NATIVE = 0x00000100;

public static final int INTERFACE = 0x00000200;

public static final int ABSTRACT = 0x00000400;

public static final int STRICT = 0x00000800;

static final int BRIDGE = 0x00000040;

static final int VARARGS = 0x00000080;

static final int SYNTHETIC = 0x00001000;

static final int ANNOTATION = 0x00002000;

static final int ENUM = 0x00004000;

static final int MANDATED = 0x00008000;

把它们转换成二进制,可以看出,其实 Modifier 使用一个二进制的位来表示是否包含某个修饰符。

而接口 Member 中有个方法,返回的整数就是该成员表示所有修饰符的数字的和的值:

public int getModifiers();

内省 (Introspector)

内省是基于反射实现的,主要用于操作 JavaBean,相比反射使用起来要方便一些。可以获取 bean 的 getter/setter 方法,也就是说,只要 JavaBean 有 getXxx() 方法,不管这个 Bean 有没有 Xxx 属性,使用内省我们都认为它有。

为了更好的理解,在讲解内省之前,我们先来介绍一下 JavaBean 的规范。

JavaBean 的规范

JavaBean是一种特殊的类,主要用于传递数据信息。

必须有一个public的无参数构造器。

属性可以通过get、set、is(可以替代get,用在布尔型属性上)方法或遵循特定命名规范的其他方法访问。

可序列化。

提供 get/set 方法,如果只有 get 方法,那么这个属性是只读属性。

属性:有 get/set 方法的成员,还可以没有成员,只有 get/set 方法。属性名称由 get/set 方法来决定,而不是成员名称。

我的理解:属性是一个逻辑概念,能访问通过符合规则的方法访问到的就是属性!

实例代码:

/**

* @author ycw

*/

import java.io.Serializable;

public class UserInfo implements Serializable { //可序列化

// 属性名:使用驼峰命名法。

private String UserId;

private String userName;

private boolean isVIP;

// 有一个public的无参数构造器。

public UserInfo() {

}

public UserInfo(String userId, String userName, boolean isVIP) {

UserId = userId;

this.userName = userName;

this.isVIP = isVIP;

}

public UserInfo(String userId, String userName) {

UserId = userId;

this.userName = userName;

}

public String getUserId() {

return UserId;

}

public void setUserId(String userId) {

UserId = userId;

}

public String getUserName() {

return userName;

}

public void setUserName(String userName) {

this.userName = userName;

}

// is 可以替代 get,用在布尔型属性上

public boolean isVIP() {

return isVIP;

}

public void setVIP(boolean VIP) {

isVIP = VIP;

}

@Override

public String toString() {

return "UserInfo{" +

"UserId='" + UserId + '\'' +

", userName='" + userName + '\'' +

", isVIP=" + isVIP +

'}' ;

}

}

MethodUtils

MethodUtils 通过反射访问对象的方法并且执行方法。

/**

* @author ycw

*/

public class MethodUtilsTest {

@Test

/* MethodUtils通过反射访问对象的方法并且执行方法。*/

public void test1() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException {

UserInfo user1 = new UserInfo();

// 直接通过反射获得类对象,方法,执行

Class> clazz = Class.forName("~.model.UserInfo");

Method method = clazz.getMethod("setUserName", String.class);

method.invoke(user1, "令狐冲");

System.out.println(user1);

// 通过方法名和参数类型获得可访问方法

method = MethodUtils.getAccessibleMethod(UserInfo.class, "setUserName", String.class);

method.invoke(user1, "东方不败");

System.out.println(user1);

// 可以直接通过invokeMethod执行方法

MethodUtils.invokeMethod(user1, "setUserName", "任我行");

System.out.println(user1);

}

}

执行结果:

UserInfo{UserId='null', userName='令狐冲', isVIP=false}

UserInfo{UserId='null', userName='东方不败', isVIP=false}

UserInfo{UserId='null', userName='任我行', isVIP=false}

ConstructorUtils

ConstructorUtils 通过反射提供了构造方法相关的便捷操作方法。

/**

* @author ycw

*/

public class ConstructorUtilsTest {

@Test

public void test2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

// 直接通过反射获得类对象,构造器,创建实例

Class> clazz = Class.forName("org.simplespring.commonsBeanutilsTest.model.UserInfo");

Constructor> constructor1 = clazz.getConstructor(String.class, String.class, boolean.class);

Object user1 = constructor1.newInstance("独孤九剑弟子1号", "令狐冲", true);

System.out.println(user1);

// ConstructorUtils 通过反射提供了构造方法获取的便捷操作方法。

Constructor constructor2 = ConstructorUtils.getAccessibleConstructor(UserInfo.class, new Class[]{String.class, String.class, boolean.class});

UserInfo user2 = constructor2.newInstance("魔教1号", "东方不拜", true);

System.out.println(user2);

// ConstructorUtils 创建实例更简洁的方法

UserInfo user3 = ConstructorUtils.invokeConstructor(UserInfo.class, new String[]{"魔教老1号", "任我行"});

System.out.println(user3);

}

}

执行结果:

UserInfo{UserId='独孤九剑弟子1号', userName='令狐冲', isVIP=true}

UserInfo{UserId='魔教1号', userName='东方不拜', isVIP=true}

UserInfo{UserId='魔教老1号', userName='任我行', isVIP=false}

PropertyUtils

PropertyUtils 通过反射提供了对象属性的便捷操作方法。

/**

* @author ycw

*/

public class PropertyUtilsTest {

@Test

public void test3() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {

UserInfo user1 = new UserInfo("魔教1号", "东方不拜", true);

UserInfo user2 = new UserInfo("魔教老1号", "任我行");

System.out.println(user1);

System.out.println(user2);

// 使用 PropertyUtils 将一个 bean 实例的所有属性全部拷贝到另一个实例上,注意属性会完全覆盖,使用时应注意!

PropertyUtils.copyProperties(user2, user1);

System.out.println(user2);

// 为一个实例设置特定属性值

PropertyUtils.setProperty(user1, "userName", "东方必败");

System.out.println(user1);

}

}

执行结果:

UserInfo{UserId='魔教1号', userName='东方不拜', isVIP=true}

UserInfo{UserId='魔教老1号', userName='任我行', isVIP=false}

UserInfo{UserId='魔教1号', userName='东方不拜', isVIP=true}

UserInfo{UserId='魔教1号', userName='东方必败', isVIP=true}

BeanUtils

BeanUtils 通过反射提供了Bean对象的便捷操作方法。

/**

* @author ycw

*/

public class BeanUtilsTest {

@Test

public void test4() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

// BeanUtils 是一个更为通用的工具,通过反射提供了Bean对象的便捷操作方法。

UserInfo user1 = new UserInfo("魔教1号", "东方不拜", true);

UserInfo user2 = new UserInfo("魔教老1号", "任我行");

// 可以代替 PropertyUtils 提供一些操作属性的方法

// 复制属性

BeanUtils.copyProperties(user2, user1);

System.out.println(user2);

// 获取属性

System.out.println(BeanUtils.getProperty(user2, "userName"));

// 设置属性

BeanUtils.setProperty(user2, "userName", "任盈盈");

System.out.println(user2);

// 封装 Map 数据到 JavaBean 对象中

UserInfo user3 = new UserInfo();

Map properties = new HashMap<>();

properties.put("userId", "华山掌门");

properties.put("userName", "岳不群");

BeanUtils.populate(user3, properties);

System.out.println(user3);

}

}

执行结果:

UserInfo{UserId='魔教1号', userName='东方不拜', isVIP=true}

东方不拜

UserInfo{UserId='魔教1号', userName='任盈盈', isVIP=true}

UserInfo{UserId='华山掌门', userName='岳不群', isVIP=false}

ConvertUtils

ConvertUtils 提供了数据类型相互转换的方法。

最基本的 convert() 方法将一个字符串转化为特定类型的实例对象。

public static Object convert(String value, Class> clazz) {

return ConvertUtilsBean.getInstance().convert(value, clazz);

}

示例:

/**

* @author ycw

*/

public class ConvertUtilsTest {

@Test

public void test5() {

// 将一个字符串转化为特定类型的实例对象。

String numStr = "123";

int num = (int) ConvertUtils.convert(numStr, Integer.class);

System.out.println(num);

}

}

执行结果:

123

反射和内省的区别

反射就像给类照镜子,这个的所有信息会毫无保留的反射到镜子中,将这个类的所有信息照出来,能照出来就是有,照不出来就是没有,得到的东西都是客观真实存在的。

而内省的目的是找出 bean 的 getter 和 setter 以便操作这个 bean,所以只要看到有 getter 或者 setter 就认为这个类有那么一个字段,比如看到 getName() 内省就会认为这个类中有 name 字段,但事实上并不一定会有 name。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值