String详解
在开发中,我们都会频繁的使用String类,掌握String的实现和常用方法是必不可少的,当然,我们还需要了解它的内部实现。
一. String的实现
在Java中,采用了一个char数组实现String类型,这个char数组被定义为final类型,这就意味着一旦一个String被创建,那么它就是不可变的。除此之外,还定义了一个int类型的hash,用来保存该String的hash值。
/** The value is used for character storage. */
private final char value[]; /** Cache the hash code for the string */
private int hash; // Default to 0
二. String中的构造器
创建String的方法很多,构造器也有多个。但是其目的就是给value数组赋值。构造器中传入的参数大致可以分为几个部分:
src:来源,就是希望在value中保存的字符串,传入的值可以是String,char数组,int数组,byte数组,Stringbuffer,StringBuilder,boolean值。
offset:src中字符串的起始位置。
count:src中赋值到value中的字符串的个数。
Charset:指定字符集。
当然,不是每一个构造器都需要这些参数,我们也不需要一个个都详细掌握,只要知道大概存在哪些构造方法即可,要用的时候可以查询API。
三. String中常用的方法以及实现
1. 获取字符数组
获取字符数组方法:
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
该方法将String中指定位置的字符复制个dst,具体的实现如下:
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
//对指定位置进行判断
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
//调用native方法进行数组复制
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
2. equals()、equalsIgnoreCase()、regionMatches()、compareTo()、compareToIgnoreCase()、hashCode()
equals()方法用来判断两个String是否相等,实现逻辑如下:
public boolean equals(Object anObject) {
if (this == anObject) {//如果两个String是引用同一个String对象,则相等
return true;
}
if (anObject instanceof String) {//否则,在长度相等的前提下,从第一个字符开始进行比较
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
equalsIgnoreCase()忽略大小写进行判断,其内部实现是调用regionMatches()方法。
regionMatches()方法是用来判断两个字符串区域是否相等。String中有两个regionMatches()方法,不同之处在于有一个增加一个boolean值决定是否忽略大小写进行判断。具体的实现如下:
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;//不满足以上条件的都返回false
}
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
if (c1 == c2) {
continue;
}
if (ignoreCase) {//是否区分大小写。
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
另外还存在一个判断两个字符串区是否相等的方法,但是是区分大小写的: public boolean regionMatches(int toffset, String other, int ooffset, int len),但是这个方法内部实现没有复用上面这个方法。
compareTo()方法是用来比较两个字符串大小,比较规则为:按字典顺序比较两个字符串。该比较基于字符串中各个字符的 Unicode 值。将此 String
对象表示的字符序列与参数字符串所表示的字符序列进行比较。如果按字典顺序此 String
对象在参数字符串之前,则比较结果为一个负整数。如果按字典顺序此 String
对象位于参数字符串之后,则比较结果为一个正整数。如果这两个字符串相等,则结果为 0。具体实现如下:
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;//字符不等,返回Unicode差值。
}
k++;
}
return len1 - len2;//两个字符串长度,返回长度差值。若为0,表示两字符串大小相等。
}
compareToIgnoreCase()方法实现:String在内部自定义了一个名为CaseInsensitiveComparator的类,实现Comparator,用来比较忽略大小写的两个字符串,比较逻辑是,依次取出两个字符进行忽略大小写的比较,其余逻辑和上面类似。
hashCode()方法返回String的hash值。
3. startWith()、endWith()
startsWith(String prefix)是判断字符串是不是以某个指定的子字符串开始,返回boolean值。
startsWith(String prefix, int toffset)是判断字符串从指定的位置开始是否是以指定的字符串开始,返回boolean值。其实现逻辑是取出对于位置的两个字符,进行判断。
endsWith(String suffix)是判断字符串是不是以某个字符串结尾。它的实现逻辑是调用startsWith(String prefix, int toffset)方法,具体实现如下:
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
4. indexOf()、lastIndexOf()
indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引。
indexOf(int ch, int fromIndex):从指定的索引开始搜索,返回在此字符串中第一次出现指定字符处的索引。
lastIndexOf(int ch):返回最后一次出现的指定字符在此字符串中的索引。
lastIndexOf(int ch, int fromIndex):从指定的索引开始搜索,返回在此字符串中最后一次出现指定字符处的索引。
indexOf(String str):返回第一次出现的指定子字符串在此字符串中的索引。
indexOf(String str, int fromIndex):从指定的索引处开始,返回第一次出现的指定子字符串在此字符串中的索引。
lastIndexOf(String str):返回在此字符串中最右边出现的指定子字符串的索引。
lastIndexOf(String str, int fromIndex):从指定的索引处开始向后搜索,返回在此字符串中最后一次出现的指定子字符串的索引。
以上方法如果没有找到索引,则返回-1.
5. substring()、concat()、matches()、contains()
substring(int beginIndex):返回一个新的字符串,它是此字符串的一个子字符串。该子字符串始于指定索引处的字符,一直到此字符串末尾。
substring(int beginIndex, int endIndex):返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex
处开始,一直到索引 endIndex - 1
处的字符。
concat(String str):将指定字符串联到此字符串的结尾。
matches(String regex):判断此字符串是否匹配给定的正则表达式。
contains(CharSequence s):判断字符串中是否有该字符序列。
6. replace()、replaceFirst()、replaceAll()
replace(char oldChar, char newChar):返回一个新的字符串,用 newChar
替换此字符串中出现的所有 oldChar
。具体实现如下:
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;
}
replaceFirst(String regex, String replacement):使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的第一个子字符串。
replaceAll(String regex, String replacement):使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的每个子字符串。
7. split()、join()
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串。数控制模式应用的次数,因此影响结果数组的长度。如果该限制 n 大于 0,则模式将被最多应用 n - 1 次,数组的长度将不会大于 n,而且数组的最后项将包含超出最后匹配的定界符的所有输入。如果 n 为非正,则模式将被应用尽可能多的次数,而且数组可以是任意长度。如果 n 为零,则模式将被应用尽可能多的次数,数组可有任何长度,并且结尾空字符串将被丢弃。
String[] split(String regex):根据给定的正则表达式的匹配来拆分此字符串。该方法的作用就像是使用给定的表达式和限制参数 0 来调用两参数 split 方法。
public String[] split(String regex, int limit) {
/* fastpath if the regex is a
(1)one-char String and this character is not one of the
RegEx's meta characters ".$|()[{^?*+\\", or
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
*/
char ch = 0;
if (((regex.value.length == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{
int off = 0;
int next = 0;
boolean limited = limit > 0;
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
} else { // last one
//assert (list.size() == limit - 1);
list.add(substring(off, value.length));
off = value.length;
break;
}
}
// If no match was found, return this
if (off == 0)
return new String[]{this}; // Add remaining segment
if (!limited || list.size() < limit)
list.add(substring(off, value.length)); // Construct result
int resultSize = list.size();
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
}
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
String join(CharSequence delimiter, CharSequence... elements):
String join(CharSequence delimiter, Iterable<? extends CharSequence> elements):这两个方法是jdk1.8中出现的,类似字符串拼接,不过可以指定连接符delimiter,后面的elements中间使用该连接符连接。具体实现如下:
public static String join(CharSequence delimiter, CharSequence... elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
// Number of elements not likely worth Arrays.stream overhead.
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);//该方法中会将连接符拼接
}
return joiner.toString();
}
8. 字符串大小写转换:
toLowerCase():转换为小写
toUpperCase():转换为大写
9. 其他方法:
trim():去掉字符串收尾的空白。
toCharArray():将此字符串转换为一个新的字符数组。
valueOf():返回 某参数的字符串表示形式。就是将其他类型转换为String类型的一种方式。
intern():当调用 intern 方法时,如果池已经包含一个等于此 String
对象的字符串,则返回池中的字符串。否则,将此 String
对象添加到池中,并且返回此 String
对象的引用。
String详解的更多相关文章
- Java的String详解
Java的String详解 博客分类: Java javaStringString详解常用方法 Java的String类在开发时经常都会被使用到,由此可见String的重要性.经过这次认真仔细的学习 ...
- Python操作redis字符串(String)详解 (三)
# -*- coding: utf-8 -*- import redis #这个redis不能用,请根据自己的需要修改 r =redis.Redis(host=") 1.SET 命令用于设置 ...
- [读书笔记]C#学习笔记八:StringBuilder与String详解及参数传递问题剖析
前言 上次在公司开会时有同事分享windebug的知识, 拿的是string字符串Concat拼接 然后用while(true){}死循环的Demo来讲解.其中有提及string操作大量字符串效率低下 ...
- String详解, String和CharSequence区别, StringBuilder和StringBuffer的区别 (String系列之1)
本章主要介绍String和CharSequence的区别,以及它们的API详细使用方法. 转载请注明出处:http://www.cnblogs.com/skywang12345/p/string01. ...
- Java堆、栈和常量池以及相关String详解
一:在JAVA中,有六个不同的地方可以存储数据: 1. 寄存器(register). 这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部.但是寄存器的数量极其有限,所以寄存器由编译器根据 ...
- String详解, String和CharSequence区别, StringBuilder和StringBuffer的区别
本章主要介绍String和CharSequence的区别,以及它们的API详细使用方法. 转载请注明出处:http://www.cnblogs.com/skywang12345/p/string01. ...
- String 详解
String String对象不可变,当对象创建完毕之后,如果内容改变则会创建一个新的String对象,返回到原地址中. 不可变优点: 多线程安全. 节省空间,提高效率. 源码: public fin ...
- java String 详解
1.java语言的字符串序列是通过字符串类实现的.java提供了3个字符串类:String类.StringBuilder类和StringBuffer类.String类是不变字符串,StringBuff ...
- C#String详解
字符串:stringLength - 字符串的长度. TrimStart() 压缩空格即消除字符串开始空格TrimEnd() 消除结尾空格Trim() 同时消除开头和结尾空格.注:中间空格不消除,因为 ...
随机推荐
- 用Java程序判断是否是闰年
我们知道,(1)如果是整百的年份,能被400整除的,是闰年:(2)如果不是整百的年份,能被4整除的,也是闰年.每400年,有97个闰年.鉴于此,程序可以作以下设计: 第一步,判断年份是否被400整除, ...
- How to create Web Deployment Package and install the package
Create Web Deployment Package To configure settings on the Connection tab In the Publish method drop ...
- python 延迟绑定
def multipliers(n): funcs = [] for i in range(n): def f(x): return x * i funcs.append(f) return func ...
- android wifi SWOL低功耗模式
1 睡眠模式RX代码流程 ar_wal_rx_patch.c::patch_rx_process_recv_status//调用rx_ctxt->data_ind_handler -> d ...
- hdu 5719(Arrange)(冷静分析)
A数组显示从0到i的最小值B数组显示从0到i的最大值由此可得:A数组是单调不增的(怎么也会不使得最小值变大)B数组是单调不减的.设premin和premax为i位以前的最小值和最大值.可以得出以下几点 ...
- day10---multiprocess 多进程
multiprocess Queue \ Pipe 只是实现进程间数据的传递 Manager 实现了进程间数据的共享,即多个进程可以修改同一份数据 进程模块 multiprocessing #! ...
- springMVC中一个controller多个方法
web.xml文件: <?xml version="1.0" encoding="UTF-8"?><web-app version=" ...
- cent7内核升级4.9
一.手动档 手动档就是从源码开始编译内核安装,好处是可以自己选择任意版本的内核,缺点就是耗时长,编译安装消耗系统资源 1.1.获取 kernel 源码 这世界上最伟大的 Linux 内核源码下载地址是 ...
- .Net平台下,分布式文件存储的实现
遇到的问题 对于Web程序,使用一台服务器的时候,客户端上传的文件一般也都是存储在这台服务器上.但在集群环境中就行不通了,如果每个服务器都存储自己接受到的文件,就乱套了,数据库中明明有这个附件的记录, ...
- Grpc微服务从零入门
快速入门 安装 JDK 毫无疑问,要想玩Java,就必须得先装Java JDK,目前公司主要使用的是Oracle JDK 8,安装完成后要配置环境才能正常使用,真蠢,不过也就那么一下下,认了吧.配置方 ...