java基础知识系列---字符串

Java字符串类(java.lang.String)是Java中使用最多的类,也是最为特殊的一个类。

String 类相关基础认知:

1、String类是final的,不可被继承。public final class String。

2、String类是的本质是字符数组char[], 并且其值不可改变。private final char value[];

然后打开String类的API文档,可以发现:

3、String类对象有个特殊的创建的方式,就是直接指定比如String x = "abc","abc"就表示一个字符串对象。而x是"abc"对象的地址,也叫做"abc"对象的引用。

4、String对象可以通过“+”串联。串联后会生成新的字符串。也可以通过concat()来串联。

5、创建字符串的方式很多,归纳起来有三类:

  其一,使用new关键字创建字符串,比如String s1 = new String("abc");

  其二,直接指定。比如String s2 = "abc";

  其三,使用串联生成新的字符串。比如String s3 = "ab" + "c";

6、Java运行时会维护一个String Pool(String池),JavaDoc翻译很模糊“字符串缓冲区”。String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。而一般对象不存在这个缓冲池,并且创建的对象仅仅存在于方法的堆栈区

对象创建

字符串对象创建的原理

原理1:当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个X在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加。

原理2:Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象。

原理3:使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象。

原理4:使用包含变量的表达式来创建String对象,则不仅会检查维护String池,而且还会在堆栈区创建一个String对象。

另外,String的intern()方法是一个本地方法,定义为public native String intern();

intern()方法的价值在于让开发者能将注意力集中到String池上。当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。

否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。

字符串中的方法(待补充)

1.equals方法

和equals经常作为对比的是==,简单区别就是equils比较对象的值, == 用于比较引用和比较基本数据类型时具有不同的功能:

a:比较基本数据类型,如果两个值相同,则结果为true

b:比较引用时,如果引用指向内存中的同一对象,结果为true)

public boolean equals(Object obj)

public boolean equals(Object obj) {
    return (this == obj);
}

其比较规则为:当参数obj引用的对象与当前对象为同一个对象时,就返回true,否则返回false.

equals()方法的本意为确定两个对象的引用是否相同。

 注:在String中equals()方法被进行了覆盖,使其意义变为比较两个对象的内容是否一致。String中的equals是平时开发中最经常用到的,源码如下:

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

2.hashCode方法

publicnativeinthashCode();

根据这个方法的声明可知,该方法返回一个int类型的数值,并且是本地方法,因此在Object类中并没有给出具体的实现。

作用:

对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode。在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。

为什么这么说呢?考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在)

 也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。下面这段代码是java.util.HashMap的中put方法的具体实现:

public V put(K key, V value) {

        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
           Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
               V oldValue = e.value;
                e.value = value;
               e.recordAccess(this);
               return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

put方法是用来向HashMap中添加新的元素,从put方法的具体实现可知,会先调用hashCode方法得到该元素的hashCode值,然后查看table中是否存在该hashCode值,如果存在则调用equals方法重新确定是否存在该元素,如果存在,则更新value值,否则将新的元素添加到HashMap中。从这里可以看出,hashCode方法的存在是为了减少equals方法的调用次数,从而提高程序效率。

有人会说,可以直接根据hashcode值判断两个对象是否相等吗?肯定是不可以的,因为不同的对象可能会生成相同的hashcode值。虽然不能根据hashcode值判断两个对象是否相等,但是可以直接根据hashcode值判断两个对象不等,如果两个对象的hashcode值不等,则必定是两个不同的对象。如果要判断两个对象是否真正相等,必须通过equals方法。

  也就是说对于两个对象,如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等;

  如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同;

  如果两个对象的hashcode值不等,则equals方法得到的结果必定为false;

  如果两个对象的hashcode值相等,则equals方法得到的结果未知。

一般情况下,当我们重写equals时,一般也会去重写hashCode方法。