String是一个对象,不是一个基本数据类型,默认值是null(因为对象的默认值是null)
String实际上是用字符数组存储的,这一点可以在源码中体现
1. String 创建方式
String ok1 = new String("ok");
String ok2 = "ok"
第一种方式String ok1=new String(“ok”)
:首先会在堆内存申请一块内存存储字符串ok,ok1指向其内存块对象。同时还会检查字符串常量池中是否含有ok字符串,若没有则添加ok到字符串常量池中。所以 new String()可能会创建两个对象.
第二种方式`String ok2=“ok”: 先检查字符串常量池中是否含有ok字符串,如果有则直接指向, 没有则在字符串常量池添加ok字符串并指向它.所以这种方法最多创建一个对象,有可能不创建对象
2. 匹配方式
常量池是在java用于保存在编译时已经确定的,已经编译的class文件的一份数据
字符串对比,看是否相等,有两种方式:==和equals
比较类中的数值是否相等使用equals(),比较两个包装类的引用是否指向同一个对象时用==
第一种方式:==
1)java代码: String ok = "ok"; String ok1 = new String("ok"); ok == ok1 (false)
原因:ok指向的是常量池中的对象,ok1指向的是堆内存中的对象,而且new的字符串在编译器是无法确定的
2)java代码: String ok = "apple1"; String ok1 = "apple" + 1; ok == ok1 (true)
原因:编译期ok1和ok都是确定的,ok和ok1都指向的是常量池里面的字符串apple1
3)java代码: String ok = "apple1"; int temp = 1;String ok1 = "apple" + temp;ok==ok1(false)
原因:编译期间ok1不是确定的(ok1含有变量),ok是确定的(指向常量池中的对象),所以不是指向同一个对象
4) java代码: String ok = "apple1"; final int temp = 1; String ok1 = "apple"+temp; ok == ok1 (true)
原因:ok1是确定的了,因为temp能在编译器确定
5) java代码:
public static void main(String[] args) {
String ok="apple1";
final int temp=getTemp();
String ok1="apple"+temp;
System.out.println(ok==ok1);//false
}
public static int getTemp()
{
return 1;
}
原因:ok1编译时无法确定,需要运行代码获得temp
3. 扩展常量池方式:intern()
intern()是扩充常量池的一个方法,当一个String实例str调用intern()方法时,java会检查常量池中是否有相同的字符串,如果有则返回其引用,如果没有则在常量池中增加一个str字符串并返回它的引用。
String ok="apple";
String ok1=new String("apple");
System.out.println(ok==ok1);//false
ok1=ok.intern();//获取常量池ok的引用
System.out.println(ok==ok1);//true--指向同一个对象
4. 常量池
字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
java代码:
String s1 = "china";
String s2 = "china";
String s3 = "china";
String ss1 = new String("china");
String ss2 = new String("china");
String ss3 = new String("china");
对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。
int i1 = 9;
int i2 = 9;
int i3 = 9;
public static final int INT1 = 9;
public static final int INT2 = 9;
public static final int INT3 = 9;
对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。
形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。
class BirthDate {
private int day;
private int month;
private int year;
public BirthDate(int d, int m, int y) {
day = d;
month = m;
year = y;
}
省略get,set方法………
}
public class Test{
public static void main(String args[]){
int date = 9;
Test test = new Test();
test.change(date);
BirthDate d1= new BirthDate(7,7,1970);
}
public void change1(int i){
i = 1234;
}
对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:
(1). main方法开始执行:int date = 9;
date局部变量,基础类型,引用和值都存在栈中。
2). Test test = new Test();
test为对象引用,存在栈中,对象(new Test())存在堆中。
3). test.change(date);
i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。
4). BirthDate d1= new BirthDate(7,7,1970);
d1为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。
5).main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。
6. String 的常用方法
String类具有immutable(不可改变)性质,当String变量需要经常变化的时候,会产生很多变量值,应该考虑使用StringBuffer提高效率,在开发的时候,注意String的创建方法。
7. == 与 equals
String 是个对象,要对比两个不同的String对象的值是否相同明显的要用到 equals() 这个方法可是如果程序里面有那么多的String对象,有那么多次的要用到 equals ,哦,天哪,真慢啊更好的办法:把所有的String都intern()到缓冲池去吧最好在用到new的时候就进行这个操作String s2 = new String(“Monday”).intern();嗯,大家都在水池里泡着了吗?哈哈现在我可以无所顾忌的用 == 来比较 String 对象的值了真是爽啊,又快又方便!
例程1:
class Str {
public static void main(String[] args) {
String s = "Hi!";
String t = "Hi!";
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
} //equals
例程2:
class Str {
public static void main(String[] args) {
String s = "HELLO";
String t = s.toUpperCase();
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
} //equals
class Str2 {
public static void main(String[] args) {
String s = "Hello";
String t = s.toUpperCase();
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
} // not equals
你了解 String 吗?解读 String 的 API ,可以看到:toUpperCase() 和 toLowerCase() 方法返回一个新的String对象,它将原字符串表示字符串的大写或小写形势;但是要注意:如果原字符串本身就是大写形式或小写形式,那么返回原始对象。
(1) charAt(int n) 返回字符串内n位置的字符,第一个字符位置为0,最后一个字符的位置为length()-1,访 问错误的位置会扔出一块大砖头:StringIndexOutOfBoundsException 真够大的
(2) concat(String str) 在原对象之后连接一个 str ,但是返回一个新的 String 对象
(3) EqualsIgnoreCase(String str) 忽略大小写的 equals 方法这个方法的实质是首先调用静态字符方法toUpperCase() 或者 toLowerCase() 将对比的两个字符转换,然后进行 == 运算
(4) trim() 返回一个新的对象,它将原对象的开头和结尾的空白字符切掉同样的,如果结果与原对象没有差别,则返回原对象
(5) toString() String 类也有 toString() 方法吗?真是一个有趣的问题,可是如果没有它,你的 String 对象说不定真的不能用在System.out.println() 里面啊小心,它返回对象自己String 类还有很多其他方法,掌握他们会带来很多方便也会有很多困惑,所以坚持原则,是最关键的。
目前String看到的就是这些了,之后会再更新的