实际上在写本文以前,我曾考虑是先探讨面向对象,仍是先选择String和Arrays,最后仍是选择了后者,并不是是面向对象对咱们不重要,相反它是Java的灵魂所在,之因此这样的安排是由于这两个是在是咱们程序中最为常见的了,因此放在面向对象前面讲解,开始讲解String以前,我先说一下我对于类的一些探讨路线,咱们依据面向接口编程来探讨,首先将类似类的共同方法所在接口进行谈论,而后才是类,本文亦是如此,咱们先来说解char的可读序列_CharSequence,下面开始今天的学习。算法
咱们查看String,发现其实现了CharSequence接口,而在StringBuffer和StringBuilder中也有该接口的实现,这就不得不引发个人注意了,经过API查阅发现对于CharSequence的解释以下【编程
public interface CharSequence数组
CharSequence 是 char 值的一个可读序列(也即Unicode字符序列,如"Java\u6211",表示的就是五个字符 'J','a','v''a','我')。此接口对许多不一样种类的 char 序列(这意味着只要是char序列,那么多半就要实现该接口了,从这里咱们也就明白了为何String、StringBuilder、StringBuffer、CharBuffer为何都实现了该接口,由于这三个底层都是字符数组,即标准的字符序列)提供统一的只读访问。char 值表示 Basic Multilingual Plane (BMP 基本多语言平面) 或代理项中的一个字符。有关详细信息,请参阅 Unicode 字符表示形式,说白了就是char----Unicode----数值。 此接口不修改 equals 和 hashCode 方法的常规协定。所以,一般未定义比较实现 CharSequence 的两个对象的结果。每一个对象均可以经过一个不一样的类实现,并且不能保证每一个类可以测试其实例与其余类的实例的相等性。所以,使用任意 CharSequence 实例做为集合中的元素或映射中的键是不合适的。下图是截取的全部实现CharSequence接口的类,咱们在后面一一认识。】数据结构
返回值 方法函数
char charAt(int index)返回指定索引的char值【该接口是char的字符序列,既然是序列,那么就必定是有长度的,咱们天然能够经过索引获取相应位置的字符,因此将该方法提取到接口中是颇有必要的,这也是将具备相同属性和行为抽取到一个类中的鲜明实现!】
学习
int length()返回次字符序列的长度
测试
CharSequence subSequence(int start,intend)返回一个新的CharSequence,它是此序列的子序列。【最初在研究String的时候,并无研究CharSequence,当时对于该方法很不理解,知道后来研究了该接口,才以为很聪明的选择,它没用用一个特定的对象来做为返回值,而是一个接口,这样就能够动态返回返回值,经过多态将该字符序列的实现类对象向上转型为接口】ui
String toString()返回一个包含此序列中字符的字符串,该字符串与此序列的顺序相同。【这是重写Object的方法,接口并无显式继承Object类,实际上也不必,Object默认是全部类和接口的父类,重写toString()返回以字符串形式表示的字符序列,而不是Object中的hashcode】
this
在"可读序列"中有这么一句话,读时有所疑惑,因此在这里拿出来再作说明,"此接口不修改 equals 和 hashCode 方法的常规协定。所以,一般未定义比较实现 CharSequence 的两个对象的结果。每一个对象均可以经过一个不一样的类实现,并且不能保证每一个类可以测试其实例与其余类的实例的相等性。所以,使用任意 CharSequence 实例做为集合中的元素或映射中的键是不合适的。"。咱们在字符序列中使用equals()每每只但愿比对字符序列内容,而非是对象引用地址(经过hashcode),可是CharSequence并无重写equals(),因此若是咱们经过多态将CharSequence的实现类对象向上转型为接口,那么此时再使用equals(),若是是同一个类的对象那么比对是经过该类重写的equals(),若不是同一个对象,那么是经过Object的equals方法来比对(经过hashcode),而不是字符序列内容,即使两个实现类重写了equals(),举例以下:spa
package cn.charsequence; public class CharSequenceTest { public static void main(String[] args) { String str1 = new String("AAA"); String str2 = "AAA"; System.out.println(str1.equals(str2)); StringBuffer sb = new StringBuffer("AAA"); CharSequence cs1 = sb; System.out.println(cs1.equals(str2)); } }
若是以对象做为集合的元素,也会出现这种状况,由于集合中的元素或键是经过equals()来比对的,可是若是是散列表中的键,那么是经过hashcode来比对的,由于散列表中键是不能重复的,以下:
// list.add("AAA");
list.add(new String("AAA"));
list.add(new StringBuffer("AAA"));
Map<CharSequence, String> map = new HashMap<>();
map.put("AAA", "String_AAA");
map.put(new StringBuffer("AAA"), "StringBuffer_AAA");
System.out.println(list.contains("AAA"));
System.out.println(map.get("AAA"));
System.out.println(map.get(new StringBuffer("AAA")));
咱们讲字符序列,那么究竟什么是字符序列呢?其实字符序列就是由一个以上的字符构成的序列,这里的序列讲的是一种数据结构,换用到这里说的就是数组了,咱们经过一段代码来看一下:
private final char value[]; public String(char value[]) { this.value = Arrays.copyOf(value, value.length); }
这是字符串String中的一个构造方法,在这个构造函数中使用到了数组的copyOf()方法,该方法的具体做用以下:
那么这个构造函数其实就是以咱们传递进来的字符数组为范本而后建立一个新的字符数组,内容同样,而后赋值给String中已经声明可是未经初始化的字符数组,因此咱们日常看到的字符串,其底层其实是一个字符数组,那么咱们可能会有这么一个疑问,那就是为何输出的时候输出字符串,在控制台上仍是以字符串出现呢,数组(对象)的输出不是一般会输出一个16进制的hashCode吗,这和咱们所说的不是矛盾吗?下面咱们以一段代码来分析:
/** * String的底层是char数组 */ @Test public void fun3(){ String[] str = {"花褪残红青杏小。","燕子飞时,","绿水人家绕。","枝上柳绵吹又少,","天涯何处无芳草。"}; int[] i = {1,2,3,4,5}; boolean[] boo = {true,false}; char[] ch = {'花','褪','残','红','青','杏','小'}; System.out.print(str); System.out.println(" "+str+"-----"+Arrays.toString(str)); System.out.print(i); System.out.println(" "+i+"-----"+Arrays.toString(i)); System.out.print(boo); System.out.println(" "+boo+"-----"+Arrays.toString(boo)); System.out.print(ch); System.out.println(" "+ch+"-----"+Arrays.toString(ch)); }
查看结果咱们发现,当咱们数组对象时,大部分数组都会输出一个看起来像是地址的字符串,只有char数组,输出了一个字符串,字符串内容是字符数组的元素。这是为何呢?然咱们一步步经过代码来解析:
首选看输出语句打开看它的底层代码以下:printStraem.class
public void println(Object x) { String s = String.valueOf(x); synchronized (this) { print(s); newLine(); } }
观察上面的代码,咱们知道输出会将一个object经过String.valueOf()方法转变为String,观察该方法
public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
public static String valueOf(char data[]) { return new String(data); }
public static String valueOf(char data[], int offset, int count) { return new String(data, offset, count); }
在String类中涉及到的valueOf方法有这三个,观察发现,其中两个都是关于char数组的,而一个是涉及的其它对象的,那么数组的输出在这里划出了两条路线,观察字符发现,它会把字符数组做为参数构建一个字符串对象.观察那个参数是Object的,若是不是null,那么会返回一个字符串,内容是Object的toString方法
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
那么此时到了这里,字符数组变成了一个字符串对象,底层仍然是字符数组,而其余类型则变成了一个字符串,内容是16进制的HashCode,接下来咱们看对字符串的打印输出
public void print(String s) { if (s == null) { s = "null"; } write(s); }
private void write(String s) { try { synchronized (this) { ensureOpen(); textOut.write(s); textOut.flushBuffer(); charOut.flushBuffer(); if (autoFlush && (s.indexOf('\n') >= 0)) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } }
到了这里咱们大概就明白了,使用字符流输出到控制台字符数组变成了字符串,而其余对象输出的确实一个看起来像是地址的HashCode(HashCode和地址不同,它有时候并不是是真实的物理地址,只是一种算法罢了,详细的hashcode咱们在介绍Object的时候在介绍).这里咱们经过String来介绍了一下,实际上CharSequence大体原理是一致的.