• String 特性

    1.其定义的字符串序列不可变。

    2.是一个final类,不可被继承,且其内部一些重要方法被定义为final类型,不可重写。

    3.内部实现Serializable接口(支持字符串序列化)和Comparable接口(支持字符串比较大小)。

    4.内部定义了final char [ ] value 用于存储字符串数据。


  • String的实例化方式

1.字面量赋值的形式实例化:
String str1 = "abc"
2.用 new + 构造器形式实例化:
String str2 = new String("abc")

下面来分析一下两种不同实例化方式的区别:

​ 当我们执行System.out.println(s1 == s2);的时候,输出结果为``false`,

​ 而执行System.out.println(s1.equals(s2));的时候,输出结果为true,

​ 这和虚拟机的内存分配有关:

​ 对于str1字面量赋值的形式来说,字符串常量是存放在常量池中。而对于str2的构造器赋值形式,堆中的value存放的是new String("abc")对象本身,而str2是栈中开辟的一个内存块,他里面存放了指向对象本身的引用地址。有一点需要知道,在常量池中存放的东西都是唯一的,不会出现两个相同的内容,这也是为了减少内存开销和提升jvm的性能优化,所以在使用str2 的时候,对象本身又会到常量池中找是否有abc,如果没有则创建新的,如果有,则直接使用。

​ 在之前的文章中也探究过==equals的区别,当用==比较的时候,对于基本数据类型,比较的是内容,值是否相等。而对于刚刚的str1和str2,他们都是引用型数据类型,用==比较的时候,比较的是地址,很明显,str1的地址直接指向常量池中的abc,而str2 的地址是指向堆内存中的实例对象,所以==比较肯定是false,而用equals比较的时候,结果为true,这是因为String类对object类的equals方法进行了重写,object类中的equals方法底层用的还是==来判断地址值。

总结区别

​ 1.字面量赋值的形式实例化,字符常量内容存于常量池,变量存于栈中,直接指向常量池。

​ 2.new + 构造器形式实例化,会先在堆中创建实例对象,引用对象存于栈中,然后再去常量 区寻找需要的字符常量,如果找到了,直接使用,没找到则开辟新的空间并存储内容。


  • String的不可变性

    我们都知道String是不可变的序列,那为什么不可变,又是怎么实现的呢?先来看一段源码:

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; /** Cache the hash code for the string */
private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L; private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];

​ 可以看出,String是一个final类,也就意味着他不可以被继承,而且其内部成员变量是private修饰,方法也是final修饰,同样也就意味着他的成员变量不会直接暴露给用户,方法不可以被重写。这样实现了方法不可变(不能重写),变量不能变,比如private final char value[];这里就是string字符串不能变的实现。

字符串不可变底层实现分析:

​ 当运行如下代码的时候:

String s1 = "Java";
String s2 = "Hello";
String s5 = s1;
s5 = "change";
String s3 = new String("Hello");
String s4 = new String("Java");

​ 字符串常量在虚拟机内存空间的情况如图所示:

​ 可见,对于String s1 = "Java"这种字面量赋值的形式,会直接在常量池中开辟一个空间用于存储相应的字符串(前提是常量池中还没有该字符串),而String s3 = new String("Hello")这样的,会先在堆中创建对象,然后再去常量池中找是否有需要的字符常量,如果有,则直接使用,如果没有,也同样需要开辟新的空间来存储。

重点看 :

String s1 = "Java";
String s5 = s1;
s5 = "change";

​ 当执行String s5 = s1时,s5会直接去使用s1在常量池中的内容,而后面当执行s5 = "change"的时候,也就是说需要对Java这个字符串进行修改,可是这个字符串除了s5自己使用外,s1也在使用,所以就不能直接修改他,而是要在空间中重新开辟一个空间,用于存储change。这就是字符串不可以直接修改的底层实现!

字符串设置为不可变的原因:

​ ①出于安全考虑,程序在运行之前虚拟机会把字符常量,静态变量等预加载到常量池(方法区) 中存储起来,在程序运行的时候直接调用,但是常量池里面的信息不会有重复的,每一个都是 唯一的(这样是为了减少内存的开销,提升性能),这些信息是线程共享的,同一个字符串可 能会被多个线程使用,如果字符串可变,当某个线程修对他做了修改,其他正在使用该字符串 的线程可能就会出现严重的错误,从而变得不安全。

​ ②保证hash值不会经常变动,具有唯一性,使得类似HashMap的容器能实现key—value的功能


  • String 字符串的拼接

    	static String s1 = "Hello";
    static String s2 = "Java";
    static String s3 = "Hello"+"Java";
    static String s4 = "HelloJava";
    static String s5 = s1 + "Java";
    static String s6 = "Hello" + s2;
    static String s7 = s1 + s2;
    static String s8 = (s1 + s2).intern();

    内存分配如图:

字符串拼接总结:

​ 1.常量和常量的拼接,结果也在常量池中,且不存在两个相同的常量。

​ 2.只要参与拼接的项里面有变量,结果就在堆中。

​ 3.使用(String).inter()方法处理拼接后,被处理的字符串会进入常量池中。


  • 说在最后

    文章仅是笔者的个人理解,难免存在许多不完善和理解不恰当之处,欢迎批评指正。

    码字不易,创作辛苦,欢迎转载分享,请注明出处。

    交流欢迎Q我:321662487

浅析Java String的更多相关文章

  1. 浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  2. 浅析Java.lang.Process类

    一.概述      Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序).      Process 类提供了执行从进程输入.执行输出到进程.等待进程完成.检查进程的 ...

  3. 浅析Java中的访问权限控制

    浅析Java中的访问权限控制 今天我们来一起了解一下Java语言中的访问权限控制.在讨论访问权限控制之前,先来讨论一下为何需要访问权限控制.考虑两个场景: 场景1:工程师A编写了一个类ClassA,但 ...

  4. [转载]浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  5. 浅析JAVA设计模式之工厂模式(一)

    1 工厂模式简单介绍 工厂模式的定义:简单地说,用来实例化对象,取代new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式能够动态决定将哪一个类实例化.不用先知道每次要实例化哪一个类. 工 ...

  6. 浅析java内存管理机制

    内存管理是计算机编程中的一个重要问题,一般来说,内存管理主要包括内存分配和内存回收两个部分.不同的编程语言有不同的内存管理机制,本文在对比C++和Java语言内存管理机制的不同的基础上,浅析java中 ...

  7. 【转】浅析Java中的final关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. ...

  8. [转帖]浅析java程序的执行过程

    浅析java程序的执行过程 转帖来源: https://www.cnblogs.com/wangjiming/p/10315983.html 之前学习过 这一块东西 但是感觉理解的不深刻. copy一 ...

  9. 浅析java修饰符之public default protected private static final abstract

    浅析java修饰符之public default protected private static final abstract 一   修饰符的作用:用来定义类.方法或者变量,通常放在语句的最前端 ...

随机推荐

  1. nyoj 263-精 挑 细 选 (sort(P, P+m, cmp); bool cmp(node a, node b)...)

    263-精 挑 细 选 内存限制:64MB 时间限制:3000ms 特判: No 通过数:14 提交数:26 难度:1 题目描述: 小王是公司的仓库管理员,一天,他接到了这样一个任务:从仓库中找出一根 ...

  2. ubuntu server 1604 设置笔记本盒盖 不操作

    sudo vim /etc/systemd/logind.conf   //打开配置文件 找到 #HandleLidSwitch=suspend  改为 HandleLidSwitch=ignore  ...

  3. 数据库05 使用percona软件来进行数据备份

    1.为什么要与用percona来备份 常见的MySQL备份工具 —跨平台性差 —备份时间长.冗余备份.浪费存储空间 mysqldump备份缺点: —效率较低.备份与还原速度慢,锁表(即备份数据库中的一 ...

  4. 万恶之源-python内容的进化

    1.整数: ​ int--计算和比较 ​ 整数可以进行的操作: ​ bit_length().计算整数在内存中占用的二进制码的长度 2.布尔值 ​ bool 布尔值--用于条件使用 ​ True 真 ...

  5. 2019-11-5:docker拉去配置oracle数据库

    转载自:https://www.cnblogs.com/OliverQin/p/9765808.html 1.拉取Oracle11g镜像 docker pull registry.cn-hangzho ...

  6. Selenium+Java(二)Selenium打开IE浏览器

    前言 已在Eclipse中配置完成Selenium的相关配置,不知道如何配置的可参考我的另一篇博文:https://www.cnblogs.com/yogouo/p/11946940.html 打开I ...

  7. 环境配置——tornado项目准备工作

    新建tornado项目后,采用Pycharm作为开发工具,采用Xshell链接Ubuntu模拟服务端方便方便测试.项目编码前进行以下几个方面的配置. 1.Ubuntu配置 1.1安装ssh服务 sud ...

  8. 设计模式之代理模式--PHP

    代理模式是常用的设计模式之一,代理模式为对象的间接访问提供了一套方案,可以对对象访问进行控制,也能监控对象访问相关的数据信息. 代理模式(Proxy)就是给某一个对象提供代理,在由代理控制原对象的访问 ...

  9. Django Form and ModelForm

    Form介绍 在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输 ...

  10. Rust 入门 (五)

    定义并介绍结构体 结构体和我们前面学习的元组类似,结构体中的每一项都可以是不同的数据类型.和元组不同的地方在于,我们需要给结构体的每一项命名.结构体较元组的优势是:我们声明和访问数据项的时候不必使用索 ...