JavaSE中的String类

1.定义方式

常见的三种字符串构造

public class Test1 {
    public static void main(String[] args) {
        // 使用常量串构造
        String str1 = "abc";
        System.out.println(str1);
        
        // 直接newString对象
        String str2 = new String("ABC");
        System.out.println(str2);
        
        // 使用字符数组进行构造
        char[] array = {'h','e','l','l','o'};
        String str3 = new String(array);
        System.out.println(str3);
    }
}

以上三种构造方法的解释:

 

注意: 

1.String是引用类型,内部并不存储字符串本身,在String类的实现源码中,String类实例变量如下:

public static void main(String[] args) {
        // s1和s2引用的是不同对象 s1和s3引用的是同一对象
        String s1 = new String("hello");
        String s2 = new String("world");
        String s3 = s1;
    }

在内存中的存储形式: 

s1和s3引用的是同一个对象

 2. 在Java中""引起来的也是String类型对象。

// 打印"hello"字符串(String对象)的长度
System.out.println("hello".length());

2.内存中的存储

字符串常量池

在JDK7 开始,字符串常量池被挪到堆里了,常量池可以说在堆内存中

规定:只要是双引号引起来的字符串常量,会存在一个字符串常量池当中。


存储逻辑:
1.先检查这个内存(字符串常量池)有没有这个字符串

2.如果没有,存进去
3.如果有,就不重复存储了。取出现有的对象即可。

练习一 

 我们知道 == 比较的是地址,通过对str1,2,3的比较我们可以知道它们在内存中是如何存储的!

public static void main(String[] args) {
        String str1 = "hello";
        String str2 = new String("hello");
        System.out.println(str1 == str2);

        String str3 = "hello";
        System.out.println(str1 == str3);
    }

这个结果说明 str1 和 str2存放的地址是不一样的, str1 和 str3 存放的地址是一样的。

 

  1. 常量池只放字符串常量,比如该例中的"hello"
  2. str2 new一个String对象,在堆上开辟内存,假设内存地址是222,在这个String 对象中,存在一个value[] 保存着 orginal传入的字符串,这个val ==“hello”,因为在字符串常量池中已经有了"hello",所以val 直接指向 常量池中的"hello".但是str2 指向的依然是 222在堆中的空间。
  3. str3 也等于"hello",他也准备把hello放在常量池当中.此时常量池中已经存在"hello",那么之后str3 在存放"hello"地址的时候,就指向的是常量池中原来hello的地址。

练习二

public static void main(String[] args) {

        String str1 = "hello";
        String str2 = "hel"+"lo";
        System.out.println(str1==str2);

        String str3 = new String("hel")+"lo";
        System.out.println(str1==str3);

    }

在内存中的存储 :

  1. str1 指向字符串常量池中的 “hello”
  2. str2 是"hel"与"lo" 组合而成的,常量在编译的时候就已经确定了,所以在编译时,已经被处理为"hello",所以也指向 常量池中的"hello"。所以str1=str2 
  3. str3 首先new 了一个String(“hel”)对象,在堆中开辟一块空间,这个对象中的"hel"同时存放在常量池中,之后又在常量池中开辟一块空间存放 “lo”。两块部分之间的"+",将 String 的对象 与常量池中的 "lo"结合在堆中再次开辟一块新的空间,这块内存中的val ==“hello”,str3指向的是合并之后的对象 ,地址为333 

练习三

 public static void func(String str,char[] array){
        str = "abcdef";
        array[0] = 'g';
    }

    public static void main(String[] args) {
        String str1 = "hello";
        char[] val = {'a'};
        System.out.println(str1);
        System.out.println(Arrays.toString(val));
        func(str1,val);
        System.out.println("=================");
        System.out.println(str1);
        System.out.println(Arrays.toString(val));
    }

 

  1. str1 指向字符串常量区的"hello"
  2. val 作为数组引用,指向堆中开辟的数组空间
  3. str 作为函数的形参,接收str1实参的值,此时str指向常量区的”hello“,但是在方法的内部,str = “abcde”,在字符串常量区中有开辟一块"abcde"的内存,但因为是形参出了该方法体就会失效,所以并没有改变原来的值
  4. array 作为函数的形参,接收val 实参的值,此时array 指向堆中 开辟的数组空间,此时通过array 来改变数组元素的内容,最终 改变的也同样是val 实参的内容. 

3.常见操作

(1)String对象的比较

1. ==比较是否引用同一个对象

public static void main(String[] args) {
        //对于基本类型变量,==比较两个变量中存储的值是否相同
        int a = 10;
        int b = 20;
        int c = 10;
        System.out.println(a == b);
        System.out.println(a == c);
        
        // 对于引用类型变量,==比较两个引用变量引用的是否为同一个对象
        String s0 = "abc";
        String s1 = new String("abc");
        String s2 = new String("abc");
        //s1,s2的val指向的都是常量池中的"abc",但s1,s2指向的对象不同
        System.out.println(s1 == s2);
    }

2. boolean equals(Object anObject) 方法:按照字典序比较

字典序:字符大小的顺序

equals比较:String对象中的逐个字符

public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = new String("abc");
        String s3 = new String("ABC");
        System.out.println(s1.equals(s2));

        //忽略大小写比较
        System.out.println(s1.equalsIgnoreCase(s3));
    }

3.compareTo比较

int compareTo(String s) 方法: 按照字典序进行比较

与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型。具体比较方式:

1. 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值

2. 如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值

public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = new String("ac");
        String s3 = new String("abc");
        String s4 = new String("abcdef");
        System.out.println(s1.compareTo(s2));  // 不同输出字符差值-1
        System.out.println(s1.compareTo(s3));  // 相同输出 0
        System.out.println(s1.compareTo(s4));  // 前k个字符完全相同,输出长度差值 -3
    }



//忽略大小写比较compareToIgnoreCase
public static void main(String[] args) {
    String s1 = new String("abc");
    String s2 = new String("ac");
    String s3 = new String("ABc");
    String s4 = new String("abcdef");
    System.out.println(s1.compareToIgnoreCase(s2));   // 不同输出字符差值-1
    System.out.println(s1.compareToIgnoreCase(s3));   // 相同输出 0
    System.out.println(s1.compareToIgnoreCase(s4));   // 前k个字符完全相同,输出长度差值 -3
}

 (2)字符串的查找

public static void main(String[] args) {
        String str = "abcdefgabccd";
        System.out.println(str.charAt(2));//输出下标为2的字符c

        System.out.println(str.indexOf('a'));//返回a第一次出现的位置。没有则返回-1
        System.out.println(str.indexOf('a',3));//从3位置d开始找a第一次出现的位置,7

        System.out.println(str.indexOf("cd"));//返回字符串“cd”第一次出现的位置,即2
        System.out.println(str.indexOf("cd",5));//从下标为5的位置开始找返回字符串“cd”第一次出现的位置,即10

        System.out.println(str.lastIndexOf('a'));//从后往前找a第一次出现的位置,10
        System.out.println(str.indexOf('a',5));//从下标为5的位置后往前找a第一次出现的位置,7

        System.out.println(str.lastIndexOf("cd"));//从后往前找“cd”第一次出现的位置,10
        System.out.println(str.lastIndexOf("cd",5));//从下标为5的位置后往前找“cd”第一次出现的位置,2
    }

(3)转化

1. 数值和字符串转化  

public static void main(String[] args) {
        //数字转字符串
        String s1 = String.valueOf(1234);
        String s2 = String.valueOf(12.34);
        String s3 = String.valueOf(true);
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);

        //字符串转数字
        int data1 = Integer.parseInt("1234");
        double data2 = Double.parseDouble("12.34");
        System.out.println(data1);
        System.out.println(data2);
    }

2.大小写转换 

public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "HELLO";
        // 小写转大写
        System.out.println(s1.toUpperCase());
        //大写转小写
        System.out.println(s2.toLowerCase());
    }

3. 字符串转数组  

public static void main(String[] args) {
        //字符串转数组
        String str1="ABCD";
        char[]array=str1.toCharArray();
        System.out.println(Arrays.toString(array));

        //数组转字符串
        char[] ch = {'a','b','c'};
        String str2 = new String(ch); 
        System.out.println(str2);
    }

 4.格式化

public static void main(String[] args) {

        String s=String.format("%d-%d-%d",2024,4,17);
        System.out.println(s);
    }

(4)字符串的替换

public static void main(String[] args) {
        String s = "hello";
        System.out.println(s.replaceAll("l","a"));
        //heaao
        System.out.println(s.replaceFirst("l", "a"));
        //healo
    }

(5)字符串拆分

1.字符串的整体拆分 

public static void main(String[] args) {
        String s = "aa bb cc dd";
        String[] result = s.split(" ");
        for (String str:result) {
            System.out.println(str);
        }

    }

 

2.字符串的部分拆分

String[] result1 = s.split(" ",2);
        for (String str1:result1) {
            System.out.println(str1);
        }

拆分是特别常用的操作. 一定要重点掌握. 另外有些特殊字符作为分割符可能无法正确切分, 需要加上转义.

3.IP地址的拆分

  • “ \. ”才能表示一个真正的 “.”
  • 同时"\"也需要进行转义,那么就又要再加一个斜杠。
  • “\\.”这时字符串才只能被 “ . ”分割。
String str = "192.168.1.1" ;
        String[] result2 = str.split("\\.") ;
        for(String str2: result2) {
        System.out.println(str2);
        }

 

注意事项:

1. 字符"|","*","+"都得加上转义字符,前面加上 "\\" .

2. 而如果是 "\" ,那么就得写成 "\\\\" .

3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符.

public static void main(String[] args) {
        String s = "111@qq.com";
        String[] result = s.split("@|\\.");
        for (String str:result) {
            System.out.println(str);
        }
    }

(6)字符串的截取

public static void main(String[] args) {
        String str = "helloworld" ;
        System.out.println(str.substring(5));
        System.out.println(str.substring(0, 5));//[0,5)
    }

 

注意事项:

1. 索引从0开始

2. 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标  

(7)其它操作方法

public static void main(String[] args) {
        String str = "  hello world  ";
        System.out.println(str.trim());

        String str1 = "HELLO WORLD";
        String str2 = "hello world";

        System.out.println(str1.toLowerCase());
        System.out.println(str1.toUpperCase());
    }

 

 4.字符串的特性

 (1)不可变性

String是一种不可变对象. 字符串中的内容是不可改变。字符串不可被修改,是因为:

1. String类在设计时就是不可改变的,String类实现描述中已经说明了

JDK1.8中 

String类中的字符实际保存在内部维护的value字符数组中,该图还可以看出:

1. String类被final修饰,表明该类不能被继承

2. value被修饰被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中 的内容可以修改。 

3.又因为value被private封装了,源码中有没有get和set方法,使得字符串具有不可变性。

所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象

final修饰类表明该类不想被继承,final修饰引用类型表明该引用变量不能引用其他对象,

但是其引用对象中的内容是可以修改的。 

public static void main(String[] args) {
        final int[]array=new int[]{1,3,4};
       // array=new int[]{1,3,6};//报错
        array[0]=99;//可以修改
        System.out.println(Arrays.toString(array));
    }

 (2)字符串的修改

public static void main(String[] args) {
        String str = "hello";
        str += " world";
        System.out.println(str); // 输出:hello world
    }

因此:尽量避免对String的直接需要

如果要修改建议尽量 使用StringBuffer或者StringBuilder。

5.StringBuffer 和 StringBuilder 

(1)常用方法

public static void main(String[] args) {
        // 追加:即尾插-->字符、字符串、整形数字
        StringBuilder sb1 = new StringBuilder("hello");
        StringBuilder sb2 = sb1.append("world");

        System.out.println(sb2);
        System.out.println(sb1);
        
        System.out.println(sb1 == sb2);
    }

String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可 以修改。频繁修改字符串的情况考虑使用StringBuilder。

注意:String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则:

String变为StringBuilder: 利用StringBuilder的构造方法或append()方法

StringBuilder变为String: 调用toString()方法。

(2)区别 

String 和 StringBuilder 及 StringBuffer 的区别

  1. String 进行拼接时,底层会被优化为StringBuilder
  2. String的拼接会产生临时对象,但是后两者每次都只是返回当前对象的引用。
  3. String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.

StringBuffer与StringBuilder大部分功能是相似的

StringBuffer 和 StringBuilder 的区别主要体现在线程安全上 。

该关键字相当于一把锁来保证线程的安全

 StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/553503.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux学习】Linux指令(四)

文章标题 🚀zip/unzip指令:🚀tar指令(重要):🚀uname –r指令:🚀关机指令🚀几个常用操作 🚀zip/unzip指令: zip 与 unzip的安装 yum i…

Day20-【Java SE高级】单元测试 反射 注解 动态代理

一、单元测试 就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。 1. 咱们之前是如何进行单元测试的?有啥问题? 只能在main方法编写测试代码,去调用其他方法进行测试。无法实现自动化测试,一个方法测试失败,可能…

学习在Debian系统上安装Shadowsocks教程

学习在Debian系统上安装Shadowsocks教程 安装shadowsocks-libev及其所需的依赖启动Shadowsocks服务:如果你想要通过代理本地流量,你可以使用ss-local:启动并设置ss-local:查看状态本地连接 安装shadowsocks-libev及其所需的依赖 …

量化交易为什么独宠Python

“我在学一门叫Python的语言”。“什么是Python,没听说过啊,为什么不学C啊”。这是发生在2014年,上海的一家量化基金,量化研究员和老板之间的对话。 “我想问一下关于Python的课程,什么时候能开班”。“Python啊&#…

数据结构-栈和队列刷题集(长期更新)

文章目录 万能计算器的实现以及源码分析1. leetcode 150 逆波兰表达式求值 万能计算器的实现以及源码分析 /*** 我们尝试写一个完整版的计算器,由于计算机不能很好的识别括号,所以一般要转换为逆波兰表达式求解* 思路解析 :* 1. 输入一个 中缀表达式* 2. 中缀表达式转化为list…

Python 数据结构和算法实用指南(一)

原文:zh.annas-archive.org/md5/66ae3d5970b9b38c5ad770b42fec806d 译者:飞龙 协议:CC BY-NC-SA 4.0 前言 数据结构和算法是信息技术和计算机科学工程学习中最重要的核心学科之一。本书旨在提供数据结构和算法的深入知识,以及编程…

28岁转行嵌入式适合转嵌入式吗?

转行到嵌入式领域是一个很好的选择,特别是如果你对电子技术、嵌入式系统和软硬件交互感兴趣的话。我这里有一套嵌入式入门教程,不仅包含了详细的视频 讲解,项目实战。如果你渴望学习嵌入式,不妨点个关注,给个评论222&a…

信息系统项目管理师0054:运维和服务(4信息系统管理—4.1管理方法—4.1.4运维和服务)

点击查看专栏目录 文章目录 4.1.4运维和服务1.运行管理和控制2.IT服务管理3.运行与监控4.终端侧管理5.程序库管理6.安全管理7.介质控制8.数据管理4.1.4运维和服务 信息系统的运维和服务应从信息系统运行的视角进行整合性的统筹规划,包括对信息系统、应用程序和基础设施的日常控…

C语言的OJ判题机设计与实现

1. 接收判题入参 判题需要作答代码、测试输入和期望输出、编译器名称、时空限制。对于支持special judge的还需要传入是否为sj和sj代码。推荐使用消息队列,应对高并发的比赛情况会比较好。 但是消息队列是异步的,我为了快点实现能提交后在当前页面获得判…

Elasticsearch:(一)ES简介

搜索引擎是什么?在不少开发者眼中,ES似乎就是搜索引擎的代名词,然而这实际上是一种误解。搜索引擎是一种专门用于从互联网中检索信息的技术工具,它主要可以划分为元搜索引擎、全文搜索引擎和垂直搜索引擎几大类。其中,…

AIGC算法1:Layer normalization

1. Layer Normalization μ E ( X ) ← 1 H ∑ i 1 n x i σ ← Var ⁡ ( x ) 1 H ∑ i 1 H ( x i − μ ) 2 ϵ y x − E ( x ) Var ⁡ ( X ) ϵ ⋅ γ β \begin{gathered}\muE(X) \leftarrow \frac{1}{H} \sum_{i1}^n x_i \\ \sigma \leftarrow \operatorname{Var}(…

【中级软件设计师】上午题08-UML(下):序列图、通信图、状态图、活动图、构件图、部署图

上午题08-UML 1 序列图2 通信图3 状态图3.1 状态和活动3.2 转换和事件 4 活动图5 构件图(组件图)6 部署图 【中级软件设计师】上午题08-UML(上):类图、对象图、用例图 UML图总和 静态建模:类图、对象图、用例图 动态建模&#xff…

【简单介绍下PostCSS】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…

仿真测试的应用领域

仿真测试在各种领域中都有广泛的应用,以下是一些应用最广泛的场景: 工业制造:通过模拟制造过程,可以预测产品的质量和性能,优化生产流程,降低成本。航空航天:飞机、导弹、航天器等的设计和研发…

AWS Key disabler:AWS IAM用户访问密钥安全保护工具

关于AWS Key disabler AWS Key disabler是一款功能强大的AWS IAM用户访问密钥安全保护工具,该工具可以通过设置一个时间定量来禁用AWS IAM用户访问密钥,以此来降低旧访问密钥所带来的安全风险。 工具运行流程 AWS Key disabler本质上是一个Lambda函数&…

如何访问内网?

在互联网万维网上,我们可以轻松访问各种网站和资源。但是,有时我们需要访问局域网内的资源,例如公司内部的文件共享、打印机等。本文将介绍几种方法,帮助您实现访问内网的需求。 内网穿透技术 内网穿透技术是一种通过互联网将局域…

SQL表连接详解:JOIN与逗号(,)的使用及其性能影响

省流版 在这个详细的解释中,我们将深入探讨SQL中表连接的概念,特别是JOIN和逗号(,)在连接表时的不同用法及其对查询性能的影响。通过实际示例和背后的逻辑分析,我们将揭示在不同场景下选择哪种连接方式更为合适。 1.…

Mysql查询表的结构信息 把列名 数据类型 等变成列数据(适用于生成数据库表结构文档) (二)

书接上文 Mysql查询表的结构信息 把列名 数据类型 等变成列数据(适用于生成数据库表结构文档) (一) 好,怎么生成文档呢?很简单 用navicat 或者sqlyog navicat操作如下 举个例子 如下查询结果 全选查询结果,右键,复制为指标…

什么是神经网络和机器学习?【云驻共创】

什么是神经网络和机器学习? 一.背景 在当今数字化浪潮中,神经网络和机器学习已成为科技领域的中流砥柱。它们作为人工智能的支柱,推动了自动化、智能化和数据驱动决策的进步。然而,对于初学者和专业人士来说,理解神经…

使用CCS软件查看PID曲线

在刚开始学习PID的时候,都需要借助PID的曲线来理解比例,积分,微分这三个参数的具体作用。但是这些曲线生成一般都需要借助上位机软件或者在网页上才能实现。如果是在单片机上调试程序的话,想要看曲线,一般就是通过串口…
最新文章