索鸟网

  1. 首页
  2. Java中的缓存设计

Java中的缓存设计


之所以写这篇博客,是因为在学习JavaSE的时候遇到了一个问题,由解决问题延伸到该类问题的总结,大概讲讲解决这个问题的思路:

一: java中基本类型对于的包装类的缓存设计:

  1. 查看Integer的源代码如下:

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }
        private IntegerCache() {}
    }

    从上面截取的源代码来看,Integer类中是有一个静态内部类的,这个静态内部类的作用是一个常量数组,里面放的是256个Integer对象的地址,而且这256个Integer对象的值是从-128到127的!这就是java对Integer类做的缓存设计!这个静态数组在.class文件加载的时候就在方法区中创建了!!每当我们要使用基本类型的自动包装来初始化对象的时候,程序会先去遍历这个方法区中常量池中的静态数组中是不是有值一样的对象,如果有的话,就把当前对象地址返回给需要的引用,从而不必再花时间和空间去堆中创建新对象了!如果在常量池中没有找到同值的对象,那么程序就会在堆中开辟一个新的对象并返回地址给需要的引用!这就解释了如下代码的结果:

测试源码:

    
public class IntegerBoxing {    
    public static void main(String[] args) {
        Integer i = 1;//1在缓存中,所以直接把缓存中值为1的对象地址给i
        Integer i1 = 1;//同上理
        System.out.println(i = i1);//答案 true
        Integer m = 220;//220没有在 [-128 , 127]区间内,所以在堆中新开辟一个空间
        Integer n = 220;//同理
        System.out.println(m == n);//false

2.通过上面对Integer类的探究,我们直接看看其他的基本类型是不是有缓存设计,如果有的话是如何设计的:

Byte类的源码中的部分:

private static class ByteCache {
        private ByteCache(){}
        static final Byte cache[] = new Byte[-(-128) + 127 + 1];
        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }

    

Short的源代码是怎么设计缓存的

private static class ShortCache {
        private ShortCache(){}
        static final Short cache[] = new Short[-(-128) + 127 + 1];
        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Short((short)(i - 128));
        }
    }

     

Long类型的源代码设计,如下

private static class LongCache {
        private LongCache(){}
        static final Long cache[] = new Long[-(-128) + 127 + 1];
        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }


Float类型,Double类型

    在查看了Float和Double的源代码,确实里面没有缓存设计!!!这要注意和前面的区分了



Character类型是有缓存的,我们这就去看看源代码的设计

private static class CharacterCache {
        private CharacterCache(){}
        static final Character cache[] = new Character[127 + 1];
        static {
            for (int i = 0; i < cache.length; i++)
                cache[i] = new Character((char)i);
        }
    }


Boolean类型的源代码中有缓存设计,代码如下:

public static final Boolean TRUE = new Boolean(true);//缓存值为true
   ...
public static final Boolean FALSE = new Boolean(false);//缓存值为:false

看出Boolean类型是有2个缓存值的


================

好了,到这儿我们就把基本类型的包装类的缓存设计带来的“==”问题解决完了。之所以有缓存设计是因为这些是我们经常用的数据对象,设定合理的缓存,可以大大提高我们代码执行速度(不用再开辟空间而耗时间了)和大大节约内存空间(不用重复new对象)



二.String类型的缓存设计

1.首先去看看源代码

private final char value[];
    ...
public String() {
     this.value = new char[0];
}

从源代码看出,java在设计String类的时候是有缓存的!缓存的值在常量池里

2.String类型的缓存理解例子

测试代码:

public class StringDemo {
    private static String getXx(){
        return "AB";
    }
    public static void main(String[] args) {
        String str1 = "ABCD";            //@1
        String str2 = "A"+"B"+"C"+"D";     //@2
        String str3 = "AB"+"CD";           //@3
        String str4 = new String("ABCD");  //@4
        String temp = "AB";                //@5
        String str5 = temp + "CD";         //@6
      
        String str6 = getXx()+"CD";        //@7
        System.out.println(str1 == str2);  //@8   true
        System.out.println(str3 == str2);  //@9   true
        System.out.println(str3 == str4);  //@10  false
        System.out.println(str4 == str5);  //@11  false
        System.out.println(str5 == str6);  //@12  false
    }
}

    这儿要区别一下编译时常量和运行时常量,编译时常量表示的是编译器在编译代码的时候就可把可以确定的常量值;而运行是常量是指运行的时候才可以确定的常量的值(也就是程序运行的时候,比如调用构造方法的时候才可以确定的常量值)。一旦常量值被确定就会被放入常量池中从当缓存的字面量!    

解析:
1).@1和@2和@3是编译时常量,也就是编译的时候就已经确定了的常量,在字节码文件加载的时候,
就开始把这些确定了的常量放入常量池。编译的时候就把“ABCD”字面量放入了常量池,在程序运
行的时候,@1操作会把常量池中的值为“ABCD”对象的地址赋给str1;@2操作也是把常量池中的值
为“ABCD”对象的地址赋给str2; @3操作也一样,所以str1 = str2 = str3
2)str4是new的对象,直接在堆中开辟空间,并把地址赋给str4,所以str4和前面的3个引用地址都
是不一样的
3)str5是运行时常量,运行的时候,会在堆中开辟一个空间,其中的值是有常量池中的“CD”
和“AB”做字符串的串联操作得到的,新地址在堆中,所以str5是和str4以及前面3个引用地址都是
不一样的
4)str6是运行是常量,只有运行时调用getXx()方法才会在堆中创建对象返回地址给str6,所以
str6与前面的5个地址都是不同的!!



三.equals方法在8总基本类型的包装类和String类中的体现


1.所有类都继承Object类,equals方法是该类原生的方法,如果子类没有重写该方法,那么调用该方法就是比较的是两个对象的地址是否相同!其中Object类该方法api如下:

  • public boolean equals(Object obj)
    
    指示其他某个对象是否与此对象“相等”。
    equals 方法在非空对象引用上实现相等关系:
    自反性:对于任何非空引用值 x,x.equals(x) 都应返回 
    true。
    对称性:对于任何非空引用值 x 和 y,当且仅当 
    y.equals(x) 返回 true 时,x.equals(y) 才应返回 
    true。
    传递性:对于任何非空引用值 x、y 和 z,如果 
    x.equals(y) 返回 true,并且 y.equals(z) 返回 
    true,那么 x.equals(z) 应返回 true。
    一致性:对于任何非空引用值 x 和 y,多次调用 
    x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 
    equals 比较中所用的信息没有被修改。
    对于任何非空引用值 x,x.equals(null) 都应返回 
    false。
    Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 
    x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。
    注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。


2.查看包装类的api发现其中的equals方法是被重写了的,equals方法是比较的是对象的值是否相等,而非对象的地址。具体如下:

public boolean equals(Object obj)

比较此对象与指定对象。当且仅当参数不为 null,并且是一个与该对象包含相同 int 值的Integer 
对象时,结果为 true。


3.查看String类的api发现其中的equals方法也是被重写了的,equals方法比较的是字符串的值是否相等,而非地址是否相等,如下:

public boolean equals(Object anObject)

将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的
String 对象时,结果才为 true。




















本文出自 “11929788” 博客,请务必保留此出处http://11939788.blog.51cto.com/11929788/1952485

equals javaSE String

来源地址:http://11939788.blog.51cto.com/11929788/1952485 版权归作者所有!

相关教程

  • java中23种设计模式

    设计模式(Design Patterns)                           
  • Java设计模式中单例设计模式

    什么是设计模式? 设计模式就是解决某一类问题最为行之有效的方法 Java中有23中设计模式,接下来要讲的是单例设计模式 单例设计模式:解决一个类在内存只存在一个对象 其目的为:想要保证对象唯一 有以下几张情况,可以考虑使用单例设计模式 1.为了避免其他程序过多建立该类对象,新禁止其他程序建立该类对象 2.还W为了让其他程序可以访问到给类对象,只好在本类中,自定义一个对象
  • 设计缓存架构时的一些注意事项

    数据还未缓存,大量并发请求 在数据还未缓存的情况下,大量并发请求过来的话,如果处理不好,很容易就打到DB。 解决思路(1):预先缓存 如果可以事先知道需要缓存哪些数据,那么就预先将这些数据缓存起来。处理方式可以是: 程序启动时 人工在后台触发 解决思路(2):排队 把映射到同一个缓存key的请求排队,挨个处理,那么第一个请求从DB获得数据并缓存后,后
  • Spring Boot中的缓存

    Spring Boot中的缓存 本文介绍了spring boot 1.3.0版本带来的缓存支持。spring boot是支持缓存的,但是直到1.3.0版本之前,是不能自动配置的。现在你就可以非常简单的在spring boot应用中使用缓存了: 缓存的自动配置 spring boot actuator增加了缓存的监控 在本文中,作者主要围绕上面两点来展
  • java web中有关页面设计的笔记

    使用link标签将css样式导入 使用script标签将js样式导入 使用div进行包装,通过设置padding以及margin来设置两个div之间的间距 通过float属性设置div向左浮动还是向右浮动,实现良好的排版 再更改页面布局时可以在界面上右击审查元素,进行各元素的调整,当调整到合适的位置时将更改的数值记住进行更改 1111111111111111111111
  • Java开发中的23种设计模式详解

    所谓单例模式,简单来说,就是在整个应用中保证只有一个类的实例存在。就像是Java Web中的application,也就是提供了一个全局变量,用处相当广泛,比如保存全局数据,实现全局性的操作等。 【Java技术交流08君羊】 好156643771【定期更新群文件】 最简单的实现 首先,能够想到的最简单的实现是,把类的构造函数写成private的,从而保证别的类不能实
  • java protostuff实现文件级别缓存,使用文件缓存对象,java文件缓存

    由于业务需要使用文件级别的缓存,特此采用protostuff加上基本的TXT文件操作,来实现基于文件的缓存。 实现以下功能: 文件级别缓存的对象工具类 1、传入一个对象和缓存时间还有缓存名称对这个对象进行缓存 2、传入一个缓存名称查询是否存在这个名称的缓存 3、传入一个缓存名称和该类的类型对象获取对应的缓存 4、传入一个缓存名称删除该名称的缓存 模块一:protostuff序
  • js中的local Storage缓存

    原先只是感觉缓存很麻烦,上网查了一大堆资料,后来无意间自己写了一次,才突然觉得也就是三行代码的事情,或许最难的事,就是未知的! <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>js本地缓存</t