java三个特殊类之String类
文章目录
String类在所有项目开发之中都会使用到
String类的两种实例化方式
直接赋值
String str="hello";//str是一个对象,那么"hello"就应该保存在堆内存中
System.out.println(str);
这种赋值方式最为常见。但是String本身毕竟是一个类,既然是类,那么里面一定存在构造方法。String类也不例外,String类的其中一种构造方法如下:
public String(String str)
传统方法:
String str=new String("hello");
System.out.println(str);
该方法符合传统做法,使用关键字new进行对象实例化
暂时不需要考虑这两者的区别以及使用,只需要清楚String类现在提供有两种对象实例化的模式。
字符串相等比较
如果现在有两个int类型的变量,判断其相等可以使用“==”完成。
范例:观察数据类型比较
package MyString;
public class equalsTest {
public static void main(String[] args) {
int x=10;
int y=10;
System.out.println(x==y);
String str1="hello";
String str=new String("hello");
System.out.println(str==str1);
}
}
我们的运行结果:
true
false
“==”本身是进行数值比较的,如果现在用于对象比较,那么所比较的就应该是两个对象所保存的内存地址数值比较,而并没有比较对象的内容。
那么要想比较对象的内容,则必须采用String类提供的equals方法。
范例:使用equals方法比较字符串内容:
String str1="hello";
String str=new String("hello");
System.out.println(str.equals(str1));
面试题:请解释String类“==”与“equals”的区别
1、"==":进行的数值比较,比较的是两个字符串对象的内存地址数值
2、”equals()“:可以进行字符串内容的比较
字符串常量是String的匿名对象
在任何语言的底层,都不会提供有直接的字符串类型。现在所谓的字符串只是高级语言提供给用户方便开发的支持而已。在java之中,本身也没有直接提供字符串常量的概念。所以使用""定义的内容本质上来讲都是String的匿名对象。
观察字符串常量
String str1="hello";
String str=new String("hello");
System.out.println("hello".equals(str));
System.out.println(str.equals(str1));
那么在之前出现的"String str=“hello”",本质上就是将一个匿名的String类对象设置有名字,而且匿名对象一定保存在堆内存中。
**小建议:**在以后的开发过程之中,如果要判断用户输入的字符串是否等同于特定字符串,一定要将特定字符串写在前面。
比较方法
String str=null;//假设由用户输入
System.out.println(str.equals("hello"));
在进行接收用户输入数据的时候一定要考虑到用户没有输入的问题,以上面代码为例,用户没有输入的时候,一定会出现"NullPointorException"问题。
任何字符串常量都是String的匿名对象,所以该对象永远不会为null.
String str=null;
System.out.println("Hello".equals(str));//假设由用户输入。
在以后
的工作学习中,我们强烈建议如上写法,将我们的字符串常量写在前面。
String类两种实例化的区别
在第一节已经给出String类的两种实例化操作,在实际开发之中,使用哪一种更好以及彼此之间的区别有哪些呢?
采用直接赋值
package MyString;
public class equalsTest {
public static void main(String[] args) {
String str1="hello";
String str2="hello";
String str3="hello";
System.out.println(str1==str2);
System.out.println(str1==str3);
System.out.println(str2==str3);
}
}
我们的运行结果是:
true
true
true
为什么没有开辟新的堆空间呢?
String类的设计使用了共享设计模式
在JVM底层实际上会自动维护一个对象池(字符串对象池),如果现在采用了直接复制的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中。如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接引用;如若没有,则开辟新的字符串对象而后将其保存在对象池之*下次使用。
所谓的对象池就是一个对象数组(目的是减少开销)
采用构造方法
类对象使用构造方法实例化是标准做法,分析如下:
String str=new String("hello");
通过分析可知,如果使用String构造方法就会开辟两块堆内存空间,并且其中一块堆内存空间将称为垃圾空间。除了这一点缺点之外,会对字符串共享产生问题。
观察我们的字符串共享问题:
package MyString;
public class equalsTest {
public static void main(String[] args) {
String str1="hello";
String str2=new String("hello");
String str3=new String("hello").intern();
System.out.println(str1==str2);
System.out.println(str1==str3);
System.out.println(str2==str3);
}
}
我们的运行结果:
false
true
false
我们在这里值得一提的就是我们的在String类中提供的方法入池操作 public String intern();
面试题:请解释String类中的两种对象实例化的区别
1、直接赋值:只会开辟一块堆空间,并且该字符串对象可以自动保存在对象池中一共下次使用。
2、构造方法:会开辟两块内存空间,其中一块成为垃圾空间,不会自动保存在对象池中,可以使用intern()方法手工入池。
因此,我们的一般会采用第一种方法即直接赋值
字符串常量不可变更
字符串一旦定义不可以改变
所有的语言对于字符串的底层实现,都是字符数组,数组的最大缺陷就是长度固定。在定义字符串常量时,它的内容不可改变。
观察下面的代码
package MyString;
public class equalsTest {
public static void main(String[] args) {
String str="hello";
str+="lele";
str=str+"!!!";
System.out.println(str);
}
}
我们的运行结果是:
hellolele!!!
以上字符串的变更时字符串对象的变更而非字符串常量。
可以发现字符串上没有发生任何变化,但是字符串对象的引用一直在改变,而且会形成大量的垃圾空间。正式因为String的特点,所以我们在开发中不应该频繁的修改我们的字符串。
原则:
1、字符串使用就采用直接赋值。
2、字符串比较就使用equals()实现。
3、字符串不要频繁改变
字符与字符串
字符串就是一个数组,所以在String类里面支持有字符数组转换为字符串以及字符串变为字符的操作方法。
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1、 | public String(char value[]) | 构造 | 将字符数组中的所有内容变为字符串 |
2、 | public String (char value[],int offset,int count) | 构造 | 将部分字符数组中的内容变为字符串 |
3、 | public char charAt(int index) | 普通 | 取得指定索引位置的字符,索引从0开始 |
4、 | public char[] toCharArray() | 普通 | 将字符串变为字符数组返回 |
观察charAt()方法
package MyString;
public class equalsTest {
public static void main(String[] args) {
String str="hello";
System.out.println(str.charAt(0));
System.out.println(str.charAt(10));
}
}
运行结果:
h
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 10
at java.lang.String.charAt(String.java:658)
at MyString.equalsTest.main(equalsTest.java:7)
值得注意的是,如果现在超过了字符串长度,则会产生StringIndexOutOfBoundsException异常
字符串和字符数组之间的转换
package MyString;
public class equalsTest {
public static void main(String[] args) {
String str="lelebaby";
char[] data=str.toCharArray();
for(int i=0;i<data.length;i++){
data[i]-=32;
System.out.println(data[i]+"、");
}
System.out.println(new String(data));
System.out.println(new String(data,5,2));
}
}
我们的运行结果:
L、
E、
L、
E、
B、
A、
B、
Y、
LELEBABY
AB
因为现在不知道字符串的长度以及包含的内容,所以最好的做法就是将字符串变为字符数组而后判断每一位字符是否是"0~9"之间的内容,如果是则为数字。
package MyString;
public class equalsTest {
public static void main(String[] args) {
String str="1a23456";
System.out.println(isNumber(str)?"字符串有所有数字组成":"字符串中有非数字成员!");
}
public static boolean isNumber(String str){
char[]data=str.toCharArray();
for(int i=0;i<data.length;i++){
if(data[i]<'0'||data[i]>'9'){
return false;
}
}
return true;
}}
字符与字符串
字节常用于数据传输以及编码转换的处理之中,在String中提供有对字节的支持。
NO | 方法名称 | 类型 | 描述 |
---|---|---|---|
1、 | public String(byte bytes[]) | 构造 | 将字节数组变为字符串 |
2、 | publlic String(byte bytes[],int offset,int length) | 构造 | 将部分字节数组中的内容变为字符串 |
3、 | public byte[] getBytes() | 普通 | 将字符串以自己数组的形式返回 |
4、 | public byte[] getBytes(String charsetName)throws UnsupportedEncodingException | 普通 | 编码转换处理 |
范例:实现字符串与字节数组的转换处理
package MyString;
import javax.lang.model.element.NestingKind;
public class equalsTest {
public static void main(String[] args) {
String str="lelebybe";
byte[] data=str.getBytes();
for(int i=0;i<data.length;i++){
data[i]-=32;
System.out.println(data[i]+"、");
}
System.out.println(new String(data));
}}
我们的运行结果:
76、
69、
76、
69、
66、
89、
66、
69、
LELEBYBE
通过程序可以发现,字节并不适合处理中文,只有字符适合处理中文。按照程序的概念来讲,一个字符等于两个字节。字节只适合处理二进制数据。
字符串比较
上面使用过Sting类提供的equals()方法,该方法本身是可以进行区分大小写的相等判断。除了这个方法之外,Sting类还提供有如下的比较操作:
NO | 方法名称 | 类型 | 描述 |
---|---|---|---|
1、 | public boolean equals(Object anObject) | 普通 | 区分大小写比较 |
2、 | public boolean equalsIgnoreCase(String anotherString) | 普通 | 不区分大小写比较 |
3、 | public int compareTo(String anotherString) | 普通 | 比较了两个字符串大小关系 |
范例:比较字符串:
package MyString;
public class equalsTest {
public static void main(String[] args) {
String str1="hello";
String str2="Hello";
System.out.println(str1.equals(str2));
System.out.println(str1.equalsIgnoreCase(str2));
}
}
我们的运行结果:
false
true
在String类中compareTo()方法是一个非常重要的方法,该方法返回一个整型,该数据会根据大小关系返回三类内容:
1、相等:返回0;
2、小于:返回内容小于0;
3、大于:返回内容大于0.
我们现在来观察compareTo()比较
public class equalsTest {
public static void main(String[] args) {
System.out.println("A".compareTo("a"));
System.out.println("a".compareTo("A"));
System.out.println("A".compareTo("A"));
System.out.println("AB".compareTo("AC"));
System.out.println("李".compareTo("白"));
}
}
我们的运行结果:
-32
32
0
-1
-3887
compareTo()是一个可以区分大小关系的方法,是String方法里是一个非常重要的方法。
字符串查找
从一个完整的字符串之中可以判断指定内容是否存在,对于查找方法有如下定义:
N0. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1、 | public boolean contatins(CharSequence s) | 普通 | 判断一个字符串是否存在 |
2、 | public int indexOf(String str) | 普通 | 从头开始查找指定字符串的位置,查到了返回位置的开始索引,若果查找不到返回-1 |
3、 | public int indexOf(String str,int fromIndex) | 普通 | 从指定位置开始查找字符串 |
4、 | public int lastIndextOf(String str) | 普通 | 由后向前查找子字符串位置 |
5、 | public int lastIndextOf(String str,int formatIndex) | 普通 | 从指定位置,由后向前查找 |
6、 | public boolean startsWith(String prefix) | 普通 | 判读是否以指定字符串开头 |
7、 | public boolean startsWith(String prefix,int toffset) | 普通 | 从指定位置开始判断是否是以字符串开头 |
8、 | public boolean endsWith(String suffix) | 普通 | 判断是否以指定字符串结尾 |
范例:字符串查找,最好用最方便的就是contains()
public class equalsTest {
public static void main(String[] args) {
String str="helloworld";
System.out.println(str.contains("world"));
}
}
我们的运行结果是:
true
该判断形式是从JDK1.5之后开始追加的,在JDK1.5以前想要实现与之类似的功能,就必须借助,indexOf()方法完成。
范例:使用indexOf()方法进行位置查找
String str="helloworld";
System.out.println(str.indexOf("hello"));//0,h开始的索引
System.out.println(str.indexOf("lele"));//-1,没有找到
if(str.indexOf("hello")!=-1){
System.out.println("可以查找到指定字符串");
}
现在基本上都是使用contains()方法完成的。
使用indexOf()需要注意的是,如果内容重复,它只能返回查找的第一个位置。
使用indexOf()的注意点:
String str="helloworld";
System.out.println(str.indexOf("l"));//2
System.out.println(str.indexOf("l",5));//8
System.out.println(str.lastIndexOf("l"));//8
在进行查找的时候往往会判断开头或结尾。
范例:判断开头或者是结尾
String str="**@@helloworld";
System.out.println(str.startsWith("**"));//true
System.out.println(str.startsWith("@@",2));//true
System.out.println(str.endsWith("!!"));//false
很多时候一些参数利用标记做一些处理,这时候就利用startsWith()与endWith()来判断。
字符串替换
使用一个指定的新的字符串替换掉已有的字符串数据,可以使用以下方法:
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1、 | public String replaceAll(String regex,String replaceAll) | 普通 | 替换所有的指定内容 |
2、 | public String replaceFirst(String regex ,String replacement) | 普通 | 替换首个内容 |
范例:字符串的替换处理
String str="helloworld";
System.out.println(str.replaceFirst("l","!"));//he!loworld
System.out.println(str.replaceAll("l","_")); //he__owor_d
字符串拆分
可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串。
可用方法如下:
NO | 方法名称 | 类型 | 描述 |
---|---|---|---|
1、 | public String split(String regex) | 普通 | 将字符串全部拆分 |
2、 | public String split(String regex,int limit) | 普通 | 将字符串部分拆分,该数组长度就是limit极限 |
范例:实现字符串的拆分处理
package MyString;
import javax.lang.model.element.NestingKind;
public class equalsTest {
public static void main(String[] args) {
String str="hello world hello lele";
String[] result=str.split(" ");
for(String s:result){
System.out.println(s);
}
String[] result1=str.split(" ",2);
for(String s:result1){
System.out.println(s);
}
}
}
以上的拆分形式都很容易。如果发现我们的有些内容无法拆分时就需要使用“\”转义
范例:拆分IP地址
String str="192.168.1.1";
String[] result=str.split("\\.");
for(String s:result){
System.out.println(s);
我们在以后的工程开发中,经常会出现类似于“姓名:年龄|姓名:年龄|…”这样的拆分模式
多次拆分
String str="lele:25|baby:30";
String[] result=str.split("\\|");
for(int i=0;i<result.length;i++){
String[] temp=result[i].split(":");
System.out.println(temp[0]+"="+temp[1]);
运行结果:
lele=25
baby=30
字符串截取:
从一个完整的字符串之中截取出部分内容。可用方法如下:
NO | 方法名称 | 类型 | 描述 |
---|---|---|---|
1、 | public String substring(int beginIndex) | 普通 | 从指定索引截取到结尾 |
2、 | public String substring(int beginIndex,int endIndex) | 普通 | 截取部分内容 |
范例:观察字符串的截取:
String str="hellolele";
System.out.println(str.substring(5));//lele
System.out.println(str.substring(0,5));//hello
索引从0开始
字符串其他操作方法
NO | 方法名称 | 类型 | 描述 |
---|---|---|---|
1、 | public String trim() | 普通 | 去掉字符串的左右空格,保留中间空格 |
2、 | public String toUpperCase() | 普通 | 字符串转大写 |
3、 | public String toLowerCase() | 普通 | 字符串转大写 |
4、 | public native String intern() | 普通 | 字符串入池操作 |
5、 | public String concat(String str) | 普通 | 字符串连接,等同于“+”不入池 |
6、 | public int length() | 普通 | 取得字符串的长度 |
7、 | public boolean isEmpty() | 普通 | 判断是否是空字符串,但不是null,而是长度为0 |
观察我们的trim()方法的使用:
String str=" lele baby ";
System.out.println("["+str+"]");//[ lele baby ]
System.out.println("["+str.trim()+"]");//[lele baby]
大小写转换
String str=" hello##@$lele 哈哈哈";
System.out.println(str.toUpperCase());//HELLO##@$LELE 哈哈哈
System.out.println(str.toLowerCase());// hello##@$lele 哈哈哈
这两个函数只转换字母
范例:字符串length()
String str=" hello##@$lele 哈哈哈";
System.out.println(str.length());//19
注意:在数组里面数组长度使用数组名称.length属性,而String中使用的是length()方法
观察isEmpty()方法
System.out.println("hello".isEmpty());//false
System.out.println("".isEmpty());//true
System.out.println(new String().isEmpty());//true
String类里面并没有提供首字母大写操作,需要自己实现。
首字母大写:
package MyString;
import javax.lang.model.element.NestingKind;
public class equalsTest {
public static void main(String[] args) {
System.out.println(firstUpper("lele"));
System.out.println(" ");
System.out.println("a");
}
public static String firstUpper(String str){
if("".equals(str)||str==null){
return str;
}
if(str.length()>1){
return str.substring(0,1).toUpperCase()+str.substring(1);
}
return str.toUpperCase();
}
}
StringBuffer类
首先来回顾以下String类的特点:
任何字符串常量都是String对象,而且String的常量一旦声明不能改变,如果改变对象内容,改变的是其引用的指向而已。
通常来讲String的操作比较简单,但是由于String的不可更改特性,威力方便字符串的修改,提供StringBuffer类。在String中使用“+”来进行字符串连接,但是这个操作在StringBuffer类中需要改为append()方法。
public synchronized StringBuffer append(各种数据类型 b)
观察StringBuffer使用
package MyString;
import javax.lang.model.element.NestingKind;
public class equalsTest {
public static void main(String[] args) {
StringBuffer sb=new StringBuffer();
sb.append("hrllo").append("world");
fun(sb);
System.out.println(sb);
}
public static void fun(StringBuffer temp){
temp.append("\n").append("aas.com.cn");
}
}
运行结果:
hrlloworld
aas.com.cn
String和StringBuffer最大的区别在于:String的内容无法修改,而StringBuffer的内容可以修改。频繁修改字符串的情况优先考虑使用StringBuffer。
为了更好的理解String和StringBuffer,我们来看这两个类的继承结构:
String类 | StringBuffer类 |
---|---|
public final class String implements java.io.Serializable,Comparable,CharSequence | public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable,CharSequence |
可以发现两个都是"CharSequence"接口的子类。这个接口描述的是一系列的字符集。所以字符串是字符集的子类,如果以后看见CharSequence,最简单的联想就是字符串。
注意String和StringBuffer类不能直接转换。如果想要相互转换,可以采用如下原则:
- String变为StringBuffer。利用StringBuffer的构造方法或者是append()方法
- StringBuffer变为String:调用toString()方法。
除了append()方法外,StringBuffer也有一些String类没有的方法:
字符串反转,删除指定范围的数据,插入数据
StringBuffer sb=new StringBuffer("helloworld");
System.out.println(sb.reverse());
System.out.println(sb.delete(5,10));
System.out.println(sb.insert(0,"lele"))
面试题:请解释String、StringBuffer、StringBuilder的区别
1、String的内容不可以修改,StringBuffer和StringBuilder的内容可以修改。
2、StringBuffer采用同步处理,属于线程安全操作;而StringBuffer采用异步处理,属于线程不安全操作。