String

String是一个很普通的类

源码分析

//该值用于字符存储
private final char value[]; //缓存字符串的哈希码
private int hash;// Default to 0 //这个是一个构造函数
//把传递进来的字符串对象value这个数组的值,
//赋值给构造的当前对象,hash的处理方式也一样。 public String(String original) {
this.value = original.value;
this.hash = original.hash;
} //String的初始化有很多种
//空参数初始化
//String初始化
//字符数组初始化
//字节数组初始化
//通过StringBuffer,StringBuilder构造

问题: 我现正在准备构造一个String的对象,那original这个对象又是从何而来?是什么时候构造的呢?

测试一下:

public static void main(String[] args) {
String str = new String("zwt");
String str1 = new String("zwt");
}

在Java中,当值被双引号引起来(如本示例中的"abc"),JVM会去先检查看一看常量池里有没有abc这个对象,

如果没有,把abc初始化为对象放入常量池,如果有,直接返回常量池内容。

Java字符串两种声明方式在堆内存中不同的体现

为了避免重复的创建对象,尽量使用String s1 ="123" 而不是String s1 = new String("123"),因为JVM对前者给做了优化。

常用的API

System.out.println(str.isEmpty());//判断是不是空字符串
System.out.println(str.length());//获取字符串长度
System.out.println(str.charAt(1));//获取指定位置的字符
System.out.println(str.substring(2, 3));//截取指定区间字符串
System.out.println(str.equals(str1));//比较字符串

isEmpty()

    public boolean isEmpty() {
return value.length == 0;
}

length()

    public int length() {
return value.length;
}

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) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
//如果截取的开始范围刚好是0并且结束范围等于数组的长度,直接返回当前对象,
//否则用该数组和传入的开始范围和结束范围重新构建String对象并返回。
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

equals()

    public boolean equals(Object anObject) {
//如果是同一个引用,直接返回true
if (this == anObject) {
return true;
}
//判断是否是String
if (anObject instanceof String) {
//判断长度是否一致
String anotherString = (String)anObject;
int n = value.length;
//判断char[]里面的每一个值是否相等
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;
}

equals()与“==”

这两者之间没有必然的联系,

在引用类型中,"=="是比较两个引用是否指向堆内存里的同一个地址(同一个对象),

equals是一个普通的方法,该方法返回的结果依赖于自身的实现

intern()

public native String intern();

//如果常量池中有当前String的值,就返回这个值,如果没有就加进去,返回这个值的引用,

一些基础

Java基本数据类型和引用类型

Java中一共有四类八种基本数据类型, 除掉这四类八种基本类型,其它的都是对象,也就是引用类型。

基本数据类型
浮点类型 float double
字符型 char
逻辑型 boolean
整型 byte short int long

Java自动装箱/拆箱

Integer 里面我们曾经说过得 valueOf (), 这个加上valueOf方法的过程,就是Java中经常说的装箱过程。

在JDK1.5中,给这四类八种基本类型加入了包装类 。

第一类:整型
byte Byte
short Short
int Integer
long Long 第二类:浮点型
float Float
double Double 第三类:逻辑型
boolean Boolean 第四类:字符型
char Character

将int的变量转换成Integer对象,这个过程叫做装箱,

反之将Integer对象转换成int类型值,这个过程叫做拆箱。

以上这些装箱拆箱的方法是在编译成class文件时自动加上的,不需要程序员手工介入,因此又叫自动装箱/拆箱。

用处:

1、对象是对现实世界的模拟 。

2、为泛型提供了支持。

3、提供了丰富的属性和API

public static void main(String[] args) {
int int1 = 180;
Integer int2 = new Integer(180);
}

表现如下图:

StringBuilder

StringBuilder类被 final 所修饰,因此不能被继承。

StringBuilder类继承于 AbstractStringBuilder类。

实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,

比如:append()、insert()、delete()、replace()、charAt()方法等。

值得一提的是,StringBuffer也是继承于AbstractStringBuilder类。

StringBuilder类实现了2个接口:

Serializable 序列化接口,表示对象可以被序列化。

CharSequence 字符序列接口,提供了几个对字符序列进行只读访问的方法,

比如:length()、charAt()、subSequence()、toString()方法等。

public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

定义的常量

//toString 返回的最后一个值的缓存。每当修改 StringBuffer 时清除。
private transient char[] toStringCache;

AbstractStringBuilder

abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
//value 用来存储字符序列中的字符。value是一个动态的数组,当存储容量不足时,会对它进行扩容。
char[] value; /**
* The count is the number of characters used.
*/
//count 表示value数组中已存储的字符数。
int count;

构造方法

public StringBuilder() {
super(16);
} public StringBuilder(int capacity) {
super(capacity);
} public StringBuilder(String str) {
super(str.length() + 16);
append(str);
} public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
} // AbstractStringBuilder.java
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}

StringBuilder类提供了4个构造方法。构造方法主要完成了对value数组的初始化。

其中:

  1. 默认构造方法设置了value数组的初始容量为16。
  2. 第2个构造方法设置了value数组的初始容量为指定的大小。
  3. 第3个构造方法接受一个String对象作为参数,设置了value数组的初始容量为String对象的长度+16,并把String对象中的字符添加到value数组中。
  4. 第4个构造方法接受一个CharSequence对象作为参数,设置了value数组的初始容量为CharSequence对象的长度+16,并把CharSequence对象中的字符添加到value数组中。

append()方法

有多种实现,一般的顺序为:

append() ----> ensureCapacityInternal() 确保value数组有足够的容量 ----> newCapacity()新的容量

    private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
} private int newCapacity(int minCapacity) {
// overflow-conscious code //扩容参数 int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}

在上面代码中可以看到具体的扩容规则是 * 2 + 2

StringBuffer

基本就是加了一个synchronized的StringBuilder。

StringBuilder 和 StringBuffer 适用的场景是什么?

stringbuffer固然是线程安全的,stringbuffer固然是比stringbuilder更慢,固然,在多线程的情况下,理论上是应该使用线程安全的stringbuffer的。

实际上基本没有什么地方显示你需要一个线程安全的string拼接器 。

stringbuffer基本没有适用场景,你应该在所有的情况下选择使用stringbuiler,除非你真的遇到了一个需要线程安全的场景 。

如果你遇见了,,,

stringbuffer的线程安全,仅仅是保证jvm不抛出异常顺利的往下执行而已,它可不保证逻辑正确和调用顺序正确。大多数时候,我们需要的不仅仅是线程安全,而是锁。

最后,为什么会有stringbuffer的存在,如果真的没有价值,为什么jdk会提供这个类?

答案太简单了,因为最早是没有stringbuilder的,sun的人不知处于何种考虑,决定让stringbuffer是线程安全的,

于是,在jdk1.5的时候,终于决定提供一个非线程安全的stringbuffer实现,并命名为stringbuilder。

顺便,javac好像大概也是从这个版本开始,把所有用加号连接的string运算都隐式的改写成stringbuilder,

也就是说,从jdk1.5开始,用加号拼接字符串已经几乎没有什么性能损失了。

扩展小知识

Java9改进了字符串(包括String、StringBuffer、StringBuilder)的实现。

在Java9以前字符串采用char[]数组来保存字符,因此字符串的每个字符占2字节,

而Java9的字符串采用byte[]数组再加一个encoding-flag字段来保存字符,因此字符串的每个字符只占1字节。

所以Java9的字符串更加节省空间,字符串的功能方法也没有受到影响。

参考链接

https://zhuanlan.zhihu.com/p/28216267

https://blog.csdn.net/u012317510/article/details/83721250

https://www.zhihu.com/question/20101840

String,String Builder,String Buffer-源码的更多相关文章

  1. String、StringBuffer、StringBuilder源码分析

    利用反编译具体看看"+"的过程 1 public class Test 2 { 3 public static void main(String[] args) 4 { 5 int ...

  2. String、StringBuffer、StringBuilder源码解读

    序 好长时间没有认真写博客了,过去的一年挺忙的.负责过数据库.线上运维环境.写代码.Code review等等东西挺多. 学习了不少多方面的东西,不过还是需要回归实际.加强内功,方能扛鼎. 去年学习M ...

  3. String类——StringBuilder类的源码及内存分析(java)

    相同:底层均采用字符数组value来保存字符串 区别:String类的value数组有final 修饰,指向不可改,同时private 未提供修改value数组的方法.StringBuilder类的v ...

  4. String、StringBuffer和StringBuilder源码解析

    1.String 1.1类的定义 public final class String implements java.io.Serializable, Comparable<String> ...

  5. netty(七)buffer源码学习2

    概述 文章主要介绍的是PoolArena,PoolChunk,PoolSubpage 三个类的源码 PoolArena PoolArena 是netty 的内存池实现类,通过预先申请一块大的空间,然后 ...

  6. netty(六) buffer 源码分析

    问题 : netty的 ByteBuff 和传统的ByteBuff的区别是什么? HeapByteBuf 和 DirectByteBuf 的区别 ? HeapByteBuf : 使用堆内存,缺点 ,s ...

  7. Buffer源码深入分析

    博客园对MarkDown显示的层次感不是很好,大家可以看这里:Buffeer. 本机环境: Linux 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33 ...

  8. netty(八)buffer源码学习3

    问题 : compositeByteBuf 是干什么和其他 compositeByteBuf 有何区别 内部实现 概述 compositeByteBuf 就像数据库中的视图,把几个表的字段组合在一起, ...

  9. string为什么是final?源码分析

    http://blog.csdn.net/zhangjg_blog/article/details/18319521

  10. java.nio.Buffer源码阅读

    Java 自从 JDK1.4 起,对各种 I/O 操作使用了 Buffer 和 Channel 技术.这种更接近于操作系统的的底层操作使得 I/O 操作速度得到大幅度提升,下面引用一段<Java ...

随机推荐

  1. Serverless Web Function 实践教程(一):快速部署 Node.js Web 服务

    作为目前广受欢迎的 Web 服务开发语言,Node.js 提供了众多支持 HTTP 场景的相关功能,可以说是为 Web 构建而生.因此,基于 Node.js,也诞生了多种 Web 服务框架,它们对 N ...

  2. 基于ABP落地领域驱动设计-03.仓储和规约最佳实践和原则

    目录 系列文章 仓储 仓储的通用原则 仓储中不包含领域逻辑 规约 在实体中使用规约 在仓储中使用规约 组合规约 学习帮助 围绕DDD和ABP Framework两个核心技术,后面还会陆续发布核心构件实 ...

  3. 透彻理解USB总线应用之枚举

    Hello,大家好,今天我们来讨论一下USB总线中的枚举(Enumeration),首先简单介绍一下USB系统的基本架构,它由USB主机.USB设备与USB电缆(本文忽略它)组成,如下图所示: 最常见 ...

  4. 【.NET 与树莓派】TM1638 模块的按键扫描

    上一篇水文中,老周马马虎虎地介绍 TM1638 的数码管驱动,这个模块除了驱动 LED 数码管,还有一个功能:按键扫描.记得前面的水文中老周写过一个 16 个按键的模块.那个是我们自己写代码去完成键扫 ...

  5. 【原创】Ingress-Nginx-Controller的Metrics监控源码改造简析

    一.背景 目前我们的生产环境一层Nginx已经容器化部署,但是监控并不完善,我们期望其具有Ingress-Nginx-Controller组件上报监控的数据.这样可以建立请求全链路的监控大盘.有利于监 ...

  6. 聊一聊Unity协程背后的实现原理

    Unity开发不可避免的要用到协程(Coroutine),协程同步代码做异步任务的特性使程序员摆脱了曾经异步操作加回调的编码方式,使代码逻辑更加连贯易读.然而在惊讶于协程的好用与神奇的同时,因为不清楚 ...

  7. Redis启动正常,一段时间后报错,连不上redis

    Redis报错 1.redis在最终目标上移动临时数据库文件时出错 错误:redis:Error moving temp DB file temp-13792.rdb on the final des ...

  8. filebeat 提取获取massage字段 利用pipeline grok 7.12

    嘴巴会说(情商)比技术有时候更重要! 水平有限,希望你看完有所收获! 背景 1,filebeat直连Elasticsearch,需要对massage提取一些特定的字段. 2,如果你对数据需要处理的比较 ...

  9. Linux Netfilter框架分析

    目录 Netfilter框架 Netfilter的5个hook点 netfilter协议栈数据流分析 连接跟踪conntrack conntrack连接跟踪表条目 连接跟踪表大小 管理连接跟踪表 ip ...

  10. HTTP:HTTP请求头和响应头详解

    HTTP请求报文 HTTP 请求报文由3部分组成(请求行+请求头+请求体) HTTP请求报文分析  1. 是请求方法,GET和POST是最常见的HTTP方法,初次以外还包括 DELETE.HEAD.O ...