1)直接赋值法
String str="abc";
String类的设计采用了共享设计模式。在JVM底层实际上会自动维护一个对象池(字符串对象池),如果采用直接赋值法赋值,JVM会先在对象池中查找,如果有,则直接引用;如果没有,则将该实例化对象放到对象池中。
所谓对象池就是一个对象数组。优点:减少内存的开销。
2)构造法
String str=new String("abc");
由此可知,如果使用构造法就会开辟两块内存空间,并且其中一块会变为垃圾空间。而且,当new两个相同字符串时,并不会像直接赋值法那样,而是会再次创建开辟内存空间,因此,它的缺点是:容易制造垃圾,不能实现字符串共享。
先开辟的那个对象为匿名对象,new开辟空间后,匿名对象空间就变为了垃圾空间。
"=="在基本数据类型中比较的是数值的大小,但是在引用数据类型中,用于对象比较,比较的是两个对象所保存的内存地址数值,而没有比较对象的内容。
public class TestString {
public static void main(String[] args) {
int a=1;
int b=1;
System.out.println(a==b);//true
String str1="abc";//直接赋值法
String str2=new String("abc");//构造法
System.out.println(str1==str2);//false
}
}
要想比较字符串的内容,必须采用String类提供的equals方法。
public boolean equals(Object anObject)
public class TestString {
public static void main(String[] args) {
String str1="abc";//直接赋值法
String str2=new String("abc");//构造法
System.out.println(str1==str2);//false
System.out.println(str1.equals(str2));//true
}
}
String类中“==”与"equals"的区别?
“==”用于比较对象的堆空间地址的大小;而equals方法用于比较两个字符串的内容是否相等。
在任何语言的底层,都不会提供有直接的字符串类型。现在所谓的字符串只是高级语言提供给用户开发的支持而已。在Java中,也没有直接提供字符串常量的概念。所有使用“”定义的被人本质上都是String的匿名对象。
匿名对象保存在堆内存中。
建议:在以后的开发过程中,如果要判断用户输入的字符串是否等同于特定字符串,一定要先将特定字符串写在前面。
如:
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String input=sc.next();
System.out.println(input.equals("hello"));//可能会抛出空指针异常,NUllPointerException异常
System.out.println("hello".equals(input));
}
}
如果用户输入为空,就会抛出异常,为避免此异常,我们通常将特定字符串写在前面。推荐第二种写法。
任何的字符串都是String的匿名对象,所以该对象不会为null。
在String类中,提供有intern()使String对象入池。
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
String s1=new String("abc");
String s2="abc";
System.out.println(s1.equals(s2));//true
System.out.println(s1==s2);//false
String s3=s1.intern();
System.out.println(s3==s2);//true
System.out.println(s1.intern()==s2);//true
}
}
所有语言对字符串的底层实现,都是字符数组。而数组最大的缺陷就是长度固定,因此在定义字符串常量时,它的内容不可改变。
String str="hello";
str=str+"world";
str+="!!!";
System.out.println(str);
以上字符串的变更属于字符串对象的变更,而非字符串常量的变更,改变的是字符串对象的引用。这个过程中共产生了5个对象,而且都不在对象池中,而是在堆空间中。
String str="hello"+" world"+"!!!";//编译器会将它优化为String str="hello world!!!"
开发原则:
1)字符串定义采用直接赋值法。
2)字符串内容的比较采用String类的equals方法。
3)字符串不应该改变太多,避免产生大量垃圾。
6.1 与字符相关的一些方法
public String(char[] value);//将字符数组中的所有内容变为字符串
public String(char[] value,int offset,int count);//将部分字符数组中的内容变为字符串,从下标为offset处开始,取长度为count的字符。
public char charAt(int index);//取得指定索引处得字符,索引从0开始。
public char[] toCharArray();//将字符串变为普通数组返回
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
char[] ch=new char[]{'J','a','v','a',' ','S','E'};
String str=new String(ch);
String str2=new String(ch,1,5);
System.out.println(str);//Java SE
System.out.println(str2);//ava S
char c=str.charAt(5);
System.out.println(c);//S
String str3="hello";
char[] chars=str3.toCharArray();
for(int i=0;i
6.2 与字节有关的方法
字节常用于数据传输以及编码转换的处理中,在String类中提供有字节的支持。
字节并不适合处理中文,只有字符适合处理中文。一个字符等于两个字节。字节只适合处理二进制数据。
public String(byte[] bytes);//将字节数组变为字符串
public String(byte[] bytes,int offset,int length);//将部分字节数组中的内容变为字符串
public byte[] getBytes();//将字符串以字节数组的形式返回
public byte[] getBytes(String charsetName);//编码转换处理
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
String str="helloworld";
byte[] data=str.getBytes();
for(int i=0;i
6.3 字符串比较
public boolean equals(Object anObject);//区分大小写的比较
public boolean equalsIgnoreCase(String anotherString);//不区分大小写的比较
public int compareTo(String anotherString);//比较两个字符串的大小关系 相等,返回0;小于,返回内容小于0;大于,返回内容大于0.
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
String str1="hello";
String str2="HELLO";
System.out.println(str1.equals(str2));
System.out.println(str1.equalsIgnoreCase(str2));
System.out.println(str1.compareTo(str2));
}
}
6.4 字符串查找
public boolean contains(CharSequence s);//判断一个子字符串是否存在
public int indexOf(String str);//从头开始查找指定字符串的位置,查到了返回位置的开始索引,没有找到返回-1.
public int indexOf(String str,int fromIndex);//从指定位置开始查找子字符串位置,查不到返回-1.
public int lastIndexOf(String str);//由后向前查找子字符串位置
public int lastIndexOf(String str,int fromIndex);//从指定位置由后向前开始查找子字符串位置。
public boolean startsWith(String prefix);//判断是否以指定字符串开头
public boolean startsWith(String prefix,int toffset);//从指定位置开始判断是否以指定字符串开头
public boolean endsWith(String suffix);//判断是否以指定字符串结尾
查找字符串,最好用最方便的是contains(String str)方法。该方法是在JDK1.5以后才有的。
使用indexOf,如果内容重复,则只返回查找的第一个位置。
6.5 字符串替换
public String replaceAll(String regex,String replacement);//替换所有的指定内容
public String replaceFirst(String regex,String replacement);//替换首个指定内容
6.6 字符串拆分
public String[] split(String regex);//将字符串拆分
public String[] split(String regex,int limit);//将字符串部分拆分,该数组长度就是limit极限。
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
String str="Java SE is best";
String[] strings=str.split(" ");
for(String s:strings){
System.out.println(s);
}
System.out.println("================================");
String[] res=str.split(" ",2);
for(String s:res){
System.out.println(s);
}
}
}
6.7 字符串截取
public String substring(int beginIndex);//从指定索引截取到结尾
public String substring(int beginIndex,int endIndex);//截取部分内容,不包括endIndex处的字符
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
String str="Java SE is best";
String str1=str.substring(11);
System.out.println(str1);
String str2=str.substring(0,7);
System.out.println(str2);
}
}
注意:substring(int beginIndex,int endIndex);截取的字符串不包括endIndex处的字符。
6.8 字符串其它操作方法
public String trim();//去掉字符串中的左右空格,保留中间空格。
public String toUpperCase();//字符串转大写
public String toLowerCase();//字符串转小写
public native String intern();//构造法的字符串入池操作
public String concat(String str);//字符串连接,等同于"+",不入池。
public int length();//取得字符串长度
public boolean isEmpty();//判断是否为空字符串,但不是null,而是长度为0.
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
String str=" Java SE is best ";
System.out.println(str);
System.out.println(str.trim());
System.out.println(str.toUpperCase());
System.out.println(str.toLowerCase());
System.out.println(str.concat("yes"));
System.out.println(str.length());
}
}
字符串常量不能修改,为了方便字符串的修改,提供StringBuffer类。在String类中使用"+"进行字符串连接,但是在StringBuffer类中使用append(str)函数来实现。
a.StringBuffer()//建立一个空的字符缓冲区,初始容量为16个字符。
b.StringBuffer(int length)//指定字符长度为length,建立空的字符缓冲区。
c.StringBuffer(String str)//缓冲区的初始内容为str,并另外提供16个字符的初始容量。
说明:即使两个StringBuffer类的对象,内容相同,但是用equals()函数,比较其内容,结果仍然为false.
String类型转为StringBuffer类:StringBuffer的构造方法或者append()。
StringBuffer类型转为String类型:调用toStirng()。
import java.util.Scanner;
public class TestString {
public static void main(String[] args) {
StringBuffer s1=new StringBuffer();
s1.append("hello world");
StringBuffer s2=new StringBuffer();
System.out.println(s2.append("hello world").toString());
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//false
String str="hello";
System.out.println(str.toString());
}
}
3.1 与String类用法相同的方法
(1)length()//返回字符串的字符个数。
(2)toString()//返回缓冲区中的字符串。(两个类函数的实现方法不同,具体见2.3.2)
(3)charAt(int index)//返回字符串中下标是index的字符。
(4)getChars(int srcBegin,int secEnd,char[] dst,int dstBegin)//将字符串下标为[srcBegin,srcEnd)的字符串赋值给dst从下标为
dstBegin开始的一组字符。
(5)substring(int beginIndex,int endIndex)//返回原字符串的一个子串[beginIndex,endIndex)
3.2 StringBuffer类独有的方法
1)setCharAt(int index,char ch)//在下标为index的位置上添加字符ch,相当替换该位置上的原有字符。
public class Astring{
public static void main(String[] args){
StringBuffer s1=new StringBuffer();
s1.append("world");
System.out.println(s1.charAt(3));//l
s1.setCharAt(3,'h');
System.out.println(s1);//worhd
}
}
2)append()//向缓冲区中添加新的字符串。
public class Astring{
public static void main(String[] args){
StringBuffer s1=new StringBuffer();
s1.append("hello");
System.out.println(s1);//hello
s1.append(" world");
System.out.println(s1);//hello world
}
}
3)insert(int Offset,String str);//在字符串中的offset位置插入字符串str.(不替换)
public class Astring{
public static void main(String[] args){
StringBuffer s1=new StringBuffer();
s1.append("hello");
System.out.println(s1);//hello
s1.append(" world");
System.out.println(s1);//hello world
s1.insert(5,"!!!");
System.out.println(s1);//hello!!! world
}
}
4)reverse()//字符串反转函数。
public class Astring{
public static void main(String[] args){
StringBuffer s1=new StringBuffer();
s1.append("hello");
System.out.println(s1);//hello
System.out.println(s1.reverse());//olleh
}
}
5)delete(int start,int end)//删除指定范围的数据 [start,end)
public class Astring{
public static void main(String[] args){
StringBuffer s1=new StringBuffer();
s1.append("hello");
System.out.println(s1);//hello
System.out.println(s1.delete(1,4));//ho
}
}
相同点:
(1)String类与StringBuffer类都用来处理字符串。
(2)部分方法在二者中的用法是相同的。eg:length()、toString()、charAt()、substring().
(3)在这两个类中,字符的索引都是从0开始的。
(4)这两个类都是CharSequence的子类。CharSequence接口描述的是一系列字符集。
不同点:
(1)String类是不可变类,因为在底端是用字符数组实现的,而数组长度固定,一旦赋值,就不能在原来的字符串上进行修改可能会浪费空间。StringBuffer类是可变类,能够在原来的数组上进行修改。
如:String类中的substring()、concat()、toLowerCase()、toUpperCase()和trim()等方法都不会改变字符串本身,
而是直接创建并返回一个包含改变后内容的新字符串对象。而StringBuffer的apend()、replaceAll()、insert()、setCharAt()等
方法都会改变字符串缓冲区中的字符串内容。
(2)String类覆盖了Object类的equals()方法,而StringBuffer类没有覆盖该方法。
(3)虽然两个类都覆盖了toString()方法,但各自的实现方式不同。String类的toString()方法返回当前String对象的引用。而StringBuffer类的toString()方法返回一个以当前StringBuffer的缓冲区的所有字符为内容的新的String对象的引用。
(4)String类对象可以用操作符"+"进行连接,而StringBuffer类的对象不能,StringBuffer类的对象可以通过append()方法添加新的字符串。
public class Astring{
public static void main(String[] args){
StringBuffer s1=new StringBuffer();
s1.append("fjasgnroehoergklfjij");
StringBuffer s2=new StringBuffer();
System.out.println(s2.append("fjasgnroehoergklfjij").toString());
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//false
String str="hello";
System.out.println(str.toString());
}
}
(1)String的内容不可修改,而StringBuffer和StringBuilder的内容可以修改。
(2)StringBuffer采用同步处理,属于线程安全操作,而StringBuilder采用异步处理,属于线程不安全操作。