1、Stringstr = "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对象用来同步锁。

深入理解String类详解的更多相关文章

  1. Java String类详解

    Java String类详解 Java字符串类(java.lang.String)是Java中使用最多的类,也是最为特殊的一个类,很多时候,我们对它既熟悉又陌生. 类结构: public final ...

  2. java 复习整理(四 String类详解)

    String 类详解   StringBuilder与StringBuffer的功能基本相同,不同之处在于StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此效率上S ...

  3. STL之string类详解

    通过在网站上的资料搜集,得到了很多关于string类用法的文档,通过对这些资料的整理和加入一些自己的代码,就得出了一份比较完整的关于string类函数有哪些和怎样用的文档了!下面先罗列出string类 ...

  4. Java的String类详解

    Java的String类 String类是除了Java的基本类型之外用的最多的类, 甚至用的比基本类型还多. 同样jdk中对Java类也有很多的优化 类的定义 public final class S ...

  5. Java常用类(一)String类详解

    前言 在我们开发中经常会用到很多的常用的工具类,这里做一个总结.他们有很多的方法都是我们经常要用到的.所以我们一定要把它好好的掌握起来! 一.String简介 1.1.String(字符串常量)概述 ...

  6. Java常用类(二)String类详解

    前言 在我们开发中经常会用到很多的常用的工具类,这里做一个总结.他们有很多的方法都是我们经常要用到的.所以我们一定要把它好好的掌握起来! 一.String简介 1.1.String(字符串常量)概述 ...

  7. std::string类详解

    之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必 担心内存是否足够.字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(甚至 ...

  8. String类详解(1)

    首先String是一个类. 1,实例化String类方法. 1)直接赋值:String name="haha"; 2)通过关键字:String name=new String(&q ...

  9. String类详解,StringBuffer

    先说一下String类的equals()方法. 下面我们先看一段代码: 这段代码输出的结果为: ture true -------------- false 咋看之下貌似Object类比较特别,那么我 ...

随机推荐

  1. BZOJ3531[Sdoi2014]旅行——树链剖分+线段树

    题目描述 S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们 ...

  2. .net异常机制

    异常机制简介 当CPU运行到一些非法的指令,例如除零错误,访问内存页失败等指令,CPU会生成一个硬件异常,不同的异常有固定的异常代码作为标识符,异常产生以后CPU暂时不能继续执行后续的指令—因为后续的 ...

  3. px与rem的换算

    在线转化工具: http://www.ofmonkey.com/front/rem rem是相对于根元素<html>,这样就意味着,我们只需要在根元素确定一个参考值,这个参考值设置为多少, ...

  4. react-navigation 中的多tabs嵌套时内部tabs无法正常切换问题

    原因 问题应该是当用户滑动或切换时,无法判断是哪个tabs应该进行滑动切换和展示切换动画. 解决 swipeEnabled - 是否允许在标签之间进行滑动 animationEnabled - 是否在 ...

  5. splinter

    # coding:utf-8 import tornado.web import tornado.options import tornado.httpserver import tornado.io ...

  6. python之抽象类

    1什么是抽象类 与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化 2为什么要有抽象类 如果说类是从一堆对象中抽取相同 ...

  7. jQusery .基础

    1.jQusery 的基本用法 <%@ page language="java" contentType="text/html; charset=UTF-8&quo ...

  8. log4j2自定义Appender(输出到文件/RPC服务中)

    1.背景 虽然log4j很强大,可以将日志输出到文件.DB.ES等.但是有时候确难免完全适合自己,此时我们就需要自定义Appender,使日志输出到指定的位置上. 本文,将通过两个例子说明自定义APP ...

  9. 利用Vmware 创建Linux虚拟机的方法

    https://blog.csdn.net/qq_34929457/article/details/52663265 使用VMware新建一个Linux系统虚拟机 原创 2016年09月25日 17: ...

  10. Java中定义常量方法及建议(Class/Interface)

    Class定义常量方法(推荐方法) //final修饰符 public final class Constants { //私有构造方法 private Constants() {} public s ...