Java反射
前言
Github:https://github.com/HealerJean
一、泛型
1、Class
1.1、获取 Class
的几种方法
/**
* 获取Class
*/
@Test
public void test1() throws ClassNotFoundException {
Class reflectClass = Class.forName("com.healerjean.proj.reflect.ReflectDTO");
reflectClass = ReflectDTO.class;
reflectClass = new ReflectDTO().getClass();
}
1.2、Class.forName
类已加载并且这个类已连接,这是正是class的静态方法forName()完成的工作。
public class ClassMain {
// 静态的参数初始化 //只会执行一次
static {
System.out.println("--静态的参数初始化--");
}
// 非静态的参数初始化 //动态 new 或者 newInstance 实例化对象的时候执行,可执行多次
{
System.out.println("--非静态的参数初始化--");
}
public ClassMain() {
System.out.println("ClassTest!");
}
public static void main(String[] args) throws ClassNotFoundException {
//类已加载并且这个类已连接,这是正是class的静态方法forName()完成的工作。
Class clazz = Class.forName("com.healerjean.proj.reflect._class.ClassMain");
System.out.println(clazz);
}
// --静态的参数初始化--
// class com.healerjean.proj.reflect._class.ClassMain
1.3、new Object()
、 class.newInstance()
1、使用newInstance
可以解耦。使用newInstance
的前提是,类已加载并且这个类已连接,这是正是class的静态方法forName()
完成的工作。newInstance
实际上是把new 这个方式分解为两步,即,首先调用class的加载方法加载某个类,然后实例化
2、newInstance: 弱类型。低效率。只能调用无参构造。 new Object(): 强类型。相对高效。能调用任何public构造。
public class ClassMain {
// 静态的参数初始化 //只会执行一次
static {
System.out.println("--静态的参数初始化--");
}
// 非静态的参数初始化 //动态 new 或者 newInstance 实例化对象的时候执行,可执行多次
{
System.out.println("--非静态的参数初始化--");
}
public static void main(String[] args) throws Exception {
// 下面二者的是一样的结果
// Class.forName("com.healerjean.proj.reflect._class.ClassMain").newInstance();
// --静态的参数初始化--
// --非静态的参数初始化--
// ClassTest!
new ClassMain();
// --静态的参数初始化--
// --非静态的参数初始化--
// ClassTest!
}
}
1.4、非静态的参数实例化
非静态的参数初始化,动态
new
或者newInstance
实例化对象的时候执行,可执行多次
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class.forName("com.healerjean.proj.reflect._class.ClassMain").newInstance();
new ClassMain();
// --静态的参数初始化--
// --非静态的参数初始化--
// ClassTest!
// --非静态的参数初始化--
// ClassTest!
}
1.5、Java
动态加载类和静态加载类的区别
new
创建对象的方式称作为静态加载
Class.forName("XXX")
称作为动态加载
1.5.1、Java
动态加载类和静态加载类的区别
它们俩本质的区别在于静态加载的类的源程序在编译时期加载(必须存在),而动态加载的类在编译时期可以缺席(源程序不必存在)。
1.5.2、为什么需要动态加载类
动态加载类增加了程序的灵活性。比如一个程序中有50个功能,但你可能只会使用其中的一个,如果你用的是静态加载的方式,你必须在编译前提供100个功能的所有定义,否则无法编译通过,若你使用的是动态加载机制,则不需要如此大费周章,用哪一个就定义哪一个即可
2、Field
2.1、DTO
1、祖父 GrandFatherDTO
@Data
public class GrandFatherDTO {
private Long privateId;
private String privateName;
private String privateGrandVar;
public Long publicId;
public String publicName;
public String publicGrandVar;
}
2、父亲 FatherDTO
@Data
public class FatherDTO extends GrandFatherDTO {
private Long privateId;
private String privateName;
private String privatefatherVar;
public Long publicId;
public String publicName;
public String publicFatherVar;
}
3、ReflectDTO
@Data
public class ReflectDTO extends FatherDTO {
private Long privateId;
private String privateName;
private Integer privateAge;
private BigDecimal privateMoney;
private Date privateDate;
public Long publicid;
public String publicName;
public Integer publicAge;
public BigDecimal publicMoney;
public Date publicDate;
}
2.1、getFields()
、getDeclaredFields()
Class<?> demoClass = Class.forName("com.reflect.Demo");
2.1.1、getFields()
1、能访问类中声明为 public 的字段
2、能访问其它类继承来的 public 的字段
(父亲(包括祖父)和自己内部有同一个字段名,都能获取到),
/** 1、getFields()
* 1、能访问类中声明为public的字段**
* 2、能访问其它类继承来的public的字段**
*(父亲(包括祖父)和自己内部有同一个字段名,都能获取到),**
*/
@Test
public void testGetFields() {
Class reflectDTOClass = ReflectDTO.class;
Field[] fields = reflectDTOClass.getFields();
Arrays.stream(fields).forEach(field -> log.info(field.getName()));
// publicId
// publicName
// publicAge
// publicMoney
// publicDate
// publicId
// publicName
// publicFatherVar
// publicId
// publicName
// publicGrandVar
}
2.1.2、getDeclaredFields()
1、能够访问类中所有的字段
2、不能访问其他类继承的字段
/**
* 1.2、getDeclaredFields :能够访问类中所有的字段,不能方位其他类继承的字段
*/
@Test
public void testGetDeclaredFields() {
Class reflectDTOClass = ReflectDTO.class;
Field[] fields = reflectDTOClass.getDeclaredFields();
Arrays.stream(fields).forEach(field -> log.info(field.getName()));
// privateId
// privateName
// privateAge
// privateMoney
// privateDate
// publicid
// publicName
// publicAge
// publicMoney
// publicDate
}
2.1.3、通过反射获取一个类包括父类中来的所有的字段
/**
* 1.3、反射获取该类所有字段属性的值(包括从父类继承来的)
*/
@Test
public void getAllFiled() throws IllegalAccessException {
ReflectDTO obj = new ReflectDTO();
obj.setPrivateId(0L);
obj.setPrivateName("");
Set<Field> allFields = new HashSet<>();
Class tempClass = obj.getClass();//这样就获取了这个对象的一些值了
while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {
allFields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
tempClass = tempClass.getSuperclass();
}
Set<String> nameSet = new HashSet<>();
if (!EmptyUtil.isEmpty(allFields)) {
for (Field field : allFields) {
field.setAccessible(true);
if (nameSet.add(field.getName())) {
if (!"serialVersionUID".equals(field.getName()) && field.get(obj) != null) {
log.info("字段名:【{}】;值:【{}】", field.getName(), field.get(obj));
}
}
}
}
}
2.2、getField()
、getDeclaredField()
1、
getField(
) 只能取的共有属性,不能取得私有属性2、
getDeclaredField()
通过暴力反射可以获取私有属性
/**
* 2、getField() getDeclaredField()
* getField 只能取的共有属性,不能取得私有属性
* getDeclaredField 通过暴力反射可以获取私有属性
*/
@Test
public void test4() throws NoSuchFieldException, IllegalAccessException {
ReflectDTO obj = new ReflectDTO();
obj.setPrivateName("privateName_VALUE");
obj.setPublicName("publicName_VALUE");
Field publicFieldName = obj.getClass().getField("publicName");
log.info("getField获取公有属性:【{}】", publicFieldName.get(obj));//publicName_VALUE
Field privateFieldName = obj.getClass().getDeclaredField("privateName");
//提示有,但是取不出来,报错,所以下面加上暴力反射可以获取 java.lang.IllegalAccessException: Class com.healerjean.proj.reflect.field.D01_FieldMain can not access a member of class com.healerjean.proj.reflect.ReflectDTO with modifiers "private"
privateFieldName.setAccessible(true);
log.info("getDeclaredField获取私有属性:【{}】", privateFieldName.get(obj));//privateName_VALUE
// Field fieldName = obj.getClass().getField("privateName");
// 下面抛出异常getField 不能获取私有属性 java.lang.NoSuchFieldException: privateName
// log.info("getField获取私有属性:【{}】", fieldName.get(obj));
}
2.3、getType()
、getGenericType()
如果属性是一个泛型,从
getType(
)只能得到这个属性的接口类型。但从getGenericType()
还能得到这个泛型的参数类型,所以一般情况下使用getGenericType
1、
getType()
: 返回class
类型2、
getGenericType()
: 返回Type
类型
/**
* 4、获取字段类型
* getType(): 获取属性声明时类型对象(返回class对象)
* getGenericType() : 返回属性声的Type类型
* 如果属性是一个泛型,从getType()只能得到这个属性的接口类型。但从getGenericType()还能得到这个泛型的参数类型。
* 所以一般情况下使用 getGenericType
*/
@Test
public void getType() throws NoSuchFieldException {
ReflectDTO obj = new ReflectDTO();
Field nameField = obj.getClass().getField("publicName");
log.info("String类型:getType :" + nameField.getType().toString());//class java.lang.String
log.info("String类型:getGenericType :" + nameField.getGenericType().toString());//class java.lang.String
Field dataField = obj.getClass().getField("publicDate");
log.info("Date类型:getType :" + dataField.getType());//:class java.util.Date
log.info("Date类型:getGenericType :" + dataField.getGenericType());//class java.util.Date
Field listFile = obj.getClass().getField("list");
log.info("List类型:getType :" + listFile.getType().toString());//interface java.util.List
log.info("List类型:getGenericType :" +
listFile.getGenericType().toString());//java.util.List<java.lang.String>
}
String类型:getType :class java.lang.String
String类型:getGenericType :class java.lang.String
Date类型:getType :class java.util.Date
Date类型:getGenericType :class java.util.Date
List类型:getType :interface java.util.List
List类型:getGenericType :java.util.List<java.lang.String>
2.4、其他基本方法
2.4.1、 field.getName()
获取属性名
2.4.2、 field.get(obj)
获取属性的值
public class Point {
private int x;
public int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
Point p = new Point(3,5);
Field fieldY = p.getClass().getField("y");
System.out.println(fieldY.get(pt1)); // 5
2.4.3、field.set(obj, value)
;
2.5、给利用 Field
反射给对象赋值
/**
* 4、给对象赋值
*/
@Test
public void setValue() {
ReflectDTO obj = new ReflectDTO();
Field[] fields = obj.getClass().getDeclaredFields();
Map<String, String> map = new HashMap<>();
map.put("publicName", "publicName_value");
map.put("privateName", "privateName_value");
Set<String> fieldNames = map.keySet();
for (String fieldName : fieldNames) {
for (Field field : fields) {
if (field.getName().equals(fieldName)) {
setFieldValue(field, obj, map.get(fieldName));
}
}
}
log.info("obj:{}", obj);
}
/**
* 为属性赋值
*
* @param field 要赋值的属性
* @param obj 属性所属对象
* @param value 值
*/
private void setFieldValue(Field field, Object obj, String value) {
try {
if (field == null || obj == null || value == null || "".equals(value)) {
return;
}
field.setAccessible(true);
String fieldType = field.getGenericType().toString();
if ("class java.lang.String".equals(fieldType)) {
field.set(obj, value);
}
if ("class java.lang.Integer".equals(fieldType)) {
Integer val = Integer.valueOf(value);
field.set(obj, val);
}
if ("class java.math.BigDecimal".equals(fieldType)) {
BigDecimal bde = new BigDecimal(value);
field.set(obj, bde);
}
if ("class java.util.Date".equals(fieldType)) {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
Date date = df.parse(value);
field.set(obj, date);
}
} catch (Exception ex) {
log.error("对象赋值失败", ex);
throw new BusinessException("处理失败");
}
}
3、Constructor
public class ConstructorPerson {
String str ;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public ConstructorPerson() {
System.out.println("无参构造器");
}
public ConstructorPerson(String str) {
System.out.println("有参 String str 构造器");
this.str = str;
}
/**
* 私有构造函数
*/
private ConstructorPerson(String str1,int n) {
System.out.println("有参 私有构造器");
this.str = str1;
}
}
3.1、 无参构造器
//反射构造函数:public Person() 无参构造器
public static void test1() throws Exception{
Class clazz = Class.forName("com.hlj.reflex.ConstructorPerson");
Constructor c = clazz.getConstructor();
// Constructor c = clazz.getConstructor(null); //一样
System.out.println(c); //打印 public com.hlj.reflex.ConstructorPerson()
}
3.2、有参构造器
//反射构造函数:public Person(String name)
public static void test2() throws Exception{
Class clazz = Class.forName("com.hlj.reflex.ConstructorPerson");
Constructor c = clazz.getConstructor(String.class);
System.out.println(c);
ConstructorPerson person = (ConstructorPerson)c.newInstance("测试成功");
System.out.println(person.str);
}
/打印
public com.hlj.reflex.ConstructorPerson(java.lang.String)
有参 String str 构造器
测试成功
3.3、私有构造器,暴力反射
//反射私有的构造函数:private Person(String str1 ,int n )
public static void test3() throws Exception{
Class clazz = Class.forName("com.hlj.reflex.ConstructorPerson");
Constructor c = clazz.getDeclaredConstructor(String.class,int.class);
c.setAccessible(true);//暴力反射
ConstructorPerson p = (ConstructorPerson) c.newInstance("私有构造器",2);
System.out.println(p.str);
}
4、Method
public class MethodPerson {
private String name;//名字
private String type;//类型
private int camp;//0,近卫;1,天灾
public MethodPerson(){}
public MethodPerson(String name, String type, int camp) {
super();
this.name = name;
this.type = type;
this.camp = camp;
}
@Override
public String toString() {
return "MethodPerson [\n name=" + name + ", \n type=" + type + ", \n camp=" + camp + "\n]";
}
}
4.1、获取全部的公开的方法
private static void getMethods() {
Class<?> methodPersonClass = MethodPerson.class;
Method[] methods = methodPersonClass.getMethods();
for (Method method : methods) {
System.out.println(method);
System.out.println(method.getName());
}
}
4.2、获取某个方法,并执行
private static void getMethodSome() {
Class<?> methodPersonClass = MethodPerson.class;
MethodPerson methodPerson = (MethodPerson)methodPersonClass.newInstance();
Method setNameMethod = methodPersonClass.getMethod("setName",String.class);
Method getNameMethod = methodPersonClass.getMethod("getName");
setNameMethod.invoke(methodPerson,"setNameMethod.setName变量");
System.out.println("调用get方法:"+getNameMethod.invoke(methodPerson));
}
二、工具类
@Slf4j
public final class ReflectUtils {
/**
* 反射获取Class字段列表
*
* @param clazz Class<?>
* @return Class字段列表
*/
public static List<String> getFieldNameList(Class<?> clazz) {
List<String> fieldNameList = new ArrayList<>();
getFieldList(clazz).forEach(f -> fieldNameList.add(f.getName()));
return fieldNameList;
}
/**
* 获取class所有Field对象
*
* @param clazz Class
* @return Field列表
*/
public static List<Field> getFieldList(Class<?> clazz) {
List<Field> fieldList = new ArrayList<>();
while (null != clazz) {
Field[] fieldArr = clazz.getDeclaredFields();
fieldList.addAll(Arrays.asList(fieldArr));
clazz = clazz.getSuperclass();
}
return fieldList;
}
}