不惑JAVA之JAVA基础 - String

 本文适合有一定java基础的同学。本博客宗旨:突出重点,分析难点。

String的本质

先看一下String源码

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
......
}    

String类名前面是final修复,变量char value[]也是final修饰。

可以得到三点:

  • String实质是字符数组;
  • 该类不可被继承;
  • 不可变性(immutable)。

常量池

Java中的常量池,实际上分为两种形态 : 静态常量池运行时常量池

  • 静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
  • 运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
public class StringTest {
    public static void main(String[] args) {
        String s = new String("abc");
        String s1 = "abc";
        String s2 = "abc";
        String s3 = s.intern();
        System.out.println(s == s1);//false
        System.out.println(s == s2);//false
        System.out.println(s == s3);//false
        System.out.println(s1 == s3);//true
    }
}

分析:

输出结果如注释所示,前两个结果前面已经说的很清楚了,现在拿最后一个说明,首先看看s3 = s.intern()这句,当调用s.intern()这句的时候,先去字符串常量池中找,是否有abc这个串,如果没有,则新增,同时返回引用,如果有,则返回已经存在的引用,此处s1和s2都指向常量池中的abc对象,所以此处是存在的,调用s.intern()后,s3和s1、s2指向同一个对象,所以s1==s3返回的是true。

intern()做到了一个很不寻常的行为:在运行期动态的在方法区创建对象,一般只有像new关键字可以在运行期在堆上面创建对象,所以此处比较特殊。属于及时编译的概念。

如对常量池感兴趣可以参看:《Java虚拟机原理图解》 2.2、常量池详解(上)

常用方法

equals 方法

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
        char v1[] = value;  //---------1---------
        char v2[] = anotherString.value;//-------2----------
        int i = offset;
        int j = anotherString.offset;
        while (n-- != 0) { // --------3------------
            if (v1[i++] != v2[j++])
            return false;
        }
        return true;
        }
    }
    return false;
    }

通过1和2获得两个字符串的字符数组,循环3进行对比。

后期文章会单独详解 重写equals和hashcode的

charAt

获取指定位置的字符.

    public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

substring

用于截取字符串

public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

来看一下 new String(value, beginIndex, subLen) 源码

 public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

有一个Arrays.copyOfRange(value, offset, offset+count); ,这个方法的大致意思是通过原字符数组拷贝一份新的字符串。

如果还需要了解那个方法的底层代码可以给我留言。

StringBuffer与StringBuilder的区别

StringBuffer和StringBuilder都继承了抽象类AbstractStringBuilder,这个抽象类和String一样也定义了char[] value和int count,但是与String类不同的是,它们没有final修饰符。因此得出结论:String、StringBuffer和StringBuilder在本质上都是字符数组,不同的是,在进行连接操作时,String每次返回一个新的String实例,而StringBuffer和StringBuilder的append方法直接返回this,所以这就是为什么在进行大量字符串连接运算时,不推荐使用String,而推荐StringBuffer和StringBuilder。

    public synchronized StringBuffer append(String str) {
        super.append(i);
        return this;
    }
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

由于StringBuffer方法上有synchronized所有是现成安全的。

继续看 super.append(i);源码

    public AbstractStringBuilder append(String str) {
        if (str == null) str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

已StringBuilder为例,它集成AbstractStringBuilder抽象类。ensureCapacityInternal(count + len);是检查添加前字符数组长度是否满足append后数组的长度。如满足不做任何处理,如不满足创建一个新的字符数组,长度是现有数组的2倍,并将现有数组拷贝到新创建的数组中。来看一下str.getChars(0, len, value, count);源码

    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

看到 System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); 是将新字符串拷贝到原有字符串中。

arraycopy 方法说明:

    public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
  • src:源数组;
  • srcPos:源数组要复制的起始位置;
  • dest:目的数组;
  • destPos:目的数组放置的起始位置; l
  • ength:复制的长度。

    注意:src and dest都必须是同类型或者可以进行转换类型的数组. 有趣的是这个函数可以实现自己到自己复制,比如: int[] fun ={0,1,2,3,4,5,6}; System.arraycopy(fun,0,fun,3,3); 则结果为:{0,1,2,0,1,2,6}; 实现过程是这样的,先生成一个长度为length的临时数组,将fun数组中srcPos 到srcPos+length-1之间的数据拷贝到临时数组中,再执行System.arraycopy(临时数组,0,fun,3,3)。

有意思的思考题

有时间补上。

时间: 2024-07-31 11:56:18

不惑JAVA之JAVA基础 - String的相关文章

不惑JAVA之JAVA基础 - NIO (一)

JAVA中最可以大书特书的我觉得至少有两个:一个是NIO,另外一个就是JVM了.这也就是为什么一直我没有去写这两个知识点的原因,因为我一直找不出来一个可以在一篇博文中全部覆盖这个知识点的总结. 这两天翻了一下了JAVA中的圣经<think in java>和<Java核心技术>,虽然写的很好,但感觉写的也不是太符合我想一篇博文覆盖NIO知识点的要求.由于NIO本来就是技术难点,并且java对IO的设计和使用也较为复杂难懂.我也是能力有限如有说明不到位或错误的地方请大家指出. 本文参

不惑JAVA之JAVA基础 - HashMap

HashMap应该是平时应用开发中或是框架设计中最为常用高效的容器.在介绍HashMap之前,先介绍两个常见的区别.后期会专门介绍CurrentHashMap. hashmap 和 hashtable 区别 HashMap和HashTable有什么区别,一个比较简单的回答是: HashMap是非线程安全的,HashTable是线程安全的. HashMap的键和值都允许有null值存在,而HashTable则不行. 因为线程安全的问题,HashMap效率比HashTable的要高. hashmap

Java基础String的方法

Java基础String的方法 字符串类型写法格式如下: 格式一: String 变量名称; 变量名称=赋值(自定义或传入的变量值); 格式二: String 变量名称=赋值(自定义或传入的变量值);在输出时任何数据类型与字符串进行拼接,结果一般是字符串 1 public class StringFunc { 2 3 public static void main(String[] args){ 4 //字符串拼接 5 String str1; 6 str1 = "hello"; 7

转!!java中Object转String

Object转为String的几种形式 在java项目的实际开发和应用中,常常需要用到将对象转为String这一基本功能.本文将对常用的转换方法进行一个总结.常用的方法有Object.toString(),(String)要转换的对象,String.valueOf(Object)等.下面对这些方法一一进行分析. 方法1:采用 Object.toString()方法请看下面的例子:Object object = getObject();System.out.println(object.toStr

Java网络编程基础(六)— 基于TCP的NIO简单聊天系统

在Java网络编程基础(四)中提到了基于Socket的TCP/IP简单聊天系统实现了一个多客户端之间护法消息的简单聊天系统.其服务端采用了多线程来处理多个客户端的消息发送,并转发给目的用户.但是由于它是基于Socket的,因此是阻塞的. 本节我们将通过SocketChannel和ServerSocketChannel来实现同样的功能. 1.客户端输入消息的格式 username:msg    username表示要发送的的用户名,msg为发送内容,以冒号分割 2.实现思路 实现思路与Java网络

[Java 05 OO] (基础篇) 《Java开发实战经典》

p5OO 第五章 面向对象 (基础篇) Notes (1), Constructor / this / String   String str1 = "hello"; 解释 : 是把一个在堆内存空间的使用权给了 str1 对象.   String str2 = "hello"; str1 == str2 是 true   String 字符串的内容不可改变 (2), Java 常用的内存区域    1), 栈内存空间    2), 堆内存空间    3), 全局数据

JAVA学习(五):Java面向对象编程基础

Java面向对象编程基础 面向对象(Object oriented programming,OOP)技术是一种强有力的软件开发方法,它採用数据抽象与信息隐藏技术,来使软件开发简单化,以达到代码重用的目的. 1.OOP的3个特性(封装.继承和多态性) 封装是类的基础.指把类的相关实现细节隐藏起来,在类中将数据和实现操作的代码集中起来放在对象的内部.调用这些类时仅仅需直接使用类预留的接口就能够了. 继承提供了子类自己主动拥有父类数据结构和方法的机制.它表示类之间的一种关系. 多态指使一个对象被看成还

Java语言的基础知识4

第五章(数组) 1.在Java中可以将数组看做是一个对象虽然基本数据类型不是对象但有基本数据类型组成的数组是对象. 2.对于二维数组求第二维就用array[0].length, array.length就是默认的是第一维的长度. 3.foreach并不是一个新的语法它是for的循环的格式化主要执行遍历功能的循环,example: int arry ={1,2,3,4,5}; for(int i :array){ system.out.println(): } 4.数组元素定义完以后可通过Arra

黑马程序员——Java I/O基础知识之I/O流

I/O流基础知识--字节流和字符流 文件存储在硬盘中,是以二进制表示的,只有内存中才能形成字符.数据的来源可以有硬盘,内存,控制台,网络,Java把数据从一个地方转到另一个地方的现象称为流,用InputStream和OutputStream接口来表示,这两个流里面的都是以字节为单位的,后来加入了Reader和Writer,里面操作的是字符,是两个字节为单位的. 字节流 字节流将数据写入文件 try { File file =new File("d:" +File .separator+