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;
}