1.享元模式:

1.共享元素模式,也就是说:一个系统中如果有多处用到了相同的一个元素,那么我们应该只存储一份此元素,而让所有地方都引用这一个元素。

2.Java中String就是根据享元模式设计的,而那个存储元素的地方就叫做 "字符串常量池——String Pool"。

public class Apple {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a == b); // true
System.out.println(a.equals(b)); // true
System.out.println(a == c); // false
System.out.println(a.equals(c)); // true
}
} // 因为String太过常用,JAVA类库的设计者在实现时做了个小小的变化,即采用了享元模式。
// 每当生成一个新内容的字符串时,他们都被添加到一个共享池中,当第二次再次生成同样内容的字符串实例时,
// 就共享此对象,而不是创建一个新对象,但是这样的做法仅仅适合于通过=符号进行的初始化。  
// 需要说明一点的是,在object中,equals()是用来比较内存地址的,
// 但是String重写了equals()方法,用来比较内容的,即使是不同地址,只要内容一致,也会返回true,
// 这也就是为什么a.equals(c)返回true的原因了。

2.享元模式分析:

int x = 10;

String y = "hello";

1). 首先,10 和 "hello"会在经过javac(或者其他编译器)编译过后变为Class文件中constant_pool table的内容。

2). 当我们的程序运行时,也就是说JVM运行时,每个Class constant_pool table中的内容会被加载到JVM内存中的方法区中各自Class的Runtime Constant Pool。

3). 一个没有被String Pool包含的Runtime Constant Pool中的字符串(这里是"hello")会被加入到String Pool中(HosSpot使用hashtable引用方式),步骤如下: 

  一是:在Java Heap中根据"hello"字面量create一个字符串对象

  二是:将字面量"hello"与字符串对象的引用在hashtable中关联起来,键 - 值 形式是:"hello" = 对象的引用地址。

4). 当一个新的字符串出现在Runtime Constant Pool 中时怎么判断需不需要在Java Heap中创建新对象呢?

  策略是这样:

  会先去根据equals来比较Runtime Constant Pool 中的这个字符串是否和String Pool中某一个是相等的(也就是找是否已经存在),

    如果有那么就不创建,直接使用其引用;

    如此,就实现了享元模式,提高的内存利用效率。

3.设计成不可变类的好处:

优点:
  1.效率:例如字符串常量池,字符串常量池可以将一些字符常量放在常量池中重复使用,避免每次都重新创建相同的对象、节省存储空间。

     但如果字符串是可变的,此时相同内容的String还指向常量池的同一个内存空间,当某个变量改变了该内存的值时,其他遍历的值也会发生改变。

  2.安全性:不可变对象天生是线程安全的,在不同线程共享对象,不需要同步机制,因为对象的值是固定的。

缺点:

  资源开销,对象需要频繁的修改属性,则每一次修改都会新创建一个对象,产生大量的资源开销。

常见的不可变类:String Integer Long等类型。

4. 如何设计一个不可变类:

1. 类使用final修饰符修饰,保证类不能被继承。

  如果类可以被继承会破坏类的不可变性机制,只要继承类覆盖父类的方法并且继承类可以改变成员变量值,那么一旦子类以父类的形式出现时,不能保证当前类是否可变。

2.类的成员变量都应该是private final的,保证成员变量不可改变。

3.任何获取/修改属性的方法都不应作用于属性本身。

 不提供修改成员变量的方法,例如setter方法。

  getter方法不能返回对象本身,要返回对象的拷贝,防止对象外泄。

  修改对象的属性时要返回新对象

4.对成员变量的初始化通过构造器进行,并进行深拷贝。

 如果使用传入的参数直接赋值,则传递的只是引用,仍然可以通过外部变量改变它的值。

5.Demo:

public static void main(String[] args) {
String s1 = "ab";
String s2 = "abc";
String s3 = s1 + "c";
System.out.println(s3 == s2); // false
System.out.println(s3.equals(s2)); // true
}
// 1.Java 语言提供对字符串串联符号(”+”)和其他对象到字符串的转换的特殊支持。
// 2.字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的,字符串转换是通过 toString 方法实现的。
// 3.在本题中,先在常量池中创建”ab“,地址指向s1,再创建”abc”,指向s2。
// 对于s3,先创建StringBuilder(或 StringBuffer)对象,通过append连接得到“abc”,再调用toString()转换得到的地址指向s3,故(s3==s2)为false。

6. 使用String str = new String("hello");会创建几个对象

  1.先定义一个名为str引用变量,放入到栈中。

  2.然后检查字符串池中是否存在内容为”hello“的对象。

  3.不存在,则在堆中创建一个指定的对象,并让str引用指向该对象(然后需要从堆中复制到常量池中,否则导致浪费池的空间)

  4.存在,则从常量池中拷贝一份到堆中(然后返回堆中的地址,让栈中的str对象指向该地址,进行关联)

  注意:对字符串进行赋值时,如果右操作数含有一个或一个以上的字符串引用时,则会在堆中建立一个字符串对象,返回引用;如String str2=str1+ “abc”;

String类为什么被设计成不可变类的更多相关文章

  1. 深刻理解Java中final的作用(一):从final的作用剖析String被设计成不可变类的深层原因

    声明:本博客为原创博客,未经同意,不得转载!小伙伴们假设是在别的地方看到的话,建议还是来csdn上看吧(原文链接为http://blog.csdn.net/bettarwang/article/det ...

  2. 在java中String类为什么要设计成final?

    大神链接:在java中String类为什么要设计成final? - 程序员 - 知乎 我进行了重新排版,并且更换了其中的一个例子,让我们更好理解. String很多实用的特性,比如说“不可变性”,是工 ...

  3. String类为什么设计成不可变的

    在Java中将String设计成不可变的是综合考虑到各种因素的结果,需要综合考虑内存.同步.数据结构以安全方面的考虑. String被设计成不可变的主要目的是为了安全和高效. 1)字符串常量池的需要 ...

  4. 为什么String要设计成不可变的?

    英文原:http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/ 转自:http://blog.csdn.net/ren ...

  5. String的内存模型,为什么String被设计成不可变的

    String是Java中最常用的类,是不可变的(Immutable), 那么String是如何实现Immutable呢,String为什么要设计成不可变呢? 前言 关于String,收集一波基础,来源 ...

  6. 在java中String类为什么要设计成final

    在java中String类为什么要设计成final? - 胖胖的回答 - 知乎 https://www.zhihu.com/question/31345592/answer/114126087

  7. 为什么Java中的String是设计成不可变的?(Why String is immutable in java)

    There are many reasons due to the string class has been made immutable in Java. These reasons in vie ...

  8. [2017-09-04]Abp系列——为什么值对象必须设计成不可变的

    本系列目录:Abp介绍和经验分享-目录 这篇是之前翻备忘录发现漏了的,前阵子刚好同事又提及过这个问题,这里补上. 本文重点在于理解什么是值对象的不可变性. Abp的ValueObject以及EF的Co ...

  9. 在Java中String类为什么要设计成final?String真的不可变吗?其他基本类型的包装类也是不可变的吗?

    最近突然被问到String为什么被设计为不可变,当时有点懵,这个问题一直像bug一样存在,竟然没有发现,没有思考到,在此总结一下. 1.String的不可变String类被final修饰,是不可继承和 ...

随机推荐

  1. 开启路由器的TCP拦截

    TCP拦截即TCP intercept,大多数的路由器平台都引用了该功能,其主要作用就是防止SYN泛洪攻击.SYN攻击利用的是TCP的三次握手机制,攻击端利用伪造的IP地址向被攻击端发出请求,而被攻击 ...

  2. 记录一次SQL函数和优化的问题

    一.前言 上次在年前快要放假的时候记录的一篇安装SSL证书的内容,因为当时公司开始居家办公了,我也打算回个家 毕竟自己在苏州这半年一个人也是很想家的,所以就打算年过完来重新写博客.不巧的是,当时我2月 ...

  3. Java基础——final、static关键字

    final关键字是最终的意思,可以修饰成员方法.成员变量.类 特点: 1.修饰方法:表示该方法是最终方法,不能被重写 2.修饰变量:表示变量是常量,不能再次被赋值 3.修饰类:表示类是最终类,不能被继 ...

  4. ssh中pam模块造成认证失败

    由于编译参数增加了pam认证 安装后发现这个模块没启用 vim /etc/ssh/sshd_config UsePAM yes 将这个模块打开后就ok了

  5. python程序unix密码破解

    # qianxiao996精心制作 #博客地址:https://blog.csdn.net/qq_36374896 #!/usr/bin/env python #指定这是一个python文件,使用这个 ...

  6. C++设计模式 - 命令模式(Command)

    行为变化模式 在组件的构建过程中,组件行为的变化经常导致组件本身剧烈的变化."行为变化" 模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现两者之间的松耦合. 典型模 ...

  7. Azure DevOps (七) 通过SSH部署上传到服务器的应用

    上一篇中,我们实现了通过FTP把流水线编译出来的制品上传到我们的公网服务器上,这一篇我们来研究一下通过azure的ssh连接到服务器 把应用在服务器上运行起来. 首先,我们书接上文,在release流 ...

  8. 怎么根据Comparable方法中的compareTo方法的返回值的正负 判断升序 还是 降序?

    public int compareTo(Student o) { return this.age - o.age; // 比较年龄(年龄的升序)    } 应该理解成return (-1)×(thi ...

  9. Springmvc入门基础(二) ---架构详解

    1.框架结构图 架构流程文字说明 用户发送请求至前端控制器DispatcherServlet DispatcherServlet收到请求调用HandlerMapping处理器映射器. 处理器映射器根据 ...

  10. 什么是消费者驱动的合同(CDC)?

    这基本上是用于开发微服务的模式,以便它们可以被外部系统使用.当我们处理 微服务时,有一个特定的提供者构建它,并且有一个或多个使用微服务的消费者. 通常,提供程序在 XML 文档中指定接口.但在消费者驱 ...