Java中String类学习总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中String类学习总结相关的知识,希望对你有一定的参考价值。

java中String类的使用频率非常高,本人在学习此模块时,认为下列几点知识值得注意:

一、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中String类学习总结的主要内容,如果未能解决你的问题,请参考以下文章

Java中String类学习总结

学号:201521123116 《java程序设计》第八周学习总结

java中封装,继承,多态,接口学习总结

201521123106《java程序设计》第二周学习总结

201621123062《java程序设计》第九周作业总结

Java学习总结——面向对象(下)(接口,抽象类,内部类,String类等)