认识Java中的String类

本文详细介绍了Java中的String类,包括其构造方式、基本用法、不可变性以及StringBuilder和StringBuffer的区别,重点强调了在不同场景下的选择和性能优化。
摘要由CSDN通过智能技术生成

1.String基础

1.1 认识String

String是Java中的一个类,属于引用数据类型,遵循引用类型的基本规律和要求。但是它也有自己的一些特点,例如用双引号去表示一个字符串,一般用双引号括起来的一串字符都是String的对象,如"hello world","java"

字符与字符串也有一定的关系,String字符串的内部是通过一个private final char[]数组来实现数据存储的,所以可以通过以下方式去表示一个字符串:

String string = new String(new char[]{'h','e','l','l','o'});

1.2 创建String的方式

1.2.1 通过构造方法创建

String类种有11种构造方法,这些方法提供不同的参数来初始化字符串,比如提供一个字符数组参数:

 char[] charArray = new char[]{'h','e','l','l','o'};
 String string  = new String(charArray);

这种方式创建过于复杂,实际开发中很少使用

1.2.2 直接赋值

String string2 = "hello";

当编译器遇到字符串常量时,这里的常量是"hello",编译器会使用该值创建一个String对象。

        String string2 = "";
        String string3 = null;

        System.out.println("string2 = " + string2);
        System.out.println("string3 = " + string3);

使用""赋值时,是有对应的string对象创建的,但是使用null去赋值,就没有对象创建,即字符串对象的引用指向的是null。

需要注意的是,String类事不可改变,所以一旦创建了String对象,那它的值就无法被改变了,如果需要对字符串做很多修改,那么可以选择StringBuffer 或 StringBuilder 类, 关于这两者的用法,后续会介绍

2.String的基本用法

2.1 常用方法总结

常用方法如图:

转化成代码:

//string的常用方法
  String str = "hello world";
  //int length()
  System.out.println("字符串的长度是 " + str.length());
  System.out.println("l字符第一次出现的位置是 " + str.indexOf('l'));
  System.out.println("ll字符串第一次出现的位置是 " + str.indexOf("ll"));
  System.out.println("l字符最后一次出现的位置是 " + str.lastIndexOf('l'));
  System.out.println("字符串从第一位到最后一位切割后的结果为" + str.substring(1));
  System.out.println("字符串从第一位到第三位切割后的结果为 " + str.substring(1, 3));
  System.out.println("与字符串hello是否相等" + str.equals("hello"));
  System.out.println("将字符串转化为大写" + str.toUpperCase());
  System.out.println("获取字符串第一位的字符" + str.charAt(1));
  System.out.println("将字符串按照ll切割,并获取其结果 " + Arrays.toString(str.split("ll")));
  System.out.println("转化为字符数组 " + Arrays.toString(str.getBytes()));

运行结果如下:

2.2 字符串与byte数组的相互转化

        //创建字符串
        String str = "hello world";
        //使用utf-8 的方式 转化为字符数组
        byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
        for (int i = 0; i < bytes.length; i++) {
            System.out.println(bytes[i]);
        }
        //将bytes数组转化为字符串
        String str2 = new String(bytes);
        System.out.println(str2);

2.3 == 运算符与equals的区别

先说下结论:== 运算符比较的是地址,equals比较的是值

String str1 = "123";
String str2 = "123";
String str3 = new String("123");

System.out.println("str1 与 str2 ==比较"  + (str1 == str2));
System.out.println("str1 与 str2 equals比较"  +(str1.equals(str2)));
System.out.println("str1 与 str3 ==比较"  +(str1 == str3));
System.out.println("str1 与 str3 equals比较"  +(str1.equals(str3)));

运行结果如下:

在string的创建方式中有提到过,直接赋值就是将常量赋值给对象,所以str1与str2 的值与地址都相同,即== 和 equals都是相等的,如果通过构造方法去创建string对象,类似str3的创建方式,对象的地址很明显就不相等了,即 == 比较出来的地址是不同,而equals比较的是值,呈现的结果是相等的。

2.4 字符串的不可变性

String对象一旦被创建,则不能被更改,是不可变的

所谓的修改只是创建了新的对象,但是所指的内存空间不变

那什么是不可变性呢?

先讲结论:String不可变很简单,给一个已有字符串"abcd"第二次赋值成"abcedl",不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。

打开String的源码

可以看到String是被final 修饰的,所以final是不可被继承的,下面的value数组也是被final修饰的,所以value数组的地址初始化后也是不可变的。

只是数组的地址不可变,我们是可以修改内部元素的值的,从而去实现修改字符串吗?

final int[] value={1,2,3} ;
int[] another={4,5,6};
 value=another;    //编译器报错,final不可变 value用final修饰,编译器不允许我把value指向堆区另一个地址。
但如果我直接对数组元素动手,分分钟搞定。

 final int[] value={1,2,3};
 value[2]=100;  
//这时候数组里已经是{1,2,100}   所以String是不可变,关键是因为SUN公司的工程师。
 //在后面所有String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。
//private final char value[]这一句里,private的私有访问权限的作用都比final大。
//而且设计师还很小心地把整个String设成final禁止继承,避免被其他人继承后破坏。
//所以String是不可变的关键都在底层的实现,而不是一个final。
//考验的是工程师构造数据类型,封装数据的功力。 

那不可变有什么好处呢?

1.首先final修饰的类只保证不能被继承,并且该类的对象在堆内存中的地址不会被改变。

2.但是持有String对象的引用本身是可以改变的,比如它可以指向其他对象。

3.final修饰的char数组保证了char数组的引用不可变,但是可以通过修改数组中的元素去修改值,不过String内部并没有提供相应的方法去实现这一操作,所以String的不可变也是基于代码封装和访问控制的。

3. StringBuilder 与 StringBuffer

3.1 StringBuffer 

3.1.1 StringBuffer简介

StringBuffer是一种可变的字符串类,即在创建StringBuffer对象后,我们还可以随意修改字符串的内容。每个StringBuffer的类对象都能够存储指定容量的字符串,如果字符串的长度超过了StringBuffer对象的容量空间,则该对象的容量会自动扩大。

另外我们在使用StringBuffer类时,比如每次调用toString()方法,都会直接使用缓存区的toStringCache 值来构造一个字符串,这每次都是对StringBuffer对象本身进行操作,而不会重新生成一个新对象。所以如果我们需要对大量字符串的内容进行修改,推荐大家使用StringBuffer。

3.1.2 StringBuffer特性

1.具有线程安全性:StringBuffer中的公开方法都由synchronized关键字(一种同步锁)修饰,保证了线程安全。

2.带有缓冲区:StringBuffer每次调用toString()方法时,都会直接使用缓冲区的toStringCache值来构造一个字符串。

3.内容可变性:StringBuffer可初始化容量,也可以指定容量,当字符串超过规定容量后可自动扩容。

4.内容类型多样性:StringBuffer中可以存储多种不同数据类型的数据。

3.1.3 基本用法

    public static void stringBuffer(){
        //创建StringBuffer对象
        StringBuffer sb = new StringBuffer("hello");

        //在字符串后面追加新的字符串
        sb.append(" world");
        System.out.println(sb);

        //删除指定位置上的字符串,从指定的下标开始和结束,下标从0开始
        sb.delete(2, 4);
        //"ll"
        System.out.println(sb);

        //在指定下标位置上添加指定的字符串
        sb.insert(0, "123");
        //123heo world
        System.out.println(sb);

        //将字符串翻转
        sb.reverse();
        //dlrow oeh321
        System.out.println(sb);

        //将StringBuffer转换成String类型
        String s = sb.toString();
        System.out.println(s);
    }

3.2 StringBuilder

3.2.1 StringBuilder 简介

要想实现可变字符串的操作,其实还有另一个StringBuilder类,该类是在java5种被提出来的。它和StringBuffer的基本用法几乎完全是一样的,所以这里就不讲解太多了。

但是StringBuilder与StringBuffer最大的区别在于,StringBuilder的各个方法都不是线程安全的,再多线程的情况下会存在线程安全的问题,与之对应的StringBuilder的执行效率要比StringBuffer快很多。

在大多数情况下,我们都是在单线程的环境下进行字符串的操作,所以使用StringBuilder并不会产生线程安全问题,针对大多数的单线程情况,还是建议大家使用StringBuilder,除非项目对线程安全有着很明确的高要求。

3.2.2 StringBuilder特性

1. StringBuilder是线程不安全的,但执行效率更快。

2.适用于单线程环境下,在字符缓冲区进行大量操作的情况。

3.2.3 StringBuilder基本用法

与StringBuffer基本一致,这里就不再赘述。

3.3 String、StringBuilder和StringBuffer的区别

String是Java中基础且重要的类,被声明为final class,除了hash这个属性其它属性都声明为final,因为它的不可变性,所以例如拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。

而StringBuffer/StringBuilder就是为了解决大量拼接字符串时产生很多中间对象问题而提供的类,二者都继承了 AbstractStringBuilder ,底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。

所以如果我们有大量的字符串拼接,如果能预知大小的话最好在new StringBuffer 或者StringBuilder 的时候设置好capacity容量,避免多次扩容的开销。扩容要抛弃原有数组,还要进行数组拷贝创建新的数组。

这里需要注意以下几点:

1.在字符串不经常发生变化的业务场景优先使用String(代码更清晰简洁)。如常量的声明,少量的字符串操作(拼接,删除等)。

2.在单线程情况下,如有大量的字符串操作情况,应该使用StringBuilder来操作字符串。不能使用String"+"来拼接而是使用,避免产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。如JSON的封装等。

3.在多线程情况下,如有大量的字符串操作情况,应该使用StringBuffer。如HTTP参数解析和封装等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值