深入理解String类

1、String str = "eee" 和String str = new String("eee")的区别

先看一小段代码,

1 public static void main(String[] args) {
2         String str1 = "eee";
3         String str2 = "eee";
4         String str3 = new String("eee");
5         System.out.println("str1 == str2 is " + (str1 == str2));
6         System.out.println("str1 == str3 is " + (str1 == str3));
7         System.out.println("str1.equals(str2) is " + str1.equals(str2));
8         System.out.println("str1.equals(str3) is " + str1.equals(str3));
9     }

运行结果为:

str1 == str2 is true
str1 == str3 is false
str1.equals(str2) is true
str1.equals(str3) is true

2、从JVM角度分析

《深入理解Java虚拟机》一书指出,JVM运行时数据区如下:

所有线程共享区域包括:

方法区:用于存储已被虚拟机加载的类信息、常亮、静态变量、即时编译器编译后的代码等数据,以及运行时常量池

Java堆:在虚拟机启动时创建,存放对象实例,几乎所有的对象实例都在这里分配内存。

线程私有区域包括:

虚拟机栈:用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

本地方法栈:与虚拟机栈类似, 区别主要是本地方法栈为Native方法服务。

程序计数器:一块较小的内存空间,当作当前线程所执行字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能需要依赖这个计数器来完成。

String是一个不可变对象,可以认为是特殊的常量,因此存在方法区的运行时常量池中,可以被共享使用,以提高效率。

从JVM角度分析以上代码:

1   String str1 = "eee";    //1、在运行时常量池中创建新的对象"eee",如果常量池中不存在的话;2、栈中创建对象的引用str1
2   String str2 = "eee";    //由于运行时常量池中已经存在该对象,直接在栈中创建对象的引用str2即可。
3   String str3 = new String("eee");    //1、通过new指令,在堆中创建新的对象,2、在栈中创建对象的引用str3。

对象之间通过==来比较,比较的是对象的引用。因此也就不难理解为什么str1 == str2, 而str != str3了。

而equals方法比较的是什么呢?如果类没有重写Object类中equals方法时,比较的也就是对象的引用;如果重写了equals方法,那么就要看重写的方法了。

3、从代码角度分析

在jdk1.8中查看String类的源码,

 1 public final class String
 2     implements java.io.Serializable, Comparable<String>, CharSequence {
 3     private final char value[];
 4     private int hash; // Default to 0
 5
 6     public String(String original) {
 7         this.value = original.value;
 8         this.hash = original.hash;
 9     }
10    
11      /** 实际比较的是value[]是否相等 */
12     public boolean equals(Object anObject) {
13         if (this == anObject) {
14             return true;
15         }
16         if (anObject instanceof String) {
17             String anotherString = (String)anObject;
18             int n = value.length;
19             if (n == anotherString.value.length) {
20                 char v1[] = value;
21                 char v2[] = anotherString.value;
22                 int i = 0;
23                 while (n-- != 0) {
24                     if (v1[i] != v2[i])
25                         return false;
26                     i++;
27                 }
28                 return true;
29             }
30         }
31         return false;
32     }
33 }

根据源代码可以看出,String类的equals方法比较的实际是value[]是否相等。根据构造函数以及之前的JVM内存模型,可以分析出str1,str2,str3在内存中关系如下:

可以很容易的理解,str1.equals(str3)为true。

4、不建议String对象作为锁去同步

直接看一个例子,

 1 public class StringAsSynchronized {
 2     public static class Service {
 3         public void print(String stringParam) {
 4             try {
 5                 synchronized (stringParam) {
 6                     while (true) {
 7                         System.out.print(Thread.currentThread().getName());
 8                         Thread.sleep(1000);
 9                     }
10                 }
11             } catch (InterruptedException e) {
12                 e.printStackTrace();
13             }
14         }
15     }
16
17     public static class ThreadA extends Thread {
18         private Service service;
19         private String stringA = "synchronized";
20
21         public ThreadA(Service service) {
22             this.service = service;
23         }
24
25         @Override
26         public void run() {
27             service.print(stringA);
28         }
29     }
30
31     public static class ThreadB extends Thread {
32         private Service service;
33         private String stringB = "synchronized";
34
35         public ThreadB(Service service) {
36             this.service = service;
37         }
38
39         @Override
40         public void run() {
41             service.print(stringB);
42         }
43     }
44
45     public static void main(String[] args) {
46         Service service = new Service();
47         ThreadA a = new ThreadA(service);
48         a.setName("A");
49         ThreadB b = new ThreadB(service);
50         b.setName("B");
51         a.start();
52         b.start();
53     }
54 }

运行结果为:AAAAAAAAA。。。。

原因为ThreadA类以及ThreadB类中的成员变量stringA以及stringB指向的是同一个对象。

改正方法为

1、第33行修改为private String stringB = new String("synchronized");

2、更好的做法是不使用String对象用来同步锁。

时间: 2024-08-01 02:27:29

深入理解String类的相关文章

深入理解String类(重点)

一.想要理解string类,先看源码: 1 public final class String 2 implements java.io.Serializable, Comparable<String>, CharSequence { 3 /** The value is used for character storage. */ 4 private final char value[]; 5 6 /** Cache the hash code for the string */ 7 pri

jdk源码理解-String类

String类的理解 简记录一下对于jdk的学习,做一下记录,会持续补充,不断学习,加油 1.String的hash值的计算方法. hash值的计算方法多种多样,jdk中String的计算方法如下,比较简单,由字符串中的字符的ASCII值计算出来. /** * Returns a hash code for this string. The hash code for a * <code>String</code> object is computed as * <block

从C# String类理解Unicode(UTF8/UTF16)

上一篇博客:从字节理解Unicode(UTF8/UTF16).这次我将从C# code 中再一次阐述上篇博客的内容. C# 代码看UTF8 代码如下: string test = "UTF-8你"; //把字符转换为 byte[] byte[] bytearray_UTF8 = Encoding.UTF8.GetBytes(test); // byte[] to 16 进制的字符形式 String hexString = BitConverter.ToString(bytearray_

有关C++ std::string 类的类型转换 其他语言永远无法理解的伤

最近做了个项目,C++的MFC窗口程序,一个基于dialog的学生-图书管理系统,有一些感触,最后会放上一些项目截图和部分代码提供大家参考.如果有什么好方法和建议欢迎指导. 强类型,为什么这么伤 我知道强类型是很必要的,但是最近使用的都是一些弱类型的语言,到回来使用C++的时候还真是很不习惯.std::string在学的时候就知道了,好用但还是跟c标准char[]有很大区别的,MFC编程中又加入了没有学过的Cstring和LPSTR.LPWSTR等,下面我就来分享一下一些有关类型转换的感受. 什

String类的深入理解(未完待续)

String不是基本数据类型,String和8种包装类型是不可变类.String和8种基本数据类型采用值传递. 0.不可变类的设计原则 public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];//数组是引用传递 /*

《Java架构筑基》从Java基础讲起——String类深入理解

一. String问题答疑 String字符串是如何设计与实现考量的? String字符串缓存 intern()方法,由永久代移到堆中. String 的演化,Java 9 中底层把 char 数组换成了 byte 数组,占用更少的空间 二. String的创建机理 由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池.其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对

String Buffer和String Builder(String类深入理解)

String在Java里面JDK1.8后它属于一个特殊的类,在创建一个String基本对象的时候,String会向“ 字符串常量池(String constant pool)” 进行检索是否有该数据(字符串)存在,如果存在则向该数据进行实例引用,返回到创建的String对象.所以当创建两个不同名字,相同字符串的常量时,不可能会有两个不同的存储内存. String常量,在JDK1.8后便可以任意修改,不会创建新的内存地址对内存应用的浪费. (常量与常量比较) String de="你好婷婷&quo

String类replaceAll方法正则替换深入分析

作者网址: https://my.oschina.net/shipley/blog/98973 背景:      前几天有人发了一个关于下面问题的贴,对这个有点好奇,故花时间做了点研究.        对单个反斜杠字符串替换成双斜杠的Java实现如下:    String s = "\\";    方法一:String sr1 = s.replaceAll("\\\\", "\\\\\\\\");    方法二:String sr1 = s.re

C++的string类

关于头文件cstring,提供了strlen及很多与字符串相关的函数的声明. 头文件string,要使用string类,必须在程序中包含头文件string,string类位于std中,必须提供一条using编译指令,或者使用std::string来引用它. 1.string类的使用: 不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象. size()是string类的一个方法.可以将C++里边的string类理解成类型,我觉的可以理解成,创建:一个新类型,就像j