Java探秘之神秘的字符串String(二)

时间:2022-08-31 22:04:34
不可变性
String可以说是最常用的类型了,即字符串类型,String是常量(final关键词修饰的),他的值不能被创建后更改,因为字符串是不可被改变的,所以可以被用来共享。
Java语言为String提供了同基本数据类型相似的操作符(+,+=),这里请注意,由于String是不可被改变的,所以每次操作都会会重新生成一个String类型。
String a = "a";

System.out.println(a.hashCode());

a += "b";
System.out.println(a.hashCode());
输出的结果是不一致的,此外String的所有方法都是返回一个新的字符串对象。
共享性
这里就需要提到jvm的内存模型了,jvm中将内存大致分为两大块(细致不止两大块,这里重点不是这个,所以就大致的解释下),引用《深入java虚拟机》作者的原话, 经常有人把Java 内存区分为堆内存(Heap)和栈内存(Stack),这种分法比较粗糙,Java内存区域的划分实际上远比这复杂。这种划分方式的流行只能说明大多数程序员最关注的、与对象内存分配关系最密切的内存区域是这两块。
堆内存呢主要存放所有对象的实例(包括数组)、而栈主要存放的是方法、局部变量等信息,而在此之外还有一个常量池用来存放字面常量和符号引用,而每当我们创建一个字符串类型时就会先在常量池中查找是否存在该字符串,如果存在则直接返回实例引用,否则将会实例化一个新的字符串存放到常量池中,从而实现了共享。
以下例子来源《Effective》一书
String a = new String("a");
这个例子中呢,创建了两个对象,第一个对象“a”实例化以后被存储到常量池中(常量池中不存在的情况下),第二个a是堆(因为a的一个对象实例)中的字符串对象,该对象是在运行时创建的。
所以聪明的人才创建了另一种写法:
String a = "a";
这种写法比上一种更为简洁而且是直接创建了对象实例,只创建了一个对象,编译时完成该分配。
String a1 = new String("a");
String a2 = new String("a");
String a3 = "a";
String a4 = "a";
System.out.println(a1 == a2);//false 因为a1跟a2都是运行时创建返回的新实例对象。
System.out.println(a1 == a3);//false
System.out.println(a3 == a4);//true a3、a4都是直接指向常量池中的内存地址。
讲到这里,我们可以拓展开继续讲下去,关于final时字符串的工作表现。
String a = "a";

String bc = "bc";

String abc1 = a + bc;

String abc2 = "abc";

String abc3 = "a" + "bc";//编译器会在编译阶段将其转化成"abc",因为这两个值在编译时就确定了。
String abc4 = a + "bc";//由于a是变量,a的值只能在运行时才能确定
System.out.println( abc1 == abc2);//false
System.out.println( abc2 == abc3);//true
System.out.println( abc2 == abc4);//false
那么我们再来看下面这个情况,加入了final修饰符,变成常量(这里要区分下,String是的值是常量类型,表示的是他的值,而String表示的是最终类)
final String a = "a";
final String bc = "bc";

String abc1 = a + bc;
String abc2 = "abc";
System.out.println( abc1 == abc2);//true 因为final修饰的常量在编译阶段的值就确定了,编译器会将该常量全部"宏替换"成指向该常量池中的实例,final修饰符在编译完成之后是不存在的(这里有点绕,要理解)。
可能刚才有的朋友会很好奇,不是说字符串类型是不可被改变的么,怎么编译器还将其优化合并成一个对象哇,有这种疑问非常好,接下来就是揭秘时刻。
该例子来着《Think in java》一书。
当我们使用“+”操作符进行连接时,编译器会帮我们对代码进行优化将其操作转换成StringBuilder(1.5版本之前是StringBuffer),由于StringBuffer是不安全的,所以1.5之前对其优化处理可能会出现并发问题,这也是为什么编译器会对其进行合并。
而对于“+=”操作符,例如:a += b;会将进行拆分成a = a + b;这是基础不废话了。
这里是java代码
package top.yangchudong.main;

public class App {

    public static void main(String[] args){

        String a = "a" + "b" + "c";

    }

}
以下是编译结果
 
E:\JavaProject\GradleTest\src\main\java>javac top\yangchudong\main\App.java
 
E:\JavaProject\GradleTest\src\main\java>javap top\yangchudong\main\App.class
Compiled from "App.java"
public class top.yangchudong.main.App {
  public top.yangchudong.main.App();
  public static void main(java.lang.String[]);
}
 
E:\JavaProject\GradleTest\src\main\java>javap -c top\yangchudong\main\App.class
Compiled from "App.java"
public class top.yangchudong.main.App {
  public top.yangchudong.main.App();     //这是默认构造方法
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":
()V
       4: return
 
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String a
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder 这里可以清楚的看到定义了一个StringBuilder对象
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<
init>":()V
      10: aload_1
      11: invokevirtual #5                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String b
      16: invokevirtual #5                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      22: astore_1
      23: new           #3                  // class java/lang/StringBuilder 这里又创建了一个
      26: dup
      27: invokespecial #4                  // Method java/lang/StringBuilder."<
init>":()V
      30: aload_1
      31: invokevirtual #5                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      34: ldc           #8                  // String c
      36: invokevirtual #5                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      39: invokevirtual #7                  // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      42: astore_1
      43: getstatic     #9                  // Field java/lang/System.out:Ljava/
io/PrintStream;
      46: aload_1
      47: invokevirtual #10                 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      50: return
}
这个例子我是故意要这样写的,因为很多人认为既然编译帮我们做了优化,那么他们就可以不注意String的使用,但这其实就是一个编程陷阱。
我们理想上面例子编译器会帮我们编译成下面这个样子:
StringBuilder a = new StringBuilder("a");

a.append("b");

a.append("c");
System.out.println(a.toString());
可现实编译器帮我们做到的只能是这样:
String a = "a";

StringBuilder builder1 = new StringBuilder(a);

builder1.append("b");

a = builder1.toString();

StringBuilder builder2 = new StringBuilder(a);

builder1.append("c");

a = builder1.toString();
System.out.println(a);
所以大家以后在编写的时候要多注意这个问题的存在,当较多的使用操作符操作字符串时还是使用StringBuilder或者StringBuffer。
 
 

Java探秘之神秘的字符串String(二)的更多相关文章

  1. java字符数组char&lbrack;&rsqb;和字符串String之间的转换

    java字符数组char[]和字符串String之间的转换 觉得有用的话,欢迎一起讨论相互学习~Follow Me 使用String.valueOf()将字符数组转换成字符串 void (){ cha ...

  2. Java基础——数组应用之字符串String类

    字符串String的使用 Java字符串就是Unicode字符序列,例如串“Java”就是4个Unicode字符J,a,v,a组成的. Java中没有内置的字符串类型,而是在标准Java类库中提供了一 ...

  3. Java使用占位符拼接字符串-String&period;format&lpar;&rpar;的使用

    String domain = "www.ykmaiz.com"; int iVisit = 0; String info = String.format("该域名%s被 ...

  4. JAVA中数组&lpar;Array&rpar;、字符串&lpar;String&rpar;、集合&lpar;List、Set&rpar;相互转换

    1.数组转List String[] arr = new String[]{"A", "B", "C"}; List list = Arra ...

  5. Java内存管理-探索Java中字符串String(十二)

    做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 一.初识String类 首先JDK API的介绍: public final class String extends O ...

  6. Java入门篇(五)——Java的字符串&sol;String类

    前面在举例时有出现过String的例子,当时肯定有一部分朋友不知道这个是做什么用的.其实String类是Java中一个比较特殊的类,字符串即String类,它不是Java的基本数据类型之一,但可以像基 ...

  7. Java入门篇(五)——字符串&sol;String类

    前面在举例时有出现过String的例子,当时肯定有一部分朋友不知道这个是做什么用的.其实String类是Java中一个比较特殊的类,字符串即String类,它不是Java的基本数据类型之一,但可以像基 ...

  8. Java字符串String类操作方法详细整理

    关于String类的基本操作,可分为以下几类: 1.基本操作方法 2.字符串比较 3.字符串与其他数据类型之间的转换 4.字符与字符串的查找 5.字符串的截取与拆分 6.字符串的替换与修改 我觉得在整 ...

  9. JAVA笔记3&lowbar;&lowbar;字符串String类&sol;对象一对一关联

    import java.lang.String; import java.util.Scanner; public class Main { public static void main(Strin ...

随机推荐

  1. response与文件下载

    参考博客: http://www.cnblogs.com/lcpholdon/p/4380980.html http://www.cnblogs.com/mingforyou/p/3281945.ht ...

  2. 如何解决Angular 2 的templateUrl和styleUrl的路径问题?

    参考地址:https://github.com/kittencup/angular2-ama-cn/issues/18 前言: templateUrl表示的是组件在浏览器中运行时依赖的模板地址,所以在 ...

  3. Nginx启动SSL功能,并进行功能优化,你看这个就足够了

    一:开始Nginx的SSL模块 1.1 Nginx如果未开启SSL模块,配置Https时提示错误 nginx: [emerg] the "ssl" parameter requir ...

  4. 22&period;allegro中PCB打印设置&lbrack;原创&rsqb;

    1. -- 2. 3. 4. ----

  5. jquery为某div下的所有textbox的赋值

    html代码 <input type="button" value="变量div_Alltext中的变量" onclick="Do_DivAll ...

  6. 【读英文文档】Whetting Your Appetite(刺激你的食欲)

    如果你有很多工作是通过计算机来完成的,那么你一定希望其中的很多事情能够自动地实现.比方说,你希望在文本文件中实现查找和替换的功能,以某一种机制实现照片的重命名以及重新排序的功能,一个小型的数据库甚至是 ...

  7. leetcode数据库题目及答案汇总

    1. 第二高的薪水 select ifnull((select distinct Salary from Employee order by Salary desc limit 1,1),null) ...

  8. &lbrack;机器学习&rsqb;回归--Polinomial Regression 多项式回归

    首先我们需要明确一个概念,我们讨论的线性或者非线性针对的是自变量的系数,而非自变量本身,所以这样的话不管自变量如何变化,自变量的系数如果符合线性我们就说这是线性的.所以这里我们也就可以描述一下多项式线 ...

  9. css点滴1—八种方式实现元素垂直居中

    这里介绍实现元素垂直居中的方式,文章是参考了<css制作水平垂直居中对齐>这一篇文章. 1.行高和高度实现 这种方式实现单行垂直居中是很简单的,但是要保证元素内容是单行的,并且其高度是不变 ...

  10. 论文阅读---Reducing the Dimensionality of Data with Neural Networks

    通过训练多层神经网络可以将高维数据转换成低维数据,其中有对高维输入向量进行改造的网络层.梯度下降可以用来微调如自编码器网络的权重系数,但是对权重的初始化要求比较高.这里提出一种有效初始化权重的方法,允 ...