Java中 String类的详解(非常全面细致)

简介: Java中 String类的详解(非常全面细致)

一、String类的使用:

  1. String:字符串,使用一对“”引起来表示
  2. String声明为final的,不可以被继承
  3. 字符串的字符使用Unicode进行编码,一个字符(不区分字母还是汉字)占两个字节
  4. String实现了Serializable接口:表示字符串是支持序列化的,说明可以在网络上传输。
    实现了Comparable接口:表示String可以比较大小
  5. String类内部定义了final char[] value用于存储字符串数据
  6. 一定要注意:value是一个final类型,不可以修改:即value不能指向新的地址,但是单个字符内容是可以变化的。
  7. String代表不可变的字符序列,简称:不可变性
    体现:1.当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值。
    2.当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
    3.当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
  8. 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中
  9. 字符串常量池中是不会存储相同内容的字符串的
    案例代码如下:
package www.demo1;
import org.junit.Test;
public class StringTest {
    @Test
    public void test1(){
        final char[] value = {'a', 'b', 'c'};
        char[] v2 = {'t', '0', 'm'};
        value[0] = 'H';
//        value=v2; //不可以修改value地址
      //"abc" 字符串常量,双引号括起的字符序列 
        String s1="abc"; //字面量的定义方式
        String s2="abc";
        //比较s1,s2的地址值,结果为true
        System.out.println(s1==s2);
        s1="hello";
        System.out.println(s1);
        System.out.println(s2);
        System.out.println("***********************");
        String s3="abc";
        s3+="def";
        System.out.println(s3);
        System.out.println("***********************");
        String s4="abc";
        String s5=s4.replace("a","m");
        System.out.println(s4);
        System.out.println(s5);
    }
}

二、String实例化的方式:

方式一:通过字面量定义的方式

方式二:通过new + 构造器() 的方式

代码理解:

//通过字面量定义的方式:此时数据abc声明在方法区中的字符串常量池中
        String s1="abc";
        String s2="abc";
        //通过new+构造器的方式: 此时s3,s4保存的地址值,是数据在堆空间中开辟以后对应的地址值
        String s3 = new String("abc");
        String s4 = new String("abc");
        System.out.println(s1==s2);//true
        System.out.println(s1==s3);//false
        System.out.println(s1==s4);//false
        System.out.println(s3==s4);//false
        System.out.println("**************************");
        Person p1 = new Person("Tom",14);
        Person p2 = new Person("Tom",14);
        System.out.println(p1.name.equals(p2.name));//true,因为比较的是实体内容(String重写了equals()方法)
        System.out.println(p1==p2);//false new的两个对象地址值是不一样的
        System.out.println(p1.name==p2.name); //true
        p1.name="Lay";
        System.out.println(p2.name); //结果为Tom

结论:

  1. 常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量
  2. 只要其中有一个结果是变量,结果就在堆中
  3. 如果拼接的结果调用intern()方法,返回值就在常量池中
    加上代码便于理解:
String s1 = "Java";
        String s2 = "Android";
        String ss ="JavaAndroid";
        String s3 = "Java"+"Android";
        String s4 = s1 + "Android";
        String s5 = "Java" + s2;
        String s6 = s1 + s2;
        System.out.println(ss==s3);//true
        System.out.println(ss==s4); //false
        System.out.println(ss==s5);//false
        System.out.println(ss==s6);//false
        System.out.println(s4==s5);//false
        String s7=s4.intern();
        System.out.println(ss==s7);//true

额外补充**************************

创建String对象的两种方式:

String的内存布局

String的相关测试

测试题1:

public class StringExercise01 {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        //String重写了equals方法 ,比较的是具体的值,所以为true
        System.out.println(a.equals(b)); //true
        //a 首先看常量池有没有abc
        //b 首先看常量池有没有abc 有,就将b直接指向abc
        //所以地址值是一样的
        System.out.println(a == b); //true
    }
}

测试题2:

public class StringExercise02 {
    public static void main(String[] args) {
        String a = "ly";
        String b = new String("ly");
        System.out.println(a.equals(b)); //true
        System.out.println(a == b); //false
        System.out.println(a == b.intern()); //true
        System.out.println(b == b.intern()); //false
        //当调用intern()方法时,如果池已经包含一个等于此String对象的字符串(用equals(Object)方法确定)
        //则返回池中的字符串.否则,将此String对象添加到池中,并返回此String对象的引用
        //解读:b.intern() 方法最终返回的是常量池的地址(对象)
    }
}

测试题3:

public class StringExercise03 {
    public static void main(String[] args) {
        String s1 = "ly"; //s1 指向常量池的ly
        String s2 = "java"; //s2 指向常量池的java
        String s4 = "java"; //s4 指向常量池的java
        String s3 = new String("java"); //指向堆中的对象 
        System.out.println(s2 == s3); //false
        System.out.println(s2 == s4); //true
        System.out.println(s2.equals(s3)); //true
        System.out.println(s1 == s2); //false
    }
}

测试题4:

public class StringExercise04 {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "lyedu";
        Person p2 = new Person();
        p2.name = "lyedu";
        System.out.println(p1.name.equals(p2.name)); //比较的内容 true
        System.out.println(p1.name == p2.name); //true
        //"lyedu"返回的地址:就是在常量池中的地址
        //p1.name 指向的也是常量池中的"lyedu" 对应的地址
        System.out.println(p1.name == "lyedu"); //true
        String s1 = new String("bcde");
        String s2 = new String("bcde");
        System.out.println(s1 == s2); //false
    }
}
class Person {
    public String name;
}

对应的内存布局图如下:

测试题5:

//以下语句创建了几个对象,并画出内存布局图
String s1="hello";
s1="haha";
//创建了两个对象

内存布局图

String类相关的面试题

面试题1:

//创建了几个对象? 只有1个对象
        String a = "hello" + "abc";//等价于==>String a = "helloabc";
        System.out.println(a);

面试题2:

一共有3个对象,内存布局图如下:

具体代码和讲解如下:

public class StringExercise05 {
    public static void main(String[] args) {
        String a = "hello"; //创建a对象
        String b = "abc"; //创建b对象
        //解读
        //1.先创建一个StringBuilder sb = new StringBuilder();
        //2.执行 sb.append("hello");
        //3.执行sb.append("abc");
        //4.String c = sb.toString();
        //最后其实是 c 指向堆中对象的(String) value[]->池中 "helloabc"
        String c = a + b;  
        String d = "helloabc";
        System.out.println(c == d); //false
        String e = "hello" + "abc";
        System.out.println(d == e);//true
    }
}

该题小结:

底层是StringBuilder sb = new StringBuilder();

sb.append(a); sb.append(b); sb是在堆中,并且append是在原来字符串的基础上追加的。

重要规则:

  1. String c1=“ab” + “cd”; 常量相加,看的是池。
  2. String c1 = a + b; 变量相加,是在堆中。

面试题3:

public class StringExercise06 {
    public static void main(String[] args) {
        String s1 = "lyedu";//指向常量池中的"lyedu"
        String s2 = "java";//指向常量池中的"java"
        String s5 = "lyedujava"; //指向常量池中的"lyedujava"
        String s6 = (s1 + s2).intern(); //指向池中的 "lyedujava"
        System.out.println(s5 == s6); //true
        System.out.println(s5.equals(s6)); //true
    }
}

面试题4:

public class StringExercise07 {
    public static void main(String[] args) {
        Test1 ex = new Test1();
        ex.change(ex.str, ex.ch);
        System.out.println(ex.str + " and ");
        System.out.println(ex.ch);
    }
}
class Test1 {
    String str = new String("ly");
    final char[] ch = {'j', 'a', 'v', 'a'};
    public void change(String str, char ch[]) {
        str = "java";
        ch[0] = 'h';
    }
}

输出结果为:

ly and 
hava

以上是String字符串的讲解,有任何问题都可以在评论区指出!


目录
相关文章
|
2月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
160 57
|
26天前
|
存储 JavaScript Java
Java 中的 String Pool 简介
本文介绍了 Java 中 String 对象及其存储机制 String Pool 的基本概念,包括字符串引用、构造方法中的内存分配、字符串文字与对象的区别、手工引用、垃圾清理、性能优化,以及 Java 9 中的压缩字符串特性。文章详细解析了 String 对象的初始化、内存使用及优化方法,帮助开发者更好地理解和使用 Java 中的字符串。
Java 中的 String Pool 简介
|
13天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
1月前
|
缓存 安全 Java
java 为什么 String 在 java 中是不可变的?
本文探讨了Java中String为何设计为不可变类型,从字符串池的高效利用、哈希码缓存、支持其他对象的安全使用、增强安全性以及线程安全等方面阐述了不可变性的优势。文中还通过具体代码示例解释了这些优点的实际应用。
java 为什么 String 在 java 中是不可变的?
|
2月前
|
JSON Java 关系型数据库
Java更新数据库报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
在Java中,使用mybatis-plus更新实体类对象到mysql,其中一个字段对应数据库中json数据类型,更新时报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
119 4
Java更新数据库报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
|
26天前
|
存储 Java
Java 11 的String是如何优化存储的?
本文介绍了Java中字符串存储优化的原理和实现。通过判断字符串是否全为拉丁字符,使用`byte`代替`char`存储,以节省空间。具体实现涉及`compress`和`toBytes`方法,前者用于尝试压缩字符串,后者则按常规方式存储。代码示例展示了如何根据配置决定使用哪种存储方式。
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
66 8
|
2月前
|
Java
在Java中如何将基本数据类型转换为String
在Java中,可使用多种方法将基本数据类型(如int、char等)转换为String:1. 使用String.valueOf()方法;2. 利用+运算符与空字符串连接;3. 对于数字类型,也可使用Integer.toString()等特定类型的方法。这些方法简单高效,适用于不同场景。
63 7
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
63 2
|
2月前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
77 17