本文主要介绍几个关于反射的使用例子,给大家分享一下有趣的知识点:
1.使用反射改变自动装箱后的值
2.改变String的不可变性
3.破坏泛型的约束
4.破坏单例
4.1.反射调用已有的构造函数
4.2.添加一个构造函数
4.3.使用Unsafe创建实例
关于反射的知识点大家应该已经知道,这里就不多说了。
1.使用反射改变自动装箱后的值
对于基本数据类型都有对应的包装类,jdk提供了自动装箱和拆箱的功能,当一个int类型的数据装箱成Integer时实际上是调用了Integer.valueOf(int i)方法:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
也就是说对于-128到IntegerCache.high的int值在自动装箱时是从IntegerCache.cache里拿到的事先创建好的Integer对象,那么我们可以改变这个cache从而改变自动装箱后的值:
public static void main(String[] args) throws Exception {
//获取IntegerCache类中的cache字段
Class integerCache = Class.forName("java.lang.Integer$IntegerCache");
Field cacheField = integerCache.getDeclaredField("cache");
cacheField.setAccessible(true);
//得到实际的cache
Integer[] cache = (Integer[]) cacheField.get(null);
Integer integer = -1;
for (int i = 0; i < cache.length; i++) {//改变cache的内容
cache[i] = integer;
}
for (int i = -128; i <= 127; i++) {
System.out.print((Integer)i);
}
}
上面这段代码打印的值对于每个i来说都是-1。
2.改变String的不可变性
大部分人都会告诉你String对象是不可变的,因为它定义为final的,而且内部大部分字段也是final的,但是String对象确实是可以变的,先看一下它的定义:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash;
……
}
对于String str = “abcd”来说,实际上是在常量池中存在一个字面量为"abcd"的String对象,而"abcd"是存储在private final char value[];这个字符数组里面的,所以我们可以拿到这个char[],然后对其进行修改:
public static void main(String[] args) throws Exception {
final String str1 = "abcd";
final String str2 = new String("abcd");
System.out.println(str2 == str1);
Field valueField = String.class.getDeclaredField("value");//获取私有字段value
valueField.setAccessible(true);//设置访问权限
char[] value = (char[]) valueField.get(str1);//得到str1所引用对象的value字段的值
value[0] = '0';
value[1] = '1';
value[2] = '2';
value[3] = '3';
System.out.println(str1);
System.out.println(str2);
}
这里输出的是0123而不是abcd,对于刚创建的str2来说,它是一个引用变量指向内存中的一个字面量也是"abcd"的String对象,只是这个String对象实际上和str1所指向的String对象共用了同一个字符数组,所以当str1所指向的String对象里的字符数组发生改变时str2所指向的String对象的值也发生了改变。
3.破坏泛型的约束
对于List<String> list = new ArrayList<String>();来说,因为加了泛型约束,list.add(..)的时候你只能加入String类型的对象,而通过反射获取add方法后就可以加入你想加入的类型了:
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<String>();
//得到add方法
Method method = List.class.getMethod("add", Object.class);
//放入一个Integer类型的对象
method.invoke(list, new Integer(1));
System.out.println(list.size());
}
4.破坏单例
4.1.反射调用已有的构造函数
单例的目的是为了内存中只能创建一个实例,它的实现有很多种:懒汉、饿汉、DCL、静态内部类、单个枚举等,不论哪种方式都是可以破坏它只能创建一个实例的目的,以静态内部类为例:
public class Singleton {
private Singleton(){
}
public static Singleton getInstance() {
return InstanceHolder.instance;
}
private static class InstanceHolder {
private static Singleton instance = new Singleton();
}
}
单例的一个重要的约束是构造函数私有化,不过我们通过反射还是可以创建对象的:
public static void main(String[] args) throws Exception {
//得到构造函数
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
//设置访问权限
constructor.setAccessible(true);
//创建对象
Singleton singleton = constructor.newInstance(null);
System.out.println(singleton == Singleton.getInstance());
}
大家都知道使用已有的构造函数可以创建对象,其实不使用已有的构造函数也是可以创建对象的,这里介绍4.2、4.3两种:
4.2.添加一个构造函数
不使用已有的构造函数,给类新添加一个构造函数用来创建对象:
public static void main(String[] args) throws Exception {
//得到Object的无参构造函数
Constructor javaLangObjectConstructor = Object.class.getConstructor();
//给Singleton新加一个构造函数
Constructor c = ReflectionFactory.getReflectionFactory()
.newConstructorForSerialization(Singleton.class, javaLangObjectConstructor);
c.setAccessible(true);
//创建实例
Singleton singleton = (Singleton) c.newInstance(null);
System.out.println(singleton == Singleton.getInstance());
}
4.3.使用Unsafe创建实例
Unsafe中提供了一个allocateInstance方法可以用来创建对象,Unsafe其实是一个单例,只是提供的获取Unsafe的方法中有安全监测致使我们不能通过它拿到内部的Unsafe对象,不过我们可以通过反射拿到内部的theUnsafe字段,进而得到已创建好的Unsafe对象:
public static void main(String[] args) throws Exception {
//通过Unsafe创建对象
Singleton allocateInstance = (Singleton) getUnsafe().allocateInstance(Singleton.class);
System.out.println(allocateInstance == Singleton.getInstance());
}
//通过反射拿到Unsafe对象
private static Unsafe getUnsafe() throws Exception {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe)f.get(null);
}
本文例子中使用的jdk版本是hotspot 1.6,文中提到的几个例子在实际开发中可能用不到,但是对学习java还是有帮助的。
分享到:
相关推荐
以下是对Java反射使用及相关知识点的详细阐述: 1. **获取Class对象** - `Class.forName(String className)`: 通过全限定类名获取Class对象。 - `object.getClass()`: 对象的`getClass()`方法返回该对象的Class...
为了实现更灵活的数据绑定,开发者常常会使用反射技术。本篇文章将详细讲解如何使用反射实现绑定的ListView,包括DataSource属性的设置,反射中文列名以及自适应列宽度的实现。 首先,我们要理解反射在Java(以及...
由于绕过了编译时的检查,反射可能导致安全风险,如访问私有成员可能破坏封装性。此外,反射操作通常比直接调用方法慢,因此在性能敏感的代码中应谨慎使用。 综上所述,反射机制在Java编程中扮演着重要角色,它可以...
这篇博客"使用反射和注解模拟Spring的依赖注入"探讨了如何通过基本的Java特性来实现类似Spring的功能。我们将深入探讨反射和注解这两个关键概念,以及它们如何在模拟依赖注入中发挥作用。 首先,让我们理解反射的...
在本文中,我们将深入探讨如何使用MATLAB进行反射阵天线单元相位的计算,这是无线通信和雷达系统中的一个重要概念。MATLAB是一种强大的数学计算软件,尤其适用于数值分析和算法开发,因此它是处理此类问题的理想工具...
现在,我们可以使用反射机制来获取这个注解。Java的`Field`类提供了`getAnnotation(Class<A> annotationClass)` 方法,用于获取指定类型的注解: ```java public class AnnotationReflectionDemo { public static ...
动态代码的使用(反射和动态生成类) 在软件开发中,尤其是在框架和底层开发时,为了更灵活地控制代码,常常需要进行一些动态的操作。动态代码的使用可以分为两大类:反射和动态生成类。 一、反射的使用 反射是...
使用反射分页,并自动封装成对象,最后返回一个对象集合
.NET框架中的反射发出...在提供的文件"说明.txt"和"CS"中,可能包含了使用反射发出的示例代码,你可以查看这些文件以了解更多细节。学习和掌握反射发出技术,对于深入理解.NET框架以及提升编程能力具有重要意义。
在LINQ(Language Integrated Query,语言集成查询)中使用反射是一项高级编程技术,它结合了.NET框架的强大功能,使得开发者可以动态地探索和操作类型的信息。这篇文章将深入探讨如何在LINQ查询中利用反射来增强...
该项目主要是对应的http://blog.csdn.net/xingfei_work/article/details/72677442博客的源码。主要是使用反射+泛型+注解来实现对ResultSet进行自动转换的源码,欢迎下载、交流。
使用java反射机制封装javabean 项目当中使用的一个通用的方法
要使用反射创建对象,首先需要获取目标类型的Type对象。这可以通过以下两种方式完成: - 使用`typeof`关键字:`Type myType = typeof(MyClass);` - 使用`Type.GetType()`方法:`Type myType = Type.GetType(...
泛型和反射是Java编程语言中的两个重要特性,它们各自有着独特的功能,但在某些场景下也可以结合使用。本文将深入探讨这两个概念以及相关的使用方法。 首先,我们来了解泛型。泛型是Java 5引入的一项特性,它允许在...
### 使用反射动态设定组件属性(C#) #### 核心知识点概述 本文主要探讨了如何在C#中利用反射机制动态地设置组件属性,并通过一个具体的案例——Ini文件的访问类来展示反射技术的应用场景。文章还提到了.NET ...
1. 尽量减少反射的使用:只在确实需要动态行为时使用反射,避免滥用。 2. 使用异常处理:反射操作可能会抛出异常,如`ClassNotFoundException`, `IllegalAccessException`, `InstantiationException`等,要妥善处理...
下面我们将通过分析给定的文件名来理解如何在Qt中使用QMetaObject实现反射机制。 首先,我们看到几个`.cpp`和`.h`文件,这些是C++的源代码和头文件。例如,`Person.cpp`和`Person.h`可能定义了一个名为`Person`的类...
### 反射实例—JAVA反射机制 #### 一、反射概念及原理 反射在计算机科学领域,特别是程序设计中,是指程序有能力访问...然而,在使用反射时,开发者应当权衡其带来的好处和潜在的风险,确保合理有效地利用这一特性。
在Java编程语言中,反射...在设计系统时,应尽可能避免不必要的反射使用,只有在确实需要动态性或元编程功能时才考虑引入。在实际开发中,结合注解处理器和依赖注入框架等技术,往往能提供更优雅的解决方案。