[TOC]
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 Byteshort Shortint Integerlong Long第二类:浮点型float Floatdouble 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.javaAbstractStringBuilder(int capacity) { value = new char[capacity];}
StringBuilder类提供了4个构造方法。构造方法次要实现了对value数组的初始化。
其中:
- 默认构造方法设置了value数组的初始容量为16。
- 第2个构造方法设置了value数组的初始容量为指定的大小。
- 第3个构造方法承受一个String对象作为参数,设置了value数组的初始容量为String对象的长度+16,并把String对象中的字符增加到value数组中。
- 第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/...
https://blog.csdn.net/u012317...
https://www.zhihu.com/questio...