String和object

一、String是不可变对象
java.lang.String类使用了final修饰,不能被继承。Java程序中的所有字面值,即双引号括起的字符串,如"abc",都是作为String类的实例实现的。String是常量,其对象一旦构造就不能再被改变。换句话说,String对象是不可变的,每一个看起来会修改String值的方法,实际上都是创造了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。String对象具有只读特性,指向它的任何引用都不可能改变它的值,因此,也不会对其他的引用有什么影响。但是字符串引用可以重新赋值。java字符串在内存中采用unicode编码方式,任何一个字符对应两个字节的定长编码,即任何一个字符(无论中文还是英文)都算一个字符长度,占用两个字节。

例1:

public class Immutable {
public static String upcase(String s) {
return s.toUpperCase();
}
public static void main(String[ ] args) {
String str1= “Hello World”;
System.out.println(str1); //Hello World
String str2 = upcase(str1);
System.out.println(str2); //HELLO WORLD
System.out.println(str1); //Hello World
}
}

当把str1传递给upcase()方法时,实际传递的是引用的一个拷贝。其实,每当把String对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过。

二、String常量池
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。Java为了提高性能,静态字符串(字面量/常量/常量连接的结果)在常量池中创建,并尽量使用同一个对象,重用静态字符串。对于重复出现的字符串直接量,JVM会首先在常量池中查找,如果常量池中存在即返回该对象。

例2:

public class test1 {
public static void main(String[] args){
String str1 = “Hello”;
//不会创建新的String对象,而是使用常量池中已有的"Hello",
String str2 = “Hello”;
System.out.println(str1 == str2); //true
//使用new关键字会创建新的String对象
String str3 = new String(“Hello”);
System.out.println(str1 == str3); //false
}
}

三、String、StringBuffer和StringBuilder的区别
1.对象的可变与不可变

String类中使用字符数组来保存数据,因为有“final”修饰符,所以string对象是不可变的。如下:

private final char value[];

StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存数据,这两种对象都是可变的。如下:

char[] value;

2.是否是线程安全

String中的对象是不可变的,也就可以理解为常量,所以是线程安全。

AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。

StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。看如下源码:

1 public synchronized StringBuffer reverse() {
2 super.reverse();
3 return this;
4 }
5
6 public int indexOf(String str) {
7 return indexOf(str, 0); //存在 public synchronized int indexOf(String str, int fromIndex) 方法
8 }

StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

3.StringBuilder与StringBuffer共同点

StringBuilder与StringBuffer有公共的抽象父类AbstractStringBuilder。

抽象类与接口的一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。

StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(…)。只是StringBuffer会在方法上加synchronized关键字,进行同步。

如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。

四、创建了几个对象的问题
1 String str1 = “abc”;
2 String str2 = new String(“abc”);
对于1中的 String str1 = “abc”,首先会检查字符串常量池中是否含有字符串abc,如果有则直接指向,如果没有则在字符串常量池中添加abc字符串并指向它.所以这种方法最多创建一个对象,有可能不创建对象。

  对于2中的String str2 = new String("abc"),首先会在堆内存中申请一块内存存储字符串abc,str2指向其内存块对象。同时还会检查字符串常量池中是否含有abc字符串,若没有则添加abc到字符串常量池中。所以 new String()可能会创建两个对象。

  所以如果以上两行代码在同一个程序中,则1中创建了1个对象,2中创建了1个对象。如果将这两行代码的顺序调换一下,则1 String str2 = new String("abc")创建了两个对象,而2 String str1 = "abc"没有创建对象。

看看下面的代码创建了多少个对象:

1 String temp=“apple”;
2 for(int i=0;i<1000;i++) {
3 temp=temp+i;
4 }
答案:1001个对象。

下面的代码创建了多少个对象:

1 String temp=new String(“apple”)
2 for(int i=0;i<1000;i++) {
3 temp=temp+i;
4 }
答案:1002个对象。

五、String的匹配相等问题
使用String类经常需要对两个字符串进行对比,看是否相等。有==和equals两种选择,这两者方法区别很大:

         比对象的内容是否相等使用equals(),比较两个引用是否指向同一个对象时用==;equals()是看内容是否相等,比较好理解。而==是看是否属于同一个对象。

首先还要明白这个概念:常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。主要看编译期字符串能否确定。
看看下面几个例子:
test1:
1 String ok=“ok”;
2 String ok1=new String(“ok”);
3 System.out.println(ok==ok1);//fasle
ok指向字符串常量池,ok1指向new出来的堆内存块,new的字符串在编译期是无法确定的。所以输出false。

test2:

1 String ok=“apple1”;
2 String ok1=“apple”+1;
3 System.out.println(ok==ok1);//true
编译期ok和ok1都是确定的,字符串都为apple1,所以ok和ok1都指向字符串常量池里的字符串apple1。指向同一个对象,所以为true。

test3:

1 String ok=“apple1”;
2 int temp=1;
3 String ok1=“apple”+temp;
4 System.out.println(ok==ok1);//false
主要看ok和ok1能否在编译期确定,ok是确定的,放进并指向常量池,而ok1含有变量导致不确定,所以不是同一个对象.输出false。

test4:

1 String ok=“apple1”;
2 final int temp=1;
3 String ok1=“apple”+temp;
4 System.out.println(ok==ok1);//true
ok确定,加上final后使得ok1也在编译期能确定,所以输出true。

test5:

1 public static void main(String[] args) {
2 String ok=“apple1”;
3 final int temp=getTemp();
4 String ok1=“apple”+temp;
5 System.out.println(ok==ok1);//false
6 }
7
8 public static int getTemp(){
9 return 1;
10 }

ok一样是确定的。而ok1不能确定,需要运行代码获得temp,所以不是同一个对象,输出false。

String的intern()方法

当调用 intern()方法时,如果常量池中已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回常量池中的字符串;否则,将此 String 对象添加到常量池中,并且返回此 String 对象的引用。

看以下几个例子:

test6:
public class test6 {

private static String a = “ab”;
public static void main(String[] args){
String s1 = “a”;
String s2 = “b”;
String s = s1 + s2;//+的用法
System.out.println(s == a); //flase
System.out.println(s.intern() == a);// true (intern的含义) }

}

test7:

1 public class test6 {
2 private static String a = new String(“ab”);
3 public static void main(String[] args){
4 String s1 = “a”;
5 String s2 = “b”;
6 String s = s1 + s2;
7 System.out.println(s == a); //flase
8 System.out.println(s.intern() == a);//false
9 System.out.println(s.intern() == a.intern()); //true
10 }
11 }

Java语言Object类的总结
一、Object类

1、Object类是所有类的父类,位于java. lang包中,是所有类的根。任何的对象,都可以调用Object类中的方法,包括数组对象。

2、Object和Object[]之间的区别

-方法中的形参是Oject类型时,任何类型的参数都可以传进去执行。

-方法中形参是Oject[]类型时,只有对象数组可以传入执行。

例:public static void arrayTest(Object[] obj){

}

public static void main(){

int [] array = new int [4];

arrayTest(array) //出现错误

}

3、Object类的常用方法

–toString()

格式: public String toString(){

String strName =“所覆写的属性名(字符串)”

return String;

}

通常,toString方法会返回一个"以文本方式表示"次对象的字符串.结果是一个简单易懂的信息表达式.

建议所有子类都重写此方法.

–equals()

把equals写入父类中可以实现多个子类的比较

格式: public boolean equals(Object obj){

String str = this.所定义的属性名 //所有本类中其他的属性属性间用+链接

if(n1 == n2){return true}

else {return false}

}

Object类的equals方法实现对象上差别可能性最大的相等关系;即,对任何非空引用值x和y,当且仅当x和y引用同一个对象时,此方法才返回true(x==y具有值true).

参数: obj -要与之比较的引用对象

返回:如果此对象与obj参数相同,则返回true; 否则返回false.

任何类可以根据实际需要,覆盖toString及equals方法,实现自定义的逻辑.

–hashCode()

获取对象的哈希码值,为16进制

–equals方法与hashCode方法关系

如果两个对象使用equals比较返回true,那么它们的hashCode值一定要相同;

如果两个对象equals比较返回false,那么它们的hashCode值不一定不同;

覆盖equals,往往需要覆盖hashCode,可以使用Eclipse自动生成,保证equals返回true,则hashCode相同;equals返回false,则hashCode不同;

你可能感兴趣的:(String和object)