- 概述
- String的创建
- 判断功能的方法
- equals()方法
- equalsIgnoreCase()方法
- contains()方法
- isEmpty()方法
- startsWith()和endWith()方法
- compareTo()方法
- 获取功能的方法
- length()方法
- charAt()方法
- indexOf()方法
- substring()方法
字符串是由多个字符组成的一串数据(字符序列)的字符串常量,java中所有字符串都是String类的实例(对象)。
字符串的值是不能改变的(一旦给定字符串的值,值就是不能改变的)因为它的底层用的是一个final修饰的char 数组
为什么是不可变的,用语言的表达来是苍白的,直接看源码:
哟,看时数组,我们都知道数组的长度是不可变的,那么String的底层就是将字符串里面的没有字母取出来,以数组的形式进行保存。
那么我们平时在写字符串的时候,感受完全是字符串是可变的。
这是因为当我们在改变字符串的长度的时候,底层代码会另创建一个新的数组,将之前的内容也保存进去。(看下面动图里面的value值,每次在增加字符串里面的元素的时候,就是另外创建了一个数组)
创建字符串是有两种创建的方式:
-
String s = “abc”;
-
一旦在堆中创建对象,值存储在堆内存的对象中。
String s = new String(“abc”);
下面我就对这两种创建做一个演示:
信息有一些小伙伴会对第一个输出为true有些疑惑,前面不刚刚都说了,字符串的底层是数组呀,数组用==号的话,可是看地址的。那么这里出现了true是不是也说明了一个问题,s1和s2的地址就是一个呢?
对,这个想法是对的。在用String s1 = “abc”的时候就是先在栈中创建一个对String类的对象引用变量s,然后去字符串常量池中查找有没有“abc”。如果没有的话才在常量池中添加“abc”,s1引用变量指向常量池中的“abc”,如果常量池中已经存在了的话,则直接指向改地址即可,不用再重复创建。
所以在这s1和s2指向的就是同一东西。这也是java的一种空间的优化。
判断两各String字符串是否相等的。
这个也是判断两个字符串是否相等的语法,但是这里的区分是不分大小写的,比如我们经常在登录的时候,输入验证码,就是不区分大小写字母的。
判断方法里面的字符串是外面字符串的子串。
返回true/false后面的更上层的源码我就不再这做具体演示了。
isEmpty()方法用来判断一个字符串是否为空。
若字符串为空就返回true,否则就返回false。
startsWith()和endWith()方法startsWith()方法使用的是判断括号内的字符串是否是需要判断的字符串的开始
endWith()方法是用来判断括号内的字符时需要判断的字符串的结尾。
这两个方法在底层其实都用了一个方法进行判断
startWith()直接调用的代码:
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
//直接调用了startsWith方法,从初位置开始
}
endWith()方法指定调用的代码:
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
//表示suffix这个数组,然后从用数组长度减去传入参数的位置开始。
}
compareTo()方法
用于判断前面字符是否比后面的大或者小。这里是将字母转换为ASCII码进行比较的。
这里有点说头,先给大家看一下测试代码和源码:
来在这源码给大家分析一波:
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]; //拿到当前k位置的两个字符串中的元素
char c2 = v2[k];
if (c1 != c2) { //若当前同一位置的字符不相等的时候
return c1 - c2; //return出两个字母ASCII码的差
}
k++;
}
return len1 - len2;
//以最小的字符长度循环结束了,都没return的话。就直接
// return调用方法的字符串长度-传入的字符串长度
}
获取功能的方法
length()方法
这方法就是返回字符串的长度的方法。
这个很简单,我也就不多说什么了
charAt()方法使用这个方法是首先给传入一个int型的参数,返回字符串中传入参数的字符。
多余的话都不说了,一切都在代码中:
indexOf方法传入的是字符,然后在检索字符串,返回字符在字符串当中出现的索引。
看代码:
当传入两个数的时候(一个要查询的字符,一个开始的索引位置)
下面我们来对这个底层indexOf()方法进行一个大致分析:
public int indexOf(int ch, int fromIndex) {
final int max = value.length; //获得字符串的长度
if (fromIndex < 0) { //若传进来的索引小于0
fromIndex = 0; //就将它设置为0
} else if (fromIndex >= max) { //若给的索引大于字符串的长度,就返回-1
// 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);
}
}
substring()方法
这个方法是一个截取字符串的方法,将输入的索引后面的字符串截取出来,保存到一个新的字符串当中来。
也可以给它传入传入两个索引,那就返回这两个索引之间的字符串。
那我们接下来就对这两个方法调用的同一个方法进行分析一波:
public String(char value[], int offset, int count) {
if (offset < 0) { //开始截取的索引若小于0,就抛异常
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) { //count是后面的索引-前面的索引
if (count < 0) { //若小于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);
//一切正常的话,就继续向下执行调用copyOfRange方法
//传入字符串,前面的索引,后面的索引
}
来,继续分析源代码:
public static char[] copyOfRange(char[] original, int from, int to) {
int newLength = to - from; //记录需要创建的字符串长度
if (newLength < 0) //若小于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; //最后返回处理好的字符串(这里还是数组)
}
本篇博客也就到此为止了,应为整理源码有点消耗时间,明天一定写完
这也是我重读javase基础的一个系列,比起当时初学的时候,现在看问题多了个高度,理解什么也相对轻松一点全面一些。学习起来更加偏向阅读源码来看,所以多为大家分享看源码。
但毕竟学过时间也较长,有什么不对和漏缺的地方,希望大家指出。
欢迎大家在评论区讨论
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)