作为开发人员经常接触使用的String,你真的弄懂了吗?不要急着回答,心里仔细思量。
- 引用JDK 1.8 API 中官方介绍
public final class String extends
Object implements Serializable, Comparable<String>, CharSequence(){
}
String类代表字符串。 Java程序中的所有字符串文字(例如"abc" )都被实现为此类的实例。
字符串不变; 它们的值在创建后不能被更改。 字符串缓冲区支持可变字符串。 因为String对象是不可变的,它们可以被共享。 例如:
String str = "abc";
相当于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
以下是一些如何使用字符串的示例:
System.out.println("abc");
String cde = "cde";
System.out.println("abc" + cde);
String c = "abc".substring(2,3);
String d = cde.substring(1, 2);
String类包括用于检查序列的各个字符的方法,用于比较字符串,搜索字符串,提取子字符串以及创建将
所有字符翻译为大写或小写的字符串的副本。 案例映射基于Character类指定的Unicode标准版本。
Java语言为字符串连接运算符(+)提供特殊支持,并为其他对象转换为字符串。 字符串连接是通过StringBuilder (或StringBuffer )类及其append方法实现的。
字符串转换是通过方法来实现toString ,由下式定义Object和继承由在Java中的所有类。 有关字符串连接和转换的其他信息,请参阅Gosling,Joy和Steele, Java语言规范 。
除非另有说明,否则传递null参数到此类中的构造函数或方法将导致抛出NullPointerException 。
A String表示UTF-16格式的字符串,其中补充字符由代理对表示 (有关详细信息,请参阅Character课程中的Character部分)。 索引值是指char代码单元,所以补充字符在String中使用两个String 。
String类提供处理Unicode代码点(即字符)的方法,以及用于处理Unicode代码单元(即char值)的方法。
- String 类的所有方法
Modifier and Type(修饰符和类型) | Method and Description (方法和描述) |
---|---|
char | charAt(int index) 返回 char指定索引处的值。 |
int | codePointAt(int index) 返回指定索引处的字符(Unicode代码点)。 |
int | codePointBefore(int index)返回指定索引之前的字符(Unicode代码点)。 |
int | codePointCount(int beginIndex, int endIndex)返回此 String指定文本范围内的Unicode代码点数。 |
int | compareTo(String anotherString)按字典顺序比较两个字符串。 |
int | compareToIgnoreCase(String str)按字典顺序比较两个字符串,忽略病例差异。 |
String | concat(String str) 将指定的字符串连接到该字符串的末尾。 |
boolean | contains(CharSequence s)当且仅当此字符串包含指定的char值序列时才返回true。 |
boolean | contentEquals(CharSequence cs)将此字符串与指定的CharSequence进行 CharSequence 。 |
boolean | contentEquals(StringBuffer sb) 将此字符串与指定的StringBuffer进行 StringBuffer 。 |
static | String copyValueOf(char[] data) 相当于 valueOf(char[]) 。 |
static | String copyValueOf(char[] data, int offset, int count) 相当于 valueOf(char[], int, int) 。 |
boolean | endsWith(String suffix)测试此字符串是否以指定的后缀结尾 。 |
boolean | equals(Object anObject)将此字符串与指定对象进行比较。 |
boolean | equalsIgnoreCase(String anotherString)将此 String与其他 String比较,忽略案例注意事项。 |
static | String format(Locale l, String format, Object… args) 使用指定的区域设置,格式字符串和参数返回格式化的字符串。 |
static | String format(String format, Object… args) 使用指定的格式字符串和参数返回格式化的字符串。 |
byte[] | getBytes() 使用平台的默认字符集将此 String编码为字节序列,将结果存储到新的字节数组中。 |
byte[] | getBytes(Charset charset) 使用给定的charset将该String编码为字节序列,将结果存储到新的字节数组中。 |
byte[] | getBytes(String charsetName) 使用命名的字符集将此 String编码为字节序列,将结果存储到新的字节数组中。 |
void | getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)将此字符串中的字符复制到目标字符数组中 |
int | hashCode()返回此字符串的哈希码。 |
int | indexOf(int ch) 返回指定字符第一次出现的字符串内的索引。 |
int | indexOf(int ch, int fromIndex)返回指定字符第一次出现的字符串内的索引,以指定的索引开始搜索。 |
int | indexOf(String str) 返回指定子字符串第一次出现的字符串内的索引。 |
int | indexOf(String str, int fromIndex)返回指定子串的第一次出现的字符串中的索引,从指定的索引开始。 |
String | intern() 返回字符串对象的规范表示。 |
boolean | isEmpty() 返回 true如果,且仅当 length()为 0 。 |
static | String join(CharSequence delimiter, CharSequence… elements)返回一个新的字符串,由 CharSequence elements的副本组成,并附有指定的delimiter的 delimiter 。 |
static | String join(CharSequence delimiter, Iterable<? extends CharSequence> elements)返回一个新 String的副本组成 CharSequence elements与指定的副本一起加入 delimiter 。 |
int | lastIndexOf(int ch) 返回指定字符的最后一次出现的字符串中的索引。 |
int | lastIndexOf(int ch, int fromIndex)返回指定字符的最后一次出现的字符串中的索引,从指定的索引开始向后搜索。 |
int | lastIndexOf(String str)返回指定子字符串最后一次出现的字符串中的索引。 |
int | lastIndexOf(String str, int fromIndex)返回指定子字符串的最后一次出现的字符串中的索引,从指定索引开始向后搜索。 |
int | length()返回此字符串的长度。 |
boolean | matches(String regex)告诉这个字符串是否匹配给定的 regular expression 。 |
int | offsetByCodePoints(int index, int codePointOffset)返回此 String内的指数,与 index codePointOffset代码点。 |
boolean | regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)测试两个字符串区域是否相等。 |
boolean | regionMatches(int toffset, String other, int ooffset, int len)测试两个字符串区域是否相等。 |
String | replace(char oldChar, char newChar)返回从替换所有出现的导致一个字符串 oldChar在此字符串 newChar 。 |
String | replace(CharSequence target, CharSequence replacement)将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。 |
String | replaceAll(String regex, String replacement)用给定的替换替换与给定的 regular expression匹配的此字符串的每个子字符串。 |
String | replaceFirst(String regex, String replacement)用给定的替换替换与给定的 regular expression匹配的此字符串的第一个子字符串。 |
String[] | split(String regex)将此字符串分割为给定的 regular expression的匹配。 |
String[] | split(String regex, int limit)将这个字符串拆分为给定的 regular expression的匹配。 |
boolean | startsWith(String prefix)测试此字符串是否以指定的前缀开头。 |
boolean | startsWith(String prefix, int toffset)测试在指定索引处开始的此字符串的子字符串是否以指定的前缀开头。 |
CharSequence | subSequence(int beginIndex, int endIndex)返回一个字符序列,该序列是该序列的子序列。 |
String | substring(int beginIndex)返回一个字符串,该字符串是此字符串的子字符串。 |
String | substring(int beginIndex, int endIndex) 返回一个字符串,该字符串是此字符串的子字符串。 |
char[] | toCharArray() 将此字符串转换为新的字符数组。 |
String | toLowerCase()将所有在此字符 String使用默认语言环境的规则,以小写。 |
String | toLowerCase(Locale locale)将所有在此字符 String ,以降低使用给定的规则情况下 Locale 。 |
String | toString()此对象(已经是字符串!)本身已被返回。 |
String | toUpperCase() 将所有在此字符 String使用默认语言环境的规则大写。 |
String | toUpperCase(Locale locale) 将所有在此字符 String使用给定的规则,大写 Locale 。 |
String | trim()返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。 |
static | String valueOf(boolean b)返回 boolean参数的字符串 boolean形式。 |
static | String valueOf(char c) 返回 char参数的字符串 char形式。 |
static | String valueOf(char[] data) 返回 char数组参数的字符串 char形式。 |
static | String valueOf(char[] data, int offset, int count)返回 char数组参数的特定子阵列的字符串 char形式。 |
static | String valueOf(double d) 返回 double参数的字符串 double形式。 |
static | String valueOf(float f) 返回 float参数的字符串 float形式。 |
static | String valueOf(int i) 返回 int参数的字符串 int形式。 |
static | String valueOf(long l) 返回 long参数的字符串 long形式。 |
static | String valueOf(Object obj)返回 Object参数的字符串 Object形式。 |
- String 日常还用应注意:
1.String对象的存放位置:
大家都知道java中的对象大都是存放在堆中的,但是String对象是一个特例,它被存放在常量池中。
当创建一个字面量String对象时,首先会去检查常量池中这个对象的存在与否。
2.在面试中经常遇到这样的问题:1、什么是不可变对象。不可变对象有什么好处。在什么情景下使用它,或者更具体一点,java的String类为什么要设置成不可变类型?
下面是我个人比较喜欢的一个回答:来自博客园的一位老师:
1、不可变对象,顾名思义就是创建后的对象不可以改变,典型的例子有java中的String类型。
2、相比于可变对象,不可变对象有很多优势:
(1)不可变对象可以提高String Pool(字符串常量池)的效率和安全性。如果你知道一个对象是不可变动 ,
那么需要拷贝的对象的内容时就不用复制它本身二只是复制它的地址,复制地址(通常一个指针的大小)需要很小的内存,
效率也很好。二对于其他引用同一个对象的其他变量也不会造成影响。
(2)可不变对象对于多线程滴安全的,因为在多线程同事进行的情况下,一个可变对象的值很可能被其他线程改变这样
会造成不可预期的结果么人使用不可变对象就可以避免这种情况出现。
java将String设成不可变最大的原因是效率和安全。
那么不可变类型到底是怎么实现的呢?
在java中考虑到各种因素,需要综合到内存,数据结构以及安全的方面的考虑,在下文中,我会为各种原因做一个总结。
1、字符串常量池的需要
字符串常量池是java堆内存中一个特殊的存储区域,当创建一个String对象,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。
代码如下:
String s1 = "ABC";
String s2 = "ABC";
在java中内存分为堆内存和栈内存,堆内存存放的是对象,栈内存存储对象的引用,字符串“ABC”存放在堆内存中,而s1,s2作为对象的引用则存放在栈内存中,原理如下:
堆内存 栈内存
String对象 "ABC"______ s1 String变量的引用
|______ s2
假设:字符串对象允许改变,那么将会导致各种逻辑错误。比如改变一个对象却影响到另外一个独立的对象。
思考一下:一下代码,s1和s2还会指向同一个对象吗?
String s1 = "AB"+"C";
String s2 = "A"+"BC";
也许很多新手都会觉得不是指向同一个对象,但是考虑到现代编译器会进行常规的优化所以他们都会指向常量池中的同一个对象。
2、运行String对象缓存HashCode
java中String对象的哈希码被频繁的使用,比如在HashMap的容器中。
字符串不变性保证了hash码的唯一性,因此可以放心的进行缓存,这也是一种性能优化手段,意味着不必每次都取计算新的哈希码,在String类的定义中有如下代码:
private int hash;//用来缓存HashCode
3、安全性
String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,
还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。
总体来说, String不可变的原因包括 设计考虑,效率优化问题,以及安全性这三大方面.
事实上,这也是Java面试中的许多 "为什么" 的答案。
4、String类不可变的好处
String是所有语言中最常用的一个类。我们知道在Java中,String是不可变的、final的。Java在运行时也保存了一个字符串池(String pool),这使得String成为了一个特别的类。
String类不可变性的好处
1.只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字 符串。但如果字符串是可变的,
那么String interning将不能实现(译者注:String interning是指对不同的字符串仅仅只保存一个,即不会保存多个相同的字符串。),因为这样的话,如果变量改变了它的值,那么
其它指向这个值的变量 的值也会一起改变。
2.如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连 接,或者在socket编程中,主机名和端口都是以字
符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符 串指向的对象的值,造成安全漏洞。
3.因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
4.类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。譬如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成
不可知的破坏。
5.因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap
中的键往往都使用字符串。
二、既然知道String类型不可变的好处和作用那么大,那么是否就不需要可变类型了呢?
当然不是,当你需要向字符串插入或修改的时候,Sting不可变类型就显得足襟见肘,这时候就需要一个可变的字符串类型:StringBuffer。
StringBuffer与String一样,都代表字符串,但是由于StringBuffer内部实现的方式和String不同,所以StringBuffer在处理字符串的时候
不产生新的对象,在内存使用上要优于String类。
-所以也许你已经猜到了,我要谈谈:String、StringBuffer和StringBuilder类的区别
Java提供了String、StringBuffer和StringBuilder类来封装字符串,并提供了一系列操作字符串对象的方法。
它们的相同点是都用来封装字符串;都实现了CharSequence接口。它们之间的区别如下:
一、可变与不可变
String类是一个不可变类,即创建String对象后,该对象中的字符串是不可改变的,直到这个对象被销毁。StringBuffer与StringBuilder都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,是可变类。
由于String是可变类,适合在需要被共享的场合中使用,当一个字符串经常被修改时,最好使用StringBuffer实现。如果用String保存一个经常被修改的字符串,该字符串每次修改时都会创建新的无用的对象,这些无用的对象会被垃圾回收器回收,会影响程序的性能,不建议这么做。
二、初始化方式
当创建String对象时,可以利用构造方法String str = new String("Java")
的方式来对其进行初始化,也可以直接用赋值的方式String s = "Java"来初始化。
而StringBuffer只能使用构造方法
StringBuffer sb = new **StringBuffer("hello")的方式初始化。
三、字符串修改方式**
String字符串修改方法是首先创建一个StringBuffer,其次调用StringBuffer的append方法,最后调用StringBuffer的toString()方法把结果返回,示例代码如下:
String str = "hello";
str += "java";
以上代码等价于下面的代码:
StringBuffer sb = new StringBuffer(str);
sb.append("java");
str = sb.toString();
上述String字符串的修改过程要比StringBuffer多一些额外操作,会增加一些临时的对象,从而导致程序的执行效率降低。StringBuffer和StringBuilder在修改字符串方面比String的性能要高。
四、是否实现了equals和hashCode方法
String实现了equals()方法和hashCode()方法,new String(“java”).equals(new String(“java”))的结果为true;
而StringBuffer没有实现equals()方法和hashCode()方法,因此,new StringBuffer(“java”).equals(new StringBuffer(“java”))的结果为false,将StringBuffer对象存储进Java集合类中会出现问题。
五、是否线程安全
StringBuffer与StringBuilder都提供了一系列插入、追加、改变字符串里的字符序列的方法,它们的用法基本相同,只是StringBuilder是线程不安全的,StringBuffer是线程安全的,。如果只是在单线程中使用字符串缓冲区,则StringBuilder的效率会高些,但是当多线程访问时,最好使用StringBuffer。
综上,在执行效率方面,StringBuilder最高,StringBuffer次之,String最低,对于这种情况,一般而言,如果要操作的数量比较小,应优先使用String类;如果是在单线程下操作大量数据,应优先使用StringBuilder类;如果是在多线程下操作大量数据,应优先使用StringBuilder类。
您的每一个点赞,我都认真当成了喜欢。