zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    项目开发管理SDEM
    google安装文件 和 缓存位置:
    数据库 查询超时 运行超时 问题
    将Access、Excel数据导出到MSSQL/使用 OpenRowSet 和 OpenDataSource 访问 Excel 97-2007
    Mybatis日志(七)
    Mybatis拦截器(六)
    Mybatis动态SQL(五)
    Mybatis的XML映射文件(四)
    Mybatis全局配置文件详解(三)
    Mybatis环境搭建(二)
  • 原文地址:https://www.cnblogs.com/baihehanqiu/p/6938850.html
Copyright © 2011-2022 走看看