`
lee372106501
  • 浏览: 7046 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

使用反射搞破坏

阅读更多
  本文主要介绍几个关于反射的使用例子,给大家分享一下有趣的知识点:
    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还是有帮助的。

分享到:
评论
1 楼 longhua2003 2015-11-18  
你果然够坏

相关推荐

    java 反射的使用

    以下是对Java反射使用及相关知识点的详细阐述: 1. **获取Class对象** - `Class.forName(String className)`: 通过全限定类名获取Class对象。 - `object.getClass()`: 对象的`getClass()`方法返回该对象的Class...

    使用反射实现绑定的 ListView

    为了实现更灵活的数据绑定,开发者常常会使用反射技术。本篇文章将详细讲解如何使用反射实现绑定的ListView,包括DataSource属性的设置,反射中文列名以及自适应列宽度的实现。 首先,我们要理解反射在Java(以及...

    反射机制反射Dao 反射机制

    由于绕过了编译时的检查,反射可能导致安全风险,如访问私有成员可能破坏封装性。此外,反射操作通常比直接调用方法慢,因此在性能敏感的代码中应谨慎使用。 综上所述,反射机制在Java编程中扮演着重要角色,它可以...

    使用反射和注解模拟Spring的依赖注入

    这篇博客"使用反射和注解模拟Spring的依赖注入"探讨了如何通过基本的Java特性来实现类似Spring的功能。我们将深入探讨反射和注解这两个关键概念,以及它们如何在模拟依赖注入中发挥作用。 首先,让我们理解反射的...

    matlab计算反射阵单元相位(460652)_matlab反射阵相位_matlab反射阵_反射阵matlab_反射阵

    在本文中,我们将深入探讨如何使用MATLAB进行反射阵天线单元相位的计算,这是无线通信和雷达系统中的一个重要概念。MATLAB是一种强大的数学计算软件,尤其适用于数值分析和算法开发,因此它是处理此类问题的理想工具...

    动态代码的使用(反射和动态生成类)

    动态代码的使用(反射和动态生成类) 在软件开发中,尤其是在框架和底层开发时,为了更灵活地控制代码,常常需要进行一些动态的操作。动态代码的使用可以分为两大类:反射和动态生成类。 一、反射的使用 反射是...

    使用反射分页,

    使用反射分页,并自动封装成对象,最后返回一个对象集合

    .net高级应用 使用反射发出

    .NET框架中的反射发出...在提供的文件"说明.txt"和"CS"中,可能包含了使用反射发出的示例代码,你可以查看这些文件以了解更多细节。学习和掌握反射发出技术,对于深入理解.NET框架以及提升编程能力具有重要意义。

    在linq查询中使用反射

    在LINQ(Language Integrated Query,语言集成查询)中使用反射是一项高级编程技术,它结合了.NET框架的强大功能,使得开发者可以动态地探索和操作类型的信息。这篇文章将深入探讨如何在LINQ查询中利用反射来增强...

    java反射的使用

    该项目主要是对应的http://blog.csdn.net/xingfei_work/article/details/72677442博客的源码。主要是使用反射+泛型+注解来实现对ResultSet进行自动转换的源码,欢迎下载、交流。

    使用反射封装javabean

    使用java反射机制封装javabean 项目当中使用的一个通用的方法

    C#,利用反射动态创建对象

    要使用反射创建对象,首先需要获取目标类型的Type对象。这可以通过以下两种方式完成: - 使用`typeof`关键字:`Type myType = typeof(MyClass);` - 使用`Type.GetType()`方法:`Type myType = Type.GetType(...

    泛型 反射 相关概念方法使用

    泛型和反射是Java编程语言中的两个重要特性,它们各自有着独特的功能,但在某些场景下也可以结合使用。本文将深入探讨这两个概念以及相关的使用方法。 首先,我们来了解泛型。泛型是Java 5引入的一项特性,它允许在...

    使用反射动态设定组件属性(C#)

    ### 使用反射动态设定组件属性(C#) #### 核心知识点概述 本文主要探讨了如何在C#中利用反射机制动态地设置组件属性,并通过一个具体的案例——Ini文件的访问类来展示反射技术的应用场景。文章还提到了.NET ...

    JAVA反射详细讲解

    1. 尽量减少反射的使用:只在确实需要动态行为时使用反射,避免滥用。 2. 使用异常处理:反射操作可能会抛出异常,如`ClassNotFoundException`, `IllegalAccessException`, `InstantiationException`等,要妥善处理...

    Qt 使用QMetaObject实现反射机制代码demo

    下面我们将通过分析给定的文件名来理解如何在Qt中使用QMetaObject实现反射机制。 首先,我们看到几个`.cpp`和`.h`文件,这些是C++的源代码和头文件。例如,`Person.cpp`和`Person.h`可能定义了一个名为`Person`的类...

    反射实例-JAVA反射机制

    ### 反射实例—JAVA反射机制 #### 一、反射概念及原理 反射在计算机科学领域,特别是程序设计中,是指程序有能力访问...然而,在使用反射时,开发者应当权衡其带来的好处和潜在的风险,确保合理有效地利用这一特性。

    反射的简单使用源码

    在Java编程语言中,反射...在设计系统时,应尽可能避免不必要的反射使用,只有在确实需要动态性或元编程功能时才考虑引入。在实际开发中,结合注解处理器和依赖注入框架等技术,往往能提供更优雅的解决方案。

    学习使用Go反射的用法示例

    什么是反射 大多数时候,Go中的变量,类型和函数非常简单直接。...在这种情况下,你需要使用反射。反射使您能够在运行时检查类型。它还允许您在运行时检查,修改和创建变量,函数和结构体。 Go中的反射是基于三个

Global site tag (gtag.js) - Google Analytics