Java高级之反射

Java语言高级内容

为日后看懂源码打下基础💪

Ps: 以下环境均使用Java8、如果Java8以上请携带VM参数、Java9模块后对反射做了保护

–add-opens java.base/java.util=ALL-UNNAMED

一、反射

Class类

Example: 可以通过Object.class获取类的所有信息 --ps :这点也适用于接口、枚举等

Class<String> cls = String.class
Class<Integer> clc =  Integer.class

Class可以通过静态方法forName。可以根据类名直接加载Class对象

Class<?> cls = Class.forName("java.util.HashMap");
//注意、当找不到的时候回抛出ClassNotFoundException
1、名称信息
public String getName() //返回Java内部使用的真正名称
public String getSimpleName()	//返回不带包名的信息
public String getCanonicalName()	//返回的名称更友好
public Package getPackage()	//返回包信息

在这里插入图片描述

2、字段信息(Field类)
public Field[] getFields() //返回所有public字段,包括父类的
public Field[] getDeclaredFields() //返回本类声明的所有字段,包括非public的,但是不包括父类
public File getFiled(String name) //返回本类、或者父类指定名称的public字段、找不到抛出异常
public Filed getDeclaredFiled() //返回本类声明指定的字段、找不到同上抛出NoSuchFiledException

同时Filed也有很多方法、可以获取字段的信息,也可以通过Field访问和操作指定对象中改字段的值

一下是Field字段的方法

//获取字段名称
public String getName()
//判断当前程序是否有改字段的权限
public boolean isAccessible()
//(关键)设置为ture的时候可以忽略Java访问检查机制,允许读写非public字段
public void setAccessible(boolean flag)
//获取指定对象obj中的改字段的值
public Object get(Object obj)
//将指定对象obj中改字段的值设为value
public void set(Object obj , Object value)
//返回字段的修饰符
public int getModifiers()
//返回字段的类型
public Class<?> getType()
//查询注解的方法
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
//忽略掉@inherited来的
public Annocation[] getDeclaredAnnotations()

在get/set方法中静态变量的obj可以被忽略(个人理解是属于类变量)。同时如果是int、long等基本类型则会进行自动装箱。

下面来进行一段简单的代码来使用get/set方法获取/修改private字段的值内容

Demo示例😁

@Data	//使用了Lombok简化。这里继承Base先不管。
@NoArgsConstructor
@AllArgsConstructor
public class Address extends Base{
    private String provides;
    private String city;
    private void secret(String name){
        System.out.println("我是私有方法"+name);
    }
}
@SneakyThrows
public static void main(String[] args) {
    Address address = new Address();
    address.setCity("上海");
    address.setProvides("上海");
    Class<?> aClass = address.getClass();
    for (Field declaredField : aClass.getDeclaredFields()) {
      //允许对private字段进行读写
      declaredField.setAccessible(true);
      System.out.println(declaredField.get(address));
    }
  }

/**
结果:上海、上海
同理、代码修改一下可以修改字段
*/

@SneakyThrows
public static void main(String[] args) {
    Address address = new Address();
    address.setCity("上海");
    address.setProvides("上海");
    Class<?> aClass = address.getClass();
    for (Field declaredField : aClass.getDeclaredFields()) {
        //允许对private字段进行读写
        declaredField.setAccessible(true);
        declaredField.set(address,"hello");
        System.out.println(declaredField.get(address));
    }
}
/**
结果:hello、hello
同理、代码修改一下可以修改字段
*/
3、方法信息
//返回所有的public方法,包括父类
public Method[] getMethods()
//返回本类包括非public的所有方法
public Method[] getDeclaredMethods()
//返回本类或父类指定名称和阐述类型的public方法
public Method getMethod(String name,Class<?>... parameterTypes)
//返回本类声明指定类型的名称与参数类型方法
public Mehod getDeclaredMehod(String name, Class<?>... parameterTypes)
 //--------------------
//同时Method可以获取方法的信息,也可以通过Methodd考用对象的方法。基本方法有
//获取方法名称
public String getName()
//同字段信息、允许操控非public方法
public void setAccessible(boolean flag)
//在指定对象obj上调用Method方法。传递的参数列表为args
public Object invoke(Object obj,Object... args)  //抛出非法访问、非法参数、错误目标异常
  //同理,如果是静态方法,args可以忽略因为是属于类方法、

Demo示例😁

@SneakyThrows
public static void main(String[] args) {
    Address address = new Address();
    Class<?> aClass = address.getClass();
    for (Method method : aClass.getDeclaredMethods()) {
        method.setAccessible(true);
      	//调用私有方法
        if (method.getParameterCount()>0) {
            method.invoke(address,"hello,world");
        }
    }
}
/**
结果:我是私有方法hello,world
*/
4、创建对象和构造方法
@Deprecated(since="9")
public T newInstance() throw InstantiationException,IllegalAccessException
  //默认调用午餐构造器实例化对象

同时、class类可以通过一些方法来获取所有的构造方法

//获取所有的public构造方法
public Constructor<?>[] getConstructors()
//获取所有的构造犯法,包括非public的 (ps:可以利用此方法破坏单例模式)
 public Construcor<?>[] getDeclaredConstructors();

拓展:利用反射破坏单例模式(恶汉式)

public class Singleton {
    private static Singleton singleton = new Singleton();

    public static Singleton getInstance(){
        return singleton;
    }
    private Singleton() {
    }
}
		@Test
    @SneakyThrows
    public void test_8() {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        Class<Singleton> singletonClass = Singleton.class;
        Constructor<Singleton> declaredConstructor = 				singletonClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Singleton singleton = declaredConstructor.newInstance();
        System.out.println(singleton); //非单例
        System.out.println(instance1); //单例
        System.out.println(instance2);	//单例
    }

结果

在这里插入图片描述

5、类型检查和转换

相信instanceof关键字大家都不陌生,在使用SpringSecurity中的SecurityContextHolder中或者JWT中拿到用户的信息都会经历过一次强转。但是instanceof后面的类型是在代码中来确定下来的。

那么,如果要检查的类型是动态的,可以使用Class类的的方法

public native boolean isInstance(Object obj)
  
//也就是说一下两种代码的所表达是一样的
if(list instanceof ArrayList){
  	System.out.println("arrary list")
}

Class cls = Class.forName("java.util.ArrayList");
if(cls instanceof ArrayList){
  System.out.println("array list")
}

//检查参数类型cls是否赋给当前Classe类型的变量
public native boolean isAssignableFrom(Class<?> cls);
6、Class的类型信息
public native boolean isArray()	//是否数组
public native boolean isPrimitive()	//是否基本类型
public boolean isEnum() //是否枚举
public boolean isAnnotation() //是否注解
public boolean isAnonymousClass() //是否匿名内部类
public boolean isMemberClass() //是否成员类
public boolean isLocalClass() //是否本地类,定义在方法内
7、类的声明信息

Class可以获得类的声明信息。如修饰符、父类、接口、注解等、

//获取修饰符,返回值可以通过Modifyier解读
public native int getModifiers();
//获取父类,如果没有则为null
public native Class<? super T> getSuperclass()
//对于类。获取声明实现的所有接口,对于接口,为直接拓展的接口,不包括继承的
public native Class<?>[] getInterfaces();
//自己声明的注解
public Annotation[] getDeclaredAnnotations()
//所有的注解,包括继承得到的
public Annotation[] getAnnotations()

反射应用实例

1、通过反射实现序列化/反序列化

Public static String toString(Object obj);
public Object fromString(String str);

1、Student类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String name;
    private int age;
    private Double source;
}

2、MyMapper类型

package com.demo.practise.serilazation;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * @Author Zzneko
 * @Date 2022/9/8 13:41
 **/
public class MyMapper {
    public static String toString(Object obj){
        try {
            Class<?> cls = obj.getClass();
            StringBuilder sb = new StringBuilder();
            //这里最好加上类名。反序列化需要使用。
            sb.append(cls.getName()+"{");
            for (Field field : cls.getDeclaredFields()) {
                if(!field.isAccessible()){
                    field.setAccessible(true);
                }
                sb.append(field.getName()+":"+field.get(obj)+",");
            }
            sb.append("}");
            sb.replace(sb.lastIndexOf(","),sb.lastIndexOf(",")+1,"");
            return sb.toString();
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static Object toObj(String str){
        try {
            //或得类名
            String className = str.substring(0, str.indexOf("{"));
            String substring = str.substring(str.indexOf("{")+1, str.lastIndexOf("}")).trim();
            //获得所有字段字段
            String[] fields = substring.split(",");
            if (fields.length<1){
                throw new IllegalArgumentException(str);
            }
            //通过类名获得类的信息
            Class<?> aClass = Class.forName(className);
            Object obj = null;
            Constructor<?>[] constructors = aClass.getConstructors();
            for (Constructor<?> constructor : constructors) {
                if(constructor.getParameterCount()==0){
                    //这里使用无参构造器来进行创建对象
                    obj = constructor.newInstance();
                }
            }
            for (int i = 0; i < fields.length; i++) {
                String[] param = fields[i].split(":");
                if(param.length!=2){
                    throw new IllegalArgumentException(param[i]);
                }
                //根据字段名查找到对应的字段。
                Field field = aClass.getDeclaredField(param[0]);
                //非public则反射获取权限
                if(!field.isAccessible()){
                    field.setAccessible(true);
                }
                SetFiled(field,obj,param[1]);
            }
            return obj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void SetFiled(Field field, Object obj, String param) throws Exception {
        //获得字段对应的类型
        Class<?> type = field.getType();
        if(type ==int.class){
            field.setInt(obj,Integer.parseInt(param));
        }else if(type == byte.class){
            field.setByte(obj,Byte.parseByte(param));
        }else if(type == short.class){
            field.setShort(obj,Short.parseShort(param));
        }else if (type == Long.class){
            field.setLong(obj,Long.parseLong(param));
        }else if(type == float.class){
            field.setFloat(obj,Float.parseFloat(param));
        }else if(type == double.class){
            field.setDouble(obj,Double.parseDouble(param));
        }else if(type == char.class){
            field.setChar(obj,param.charAt(0));
        }else if(type == boolean.class){
            field.setBoolean(obj,Boolean.parseBoolean(param));
        }else if(type ==String.class){
            field.set(obj,param);
        }else{
            //这样写必须保证对象需要又一个有参构造器
            Constructor<?> constructor = type.getConstructor(new Class[]{String.class});
            Object o = constructor.newInstance(param);
            field.set(obj,o);
        }
    }
}

3、测试

public class SimpleMapper {
    public static void main(String[] args) {
        Student zzneko = new Student("zzneko", 22, 99.5);
        String str = MyMapper.toString(zzneko);
        System.out.println(str);
        Student zzneko2 = (Student) MyMapper.toObj(str);
        System.out.println(zzneko2);

    }
}

输出结果:

在这里插入图片描述

2、反射组装对象,道理同上

@Test
    @SneakyThrows
    public void test_10(){
        Map<String, String> param = Map.of("ENSUREDATE", "1", "FUNDCODE", "2", "CONFIRMSHARE", "3", "STATUS", "4", "CONFIRMBALANCE", "5", "TRADETYPE", "6", "PDVALUE", "7");
//        Class<ZhHistoryTradeVO> zhHistoryTradeVOClass = ZhHistoryTradeVO.class;
//        //生成类
//        ZhHistoryTradeVO tradeVO = zhHistoryTradeVOClass.getConstructor().newInstance();
//        for (Field field : zhHistoryTradeVOClass.getDeclaredFields()) {
//            String paramField = field.getName().toUpperCase();
//            field.setAccessible(true);
//            field.set(tradeVO,param.get(paramField));
//        }
//        System.out.println(tradeVO);
        ZhHistoryTradeVO zhHistoryTradeVO = (ZhHistoryTradeVO) assembleObj(param, ZhHistoryTradeVO.class);
        System.out.println(zhHistoryTradeVO);
    }
	/*
	*通用封装
	*/
    public static Object assembleObj(Map map,Class<?> clazz){
        try {
            Object instance = clazz.getConstructor().newInstance();
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                field.set(instance,map.get(field.getName().toUpperCase()));
            }
            return instance;
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }

实体类

package com.demo.practise.vo;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDate;

/**
 * @Author Zzneko
 * @Date 2022/8/29 17:36
 **/
@Data
@ApiModel("专户交易VO")
public class ZhHistoryTradeVO {

    @ApiModelProperty("交易确认日期")
    private String ensureDate;

    @ApiModelProperty("基金代码")
    private String fundCode;

    @ApiModelProperty("确认份额")
    private String confirmShare;

    @ApiModelProperty("确认状态")
    private String status;

    @ApiModelProperty("确认金额")
    private String confirmBalance;

    @ApiModelProperty("交易类型")
    private String tradeType;

    @ApiModelProperty("净值")
    private String pdValue;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值