
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
在一些Java编程过程中,对于一些小技巧我们还是应该去掌握的,这样在Java开发过程中字符串在任何语言中都非常重要,我们有必要掌握关于一切!Java中的字符串处理主要使用以下三个类来:String、StringBuilder、StringBuffer。
接下来就由我们佛山IT培训的小编带大家一起来看看下面的这些知识和技巧内容。
一、String
1、String简介
字符串String广泛应用在Java 编程中,在Java中字符串属于对象,Java提供了String类来创建和操作字符串。
初始化:由String声明的字符串,长度是不可变的,这是String与StringBuilder和StringBuffer最明显的一个区别。一般初始化方式:String string= "hi string";经过这条语句,JVM的栈内存中产生一个string变量,堆内存中产生hi string字符串对象。string指向了hi string的地址,产生的字符串属于直接量字符串对象,JVM在处理的时候会进行缓存,初始化时放入字符串池,再次使用的时候,无需重新新的字符串,而是直接指向已存在的字符串。如下代码:
public static void test1() {
String string1 = "hi string";
String string2 = "hi string";
System.out.println(string1 == string2);
}
该程序输出结果为true ,因为string1和string2都指向了hi string字符串,他们的内存地址是同一个。
前面我们说过,由String声明的字符串,长度是不可变的,就是说,当一个String对象创建后,该对象的内容就固定下来了,它是一个“不可变的字符串”,但是为什么下面程序的两个输出结果不一样呢?
public static void test2() {
String string = "i am a ";// a
System.out.println(System.identityHashCode(string));
string = string + "string";// b
System.out.println(System.identityHashCode(string));
}
程序输出:
14576877
12677476
string好像变了,这是为什么呢?其实string只是一个引用变量,当程序执行完a后,string指向“i am a ”。当程序执行完b之后,连接运算符会将两个字符串连在一起,并且让string指向新的串:"i am a string",所以,最初的对象确实没变,只是string所指向的对象在改变。
String对象的初始化还可以采用String类提供的构造方法进行。String类提供了16种构造方法,常用的有:
String(char[] value) –通过一个字符数组创建
String() -初始化一个空字符序列String对象
String(String value) –通过直接量创建一个新串
String(char[] value,int offset,int count) -,从offset开始count个字符,截取字符数组创建
String(StringBuffer buffer) -利用StringBuffer创建
如:
String string1 = new String(“string”);
String string2 = new String();
char[] char = {'s','t','r','i','n','j'};
String string4 = new String(char);
String string5 = new String(char,1,3);
StringBuffer stringBuffer=new StringBuffer("string");
String string6 =new String(stringBuffer);
以上就是String类的基本初始化方法。
2、常见问题
问题1、如果spilt()函数的参数在要分割的字符串中没有怎么办?如String s = "helloworld" ,我现在调用String[] s2 = s.spilt("abc"),返回什么?
这样的题目,如果不亲自遇到过,或者看过源代码,很难准确的写出来。
做一个简单的测试:
public static void test3() {
String string = "hi string good";
String[] sArray = string.split("ni");
for (int i = 0; i < sArray.length; i++) {
System.out.println(sArray[i] + " " + i);
}
}
输出结果:hi string good 0,说明当遇到源字符串中没有的字符时,会把整个串放入到数组中第一个元素。
问题2、String string = "abc" + "def";在内存中产生几个字符串对象?
这是个比较有争议的问题,面试时也经常被问,此处分析一下,因为Java字符串的缓存机制,编译器在编译的时候会进行优化,所以在编译的过程中abc和def被合成了一个字符串"abcdef ",因此,如果缓存池中目前没有abcdef这个对象,那么会产生一个,即"" abcdef ",且栈中产生一个引用string指向它,如果缓存池中已经存在" abcdef ",那么将产生0个对象,直接用string指向它。
问题3、关于字符串自动类型转换分析
如下代码输出什么?
public static void test3() {
int a = 1;
int b= 2;
String string = "4";
System.out.println(a+b+string);
System.out.println(a+string+b);
}
输出结果为:
34
142
首先a+b=3,然后3和4自然连接,这里涉及到java的自动类型转换,此处int型的直接转成String类型的。第二个依次连接,都转化为String类型的了。
问题4、常量池、字符串常量池、运行时常量池是什么?
常量池一般就是指字符串常量池,是用来做字符串缓存的一种机制,当我们在程序中写了形如String string = "123"这样的语句后,JVM会在栈上为我们分配空间,存放变量string和对象”123“,当我们再次需要123对象时,如果我们写下:String string1 = "123"的语句时,JVM会先去常量池中找,如果不存在,则新创建一个对象。如果存在,则直接将string1指向之前的对象”123“,此时,如果我们用==来判断的话,返回的true。这样做的好处就是节省内存,系统响应的速度加快,(因为省去了对象的创建时间)这也是缓存系统存在的原因。常量池是针对在编译期间就确定下来的常量而言的,如上所说的String类的一些对象。但是,当类被加载后,常量池会被搬到方法区的运行时常量池,此时就不再是静态的了,那么是不是就不能向常量池中添加新的内容了呢(因为我们刚刚说过,常量池是在编译期确定好的)?答案是否定的,我们依然可以在运行时向常量池添加内容!这就是我们说过的String类有个方法叫intern(),它可以在运行时将新的常量放于常量池。
3、String类的一些常用方法
String string=new String("nihaonihao");
String string1=new String("haohao");
获取字符串长度:int length= string.length(); 返回值类型int型;
字符串查找,查找i首次出现在字符串的位置:int i= string.indexOf("i") ; 返回值类型int型;
字符串查找,查找a最后一次出现的位置:int i= string.LastIndexOf("a");返回值类型int型,若LastIndexOf的参数为"",返回结果与length()相同;
获取指定索引位置的字符:char i= string.charAt(3);返回值类型char;
字符串截取:String i= string.substring(int beginIndex);beginIndex为开始截取的位置;
字符串截取:String i= string.substring(int beginIndex,int endIndex); beginIndex为开始截取的位置,endIndex为结束位的前一位(不包括结束位);
去掉前尾部空格:String i= string.trim();
字符串替换:String i= string.replace("ni","NI");
判断字符串是否以特定字符串开始:boolean i= string.startsWith("hel");返回值布尔型;
判断字符串是否以特定字符串结尾boolean i= string.endsWith("o");返回值布尔型;
判断字符是否相等:boolean i= string.equals(string1);返回值布尔型, equals()比较的是对象的内容,也就是JVM堆内存中的内容,==比较的是地址,也就是栈内存中的内容。看下面这段代码:
public static void test3() {
String string = "cd";
String string1 = "c";
String string2 = string1 + "d";
String string3 = "cd";
System.out.println(string1.hashCode());
System.out.println(string2.hashCode());
System.out.println(string3.hashCode());
System.out.println(string == string2);
System.out.println(string2 == string3);
System.out.println(string2.hashCode() == string3.hashCode());
String string4 = "ef";
String string5 = "e" + "f";
String string6 = "ef";
System.out.println(string4 == string5);
System.out.println(string4 == string6);
System.out.println(string4.hashCode());
System.out.println(string5.hashCode());
System.out.println(string6.hashCode());
}
输出结果为:
99
3169
3169
false
false
true
true
true
3233
3233
3233
此处主要是想说明:string1 + "d"和"c"+"d"的不同,从结果中我们可以看出,string == string2返回了false,并且他们的hashCode也不相同,说明他们指向的引用不是同一个,即string1 + "d"的过程中产生了新的对象,为什么会产生新的对象?而没有去常量池查找是否已经存在cd对象,以致于string == string2返回false。因为常量池是在编译期确定好的,所以如果我们的语句是string3 = "cd"的话,这个是在编译期确定的,会去常量池查找,而此处我们的语句是string2 = string1 + "d",string2的值只有在运行期才能确定,所以不会去常量池查找,也就是产生了新串。
那么这里string2的值是在哪儿分配的呢?堆、栈还是运行时常量池?是在堆上分配,因为+的内部实现是用StringBuilder来实现的。String string2 = string1 + "d"内部是这样实现的:String string2 = new StringBuilder(string1).append("d").toString();所以是在堆上来分配的。
既然string2 == string3返回fasle,那么为什么调用s2.hashCode() == s3.hashCode()返回true?我们说过,==比较的是他们的地址,string1 + "d"会产生一个新的串,所以和string和string 2用==比,返回false,如果用equals的话,肯定是true,因为equals()比较的是对象的内容(String类是这样的,因为它重新了Object的equals())。至于hashCode,是这样的:如果没有重写Object的hashCode(),那么如果对象调用equals()返回true,则这两个对象调用hashCode()后返回的整数一定相等。对于Object类而言,原生的equals()方法,必须两个对象的地址和内容都一样才返回true,同时Object类原生的hashCode()是参照对象的地址和内容根据一定的算法生产的。所以原生的hashCode()只有调用equals()返回true才相等。而String类不同,String类重写了Object的equals(),放松了条件,只要对象地址或者内容相等就返回true,同时,String类重写了hashCode()方法,只要内容相等,则调用hashCode返回的整数值也相等,所以此处:string3和string2虽然地址不等,但是内容相等,所以会有:string2.hashCode() == string3.hashCode()返回true。但这句话反过来讲就不一定成立了,因为毕竟hashCode()只是一种算法。
判断字符是否相等,判断时忽略字符串内大小写:equalsIgnoreCase();
字母大小写转换:str.toLowerCase; 大转小,str.toUpperCase; 小转大,数字和非字符不受影响,若没有需要转换的,则将原字符串返回。
分割字符串,得到一个String类型的数组:public String[] split(String regex),根据regex可知,参数是个正则表达式,当分隔符为.的话,处理起来不一样,必须写成\\.因为.是正则表达式里的一个特殊符号,必须进行转义;
public native String intern();intern()方法和前面说的equals()方法关系密切,从public native String intern()看出,它是Java的本地方法,返回字符串一个规范的表示,比如有两个字符串string1和string2,string1.equals(string2),则string1.intern()==string2.intern();如下示例:
public static void test3() {
String string = new String("hello");
String string1 = "hello";
String string2 = "hello";
String string3 = string.intern();
System.out.println(string == string1);// false
System.out.println(string1 == string2);// true
System.out.println(string == string3);// false
System.out.println(string1 == string3);// true
}
输出结果如注释所示,System.out.println(string1.intern() == string3);为什么为true,首先看看string3 = string.intern()这句,当调用string.intern()这句的时候,先去字符串常量池中找,是否有hello这个串,如果没有,则新增,同时返回引用,如果有,则返回已经存在的引用,此处string 1和string 2都指向常量池中的hello对象,所以此处是存在的,调用hello.intern()后,hello3和hello1、hello2指向同一个对象,所以string1==string3返回的是true。intern()做到了一个很不寻常的行为:在运行期动态的在方法区创建对象,一般只有像new关键字可以在运行期在堆上面创建对象,所以此处比较特殊。属于及时编译的概念。
注意:字符串索引与数组相同,从0开始。
对于想了解更多的IT技术和知识,那么欢迎来佛山IT培训机构进行更多的了解和咨询,或者想从事Java编程开发方面的工作,那么欢迎来佛山达内IT培训机构进行更多的了解和咨询。