傻傻分不清楚?: 一文看懂Java中String的New操作和直接赋值字符串的区别

简介: 傻傻分不清楚?: 一文看懂Java中String的New操作和直接赋值字符串的区别

简介

  • String str=new String("a")和String str = "a"有什么区别?
  • String s = new String("xyz");创建了几个StringObject?
  • 这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";
  • `String s1 = "a";
    String s2 = s1 + "b";` s2 == “ab” 吗?

是不是被上面的题目逼疯了,哈哈,微观Java对考查Java变量初始化原理十分普遍,不过不用着急,本篇文章赋予你杀虫利器!!!

方法/步骤

1,区别字符串New和直接赋值

百度的面试官问: 这两个值,A,B 是否相等,如果都往HashSet里面放,能放下吗?

String A="ABC";
String B=new String("ABC");

答:(a)A==B 的判断为false;

      (b)A.equals(B)为true ;因为值相等,所以都往HashSet里面放不下,只能放一个 


问题:==与equals()的区别:

==:比较引用类型比较的是地址值是否相同
equals:比较引用类型默认也是比较地址值是否相同,注意:String类重写了equals()方法,比较的是内容是否相同。

String A = "ABC";内存会去查找永久代(常量池) 如果没有的话,在永久代中中开辟一块儿内存空间,
把地址付给栈指针,如果已经有了"ABC"的内存,直接把地址赋给栈指针;

因此

String str1="aa";

Srting str2="aa";

String Str3="aa";

....

这样下去,str1== Str2 == str3;会一直相等下去,
(a) == 的判断,(b)equals()的判断;都相等,因为他们的地址都相等,因此只在常量池中有一份内存空间,地址全部相同;
而String str = newString("a");是根据"a"这个String对象再次构造一个String对象;在堆中从新new一块儿内存,把指针赋给栈,将新构造出来的String对象的引用赋给str。 因此 只要是new String(),则,栈中的地址都是指向最新的new出来的堆中的地址, (a)“”==“” 是判断地址的,当然不相同;

(b)至于equals,String类型重写了 equals()方法,判断值是否相等,明显相等,因此 equals 是相等的;

Demo1:

public class StringDemo1 {
    public static void main(String[] args) {
        String s1 = new String("hello");
        String s2 = "hello";
        System.out.println(s1 == s2);// false
        System.out.println(s1.equals(s2));// true
    }
}
** 运行结果:**

> false 
> true

Demo1 代码详解

  1. 首先,通过main()方法进栈。
  2. 然后再栈中定义一个对象s1,去堆中开辟一个内存空间,将内存空间的引用赋值给s1,“hello”是常量,然后去字符串常量池 查看是否有hello字符串对象,没有的话分配一个空间存放hello,并且将其空间地址存入堆中new出来的空间中。
  1. 在栈中定义一个对象s2,然后去字符串常量池中查看是否有”hello”字符串对象,有,直接把”hello”的地址赋值给s2.
  2. 即s1中存的是堆中分配的空间,堆中分配的空间中存的是字符串常量池中分配空间存放”hello”的空间的地址值。而s2中之间存的是字符串常量池中分配空间存放”hello”的空间的地址值。
  3. 由于s1与s2中存放的地址不同,所以输出false。因为,类String重写了equals()方法,它比较的是引用类型的的值是否相等,所以输出true。即结果为false、true。

Demo2:

public class StringDemo2 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "world";
        String s3 = "helloworld";
        System.out.println(s3 == s1 + s2); //false
        System.out.println(s3.equals((s1 + s2)));//true
        System.out.println(s3 == "hello" + "world");//true
        System.out.println(s3.equals("hello" + "world"));//true
    }
}

运行结果:
在这里插入图片描述
Demo2 代码详解

  1. String s = new String(“hello”)会创建2(1)个对象,String s = “hello”创建1(0)个对象。

注:当字符串常量池中有对象hello时括号内成立!

  1. 字符串如果是变量相加,先开空间,在拼接。
  1. 字符串如果是常量相加,是先加,然后在常量池找,如果有就直接返回,否则,就创建。

那有读者会问了,那么不是常量相加呢?类似这种:

String s1 = "a";
String s2 = s1 + "b";

s2 == "ab"吗?下面就来解答!!!

2,引用赋值与直接赋值的区别

  • 先看Demo
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()方法处理拼接后,被处理的字符串会进入常量池中。**

回归之前的题目

String str=new String("a")和String str = "a"有什么区别?
解答: 一个是在常量池里面,new的对象是在heap里面,两个当然是不==的

String s = new String("xyz");创建了几个StringObject?
如果之前在常量池里面有了"xyz",就创建了一个,如果没有就创建两个(一个放在常量池里面,一个在heap里面)

这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";
解答: 题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。

`String s1 = "a";
String s2 = s1 + "b";` s2 == “ab” 吗?
不相等!!! s2指向heap里面,s1是指向的是常量池!!!

参考资料 & 致谢

java String 两种不同的赋值 比较
java String 内容的比较

目录
相关文章
|
25天前
|
存储 Java C语言
Java List 复制:浅拷贝与深拷贝方法及区别
我是小假 期待与你的下一次相遇 ~
|
19天前
|
算法 Java 数据库连接
Java 与 C++ 区别深入剖析及应用实例详解
本文深入剖析了Java和C++两种编程语言的区别,从编译与执行机制、面向对象特性、数据类型与变量、内存管理、异常处理等方面进行对比,并结合游戏开发、企业级应用开发、操作系统与嵌入式开发等实际场景分析其特点。Java以跨平台性强、自动内存管理著称,适合企业级应用;C++则因高性能和对硬件的直接访问能力,在游戏引擎和嵌入式系统中占据优势。开发者可根据项目需求选择合适语言,提升开发效率与软件质量。附面试资料链接:[点此获取](https://pan.quark.cn/s/4459235fee85)。
54 0
|
2月前
|
Java
Java 中 Exception 和 Error 的区别
在 Java 中,`Exception` 和 `Error` 都是 `Throwable` 的子类,用于表示程序运行时的异常情况。`Exception` 表示可被捕获和处理的异常,分为受检异常(Checked)和非受检异常(Unchecked),通常用于程序级别的错误处理。而 `Error` 表示严重的系统级问题,如内存不足或 JVM 错误,一般不建议捕获和处理。编写程序时应重点关注 `Exception` 的处理,确保程序稳定性。
|
3月前
|
Java 编译器 程序员
java中重载和多态的区别
本文详细解析了面向对象编程中多态与重载的概念及其关系。多态是OOP的核心,分为编译时多态(静态多态)和运行时多态(动态多态)。编译时多态主要通过方法重载和运算符重载实现,如Java中的同名方法因参数不同而区分;运行时多态则依赖继承和方法重写,通过父类引用调用子类方法实现。重载是多态的一种形式,专注于方法签名的多样性,提升代码可读性。两者结合增强了程序灵活性与扩展性,帮助开发者更好地实现代码复用。
134 0
|
Java Android开发
WSDL2Java操作指南
1. 安装JDK1.5, 配置系统环境变量:     下载安装JDK后, 设置环境变量:     JAVA_HOME=C:\Program Files\Java\jdk1.5.0_02     Path=%Path%;%JAVA_HOME%\bin(这里的%Path%指你系统已经有的一系列配置)     CLASSPATH=%JAVA_HOME%\lib  2. 下载axis,
1497 0
|
2月前
|
算法 Java 调度
Java多线程基础
本文主要讲解多线程相关知识,分为两部分。第一部分涵盖多线程概念(并发与并行、进程与线程)、Java程序运行原理(JVM启动多线程特性)、实现多线程的两种方式(继承Thread类与实现Runnable接口)及其区别。第二部分涉及线程同步(同步锁的应用场景与代码示例)及线程间通信(wait()与notify()方法的使用)。通过多个Demo代码实例,深入浅出地解析多线程的核心知识点,帮助读者掌握其实现与应用技巧。
|
5月前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
297 60
【Java并发】【线程池】带你从0-1入门线程池
|
3月前
|
Java 中间件 调度
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
142 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
|
2月前
|
Java
java 多线程异常处理
本文介绍了Java中ThreadGroup的异常处理机制,重点讲解UncaughtExceptionHandler的使用。通过示例代码展示了当线程的run()方法抛出未捕获异常时,JVM如何依次查找并调用线程的异常处理器、线程组的uncaughtException方法或默认异常处理器。文章还提供了具体代码和输出结果,帮助理解不同处理器的优先级与执行逻辑。

热门文章

最新文章