实际上在写本文之前,我曾考虑是先探讨面向对象,还是先选择String和Arrays,最后还是选择了后者,并非是面向对象对我们不重要,相反它是Java的灵魂所在,之所以这样的安排是因为这两个是在是我们程序中最为常见的了,所以放在面向对象前面讲解,开始讲解String之前,我先说一下我对于类的一些探讨路线,我们依据面向接口编程来探讨,首先将相似类的共同方法所在接口进行谈论,然后才是类,本文亦是如此,我们先来讲解char的可读序列_CharSequence,下面开始今天的学习。

1.可读序列

  我们查看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接口的类,我们在后面一一认识。】

2.接口的方法摘要

  返回值       方法

  char          charAt(int index)返回指定索引的char值【该接口是char的字符序列,既然是序列,那么就一定是有长度的,我们自然可以通过索引获取相应位置的字符,所以将该方法提取到接口中是很有必要的,这也是将具有相同属性和行为抽取到一个类中的鲜明实现!】

  int           length()返回次字符序列的长度

CharSequence  subSequence(int start,intend)返回一个新的CharSequence,它是此序列的子序列。【最初在研究String的时候,并没有研究CharSequence,当时对于该方法很不理解,知道后来研究了该接口,才觉得很聪明的选择,它没用用一个特定的对象来作为返回值,而是一个接口,这样就可以动态返回返回值,通过多态将该字符序列的实现类对象向上转型为接口】

  String        toString()返回一个包含此序列中字符的字符串,该字符串与此序列的顺序相同。【这是重写Object的方法,接口并没有显式继承Object类,实际上也没必要,Object默认是所有类和接口的父类,重写toString()返回以字符串形式表示的字符序列,而不是Object中的hashcode】

3.小提醒

  在"可读序列"中有这么一句话,读时有所疑惑,所以在这里拿出来再做说明,"此接口不修改 equals 和 hashCode 方法的常规协定。因此,通常未定义比较实现 CharSequence 的两个对象的结果。每个对象都可以通过一个不同的类实现,而且不能保证每个类能够测试其实例与其他类的实例的相等性。因此,使用任意 CharSequence 实例作为集合中的元素或映射中的键是不合适的。"。我们在字符序列中使用equals()往往只希望比对字符序列内容,而非是对象引用地址(通过hashcode),但是CharSequence并没有重写equals(),所以如果我们通过多态将CharSequence的实现类对象向上转型为接口,那么此时再使用equals(),如果是同一个类的对象那么比对是通过该类重写的equals(),若不是同一个对象,那么是通过Object的equals方法来比对(通过hashcode),而不是字符序列内容,即便两个实现类重写了equals(),举例如下:

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")));

4.字符序列的底层是字符数组

  我们讲字符序列,那么究竟什么是字符序列呢?其实字符序列就是由一个以上的字符构成的序列,这里的序列讲的是一种数据结构,换用到这里说的就是数组了,我们通过一段代码来看一下:

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大致原理是一致的.

Notes 20180309 : String第一讲_char的可读序列的更多相关文章

  1. Notes 20180310 : String第二讲_String的声明与创建

    1  字符串的声明与创建 学习String的第一步就是创建(声明)字符串,我们在这里之所以分为创建和声明(其实是一个意思,都是创建字符串,但两者却有本质的区别)是因为String是一个很特殊的类,它的 ...

  2. (转)【风宇冲】Unity3D教程宝典之AssetBundles:第一讲

    自:http://blog.sina.com.cn/s/blog_471132920101gz8z.html 原创文章如需转载请注明:转载自风宇冲Unity3D教程学院                 ...

  3. CS193P - 2016年秋 第一讲 课程简介

    Stanford 的 CS193P 课程可能是最好的 ios 入门开发视频了.iOS 更新很快,这个课程的最新内容也通常是一年以内发布的. 最新的课程发布于2016年春季.目前可以通过 iTunes ...

  4. POI教程之第一讲:创建新工作簿, Sheet 页,创建单元格

    第一讲 Poi 简介 Apache POI 是Apache 软件基金会的开放源码函数库,Poi提供API给java程序对Microsoft Office格式档案读和写的功能. 1.创建新工作簿,并给工 ...

  5. 《ArcGIS Engine+C#实例开发教程》第一讲桌面GIS应用程序框架的建立

    原文:<ArcGIS Engine+C#实例开发教程>第一讲桌面GIS应用程序框架的建立 摘要:本讲主要是使用MapControl.PageLayoutControl.ToolbarCon ...

  6. 32位汇编第一讲x86和8086的区别,以及OllyDbg调试器的使用

    32位汇编第一讲x86和8086的区别,以及OllyDbg调试器的使用 一丶32位(x86也称为80386)与8086(16位)汇编的区别 1.寄存器的改变 AX 变为 EAX  可以这样想,16位通 ...

  7. 异常处理第一讲(SEH),筛选器异常,以及__asm的扩展,寄存器注入简介

    异常处理第一讲(SSH),筛选器异常,以及__asm的扩展 博客园IBinary原创  博客连接:http://www.cnblogs.com/iBinary/ 转载请注明出处,谢谢 一丶__Asm的 ...

  8. 常见注入手法第一讲EIP寄存器注入

    常见注入手法第一讲EIP寄存器注入 博客园IBinary原创  博客连接:http://www.cnblogs.com/iBinary/ 转载请注明出处,谢谢 鉴于注入手法太多,所以这里自己整理一下, ...

  9. 逆向实用干货分享,Hook技术第一讲,之Hook Windows API

    逆向实用干货分享,Hook技术第一讲,之Hook Windows API 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) ...

随机推荐

  1. JavaWeb学习总结(十一):Session解决form表单重复提交

    在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交. 一.表单重复提 ...

  2. web第一章(html)

    HTML介绍 HyperText(超文本) Markup(标记) Language(语音) 类似于XML都是由标签组成 xml:是可扩展标记语言,标签可以任意自定义 HTML:不可以使用任意标签,学习 ...

  3. css3+javascript实现翻页幻灯片

    先上效果图 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...

  4. ztree 获取当前选中节点的子节点集合

    功能:获取当前选中节点的子节点id集合. 步骤:1.获取当前节点 2.用ztree的方法transformToArray()获取当前选中节点(含选中节点)的子节点对象集合. 3.遍历集合,取出需要的值 ...

  5. Spring Data MongoDB 分页查询

    在上篇文章 Spring Data MongoDB 环境搭建 基础上进行分页查询 定义公用分页参数类,实现 Pageable 接口 import java.io.Serializable; impor ...

  6. Windows server 2008 Tips

    Tips for remote server in domain. Some Definition user [user] group workgroup domain Local account d ...

  7. 4 使用Selenium模拟登录csdn,取出cookie信息,再用requests.session访问个人中心(保持登录状态)

    代码: # -*- coding: utf-8 -*- """ Created on Fri Jul 13 16:13:52 2018 @author: a " ...

  8. 使用ajax请求数据时的几种做法

    在进行前后端交互的时候,一般前端使用ajax向后端发送数据,后端根据发送的数据来返回数据,前端将这些数据接收并进行相应的处理 以下是在日常工作中总结的几点使用ajax传递数据时的情况: 1.在本页面( ...

  9. IDEA Properties中文unicode转码问题

    在IDEA中创建了properties文件,发现默认中文不会自动进行unicode转码.如下 在project settings - File Encoding,在标红的选项上打上勾,确定即可 效果图 ...

  10. what's up ? docker, all right.

    Docker install 下载对应安装包,离线安装 Docker 需要 docker-engine.docker-engine-selinux.libtool-ltdl这三个软件包. 下面以安装 ...