Java面试复习之Java深入解析三

String类:

  1. String中的“+”号:“+”是用来拼接两个字符串的,底层实现时是调用了StringBuilder类的append方法进行拼接的。如下所示:
String str1="aaa";
String str2="bbb";
System.out.println(str1+str2);
//其中valueof方法保证用字符串来创建StringBuilder类,与str1+str2等价
System.out.println(new StringBuilder(String.valueOf(str1)).append(str2));

当“+”号两边时常量时,在编译的时候就计算出字符串的值。而不会在运行时创建临时的StringBuilder对象来完成字符串的连接。
如果在循环中对String对象进行连接,应使用StringBuilder来代替“+”,这样可以提高性能。

public static void main(String[] args) {
      new Thread(new Runnable() {
          @Override public void run() {
              String string1=null;
              long start;
              long end;
              start=System.nanoTime();
              for(int i=0;i<10000;i++)
              {
                  string1+='a';
              }
              end=System.nanoTime();
              System.out.println("+号所用的时间:"+(end-start));
          }
      }).start();
      new Thread(new Runnable() {
          @Override public void run() {
              String string2=null;
              long start;
              long end;
              start=System.nanoTime();
              StringBuilder stringBuilder=new StringBuilder(10000);
              for(int i=0;i<10000;i++)
              {
                  stringBuilder.append('a');
              }
              string2=stringBuilder.toString();
              end=System.nanoTime();
              System.out.println("append所用的时间是:"+(end-start));
          }
      }).start();
    }

运行结果如下:

append所用的时间是:454230
+号所用的时间:101082562

由运行结果可得:“+”号在循环中的使用会使性能大大降低。在循环中,应该用append方法来拼接以提高效率。

2.不可修改的String对象:String类是final类型的,因此无法继承。并且所有的成员变量都是私有的,而且没有提供修改私有成员的共有方法。因此对String对象的操作都没有修改当前对象,而是创建了新的对象。例如:toUpperCase方法、concat方法、substring方法、replace方法、spilt方法等。这样做的好处是可以实现资源共享 ,在多线程操作时,可以将其认为是不变的,而不用担心其他线程会对其进行修改。这样就没有必要使用线程同步了,简单方便。
3.String对象的内部是用一个字符数组来维护序列的。数组的长度可用byte、short、char、int来指定,因此数组最大长度为2147483647。但是在实际中所取得值比理论值要小。因为Java对象都是分配在堆上的,因此这个长度还和堆的大小有关系。可用java -Xmx1G chapter.Length2 来设置对的大小为1G。下面的代码来探索最大的长度。

 public static void main(String[] args) {
        int high=Integer.MAX_VALUE;
        int low=0;
        int mid=(high+low)/2;
        for(int i=0;i<32;i++)
        {
            try {
                char[] ch=new char[mid];
                //申请成功
                System.out.println("第"+i+"次申请成功,长度为:"+mid);
                low=mid+1;
                mid=(low+high)/2;
            } catch (OutOfMemoryError e) {
                System.out.println("第"+i+"次申请失败");
                high=mid-1;
                mid=(low+high)/2;
            }
        }
}        

运行结果最后一行:

第31次申请成功,长度为:347812940

4.equals方法和“==”的对比:equals方法是定义在Object方法中的,Object中的equals的内部就是使用"==”来进行比较的。String类重写了equals方法,其内部比较的时两个字符串的内容。StringBuilder和StringBuffer类没有重写equals方法。

String string1="abc";
String string2="abc";
StringBuilder stringBuilder1=new StringBuilder("Builder");
StringBuilder stringBuilder2=new StringBuilder("Builder");
StringBuffer stringBuffer1=new StringBuffer("Buffer");
StringBuffer stringBuffer2=new StringBuffer("Buffer");
System.out.println(string1.equals(string2));
System.out.println(stringBuilder1.equals(stringBuilder2));//没有重写,调用的时Object类中的方法
System.out.println(stringBuffer1.equals(stringBuffer2));

运行结果为:

true
false
false

重写equals的规则:

  • 自己和自己equals返回true;
  • x.equals(y)返回true时,y.equals(x)也返回true;
  • x.equals(y)返回true,y.equals(z)也返回true时,x.equals(z)也返回true;
  • x和y的值没有改变时,equals返回的值始终是true或始终是false;
  • x.equals(null)返回false。
    hashcode在添加键值对的时候,会根据key的hashcode计算下标。如果key是自定义的一个类。那么key类除了要重写equals方法外,还要重写hashCode方法。在map中相等的key是指equals返回true,hashCode相等。如果仅仅只是equals返回true,那么get(key1)和get(key2)所取到的值可能不一样。
  1. String常量池:因为String是不可改变的类,一经创建,不能修改。由于这个特性,对象可以自由的由多个线程所共享,不必担心会被修改。因此就没有必要去创建重复的String对象。常量池能自动将String字面常量加入其中。当程序中出现String字面常量的时候,会搜索常量池中是否存在该String对象(用String类中的equals方法来判断)。若不存在,将其加入常量池中,并返回该对象。如果存在,直接返回该对象。
String string1=new String("abc");
String string2=new String("abc");
String string3="abc";
String string4="abc";
System.out.println("string1==string2:"+(string1==string2));
System.out.println("string3==string4:"+(string3==string4));
System.out.println("string1==string3:"+(string1==string3));

运行结果:

tring1==string2:false
string3==string4:true
string1==string3:false

除了字面常量,在编译时期就能确定其值的String类型的表达式也可以放到常量池中。final修饰的变量可以当做常量。
6.intern方法:可以将string对象加入常量池中。常量或者常量表达式加入到常量池也是通过调用intern方法实现。

猜你喜欢

转载自blog.csdn.net/Hello_1024/article/details/83212918