JDK源码学习笔记——String
1、学习jdk源码,从以下几个方面入手:
类定义(继承,实现接口等)
全局变量
方法
内部类
2、hashCode
private int hash;
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
为什么是31?
(1)计算hashcode值一般选质数
(2)太小的数计算的hashcode值冲突率高,太大的数乘法计算会溢出int范围
(3)有以上两点和实验得 出:31, 33, 37, 39 ,41 作为乘子比较合适
(4)这几个数字中31的乘法运算可以被优化:31 * i == (i << 5) - i
3、构造两种:
(1)直接将otherStr引用给this
(2)数组copy
4、String对“+”的支持
public static void main(String[] args) {
String s1="a" + "b";// 编译之后 String s1 = "ab";
String s = "a";
String s2= s+ "b";// 编译之后 String s = (new StringBuilder(String.valueOf(s))).append("b").toString();
}
5、jdk1.7修改subString()
// jdk1.6
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
// subString方法部分
return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); // jdk1.7
public String(char value[], int offset, int count) {
.
.
.
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
// subString方法部分
return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen);
jdk1.6的substring:
(1)直接将引用赋值,性能好,共享内部数组节约内存
(2)由于原String的value是private final,可以保证安全性
(3)可能导致内存泄漏
String aLongString = "...a very long string..."; // 很长
String aPart = data.substring(2, 4);
return aPart;
假设从一个很长的字符串中提取一小段内容:
当aLongString不再使用,aPart继续使用时,
aLongString被回收,aLongString的value还被aPart的value引用,不能被回收
导致内存泄漏
6、编程技巧学习
/**
* 先比较是否同一个对象
* 先比较长度
* 虽然代码写的内容比较多,但是可以很大程度上提高比较的效率
*/
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;
}
static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. 先比较第一个字符*/
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
/**
* 三目运算符代替多个if
*/
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
7、intern()
(1)String s = new String("abc");创建个几个对象
类加载时创建"abc"放入常量池 第一个
执行代码时new String() 第二个
(2)String存入常量池方式:
一,直接使用双引号声明出来的String对象,在类加载时会直接存储在常量池中。
二,如果不是用双引号声明的String对象,可以使用String提供的intern方法。
intern 方法:
如果常量池中存在当前字符串, 就会直接返回当前字符串。 如果常量池中没有此字符串, 会将此字符串放入常量池中后,再返回。
(3)jdk6 和 jdk7 下 intern 的区别
在 JDK1.2 ~ JDK6 的实现中,HotSpot 使用永久代实现方法区
JDK7+ 移除永久代 字符串常量和类引用被移动到 Java Heap中
jdk6 intern:如果常量池中存在当前字符串, 就会直接返回当前字符串。 如果常量池中没有此字符串, 会将此字符串复制一份到方法区,放入方法区中常量池,再返回。
jdk7 intern:如果常量池中存在当前字符串, 就会直接返回当前字符串。 如果常量池中没有此字符串, 会将此字符串的引用存储一份,放入堆中常量池,再返回。
String s4 = "11"; // 编译时不处理,运行到这行代码时将“11”在常量池中创建(运行时,第一次遇到字面才将其在常量池中创建)
举例:
public static void main(String[] args) {
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);// jdk1.6-false jdk1.7-false
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);// jdk1.6-false jdk1.7-true
}
public static void main(String[] args) {
String s = new String("1");
String s2 = "1";
s.intern();
System.out.println(s == s2);// jdk1.6-false jdk1.7-false
String s3 = new String("1") + new String("1");
String s4 = "11";
s3.intern();
System.out.println(s3 == s4);// jdk1.6-false jdk1.7-false
}
不再过多解释: (注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向)



(4)应用举例:
static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX]; public static void main(String[] args) throws Exception {
Integer[] DB_DATA = new Integer[10];
Random random = new Random(10 * 10000);
for (int i = 0; i < DB_DATA.length; i++) {
DB_DATA[i] = random.nextInt();
}
long t = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
} System.out.println((System.currentTimeMillis() - t) + "ms");
System.gc();
}
8、private final char[] value;final--->String的长度是不能改变的
(1)常量池高效,常量池里String对象改变,引用受影响
(2)安全,不可变,只能读不能写,保证线程安全
参考资料:
1、《成神之路-基础篇》Java基础知识——String相关
2、科普:为什么 String hashCode 方法选择数字31作为乘子
JDK源码学习笔记——String的更多相关文章
- JDK源码学习笔记——LinkedHashMap
HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...
- JDK源码学习笔记——Integer
一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...
- JDK源码学习笔记——Enum枚举使用及原理
一.为什么使用枚举 什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数.一年四季等.或者是在我们编译前就知道其包含的所有值的集合. 利用 public final static 完全可 ...
- JDK源码学习笔记——Object
一.源码解析 public class Object { /** * 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用 */ private static native void ...
- jdk源码阅读笔记-String
本人自学java两年,有幸初入这个行业,所以功力尚浅,本着学习与交流的态度写一些学习随笔,什么错误的地方,热烈地希望园友们提出来,我们共同进步!这是我入园写的第一篇文章,写得可能会很乱. 一.什么是S ...
- JDK源码学习笔记——HashMap
Java集合的学习先理清数据结构: 一.属性 //哈希桶,存放链表. 长度是2的N次方,或者初始化时为0. transient Node<K,V>[] table; //最大容量 2的30 ...
- JDK源码学习笔记——HashSet LinkedHashSet TreeSet
你一定听说过HashSet就是通过HashMap实现的 相信我,翻一翻HashSet的源码,秒懂!! 其实很多东西,只是没有静下心来看,只要去看,说不定一下子就明白了…… HashSet 两个属性: ...
- JDK源码学习笔记——TreeMap及红黑树
找了几个分析比较到位的,不再重复写了…… Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例 [Java集合源码剖析]TreeMap源码剖析 java源码分析之TreeMap基础篇 ...
- JDK源码学习笔记——LinkedList
一.类定义 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E& ...
随机推荐
- C题 hdu 1408 盐水的故事
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1408 盐水的故事 Time Limit: 2000/1000 MS (Java/Others) ...
- Java 中的成员内部类
内部类中最常见的就是成员内部类,也称为普通内部类.我们来看如下代码: 运行结果为: 从上面的代码中我们可以看到,成员内部类的使用方法: 1. Inner 类定义在 Outer 类的内部,相当于 Out ...
- SourceTree 过期,注册导入许可证
参考这里:SourceTree过期,需要注册导入 SourceTree License 许可证 很详细 补充: 如果在 SourceTree 软件里注册失败,可以在网页注册. 如果其他邮箱不支持,可以 ...
- 使用IDA PRO+OllyDbg+PEview 追踪windows API 动态链接库函数的调用过程
使用IDA PRO+OllyDbg+PEview 追踪windows API 动态链接库函数的调用过程 http://blog.csdn.net/liujiayu2/article/details/5 ...
- 【LabVIEW技巧】LabVIEW OOP怎么学
前言 有很多人对LabVIEW OOP存在比较极端的看法,大致分为两类: 1. 绝对否定派认为LabVIEW OOP只不过是LabVIEW为了追求时髦,在面向过程的基础上用簇做了一些特性,实际上完全不 ...
- 10:django 模板语言
Django的模板语言的目的是取得力量和易用性之间的平衡,与其他的模板语言相比,django模板语言显得更简单,更专一, django模板系统由模板,变量,过滤器,标签,注释等主要部分组成 模板 一个 ...
- 一、安装ansible
yum -y install epel-release \\安装epel源 yum -y install ansible1.9.noarch \\安装ansible自动化 ansible目录简要 ...
- redis之(四)redis的字符串类型的命令
[一]获得符合规则的键名列表 -->命令 keys [pattern] -->keys命令需要遍历Redis中所有的键,当键的数量比较多会影响性能,生产环境不建议用 -->pat ...
- hdu 2686&&hdu 3376(拆点+构图+最小费用最大流)
Matrix Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- 六十六 aiohttp
asyncio可以实现单线程并发IO操作.如果仅用在客户端,发挥的威力不大.如果把asyncio用在服务器端,例如Web服务器,由于HTTP连接就是IO操作,因此可以用单线程+coroutine实现多 ...