从概念上讲,java字符串就是Unicode字符序列,java没有内置的字符串类型,而是在标准java类库中提供了一个预定义类,人们自然将它叫做String,用双引号括起来的字符串就是String的一个实例。
1、String的两种实例化方式
(1)String 变量=“字符”; String的直接赋值
eg: String str=“我的未来不是梦”;//直接赋值
System.out.println(str);
解释一下:String str=”我的未来不是梦”;
此时在内存中开辟了一块堆内存,并且由一块栈内存指向此堆内存,是一个指向对象的引用,字符串匿名对象给了一个名字,名为”str”,.可以指向类型为String的任何对象,我们并没有声明一个String对象,我们只是声明了一个只能指向String对象的引用变量,如果在上面的代码后面再加一个语句
String s=str;
我们声明了另外一个只能指向String对象的引用,名为s,内容为“我的未来不是梦”,并没有第二个对象的产生,s还是指向原来的那个对象,就是和str在内存中指向同一个对象。
(2)标准的实例化方式
String str=new String(“我的未来不是梦”);//构造方法实例化
System.out.println(str);、
当然了字符串的构造方法还有很多,大家可以查看文档。
2、字符串的相等判断
我们首先来看一个例子。
eg:
public class StringTest {
public static void main(String args[]){
String stra="hello";
String strb=new String("hello");
String strc=strb; //引用传递,内存地址的数值是相同的
System.out.println(stra==strb);//false
System.out.println(strc==strb);//true
System.out.println(stra==strc);//false
}
这里就要说到“”和“equal”的区别(面试题)
eg: int a=10;
int b=10;
ab是true
为什么System.out.println(strastrb);输出的是false,对象变量其实是一个引用,它们的值是指向对象的所在的内存地址,而不是对象本身,而当每个对象经行了new 操作之后,就在内存中分配了不同的内存空间,意味着在内存中产生两个不同地址的内容都为“hello”的字符串,stra和strb其实是两个不同的内存地址的数值,它们的内容都为“hello”,看起来是“相等”,但是“”操作符判断并不涉及对象内容的比较。
System.out.println(strc=strb);输出之所以是true,是因为经过引用传递语句 String strc=strb; 使它们指向了同一个内存地址。
还有一个比较字符串内容相等,用的是equel(注意equal区分大小写)
System.out.println(stra.equals(strb));//true
System.out.println(strc.equals(strb));//true
在使用中多数情况是使用equal比较字符串是否相等。
3.字符串匿名对象
String str="hello";
System.out.println("hello".equals(str));
字符串常量“hello”就是String的匿名对象,但是这个字符串不属于基本数据类型,它是将字符串作为String类的匿名对象形成
String str="hello";
System.out.println("hello".equal(str));
输出为true。我再举一个例子
String input=null;//我们假设这个是用户输入的数据
if(input.equals("hello")){
System.out.println("hello world");
}
将会出现java.lang.NullPointerException空指向异常,因为客户端输入的数据为空,为了避免这种错误,在以后将要判断输出的内容是否是某一字符串,我们将字符串写在最前面。如上的代码改为
eg:
if("hello".equals(input)){//输出为空
System.out.println("hello world");
}
这个例子的在控制台没有输出什么,但不会出现异常。
4.共享设计模式:在JVM的底层会存在一个对象池(不一定只保存String对象),当我们代码之中使用直接赋值的方式定义了一个String对象时,会将字符串对象所使用的匿名对象入池保存,而后如果还有其他String对象采用了直接赋值的方式并且设置了同样内容的时候,那么将不会开辟新的内存空间,而是直接使用池中的对象。
使用构造方法
(1)String(“hello”)匿名对象会先在内存中开辟一块空间空间存放”hello“
(2)new操作之后又会开辟一块新的内存空间,那就说明一共开辟了两块内存空间,随后指向的new
操作之后的”hello“,而另外一个开辟的空间就会成为垃圾。
(3)除了内存的浪费之外,如果使用了构造方法定义的String类对象,其内容不会保存在对象池中,因为是使用关键字new开辟了新内存,如果希望开辟新的内存数据也可以进行对象池的保存,那么可以采用String定义的一个手工入池的方法:public String intern(){}
这里就会形成一道面试题:试简述String对象两种实例化方式的区别
- (String str=”字符串”)直接赋值:只会开辟一块内存空间,并且会自动保存在对象池中,以备下次重复使用。
- (String str=new String(“字符串”):会开辟两块内存空间,有一块会成为垃圾,但是可以使用intern()方法手工入池。
手工入池方法介绍 String定义的一个手工入池的方法:public String intern(){}
public String intern()返回字符串对象的规范化表示形式。
一个初始为空的字符串池,它由类 String 私有地维护。
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作。
返回:一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池
String stra=new String("hello");
String strb="hello";
System.out.println(strb==stra); 输出为false
String stra=new String("hello").intern();//手工入池
String strb="hello";
System.out.println(strb==stra); 输出为true
5.字符串常量
以上的操作,所谓的字符串的操作内容实际上根本就没有改变(java定义好了String,而不能改变),对于字符串内容的改变,只是利用了引用关系的变化实现的,但是每一次的变化都会产生垃圾:
String str=””;
for(int i=0;i<100;i++){
str=str+i;
}
System.out.println(str);
此类代码在开发中避免使用,String的内容不要过多的修改,不然会产生大量的垃圾,
String类的总结:
- String类对象的相等使用equasl()方法完成,“==”完成的是地址数值的比较。
- 字符串的内容一旦声明便不可改变,String类对象内容的改变依靠的是引用关系的改变。
- String有两种实例化方式,使用直接的赋值不产生垃圾,并且自动改入池,一般不使用构造方法实例化。
7、字符串查找
由于方法较多我只仅仅写了方法的引用,当然了这些方法也可以自己实现只需 要将字符将一个一个输出,并判断即可,举一个小例子:
String str="wodeweilaibushimeng";
str=str.substring(0,str.lastIndexOf("w"));
System.out.println(str);//输出"word",substring(0,4)返回的是索引0-3(包含0,3)之间的新的字符串
public class TestDemo {
public static void main(String args[]){
String str="wodeweilaibushimeng";
char[] cs=str.toCharArray();//toCharArray()方法将此字符串转为一个新的字符数组
int count=0;
for(int i=0;i<str.length();i++){
if(cs[i]=='w'){
count++;
System.out.println(cs[i]+":字母"+"第"+count+"次出现的位置索引为"+i);
}else{
continue;
}
}
}
}
输出的结果:w:字母第1次出现的位置索引为0
w:字母第2次出现的位置索引为4
下面我将就几个方法举例
(1) contains查找指定的字符串
String str2="##@@wodeweilaibushimeng**";
if(str2.contains("wode")){//查找在原字符串中是否存在指定的字符串
System.out.println("查找到指定的字符串");
}
System.out.println(str2.startsWith("##"));//true
System.out.println(str2.endsWith("**"));//true
}
(2)(4)字符串的查找indexof()、 latIndexof(String str)
String str="we are the world";
int size=str.indexOf("a");
System.out.println(size);
int size1=str.lastIndexOf("");
System.out.println(size1+"输出字符的长度:"+str.length());
8、字符串的拆分
全部拆分
String str1="hello world nihao mldn";
String[] result=str1.split(" ");//空格拆分
for(int i=0;i<result.length;i++){
System.out.println(result[i]);
}
部分拆分
String str1="hello world nihao mldn";
String[] result=str1.split(" ",3);//空格拆分,拆分的模式最多经行2次
for(int i=0;i<result.length;i++){
System.out.println(result[i]);
}
实现IP地址的拆分
对于一些明敏感的字符(正则标记)是拆分不了的,就要进行转义。
String str1="219.244.0.18";
String[] result=str1.split("\\.");//空格拆分,拆分的模式最多经行2次
for(int i=0;i<result.length;i++){
System.out.println(result[i]);
}
输出的结果为:219 244 0 18
拆分在实际的应用中也很多比如: 张三:20|李四:12|王五:34 如果按”|“进行拆分,和上边的例子是一样的,对”|“转义即可,具体实现如下:
String str1="张三:20|李四:12|王五:34";
String[] result=str1.split("\\|");
for(int i=0;i<result.length;i++){
String[] temp=result[i].split(":");
System.out.println("姓名:"+temp[0]+","+"年龄"+temp[1]);
}
输出的结果为: 姓名:张三,年龄20
姓名:李四,年龄12
姓名:王五,年龄34
像这样拆分在开发中可能会很常见,原因是什么呢?因为我们常常会在程序中传入像上边的” 张三:20|李四:12|王五:34“没有经过处理的数据,需要我们处理。
总结
1、字符串两种创建的区别;
2、字符串的操作的常用方法;
3、对于“==”和“equals()”的区别;
4、String是不可变类,一旦创建就不能修改,只可改变引用的指向;