一,利用反射跳过泛型约束(ArrayList)
利用反射,获取到ArrayList的add()方法,进行一个修改,使可以达到
ArrayList list; list.add(“hello”),实现一个Integer数组,可以存入字符串,或者其他数据类型,代码实现如下:
/*
实现跳过泛型约束,add方法添加数据
*/
public class ArrayReflect {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<Integer> list=new ArrayList<>();
// list.add("18");会报错,需通过反射,直接跳过泛型检测
Class<? extends ArrayList> aClass = list.getClass();
// 通过反射获取add方法并增强,使参数为Object类型的数据,在list集合字节码文件中,add方法参数有Object类型的
Method add = aClass.getDeclaredMethod("add", Object.class);
// 忽略访问权限
add.setAccessible(true);
// 方法执行
add.invoke(list,"林梦豪");
add.invoke(list,1314);
add.invoke(list," ");
add.invoke(list,520);
add.invoke(list,"杨月");
System.out.println(list);
}
}
二,利用反射修改字符串但不改变对象指向
原理
- 首先,String类型的数据做修改时,对象指向会变,也就是字符串改变了,相当于new了一个新对象(当然忽略常量池)
- 其次,String类的创建对象时,是将数据存到 char value[]中的
- 利用反射获取到value[],并修改数值,既可以达到字符串修改,并且指向不变的效果
代码和部分源码如下
public class StringReflect {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
String s = "abc";
// 在不改变s的指向,输出abcd
/*编写代码,由于String赋值是创建一个新的对象,则会改变指向,
StringBuilder中的apend()方法可以不创建新的对象,但是构造方法是把s作为参数初始化,并没有让s后面多一个d
这时候可以使用反射
*/
/*
String类构造方法源码:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
private final char value[];
实质是将参数给char[]
*/
// 获取类加载
Class<? extends String> sClass = s.getClass();
// 获取变量char[] value
Field value = sClass.getDeclaredField("value");
// 忽略私有修饰符,直接暴力修改
value.setAccessible(true);
value.set(s,"abcd".toCharArray());
System.out.println(s);
}
}
其中,StringBuffer和StringBuilder可以实现修改字符串,指向不改变
值得一提的是String创建对象的指向问题
- 字符串通常有连个地方存储,一个是堆,一个是字符常量池(栈中的只是字符串指向或者说是内存地址)
String s=new String("abc");
这种方法创建,会创建两个对象,一个是字符串对象,存在堆中,一个是“abc”字符常量,存在字符常量池中String s="abc"
这种方式,首先去看字符常量池中有无相同字符串,如果有,直接指向该常量,如果没有,则创建