专栏首页Java知其所以然String 详解以及内存分析

String 详解以及内存分析

字符串(java.lang.String 类)的使用

Java 字符串就是 Unicode 字符序列,例如串 “Java” 就是 4 个 Unicode 字符 J,a,v,a 组成的。

Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是 String 类的一个实例。

String e = “” ; // an empty string

String greeting = “Hello World”;

Java 允许使用符号 "+" 把两个字符串连接起来

String s1 = “Hello”;

String s2 = “World!”;

符号 “+” 把两个字符串按给定的顺序连接在一起,并且是完全按照给定的形式。

当 “+” 运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接

int age = 18;

String s = "age is" + age; //s赋值为age is 18

这种特性通常被用在输出语句中:

System.out.println(“age is” + age);

字符串类常用的方法

char charAt(int index)

返回字符串中第 index 个字符。

boolean equals(String other)

如果字符串与 other 相等,返回 true。因为 String 类重写了 equals 方法。

boolean equalsIgnoreCase(String other)

如果字符串与 other 相等(忽略大小写),则返回 true

int indexOf(String str) lastIndexOf()

返回与 str 匹配的第一个字符串的开始位置,该位置从 0 开始计算,如果原始串中不存在 str,返回 -1。

int indexOf(String str,int fromIndex)

返回与 str 匹配的第一个字符串的开始位置,该位置从 fromIndex 开始计算,如果原始串中不存在 str,返回 -1。

int length()

返回字符串的长度。

String replace(char oldChar,char newChar)

返回一个新串,它是通过用 newChar 替换此字符串中出现的所有oldChar而生成的

boolean startsWith(String prefix)

如果字符串以prefix开始,则返回 true

boolean endsWith(String prefix)

如果字符串以 prefix 结尾,则返回 true

String substring(int beginIndex)

返回一个新字符串,该串包含从原始字符串 beginIndex 到串尾的所有字符

String substring(int beginIndex,int endIndex)

返回一个新字符串,该串包含从原始字符串 beginIndex 到串尾或 endIndex-1 的所有字符

String toLowerCase()

返回一个新字符串,该串将原始字符串中的所有大写字母改成小写字母

String toUpperCase()

返回一个新字符串,该串将原始字符串中的所有小写字母改成大写字母

String trim()

返回一个新字符串,该串删除了原始字符串头部和尾部的空格 注:在 Java 中某个索引区间进行一些操作的方法,索引取值范围一般都是包头不包尾,就拿上面的 String substring(int beginIndex,int endIndex) 来说,它是截取子串 从 beginIndex 开始 到 endIndex - 1 结束。

字符串相等的判断

equals 方法用来检测两个字符串内容是否相等。如果字符串 s 和 t 内容相等,则s.equals(t) 返回 true,否则返回 false.

s 和 t 既可以是字符串变量,也可以是字符串常量,例如: “Hello”.equals(t);

要测试两个字符串除了大小写区别外是否是相等的,需要使用 equalsIgnoreCase 方法,例如:

“Hello”.equalsIgnoreCase(“hellO”); //true

判断字符串是否相等不要使用 "==","=="比较是引用是否相等(是否为同一个对象)

String 内存分析

创建了 4 个对象

分析:

先去 "字符串池" 中找 "a", 没有找到,在 "字符串池" 中创建 "a" 这个String 对象,先去先去 "字符串池" 中找 "a",找到 "a", new String("a") 在堆中创建一个 String 对象(因为 new 关键字一出现,肯定会创建一个对象)。

循环

i = 0; gh = "a0"; 先去 "字符串池" 中找 "a0", 没有找到,在 "字符串池" 中创建 "a0" 这个String 对象

i = 1; gh = "a01"; 先去 "字符串池" 中找 "a01", 没有找到,在 "字符串池" 中创建 "a01" 这个String 对象

思考

String 不是不可变对象吗?那为什么可以字符串拼接啊?你是在逗我吗?

String JDK 源码

final 修饰了 String 类,使得 String 类不可被继承。

final 修饰了 char value[],使得字符串的值不可以改变。

但是 final 并没有修饰 String 的引用,即 final String str;

这样的话,字符串的引用可以改变指向,比如上面的字符串拼接,gh 刚开始指向 "a",随着拼接又指向了 "a0"、"a01"。实际上这个过程中只是 gh 这个引用的指向在改变,"a"、"a0"、"a01" 并没有被改变。

注:这是我画的几个草图为了帮助大家理解,实际上字符串的内存分析要比这个复杂点。

明显可以看出来(在字符串拼接的过程中,创建出来的这些中间 String 对象并不会被回收),"+" 字符串拼接对于内存的浪费比较大,如果是服务器端编程,多线程将会很浪费空间。 字符串拼接存在的问题

要得到上面的 s4,就会 s1 和 s2 拼接生成临时一个 String 对象 t1,内容为 "hello word",然后有 t1 和 s3 拼接生成最终我们需要的 s4 对象,这其中,产生了一个中间的 t1,而且 t1 创建之后,没有主动回收,势必会占一定的空间。如果是一个很多(尤其是一个部署到服务器上的项目,每个用户开一个线程,那用户越来越多的时候这个性能的损耗就很明显了)字符串的拼接,那么代价就更大了,性能一下会降低很多。

一个 Java 程序如果想运行起来,需要经过两个时期,编译时和运行时。在编译时,Java 编译器(Compiler)将 java 文件转换成字节码。在运行时,Java 虚拟机(JVM)运行编译时生成的字节码。通过这样两个时期,Java 做到了所谓的一处编译,处处运行。

早期的版本中,字符串拼接是会在常量池创建对象的,所以不少编程规范都会说不要直接用加号去拼接字符串,因为老是去常量池创建对象的话,开销也不小。

在 jdk5.0 之后 java 对字符串拼接做了编译器的优化处理

当 Java 编译器遇到字符串拼接的时候,会创建一个 StringBuilder 对象,后面的拼接,实际上是调用 StringBuilder 对象的 append 方法。这样就不会有我们上面担心的问题了。

分析上边这个代码,看起来没毛病。但是这里面有一个很重要的就是 StringBuilder 对象创建发生在循环之间,也就是意味着有多少次循环会创建多少个 StringBuilder 对象,这样明显不好。稍微优化一下。

StringBuilder 对象的创建在循坏外面,这样就只创建了一个对象,比较好。 总结

  1. 我们在循环体中需要尽量避免隐式或者显式创建 StringBuilder。 也就是说平时简单的拼接,直接 "+" 号拼接就行;如果要用循环,就在外面 new 个StringBuilder 对象,然后循环里 append 拼接就行了好。
  2. 其实对于循坏来说,尽量避免在循坏里创建对象,可以将创建对象这个操作放在循坏外面,这样我们就让这个对象达到复用了。

本文分享自微信公众号 - Java知其所以然(gh_37a1335e2608)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-07-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 跨专业转CS拿下百度java后台开发的干货分享

    人们总以为时间是一个小偷,偷走了我们所爱的一切。但,时间是先给予再拿走,每天都是一份礼物,每小时,每一分,每一秒。——《爱丽丝梦游仙境2》

    牛客网
  • Java|用 GitLab CI 进行持续集成:简介一些概念GitLab Runner.gitlab-ci.yml

    从 GitLab 8.0 开始,GitLab CI 就已经集成在 GitLab 中,我们只要在项目中添加一个 .gitlab-ci.yml 文件,然后添加一个 ...

    黄小怪
  • Java 控制台程序 JDBC连接数据库

    首先下载mysql-connector jar包 https://dev.mysql.com/downloads/file/?id=480090

    赵哥窟
  • Visual Studio Code(CS code)你们都在用吗?或许你们需要看一下这篇博文

    在前端开发中,有一个非常好用的工具,Visual Studio Code,简称VS code。

    Dawnzhang
  • SCRUM迭代开发 v0.2

    其中的“故事会”的说法是我从Daniel(滕振宇)老师的PO认证课上学来的。指的是在每个迭代中期,PO组织团队全员一起编写下一个迭代要做的用户故事及其验收条件,...

    吾真本
  • 前端入门20-JavaScript进阶之异步回调的执行时机声明正文-异步回调的执行时机

    作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。

    请叫我大苏
  • Java异常知识点思考与总结

    Java 中的异常可以是方法执行过程中引发的,也可以是通过 throw 语句手动抛出的。一旦程序运行过程中发生了异常,JRE 就会试图寻找异常处理程序来处理异常...

    happyJared
  • 前端入门21-JavaScript的ES6新特性声明正文-ES6新特性

    阮一峰的这本书,我个人觉得写得挺好的,不管是描述方面,还是例子,都讲得挺通俗易懂,每个新特性基本都还会跟 ES5 旧标准做比较,说明为什么会有这个新特性,这更于...

    请叫我大苏
  • java8 函数式编程一

    为什么要先从函数接口说起呢?因为我觉得这是 java8 函数式编程的入口呀!每个函数接口都带有 @FunctionalInterface 注释,有且仅有一个未实...

    JMCui
  • java小知识,大智慧

    java知识面很多,如果想系统的学习java知识,最好是看完一本相关书籍,其实这本书籍不用是什么名人写的,只要他有几年的开发经验基本上可以写出一个入门级的jav...

    哲洛不闹

扫码关注云+社区

领取腾讯云代金券