一文带你看穿String

2024-06-07 19:18
文章标签 string 一文 看穿

本文主要是介绍一文带你看穿String,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.1 前言

        String对象是不可变的。String类中每一个看起来会修改String值的方法,例如拼接、裁剪字符串,实际上都会创建一个全新的String对象,用来包含修改后的字符串内容。因此字符串的相关操作往往对性能有明显的影响。

1.2 定义

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence

        从代码可以看出String是final类型的,表示该类不能被继承,并且实现了Serializable、Comparable、CharSequence三个接口。

  • Serializable接口,表明String类是可序列化的。
  • Comparable接口,提供了一个compareTo(T o) 方法。
  • CharSequence接口,提供了length(),charAt(int index),subSequence(int start,int end),toString()方法。

1.3 属性

//final类型的字符数组,用于存储字符串内容
private final char value[];//存放字符串的哈希值
private int hash; // Default to 0//序列化id
private static final long serialVersionUID = -6849794470754667710L;

1.4 构造函数

//不含参数的构造函数
public String() {this.value = "".value;
}
//使用字符串类型的参数来初始化
public String(String original) {this.value = original.value;this.hash = original.hash;
}
//使用字符数组初始化
public String(char value[]) {this.value = Arrays.copyOf(value, value.length);//将原有的字符数组中的内容逐一的复制到String中的字符数组中
}
//从位置offset复制count个字符
public String(char value[], int offset, int count) {if (offset < 0) {throw new StringIndexOutOfBoundsException(offset);}if (count <= 0) {if (count < 0) {throw new StringIndexOutOfBoundsException(count);}if (offset <= value.length) {this.value = "".value;return;}}// Note: offset or count might be near -1>>>1.if (offset > value.length - count) {throw new StringIndexOutOfBoundsException(offset + count);}this.value = Arrays.copyOfRange(value, offset, offset+count);
}
//使用整型数组初始化
public String(int[] codePoints, int offset, int count) {if (offset < 0) {throw new StringIndexOutOfBoundsException(offset);}if (count <= 0) {if (count < 0) {throw new StringIndexOutOfBoundsException(count);}if (offset <= codePoints.length) {this.value = "".value;return;}}// Note: offset or count might be near -1>>>1.if (offset > codePoints.length - count) {throw new StringIndexOutOfBoundsException(offset + count);}final int end = offset + count;// Pass 1: Compute precise size of char[]int n = count;for (int i = offset; i < end; i++) {int c = codePoints[i];if (Character.isBmpCodePoint(c))continue;else if (Character.isValidCodePoint(c))n++;else throw new IllegalArgumentException(Integer.toString(c));}// Pass 2: Allocate and fill in char[]final char[] v = new char[n];for (int i = offset, j = 0; i < end; i++, j++) {int c = codePoints[i];if (Character.isBmpCodePoint(c))v[j] = (char)c;elseCharacter.toSurrogates(c, v, j++);}this.value = v;
}
//检查字符数组是否越界
private static void checkBounds(byte[] bytes, int offset, int length) {if (length < 0)throw new StringIndexOutOfBoundsException(length);if (offset < 0)throw new StringIndexOutOfBoundsException(offset);if (offset > bytes.length - length)throw new StringIndexOutOfBoundsException(offset + length);
}
//从bytes数组中的offset位置开始,将长度为length的字符,使用charsetName格式解码,初始化字符串
public String(byte bytes[], int offset, int length, String charsetName)throws UnsupportedEncodingException {if (charsetName == null)throw new NullPointerException("charsetName");checkBounds(bytes, offset, length);this.value = StringCoding.decode(charsetName, bytes, offset, length);
}
//从bytes数组中的offset位置开始,将长度为length的字符,使用charset解码,初始化字符串
public String(byte bytes[], int offset, int length, Charset charset) {if (charset == null)throw new NullPointerException("charset");checkBounds(bytes, offset, length);this.value =  StringCoding.decode(charset, bytes, offset, length);
}
//通过charsetName来解码指定的byte数组,将其解码成unicode的char[]数组,够造成新的String
public String(byte bytes[], String charsetName)throws UnsupportedEncodingException {this(bytes, 0, bytes.length, charsetName);
}
//通过charset来解码指定的byte数组,将其解码成unicode的char[]数组,够造成新的String
public String(byte bytes[], Charset charset) {this(bytes, 0, bytes.length, charset);
}
//从bytes数组中的offset位置开始,将长度为length的字符,初始化字符串
public String(byte bytes[], int offset, int length) {checkBounds(bytes, offset, length);this.value = StringCoding.decode(bytes, offset, length);
}
//使用字节数组来初始化
public String(byte bytes[]) {this(bytes, 0, bytes.length);
}
//使用StringBuffer来构建字符串,不建议使用,可以使用StringBuffer.toString()来得到字符串
public String(StringBuffer buffer) {synchronized(buffer) {this.value = Arrays.copyOf(buffer.getValue(), buffer.length());}
}
//使用StringBuilder来构建字符串,不建议使用,可以使用StringBuilder.toString()来得到字符串
public String(StringBuilder builder) {this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
//保护类型的构造函数,其中参数share没有被使用,加入这个share的只是为了区分于String(char[] value)方法,
//String(char[] value)方法在创建String的时候会用到 会用到Arrays的copyOf方法将value中的内容逐一复制到String当中,而这个String(char[] value, boolean share)方法则是直接将value的引用赋值给String的value。那么也就是说,这个方法构造出来的String和参数传过来的char[] value共享同一个数组。
//优点:性能好,节约内存
//该方法之所以设置为protected,是因为一旦该方法设置为公有,那就破坏了字符串的不可变性
String(char[] value, boolean share) {// assert share : "unshared not supported";this.value = value;
}

1.5 常用方法

1.5.1 length()
//返回字符串长度
public int length() {return value.length;
}
1.5.2 isEmpty()
//返回字符串是否为空
public boolean isEmpty() {return value.length == 0;
}
1.5.3 charAt(int index)
//返回字符串中第(index+1)个字符
public char charAt(int index) {if ((index < 0) || (index >= value.length)) {throw new StringIndexOutOfBoundsException(index);}return value[index];
}
1.5.4 startsWith(String prefix,int toffset)
//用于检测字符串是否以指定的前缀开始,其中toffset是字符串中开始查找的位置
public boolean startsWith(String prefix, int toffset) {char ta[] = value;int to = toffset;char pa[] = prefix.value;//字串int po = 0;int pc = prefix.value.length;//字串长度// Note: toffset might be near -1>>>1.if ((toffset < 0) || (toffset > value.length - pc)) {//如果toffset为负或大于此String对象的长度,返回falsereturn false;}while (--pc >= 0) {if (ta[to++] != pa[po++]) {//依次比较return false;}}return true;
}
1.5.5 endsWith(String suffix)
//此字符串是否以指定的后缀结束
public boolean endsWith(String suffix) {return startsWith(suffix, value.length - suffix.value.length);
}
1.5.6 index(int ch,int fromIndex)
//返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索,如果此字符串中没有这样的字符,则返回 -1
public int indexOf(int ch, int fromIndex) {final int max = value.length;if (fromIndex < 0) {fromIndex = 0;} else if (fromIndex >= max) {// Note: fromIndex might be near -1>>>1.return -1;}if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {// handle most cases here (ch is a BMP code point or a// negative value (invalid code point))final char[] value = this.value;for (int i = fromIndex; i < max; i++) {if (value[i] == ch) {return i;}}return -1;} else {return indexOfSupplementary(ch, fromIndex);}
}
1.5.7 substring(int beginIndex)
//返回一个新的字符串,它是此字符串的一个子字符串
//使用String(value, beginIndex, subLen)方法创建一个新的String并返回,这个方法会将原来的char[]中的值逐一复制到新的String中
public String substring(int beginIndex) {if (beginIndex < 0) {throw new StringIndexOutOfBoundsException(beginIndex);}int subLen = value.length - beginIndex;if (subLen < 0) {throw new StringIndexOutOfBoundsException(subLen);}return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
1.5.8 concat(String str)
//拼接字符串
public String concat(String str) {int otherLen = str.length();if (otherLen == 0) {return this;}int len = value.length;char buf[] = Arrays.copyOf(value, len + otherLen);str.getChars(buf, len);return new String(buf, true);
}

        concat(String str)方法首先获取拼接字符串的长度,判断这个字符串长度是否为0(判断这个用来拼接的字符串是不是空串),如果是就返回原来的字符串(等于没有拼接);否则就获取源字符串的长度,创建一个新的char[]字符数组,这个字符数组的长度是拼接字符串的长度与源字符串的长度之和,通过Arrays类的copyOf方法复制源数组,然后通过getChars方法将拼接字符串拼接到源字符串中,然后将新串返回。

1.5.9 replace(char oldChar,char newChar)
//将字符串中的oldChar 字符换成 newChar 字符
public String replace(char oldChar, char newChar) {if (oldChar != newChar) {int len = value.length;int i = -1;char[] val = value; /* avoid getfield opcode */while (++i < len) {//先找到旧值最开始出现的位置,减少对比的时间,有效提升效率if (val[i] == oldChar) {break;}}//从找到旧值那个位置开始,直到末尾,用新值代替出现的旧值if (i < len) {char buf[] = new char[len];for (int j = 0; j < i; j++) {buf[j] = val[j];}while (i < len) {char c = val[i];buf[i] = (c == oldChar) ? newChar : c;i++;}return new String(buf, true);}}return this;
}
1.5.10 contains(CharSequence s)
//判断字符串是否包含字符序列 s
public boolean contains(CharSequence s) {return indexOf(s.toString()) > -1;
}
1.5.11 trim()
//去掉字符串两端空格
public String trim() {int len = value.length;int st = 0;char[] val = value;    /* avoid getfield opcode *///找到字符串前端没有空格的位置while ((st < len) && (val[st] <= ' ')) {st++;}//找到字符串末尾没有空格的位置while ((st < len) && (val[len - 1] <= ' ')) {len--;}//如果前后都没有出现空格,返回字符串本身return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

1.5.12  toCharArray()

//将字符串转化成字符数组
public char[] toCharArray() {// Cannot use Arrays.copyOf because of class initialization order issueschar result[] = new char[value.length];System.arraycopy(value, 0, result, 0, value.length);return result;
}
1.5.13 equals(Object anObject)
//比较对象
public boolean equals(Object anObject) {if (this == anObject) {//判断当前对象与anObject是不是同一个对象,若是,直接返回truereturn true;}if (anObject instanceof String) {//anObject是不是String类型的,如果不是,直接返回falseString anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {//比较两个数组长度是否相等,若不相等,返回falsechar v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {//循环逐一比较值,若都相等者返回trueif (v1[i] != v2[i])return false;i++;}return true;}}return false;
}
1.5.14 compareTo(String anotherString)
//比较字符串
public int compareTo(String anotherString) {int len1 = value.length;int len2 = anotherString.value.length;int lim = Math.min(len1, len2);//取两个字符串的长度的最小值char v1[] = value;char v2[] = anotherString.value;int k = 0;while (k < lim) {char c1 = v1[k];char c2 = v2[k];if (c1 != c2) {return c1 - c2;}k++;}return len1 - len2;
}
  • 判断两个字符串的长度是否相等。
  • 若相等,再继续判断每个字符是否相同,若相同则返回0,不相同,则返回第一个不同字符的ascii码的差值。
  • 若不相等,则判断短的字符串是否是长串的字串,若是,则返回长度的差值,若不是,则返回第一个不同字符的ascii码的差值。

1.6 方法总结

方法名说明
length() 返回字符串长度
isEmpty()返回字符串是否为空
charAt(int index)返回字符串中第(index+1)个字符
char[] toCharArray()转化成字符数组
trim()去掉字符串两端空格
toUpperCase()转化为大写
toLowerCase()转化为小写
concat(String str)拼接字符串
replace(char oldChar, char newChar)将字符串中的
oldChar 字符换成 newChar 字符
boolean matches(String regex)判断字符串是否匹配给定的regex正则表达式
boolean contains(CharSequence s)判断字符串是否包含字符序列 s
String[] split(String regex, int limit)按照字符 regex将字符串分成 limit 份
String[] split(String regex)按照字符 regex 将字符串分段
equals(Object anObject)比较对象
equalsIgnoreCase(String anotherString)忽略大小写比较字符串对象
startsWith(String prefix,int toffset)字符串从指定索引开始的子字符串是否以指定前缀开始
endsWith(String suffix)此字符串是否以指定的后缀结束

 

1.7 String的一些注意点

  • String 对 “+” 的支持其实就是使用了 StringBuilder 以及他的 append、toString 两个方法。
  • 字符串的 switch 是通过 equals() 和 hashCode() 方法来实现的。记住,switch 中只能使用整型,比如 byte,short,char(ackii码是整型) 以及 int。

1.8 String经典的面试题

String s1="abc"; 
String s2="abc"; 
System.out.println(s1==s2); 
System.out.println(s1.equals(s2));
/*output: 
true true 
*/

    该题主要考察对于java常量池的理解,先在常量池中创建”abc“,并指向s1,而后在创建s2时,由于常量池中已经存在”abc“,只需指向s2就可以,而不需要再创建。”==”在这里比较的是对象引用,故结果为”true”,String 中的equals方法经过重写后操作为比较此字符串与指定的对象的值是否相等,因此是true。

String s1=new String("abc");
String s2="abc";
System.out.println(s1==s2); 
System.out.println(s1.equals(s2));
/*output:
false
true
*/

    s1是通过new创建的对象在堆内存,s2在方法区中的常量池中,因此地址不一样,==是false。

String s1="a"+"b"+"c";
String s2="abc";
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
/*output:
true
true
*/

    编译时s1已经成为“abc”在常量池中查找创建,s2不需要再创建。

String s1="ab";
String s2="abc";
String s3=s1+"c";
System.out.println(s3==s2);
System.out.println(s3.equals(s2));
/*output:
false
true
*/

    先在常量池中创建”ab“,地址指向s1,再创建”abc”,指向s2。对于s3,先创建StringBuilder(或 StringBuffer)对象,通过append连接得到“abc”,再调用toString()转换得到的地址指向s3。故(s3==s2)为false。

1.9总结

  • 一旦 String 对象在内存(堆)中被创建出来,就无法被修改。
  • 如果你需要一个可修改的字符串,应该使用 StringBuffer 或者
    StringBuilder。
  • 如果你只需要创建一个字符串,你可以使用双引号的方式,如果你需要在堆中创建一个新的对象,你可以选择构造函数的方式。

参考资料

https://www.jianshu.com/p/799c4459b808

https://blog.csdn.net/Sqirt/article/details/72765071

原文:https://my.oschina.net/wuchanghao/blog/1831272

这篇关于一文带你看穿String的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1040063

相关文章

一文详解SpringBoot中控制器的动态注册与卸载

《一文详解SpringBoot中控制器的动态注册与卸载》在项目开发中,通过动态注册和卸载控制器功能,可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,下面我们就来看看Sp... 目录项目结构1. 创建 Spring Boot 启动类2. 创建一个测试控制器3. 创建动态控制器注

一文详解Git中分支本地和远程删除的方法

《一文详解Git中分支本地和远程删除的方法》在使用Git进行版本控制的过程中,我们会创建多个分支来进行不同功能的开发,这就容易涉及到如何正确地删除本地分支和远程分支,下面我们就来看看相关的实现方法吧... 目录技术背景实现步骤删除本地分支删除远程www.chinasem.cn分支同步删除信息到其他机器示例步骤

一文详解Java Stream的sorted自定义排序

《一文详解JavaStream的sorted自定义排序》Javastream中的sorted方法是用于对流中的元素进行排序的方法,它可以接受一个comparator参数,用于指定排序规则,sorte... 目录一、sorted 操作的基础原理二、自定义排序的实现方式1. Comparator 接口的 Lam

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

一文全面详解Python变量作用域

《一文全面详解Python变量作用域》变量作用域是Python中非常重要的概念,它决定了在哪里可以访问变量,下面我将用通俗易懂的方式,结合代码示例和图表,带你全面了解Python变量作用域,需要的朋友... 目录一、什么是变量作用域?二、python的四种作用域作用域查找顺序图示三、各作用域详解1. 局部作

java String.join()方法实例详解

《javaString.join()方法实例详解》String.join()是Java提供的一个实用方法,用于将多个字符串按照指定的分隔符连接成一个字符串,这一方法是Java8中引入的,极大地简化了... 目录bVARxMJava String.join() 方法详解1. 方法定义2. 基本用法2.1 拼接

一文彻底搞懂Java 中的 SPI 是什么

《一文彻底搞懂Java中的SPI是什么》:本文主要介绍Java中的SPI是什么,本篇文章将通过经典题目、实战解析和面试官视角,帮助你从容应对“SPI”相关问题,赢得技术面试的加分项,需要的朋... 目录一、面试主题概述二、高频面试题汇总三、重点题目详解✅ 面试题1:Java 的 SPI 是什么?如何实现一个

一文详解PostgreSQL复制参数

《一文详解PostgreSQL复制参数》PostgreSQL作为一款功能强大的开源关系型数据库,其复制功能对于构建高可用性系统至关重要,本文给大家详细介绍了PostgreSQL的复制参数,需要的朋友可... 目录一、复制参数基础概念二、核心复制参数深度解析1. max_wal_seChina编程nders:WAL