今天有时间,看了下String类中的substring()方法,现简要分析如下:
/**
* Returns a new string that is a substring of this string. The substring begins
* with the character at the specified index and extends to the end of this string.
* 上面的这段话的意思是,它会返回一个新的字符串,这个新的字符串是原来字符串的一部分,
* 从某个下标的字母开始,一直到这个字符串的结束。
* 例如:"unhappy".substring(2),从下标2开始,到结束->happy
*
**/
public String substring(int beginIndex) {
return substring(beginIndex, count);
}
注:The count is the number of characters in the String.
可以看出如果只是指定了开始的下标,那即是默认将该下标之后的所有字符都作为新的字符串的一部分。
接着看String.substring(int beginIndex, int endIndex)方法:
/**
* Returns a new string that is a substring of this string. The
* substring begins at the specified <code>beginIndex</code> and
* extends to the character at index <code>endIndex - 1</code>.
* Thus the length of the substring is <code>endIndex-beginIndex</code>.
* 从这段说明中也可看出是左闭右开的(即[a,b),包括下标为a的但不包括下标为b的元素)。
* 例如:"hamburger".substring(4, 8) returns "urge"
**/
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}
在String类的一开始定义了一些变量:
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
this.value = value; // 保存的字符串
this.offset = offset; // 开始的位置
this.count = count; // 字符数目
}
从上面的代码中不难发现substring(int begin, int endIndex)方法使用的是和new String()相同的value, 而value存储的就是要截取的字符串的值(本身String就是用一个char数组来存储的),然后通过修改offset和count来得到要截取的字串,这样也不用移动元素,也不用单独分配新的空间。
下面是debug的截图:
原字符串: 截取的字符串:
证实了前面所说的使用的是同一个value,只是offset和count不同
注:代码 public class NewString { public static void main(String[] args) { String str = "Happy"; String substr = str.substring(1,3); System.out.println(substr); } }
最后的这个使用的value的问题是参考了下面的这个博客,感兴趣的可以看一下:
博客地址:http://www.cnblogs.com/tianchi/archive/2012/11/14/2768851.html
Compiled from "NewString.java" public class NewString extends java.lang.Object{ public NewString(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2; //String Happy 2: astore_1 3: aload_1 4: iconst_1 5: iconst_3 6: invokevirtual #3; //Method java/lang/String.substring:(II)Ljava/lang/String; 9: astore_2 10: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_2 14: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 17: return }
上面这部分是将前面的代码使用反汇编之后得到的结果。附上指令介绍,仅供参考:
ldc 将int、float或String型常量值从常量池中推送至栈顶
astore index 将栈顶数值(objectref)存入当前frame的局部变量数组中指定下标(index)处的变量中,栈顶 数值出栈.
aload index 当前frame的局部变量数组中下标为index的引用型局部变量进栈.
iconst_1 int型常量值1进栈
iconst_3 int型常量值3进栈
invokevirtual 调用实例方法substring()
...
###########################################################################
以上,编译和运行都是在jdk 1.6中的,但是看了jdk 1.7的源码,发现有些变化,下面附上jdk 1.7中的实现:
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); } // 没有再通过substring(int,int)转,而是直接使用new String(...)
public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } // 长度是用最后一个元素下标减去开始下标 int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
前面的基本上没什么变化,关键在最后调用的new String(value, beginIndex, subLen)方法:
/** * Allocates a new {@code String} that contains characters from a subarray * of the character array argument. The {@code offset} argument is the * index of the first character of the subarray and the {@code count} * argument specifies the length of the subarray. The contents of the * subarray are copied; subsequent modification of the character array does * not affect the newly created string. * * @param value Array that is the source of characters * @param offset The initial offset * @param count The length * @throws IndexOutOfBoundsException * If the {@code offset} and {@code count} arguments index * characters outside the bounds of the {@code value} array */ public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } // 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); }
继续看看Arrays.copyOfRange()方法
public static char[] copyOfRange(char[] original, int from, int to) { int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); char[] copy = new char[newLength]; System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); return copy; }
debug模式下查看了两个value,发现两个value不是同一个value:
而且,仔细看这个String类,发现也没有了offset和count两个变量。
从上面的代码可以发现他是将原有的数组中截取的内容拷贝到了一个新的数组中,然后将此数组作为新的value。
所以相比于1.6中的共用value,根据下标得到最后的结果,到现在1.7中会创建一个新的字符数组,然后通过System.arraycopy()方法复制数据。
个人理解能力有限,目前还没有看出为什么做这样的变动。
总结:
对于substring(int index)会返回原来字符串的从第index+1个字符及其之后的字符;
如"Happy".substring(1)->appy;
substring(int beg,int end)会返回原来字符串的从第beg+1个字符到第end个字符
如"Happy".substring(1,3)->ap
end
相关推荐
【String的substring方法详解】 在Java编程语言中,`String`类提供了多种方法来操作字符串,其中`substring()`方法用于截取字符串中的某一部分。它有两种重载形式,分别是单参数和双参数。 1. **单参数substring...
在JavaScript中,字符串(string)作为基本数据类型,本身并不具备方法,但我们可以直接对字符串调用诸如`substring`、`length`等方法。这是因为JavaScript为基本数据类型提供了三种特殊引用类型:String、Number和...
newString = newString.Substring(0, newString.Length - 1); } int byteLength = newString.Length / 2; byte[] bytes = new byte[byteLength]; for (int i = 0; i ; i++) { string hex = new String(new ...
newString = newString.Substring(0, newString.Length - 1); } int byteLength = newString.Length / 2; byte[] bytes = new byte[byteLength]; string hex; int j = 0; for (int i = 0; i < newString....
本篇文章将详细介绍`String`类中的一些常用方法,帮助读者更好地理解和使用这些方法,以提高编程效率和代码质量。 #### 创建字符串 `String`类有两种主要的创建方式: 1. **使用字符串字面量**:这是最简单的方式,...
- 使用`substring(startIndex, endIndex)`方法,如 `substring = originalString.substring(startIndex, endIndex)`。 - **JavaScript**: - 使用`substring(indexStart[, indexEnd])`,如 `substring = ...
ing对象。8. boolean equals(Object anObject):检查...理解并熟练运用这些方法对于进行Java编程,特别是在处理文本信息时,是非常关键的。在准备考试或实际工作中,掌握String类的各种方法能够提高代码的效率和质量。
`String.indexOf`方法是Java和JavaScript等编程语言中处理字符串时非常常用的一个功能,用于查找一个子字符串在原字符串中首次出现的位置。这个方法对于文本分析、数据处理以及字符串操作等场景都至关重要。 方法的...
C#的`string`类提供了丰富的操作方法,如`IndexOf()`、`Substring()`和`Split()`等。假设我们要从字符串中提取某个子串,我们可以先找到它的起始位置,然后用`Substring()`获取。 ```csharp string fullString = ...
### Java字符串方法详解 #### 一、创建并初始化字符串 在Java中,字符串是一个非常重要的数据类型,通常用于处理文本信息。...这些方法不仅在日常编程中频繁使用,也是理解和掌握Java语言基础的关键部分之一。
在`String`类中,`substring()`方法用于获取字符串中的子串。它接受两个参数,分别表示子串的起始索引(包含)和结束索引(不包含)。例如: ```java String str = "Hello, World!"; String subStr = str.substring...
`string.IndexOf()`和`string.Substring()`用于查找子串和提取子串。`string.Trim()`和`string.ToLower()`等方法用于去除空白字符和转换大小写。正则表达式(Regex)类提供了强大的模式匹配功能,可用于验证输入或...
例如,使用`String.Concat`方法进行字符串连接,`String.CompareTo`进行字符串比较,`String.IndexOf`查找子串位置,以及`String.Substring`获取子串。同时,需要考虑错误处理和输入验证,确保操作的合法性。 5. ...
- 使用 `Replace` 方法简单快捷地替换子串。 #### 四、额外示例 ##### 7. 截取两个索引之间的子串 **代码示例:** ```csharp string str = "adcdef"; int indexStart = str.IndexOf("d"); int endIndex = str....
总之,`char`和`String`在Java中扮演着重要角色,它们提供的丰富函数方法使得处理文本数据变得简单高效。理解和熟练使用这些方法是Java编程的基本功,对于开发涉及文本处理的应用尤其关键。通过深入学习和实践,...
无论你是初学者还是有经验的开发者,理解和熟练运用这些方法将极大地提高你的代码质量和效率。以下是对"Java常用方法大全"的详细阐述。 首先,我们要理解Java方法是代码的可重用单元,它们执行特定任务并可能返回...
String key = method.getName().substring(3); key = key.substring(0, 1).toLowerCase() + key.substring(1); map.put(key, method.invoke(bean)); } } return map; } } ``` 这段代码通过遍历Java Bean类的...
理解并熟练运用C#中的字符串和正则表达式,能让你在处理文本数据时游刃有余,无论是简单的查找替换,还是复杂的文本分析,都能得心应手。这份参考手册将深入讲解这些概念,通过示例代码和实践练习,帮助开发者更好地...