• 《Java程序猿面试宝典》之字符串


    前不久刚看完这一章,然而这遗忘速度实在是不能忍,既然总是遗忘,那么老衲就和你磨上一磨。

    1.字符串基础

    先说字符串吧,看例1:

    1 String a = "abc";
    2 String b = "abc";
    3 a==b; //true
    4 a.equals(b) //true

    再来看看例2:

    String a = new String("abc");
    String b = new String("abc");
    a==b; //false
    a.equals(b); //true

    在例1中,"abc"是放在常量池(Const pool)中的,所以虽然a,b都等于"abc",但是内存中只有一份副本,所以"==" 返回true。"abc"是编译期常量,编译时已经能够确定它的值,在编译好的class文件中,它已经在String Pool中了, String a = "abc"; 会在String Pool中查找等于"abc"的字符串(用equals),若存在就把引用返回,若不存在就会创建一个"abc"放在String Pool中,然后把引用返回。

    在例2中,new方法决定了两个不同的String "abc"被创建放在了内存heap区,分别被a,b指向,因此,"==" 返回了false;这里要注意的一点是:Const Pool存储在Method Area中,而不是堆中。所以,我们可以看看例3:

    1 String s1 = new String("aaa777");
    2 String s2 = "aaa777";
    3 System.out.println(s1==s2);//结果为false

    在Java中,使用new关键字会创建一个新对象,在本例中,不管在String Pool中是否已经有值相同的对象,都会创建一个新的String对象存储在heap中,然后返回其引用。s2指向的对象存储在String Pool中,他们肯定不是同一个对象,只不过存储的字符串相同罢了,所以返回的结果必然是false。

    下面我们再来延伸一下:

    String s = "a" + "b" + "c" + "d" + "e";

    这里一共创建了几个对象?A.没有创建 B.1个对象 C.2个对象 D.3个对象

    答案是:B   要注意的是,赋值语句后面部分的"a"、"b"、"c"、"d"、"e"都是常量,对于常量,编译时就直接存储他们的字面值,而不是它们的引用,在编译时就直接将它们连接的结果提取出来变成了"abc"。关于这个String类重载的连接符,后面我们还会讲到,这里先点到为止。再说一点,String使用private final char value[]来实现字符串的存储,也就是说String对象创建之后,就不能再修改此对象中存储的字符串内容,就是因为如此,才说String类型是不可变的(immutable)。

    再看例4:

    1 String s1 = new String("aaa777");
    2 s1 = s1.intern();
    3 String s2 = "aaa777";
    4 System.out.println(s1==s2);

    你猜一猜这个最终的执行结果会是什么?是true!没错,是true!

    当调用intern方法时,如果String Pool中已经包含一个等于此String对象的字符串(用equals确定),则返回池中的字符串,否则将此String对象添加到池中,并返回此String对象在String Pool中的引用。由于执行了 s1 = s1.intern(); ,会使s1指向String Pool中值为"aaa777"的字符串对象,s2也指向了同样的对象,所以结果为true.

    例5:

    1 String s1 = new String("777");
    2 String s2 = "aaa777";
    3 String s3 = "aaa"+"777";
    4 String s4 = "aaa" + s1;
    5 System.out.println(s2==s3); //true
    6 System.out.println(s2==s4); //false
    7 System.out.println(s2==s4.intern()); //true

    显然,行5和行7结果就不需要我来讲了,问题在行6。为什么行6的结果是false呢?由于s1是变量,在编译期不能确定它的值是多少,所以会在执行的时候创建一个新的String对象存储到heap中,然后赋值给s4!注意,是heap中,而s2在Const Pool中,显然s2和s4的引用值自然是不相等的。

    好了,字符串基础快讲完了,有点长,最后我们再看一个函数结束吧~

    1 String str = "ABCDEFGH";
    2 String str1 = str.substring(3,5);
    3 System.out.println(str1);

    猜猜结果是多少?"DEF"吗?差点对=。=  这里要注意啊,java中的substring是前包括后不包括的,所以应该是"DE"。

    2.StringBuffer

     例1:

    1 String result = "hello" + "world";
    2 StringBuffer result = new StringBuffer.append("hello").append("world");

    行1的效率好于行2,这是因为JVM会进行如下处理:

    • 将result字符串进行"hello"+"world"处理,然后才赋值给result,只开辟了一次内存段。
    • 编译StringBuffer后还要进行append处理,开辟了一次内存,扩展了2次,花的时间要长一些。

    例2:

    1 public String getString(String s1,String s2){
    2     return s1+s2;
    3 }
    4 
    5 public String getString(String s1,String s2){
    6      return new StringBuffer().append(s1).append(s2);
    7 }

    这两个的效率是一样的,都是先开辟一个内存段,再合并(扩展)内存,所以两者执行的过程是一致,效率相当。

    例3:

    (1)String s = "s1";
               s+="s2";
               s+="s3";
    (2) StringBuffer s = new StringBuffer().append("s1").append("s2").append("s3");

    (2)的效率好于(1),因为String是不可变对象,每次"+="操作都会构造新的String对象,实际上是另外创建了一个对象,而原来指向的那个对象就成了垃圾,比如如下代码:

    1 String tmp = "";
    2 for(int i =0;i<9999;tmp += "x"){}

    一个循环就产生了n个对象,从而造成内存和时间的浪费。

    例4:

    1 (1)StringBuffer s = new StringBuffer();
    2     for(int i=0;i<50000;i++){
    3          s.append("hello");
    4       }
    5 (2)StringBuffer s = new StringBuffer(250000);
    6      for(int i=0;i<50000;i++){
    7           s.append("hello");
    8        }

    (2)的效率好于(1),因为StringBuffer内部实现的是char数组,默认初始化长度为16,每当字符串长度大于char数组长度的时候,JVM会构造更大的新数组,并将原先的数组复制到新数组,(2)避免了数组复制的开销。

    最后再看一个例子:

    例5,以下程序创建了几个对象?A. 4 B. 3 C. 5 D. 6

    1 String A,B,C;
    2 A = "a";
    3 B = "b";
    4 A = A + B;
    5 StringBuffer D = new StringBuffer("abc");
    6 D = D.append("567");

    首先,我们先搞清几个概念:

    • 引用变量与对象。A aa; 语句声明一个类A的引用变量aa(常称为句柄),而对象一般通过new创建。所以题目中D仅仅是一个引用变量,他不是对象。而字符串"abc"是一个String对象
    • Java中所有的字符串文字(字符串常量)都是一个String的对象。有人在一些场合喜欢把字符串当做字符数组,因为字符串与字符数组存在一些内在的联系。事实上,它与字符数组是两种完全不同的对象。如System.out.println("Hello".length()); 这里length()显然是对象的方法,而char[] cc = {'H','i'};System.out.println(cc.length); 这里的cc.length则是数组的属性,要注意区分。

    字符串对象的创建。由于字符串对象的大量的使用,Java中为了节省内存空间和运行时间,在编译阶段就把所有的字符串文字放到一个文字池(pool of literal strings)中,而运行时文字池成为常量池的一部分。文字池的好处就是该池中所有相同的字符串常量被合并,只占用一个空间。我们来看一段代码:

    1 String s1 = new String("abc");
    2 String s2 = new String("abc");

    String s1 = new String("abc");语句,这里"abc"本身就是pool中的一个对象,而在运行时执行new String()时,将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有。这条语句就创建了2个String对象。于是,上面的两行代码创建了3个对象,pool中一个,heap中2个!

    OK,我们现在来对例5进行一下解析。

     StringBuffer D = new StringBuffer("abc"); 产生了两个对象,"abc"本身与经过new创建出来的不是一个对象。 A=A+B; 此处创建了一个对象,并由引用A来引用,那么原来A所指向的对象就成为了垃圾,被回收。StringBuffer的特点是改变对象本身而不是创建新的对象,因此,此处 D= D.append("567"); 都是对同一个对象进行处理。所以整个例5一共创建了1+1+1+2=5个对象,答案是C

    3.正则表达式

    例1: String s = "32fdsfd8fds0fdsf9323k32k" ,从中找出3280932332,你会怎么做?

    一般做字符串替换,我们能想到的一般方法都是正则表达式。所以可以这样: String a = s.replaceAll("[^0-9]",""); ,"[^0-9]"是正则表达式,表示除了0到9以外的字符,这条代码的意思是将s中所有非0-9的字符替换为空串。

    例2: String str = "2006-04-15 02:31:04" ,要把这个串变成20060415023104,你会怎么做?

    首先,这个简单的任务可以用最笨的方法:

     str = str.replaceAll("-","");str = str.replaceAll(":","");str = str.replaceAll(" ",""); ,不过这有点太low逼了,看下面的方法:

     1 class Test{
     2     public static void main(String[] args){
     3         String  str = "2006-04-15 02:31:04";
     4         String str2 = "";
     5         String[] result = str.split("\D");
     6         for(int i=0;i<result;i++){
     7             System.out.print(result[i]);
     8             str2 += result[i];
     9         }
    10         System.out.println(str2);
    11     }
    12 }

    这里"\D"表示非数字字符。

  • 相关阅读:
    Python学习笔记第二十三周(Flask架构)
    Python学习笔记第二十二周(前端知识点补充)
    Python学习笔记第二十一周
    Python学习笔记第二十周
    Python学习笔记第十九周
    Python学习笔记第十八周
    Python学习笔记第十七周
    Python学习笔记第十六周
    python完成九九乘法表
    python
  • 原文地址:https://www.cnblogs.com/xuanxufeng/p/6701993.html
Copyright © 2020-2023  润新知