Java中的包装类、Object类和String类(二)


前言

在C语言中,要定义一个字符串需要先定义一个char类型数组,继而将此数组作为字符串处理。而Java中有String类,但不是八大基本数据类型,String类是作为一种引用数据类型使用的。
String类是Java中一种使用很多的特殊类,在面试和笔试中永远都是座上宾。


一、String类介绍

1、String类的创建和实现

Java中的String类是引用数据类型,即String和对象一样,通过引用的方式进行访问。

关于字符串的创建方式常用的有四种方式:

1、使用字符串的常量直接赋值
2、通过字符串常量的构造方法
3、通过字符数组进行构造(类似C语言)
4、通过字符串的valueOf方法

在这里插入图片描述
String是引用数据类型,因此String str只是保存了字符串对象的地址,而具体内存中是怎么保存的?

在这里插入图片描述

这张图的本质就是:Java中的字符串实际内部也是使用字符数组来存储具体的数值。事实上,所有高级语言,字符串类型本质上都是使用的字符数组,字符串就是由一个个的字符来组成的。Java中使用String类型来包装字符数组,提供了非常多的内置方法,让字符串变得更加好用。

2、String类的不可变性

字符串在设计时,被设计为当一个字符串对象产生之后,它的内容就无法修改,这称之为字符串的不可变性

首先看看String类的源码:
![在这里插入图片描述](https://img-blog.csdnimg.cn/280500a439d34fec8d6dc68f5b7a410c.png

我们知道final关键字修饰的内容不可变,所以String类型不可变就是因为使用了final关键字修饰吗?网上有很多解答是这么说的,但其实不对!我们来看个栗子:

在这里插入图片描述

可以看到,我们使用了final修饰value数组,但是其内容照样被修改,final修饰引用数据类型的变量,不能改的是这个引用的指向不变至于引用的对象内部如何变化,这是可以的! 。就像我们使用final修饰了一个遥控器,这个遥控器就只能操控某个特定的空调,但是空调本身还是可以改变状态的。

因此字符串对象内容不可变和final修饰没啥关系,final只是确保了value数组的指向不变!

在这里插入图片描述

之所以字符串对象的内容不可变,本质在于private封装!<\font>

private修饰的内容只在类的内部可见,因此出了String类,外部根本无法直接操纵这个value数组!,在String类中,将value数组完全封装,对外没有提供任何获取和修改该value内容的方法,因此字符串对象一旦产生,内容无法修改!==》本质在于String类的外部完全无法拿到这个value数组才无法修改。

这个栗子可以很好的解释String的不可变性:
在这里插入图片描述
可以看到,在内存中,调用fun函数时,数组定义的内容可以直接更改,而str则是重新生成了一个字符数组。

话又说回来,既然private保证了它的不可变性,那用final修饰是出于什么目的?其实这是出于线程安全的考虑,被final修饰的字符串对象是线程安全的,这在StringBuffer(线程安全,效率低)和StringBuilder(线程不安全,效率高)中得以体现。

为什么String类设计成不可变的,主要有以下几点:

1.方便实现字符串对象池.如果String 可变,那么对象池就需要考虑写时拷贝的问题了.
2.不可变对象是线程安全的.
3.不可变对象更方便缓存 hash code,作为 key时可以更高效的保存到HashMap 中.

二、String类常见操作

1.字符串的比较

字符串比较通常有四种形式:

1、通过==比较,此时比较的是两个字符串的地址,不看内容,相等则表示指向同一个引用。
2、通过equals方法比较两个字符串的内容是否相等(大小写敏感)
3、通过equalsIgnoreCase方法比较两个字符串的内容是否相等(大小写不敏感)
4、通过compareTo方法比较两个字符串对象的大小关系

其中,两个equals方法返回的是Boolean类型,而compareTo方法返回值为int类型。

在之前的文章有介绍过compareTo方法,这里很明显String类实现了comparable接口并重写了compareTo方法。String类中的compareTo方法比较规则如下:

a.首先按照两个字符串的字典序比较大小,若出现不相等字符,直接返回这两个字符的大小差值(ASCⅡ码)
在这里插入图片描述‘h’-‘H’=32
b.若前k个字符相等(k是较短的字符串长度),返回长度差

在这里插入图片描述

2、字符串内容的查找

2.1、在字符串中查找指定字符

char charAt(int index):返回字符串中指定索引index处的字符这个方法非常常用,所以举个栗子:

比如写一个方法判断一个字符串是否由纯数字组成(在日常生活中,注册时填手机号啊之类的地方都是这么判断的)

public static boolean isDigit(String str) {
        // 1.边界条件,字符串为空null 或内容为空
        if (str == null || str.length() == 0) {
            return false;
        }
        // 2.需要将字符串拆分为一个个的字符来依次判断
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            // 如何知道c是一个数字型的字符?
            if (c < '0' || c > '9') {
                // 一定不是数字字符!,找到反例!
                return false;
            }
//            // 判断c是否是数字字符     这里也可使用Character的内置方法进行判断
//            if (!Character.isDigit(c)) {
//                return false;
//            }
        }
        // 走完整个循环,还没找到反例,则str一定是纯数字字符串!
        return true;
    }

2.2、其他查找方法:

写在前面:所有和索引相关的方法调用一定要注意是否越界的问题
在这里插入图片描述
补充:还有一个判断字符串中是否有指定子串的方法: boolean contains(“*****”),若有返回true,否则返回false。

3、字符串转化

3.1 将其他类型转化为字符串对象,使用 valueOf方法,可以接受所有参数。
在这里插入图片描述

3.2大小写转化

在这里插入图片描述

这两个方法只作用于字母字符串,只对英文字符起效果。

3.3字符串转数组

第一种,使用方法toCharArray();
在这里插入图片描述

第二种,转为字节数组getBytes(可以传入指定的字符编码)

在这里插入图片描述

拓展:String中提供了format()方法,可以将字符串格式化输出,类似于C语言中的printf,但是java中支持正则表达式
在这里插入图片描述

4、字符串的替换

对字符串内部分内容进行替换,使用replace方法
在这里插入图片描述

5、字符串的拆分

将一个字符串按照指定格式拆分为字符串数组,较为常用。使用split()方法
在这里插入图片描述

注意:当调用split碰到某些特殊字符时,需要用到转义字符,否则无法正确拆分
以拆分IP地址为例:
在这里插入图片描述

6、字符串的截取

在这里插入图片描述
演示示例:传入一个字符串(只包含字母,进行首字母大写的操作)
在这里插入图片描述

拓展:String中提供了trim()方法,可以将字符串两端的空格(制表符、换行符)去掉,保留中间的部分。
在这里插入图片描述

7、力扣题

通过一个力扣题将上面所使用的方法融会贯通,题目如下:

在这里插入图片描述
代码:

public String capitalizeTitle(String title) {
		//使用StringBuffer便于修改字符串
        StringBuilder sb=new StringBuilder();
        //按指定格式拆分为字符串数组
        String[] strings=title.split(" ");
        for (int i = 0; i < strings.length; i++) {
            //对每一个字符串进行处理
            String s=strings[i];
            if(s.length()<3){
                sb.append(s.toLowerCase());
            } else{
                String s1=s.substring(0,1).toUpperCase();
                String s2=s.substring(1).toLowerCase();
                sb.append(s1).append(s2);
            }
            sb.append(" ");
        }


        return sb.toString().trim();
    }

三、字符串常量池

池化思路,是程序设计中为了节省内存提高效率而引出的一种规则,字符串字面量经常会被重复使用,因此放入常量池,当再次使用该内容时,省去了创建对象,开辟内存的时间,直接复用已有对象。

先来看一段代码:

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

这段代码的运行结果是什么?按照引用类型的比较规则,比较内容时应使用.equals()方法,所以这里输出应该全为false,但事实并非如此。

在这里插入图片描述

这是因为采用直接赋值类似String str = "字符串字面量"的方式创建字符串对象,会使用字符串的常量池,若该对象是第一次使用,就在堆中开辟新空间,产生新对象,产生之后将该对象置入字符串的常量池。当下次再次使用该对象时(还是采用直接赋值的方式),若常量池中已有该内容的字符串,直接引用常量池中的对象,不再创建字符串

str被创建的时候,“hello”第一次出现,JVM产生该对象并置入常量池,此时常量池中已经有了值为hello的对象,定义str1时,JVM直接将池中对象的地址赋值给str1,不再创建新对象。只有直接赋值的方式会用到字符串的常量池!

字符串的intern方法

上面说了,只有直接赋值才会创建字符串常量放入常量池,那么如何将别的方法构造出的字符串放入常量池呢?答案是使用intern方法:调用intern方法,会将产生的字符串对象放入常量池(不存在的前提下),若常量池已有该对象,则该方法返回常量池中的字符串对象引用

来看下面的代码:

char[] ch = new char[] {'a','b','c'};
        String s1 = new String(ch);
//        s1.intern();
        String s2 = "abc";
        // 输出true还是false
        System.out.println(s1 == s2);

在调用s1.intern前,输出false,调用之后将s1指向的字符串放入了常量池,因此s2定义时,直接指向了常量池中的“abc”输出true。

注意:每次调用intern后,都要接受一下返回值,因为不确定此时常量池中有没有该字符串对象,不接受的话有可能还在指向堆中创建的地址。

在这里插入图片描述
因为常量池已有该对象,则该方法返回常量池中的字符串对象引用,即0x100,但是这里没接受,所以还是指向0xf300这个地址。
不接收输出true ,false
如果改为str2=str2.intern(),则此时输出都是true。

常量池小结:

1.存储一系列的字符串对象,当字符串常量第一次出现,且常量池中没有该值的字符串对象,就会将该对象置入常量池。
⒉.若采用直接赋值的方式声明字符串引用,若常量中存在该对象,则直接返回常量池的字符串对象,不会创建新对象
3.关于intern方法,尝试将当前字符串对象置入常量池,若常量池没有该值,则将当前对象置入常量池若常量池已经包含了该值的对象,则不会将对象对象置入常量池,intern方法直接返回常量池对应的对象


总结

String类时Java开发时非常常见和使用的类,本文对String类做了详细的介绍以及其内部的各种方法的使用进行了介绍,以及字符串常量池的规则和使用以及注意细节。
总的来说,String类是开发学习中很基础也很重要的知识点,所以单独拉出来写了一篇,一定要手拿把掐!

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭彭彭摆鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值