在前面几章, 看了整个 String 类的源码, 给每个方法都行写了注释, 但是太过凌乱, 今天我就把 String 类的方法整理归纳, 然后再讲一下 String 类比较难以理解的部分
特此声明: 本文篇幅较大, 涵盖知识点较多, 请耐着性子读下去, 毕竟写文章不易, 写知识性文章更加不易!
第一部分: 函数介绍
这是第一部分的内容, 由于 String 的函数较多, 我将他们分为四大类, 分别是构造性函数, 转换性函数, 功能性函数以及私有函数
私有函数: 主要是把属性以及一些私有方法列出来
构造性函数: 也就是字符串的构造器
转换性函数: 负责字符串和其他类型之间的转换, 比如说 valueOf
功能性函数: 其实除了构造函数和转换函数, 其他函数都具些特别常用的功能, 我们经常使用功能函数, 所有我将他们罗列出来(ps: 重点)
私有函数:
如果你仔细看源代码的话, 会发现每个私有函数都会在代码中被频繁调用, String 把这些被多次用到的重复代码进行封装,
- private final char value[];
- private int hash; // Default to 0
- private static final long serialVersionUID = -6849794470754667710L;
- private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
首先是四个属性, 最后两个是用来实现序列化和反序列化的, 详细内容会单独讲解
第一个属性: char value[]就表明了 String 字符串的本质, char 数组表明它是引用类型, final 表明它是常量, 所以他是一个在 JVM 线程共享区的方法区中的常量,
所以每当我们创建一个 String 对象时, 都会在常量池中查找是否已经存在该常量, 若存在就将该对象指向该常量, 若不存在就先在常量池中创建该常量, 然后再指向这个常量.
这里讲一下常量和变量: 可能有的同学对常量的概念很模糊, 只知道常量和变量是相对的,
变量: 我们常说的局部变量和成员变量都是变量, 变量分为基本类型和引用类型, 基本类型就是那八大类型 (boolean,byte,char,short,int,long,float,double) 而引用类型分为三种, 数组, 类和接口
常量: 常量分为俩种, 字面值常量和自定义常量, 比如说 Math.min(2,3)其中的 2 和 3 就是直接传入的俩个常量, 在 Math 类中 public static final double PI = 3.14159265358979323846 这么定义的 PI 就是自定义常量,
也就是我们通常意义上的常量, 常量就是其值不可改变的, 也就是用 final 修饰, 比如说 2 就表示 2,PI 就表示 3.14159265358979323846.static 修饰词限定了该常量的值被类的对象所共享, 可以通过类名直接调用, Math.PI.
第二个属性 hash, 这个属性会带你走进 hashCode()方法的神秘世界, 详情请参照 hashCode()方法, 这个 hash 属性是用来在一定程度上标识字符串唯一性的, 你可以把它认为成一种 ID, 但是 hash 并不是地址值, 详细去看一下 hash 表的数据结构,
这个 hash 只能称之为哈希码, 绝对不是什么地址值, 也就是说 hashCode 方法返回的是对象在内存中的地址的说法是错误的
private static void checkBounds(byte[] bytes, int offset, int length)
在 String 构造函数中用于检查边界, 也就是检查传入的 offset 和 length 是否有问题
String(char[] value, boolean share)
打包私有构造函数, 它为 speed.this 构造函数共享值数组, 总是需要使用 share == true 来调用. 需要单独的构造函数, 因为我们已经有一个公共 String(char [])构造函数, 它可以复制给定的 char[].
不要纠结与这个 Boolean 类型的参数, 它只是一个用来区别于 String(char[])构造器的
- private boolean nonSyncContentEquals(AbstractStringBuilder sb)
- private static class CaseInsensitiveComparator implements Comparator<String>, java.io.Serializable {}
局部内部类, 是 String 实现不区分大小写的 equals 方法的核心代码实现, 详情在 public boolean equalsIgnoreCase(String anotherString)中
- private int indexOfSupplementary(int ch, int fromIndex)
- private int lastIndexOfSupplementary(int ch, int fromIndex)
- static int indexOf(char[] source, int sourceOffset, int sourceCount,String target, int fromIndex)
- static int indexOf(char[] source, int sourceOffset, int sourceCount,char[] target, int targetOffset, int targetCount,int fromIndex)
- static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,String target, int fromIndex)
- static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,char[] target, int targetOffset, int targetCount,int fromIndex)
构造性函数:
String 的构造函数, 还是很让人懵逼的, 总的来说他们的具体功能是就是将 String ,char[],byte[],StringBuilder StringBuffer 转换为 String 类型, 后几个大家都容易理解, 就是将其他类型装换为 String 类型, 但是第一个呢, 将 String 转换为 String? 这有什么用呢? 且听我细细道来
我们先看源代码 (由于我之前在 String 源码解析中已经发过了, 这里就不重复发了),new String() 只有一行 this.value = "".value;,new String(String original)只有两行 this.value = original.value;this.hash = original.hash; 很简单的吧
这就引出了一道经典的面试题: String s = new String("gollong")创建了几个对象?
首先你要明白什么创建对象, String s; 并没有创建对象, 只是声明一个引用, new String("gollong")才是真正的创建对象, 然后 String s = new String("gollong")完成引用的实例化.
其实你要明白这是调用的一个构造函数, 传入的参数是 String 类型的, 那么就很简单了, 我们将这一行代码分解为如下两行
- String temp = "gollong";
- String s = new String(temp);
这就好理解了吧, 首先会在常量池中创建一个 "gollong", 这是第一个对象(当然是在常量池中本身没有这个对象的前提下), 其次会在堆中创建第二个对象, 因为 new 出来的对象肯定及一定在堆中, 再把前一个对象当做实参传入到其中
我用一张概念图来解释这个问题:
- public String()
- public String(String original)
- public String(char value[])
- public String(char value[], int offset, int count)
- public String(int[] codePoints, int offset, int count)
看上去好像是将 int 数组转换为 String, 但是你去试试, 输出的东西并不是简单的把每一个 int 值链接起来, 这里面大有学问, 我们都知道 java 是采用 Unicode 字符集的, 那 Unicode 内是怎么表示的呢,
Unicode 内部一共有三种长度的字符, 分别占用一个字节, 两个字节, 四个字节, 而这个 int 类型的数组就是指符合 Unicode 的四个字节长度的字符, 在编码一章中再讲吧, 太复杂了
- @Deprecated
- public String(byte ascii[], int hibyte, int offset, int count)
- public String(byte ascii[], int hibyte)
已经过时的俩个方法, 其意义是将 byte 数组转换为 String 对象, 我们都知道 byte 类型只占用一个字节, 而组成 String 的 char 类型却占有两个字节, 这就涉及到互相转换时高 8 位的处理问题了
而对于不同的编码方式(请注意: 在不详细区别编码和解码是, 编码就是指编码和解码的统称), 处理方式是不同的, 所以这两个构造器可以让使用者指定高 8 位的内容, 但是人为指定的往往是不正确的
所以标注为过时(请注意: 过时并不意味着不能用, 只是不推荐使用, 因为它总有一 天会被淘汰的), 才有了以下的六个指定编码方式的方法.
- public String(byte bytes[], int offset, int length, String charsetName)throws UnsupportedEncodingException
- public String(byte bytes[], int offset, int length, Charset charset)
- public String(byte bytes[], String charsetName)throws UnsupportedEncodingException
- public String(byte bytes[], Charset charset)
- public String(byte bytes[], int offset, int length)
- public String(byte bytes[])
指定编码方式的将 byte 转换为 char, 其中可以通过两个方式指定, 一种是传入编码的字符串表示, 最经常使用的方法, 另一种是传入 Charset 对象, 毕竟能不造对象就不造, 浪费空间
- public String(StringBuffer buffer)
- public String(StringBuilder builder)
转换性函数:
就是一些负责把其他类型的变量转换为 String 类型, 或者把 String 转换为 byte[],char[]
- public static String format(String format, Object... args)
- public static String format(Locale l, String format, Object... args)
- public static String join(CharSequence delimiter, CharSequence... elements)
- public static String join(CharSequence delimiter,Iterable<? extends CharSequence> elements)
- public static String valueOf(Object obj)
- public static String valueOf(char data[])
- public static String valueOf(char data[], int offset, int count)
上面俩个内部调用的就是 String 的构造器 new String(char data[])和 new String(char data[],int offset,int count)
- public static String copyValueOf(char data[], int offset, int count)
- public static String copyValueOf(char data[])
- public static String valueOf(boolean b)
- public static String valueOf(char c)
- public static String valueOf(int i)
- public static String valueOf(long l)
- public static String valueOf(float f)
- public static String valueOf(double d)
static 修饰的类函数, 可通过 String. 直接调用, 返回一个 String, 类似于工具类的做法, 经常被用来将单个的各种类型转换为 String, 其实内部调用的就是八大数据类型的包装类的 toString 方法
- public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
- public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin)
- public byte[] getBytes(String charsetName)throws UnsupportedEncodingException
- public byte[] getBytes(Charset charset)
- public byte[] getBytes()
- public char[] toCharArray()
功能性函数:
由于功能性函数很多, 我把他们进行了详细的分类
来自于 Object 的函数:
继承自 Object 类的三个方法, 都很重要
equals : 重写后用来比较俩个字符串的每对应的俩个字符是否相等, 也就是比较内容是否相等, 很容易理解, 但是往往与他同时出现的 == 就不是那么容易理解了, 在后面的第二部分中讲解
hashCode: 返回的是字符串额 hash 值, 而在 java 中 String 的哈希码计算规则是 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1], 这里就不列计算过程, 详细去 google. 一定要注意 hashCode 返回的不是地址值, 返回的是哈希码, 是通过规则算出来的
toString: 返回当前对象, 并不是你想象当中的遍历字符串中的每一个字符, 而字符串对象直接输出就是遍历. 根本不需要重写 toString 方法, 我相信你肯定听说过这句话, 输出一个字符串对象就是输出它的值, 是因为 String 重写了 toString 方法, 现在我说这句话是错的你信吗?
- public boolean equals(Object anObject)
- public int hashCode()
- public String toString()
判断函数:
通过比较此字符串和指定的内容, 返回一个 boolean 类型, 一般用在 if()和 while()语句中, 都是很常用的方法
public boolean isEmpty()
判断字符串是否为空, 也及时判断 char 数组的长度是否为 0
- public boolean contentEquals(StringBuffer sb)
- public boolean contentEquals(CharSequence cs)
判断内容是否相等, StringBuffer 是什么我们都知道, CharSequence 是个什么呢? 其实 CharSequence 是字符序列接口, 我们所接触到的像 String,StringBuffer,StringBuider 等都是它的实现类. 大家既然都是字符串, 哪来比较下内容也无可厚非嘛
public boolean equalsIgnoreCase(String anotherString)
忽略大小写的比较, 核心调用的是 regionMatches 方法, 而 regionMathes 是用来测试俩个字符串某些部分是否相等, 而 equals 只能整个比较, 这就是我们用它的原来, 但是一般不怎么使用, 都是用 contains
- public boolean regionMatches(int toffset, String other, int ooffset,int len)
- public boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len)
判断字符串是否以指定字符串开始或结尾
- public boolean startsWith(String prefix, int toffset)
- public boolean startsWith(String prefix)
- public boolean endsWith(String suffix)
判断字符串是否匹配指定的正则表达式
public boolean matches(String regex)
判断字符串是否包含指定的字符序列, 注意传入的是 CharSequence 对象
public boolean contains(CharSequence s)
比较函数:
比较函数其实也是判断函数, 只不过下面的三个函数返回值不是 boolean 类型的, 我将他们单独罗列出来, 其实我们一般不会手动调用这些函数的, 数组工具类 Arrays 中有一个方法排序方法 sort()
public static void sort(Object[] a): 这是不指定排序方式 (元素之间怎么比较) 的方法, 其实内部就是调用自然排序, 也就是实现 comparable 接口实现的 comparTo 方法
public static <T> void sort(T[] a, Comparator<? super T> c): 这是指定外部比较器的方法, 这个比较器就是新建一个类实现 Comparator 接口重写 compare 方法的比较方式, 要比较的类型当做泛型传递进去,
在 String 类中, 写了一个成员内部类来实现这个比较器, 在重写 compare 方法, 得到的功能是忽略大小写的比较, 在 public int compareToIgnoreCase(String str)方法内得到体现, 其实这个方法和 public boolean equalsIgnoreCase(String anotherString)功能是一样的
- public int compareTo(String anotherString)
- public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
- public int compareToIgnoreCase(String str)
查询函数:
很常用的一些函数, 具体作用是查询某个字符或字符串的位置
public int length()
返回字符串的长度, 也就是 value 的长度
public char charAt(int index)
返回字符串中某个下标对应的字符, 很实用的方法
public int codePointAt(int index)
还记得 codePoint 是什么吗? 他为什么是 int 类型的呢? 去上面看看就明白了. 返回对应下标的 Unicode codePoint
public int codePointBefore(int index)
返回对应下标前一个的 Unicode codePoint
public int codePointCount(int beginIndex, int endIndex)
返回指定范围内 Unicode codePoint 的总数
public int offsetByCodePoints(int index, int codePointOffset)
返回此 String 中从给定的 index 处偏移 codePointOffset 个代码点的索引
- public int indexOf(int ch)
- public int indexOf(int ch, int fromIndex)
- public int lastIndexOf(int ch)
- public int lastIndexOf(int ch, int fromIndex)
- public int indexOf(String str)
- public int indexOf(String str, int fromIndex)
- public int lastIndexOf(String str)
- public int lastIndexOf(String str, int fromIndex)
- public int indexOf(int ch)
- public int indexOf(int ch, int fromIndex)
- public int lastIndexOf(int ch)
- public int lastIndexOf(int ch, int fromIndex)
- public int indexOf(String str)
- public int indexOf(String str, int fromIndex)
- public int lastIndexOf(String str)
- public int lastIndexOf(String str, int fromIndex)
上面的 indexOf 方法都是返回指定字符或者字符串第一次出现的下标, lastIndexOf 是返回指定字符或者字符串的最后一次出现的下标, fromIndex 用于指定开始的位置, 这里需要注意一点:
从我们的习惯出发, 肯定是自左向右搜索, indexOf 方法便是这样自左向右, fromIndex 参数用于指定开始搜索的位置
而 lastIndexOf 却是自右向左搜索, 找到的第一个便是我们要寻找的最后一个字符或字符串, 所以 fromIndex 是指定开始搜索的位置, 实际上就是我们习惯自左向右搜索方式的结束位置
功能函数:
平时我们获得字符串可以不是很满意, 需要我们通过一些函数去掉里面的某些字符或者替换一些, 再或者进行大小写转换, 没错, 下面的函数你都会用到.
- public String substring(int beginIndex)
- public String substring(int beginIndex, int endIndex)
切片函数, 说实话我学 java 到现在, 不知道怎么叫这个函数, 只时称呼他 substring, 这几天在看 python,python 里面是切片函数, 听起来还不错就借用啦, 哈哈, 顾名思义, 用一个字符串切出你想要的部分, 说实话 java 中的切片函数远没有 python 中的好用, 大家去看看 python 的就明白了
public CharSequence subSequence(int beginIndex, int endIndex)
也是切片, 返回的是 CharSequence 接口对象, 说实话返回的就是一个 String 对象(自己去看源码就知道了), 其实就是一个向上转型(CharSequence s = new String()).
public String concat(String str)
类似于 StringBUffer 中的 append 函数, 在字符串的结尾追加字符串, 其内部调用的就是 String(char[] ,boolean)构造器的, 但是这种字符串追加的代价高昂, 所以我们一般选择 StringBuffer 和 StringBuilder
- public String replace(char oldChar, char newChar)
- public String replaceFirst(String regex, String replacement)
- public String replaceAll(String regex, String replacement)
- public String replace(CharSequence target, CharSequence replacement)
以上四个是替换函数, 其中第一个只能替换字符串中的所有的指定字符, 注意只能替换单个字符
而第四个函数重构此方法后参数就变为了 CharSequence 接口, 现在对 CharSequence 很熟悉了吧, 所以 replace 其实什么都可替换, 只不过一般我们都是操作字符串 String, 所以经常使用的是第三个. 而第二个仅仅替换找到的第一个指定字符串
- public String[] split(String regex, int limit)
- public String[] split(String regex)
切割函数, 根据指定的正则表达式切割字符串, 返回结果是 String 数组, 其中参数 limit 用于现代最终字符串数组的长度, 前面都讲过了, 不进行详细的讲解了
- public String toLowerCase(Locale locale)
- public String toLowerCase()
- public String toUpperCase(Locale locale)
- public String toUpperCase()
将字符串中的字符全部转换为大写或者小写, 其中 locale 用于指定本地规则
public String trim()
去除字符串两端的空格, 很实用的小函数
public native String intern();
最后一个函数 intern 很有意思, 作为一个 native 方法, 很底层, 但是我们依然可以通过一个例子来揭开它神秘的面纱, 我们明天实战练习见!!!
写了一天, 可能有些字打错了, 或有些没讲清楚, 望大家见谅(>.).
来源: https://www.cnblogs.com/gollong/p/9372868.html