[Java 泥水匠] Java Components 之一:Java String (肯定有你不懂的)

作者:泥沙砖瓦浆木匠

网站:http://blog.csdn.net/jeffli1993

个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。

1.1 前言

说起String,大家最熟悉不过了。我也是那么说过,但是仿佛这熟悉的里面也有很多细节,或者是我们没掌握的东西。往往有很多旧东西里面爆出很多光点。比如,作者泥瓦匠近期在冬天的牛仔裤里面搜出了一张100现金(不宜乐乎)。回到正题,我们下面以下几点讲下String。分两个部分:

基础部分:(JDK源码 文档)

  • 1.2 你好 String
  • 1.3 String字符串操作

扩展部分:

  • 1.3  == 与 equals 不是亲家
  • 1.4 也不难 泥瓦匠再出奇招
  • 1.5 equals碰到n长字符串呢?
  • 1.6 intern妙用

楼主不是大牛级别的人物,泥瓦匠一直认为的是“打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。”所以,操着键盘,听着喜欢的音乐。幽默的对你说我的理解体会。

(希望大牛指出错误,万分感谢!)

1.2 你好 String

怀着初次见你的心情,泥瓦匠和你一起打开JDK1.7文档。我最近想写一些关于JDK1.7的理解,都知道JDK8出来了,新特性我准备下阶段有空学习。

泥瓦匠想说,阅读E文文档有利于体会原汁原味。但毕竟国内大牛翻译的很不错了,咱们不加评判,喜欢哪种自己挑。能抓老鼠,能解决实际项目,适应业务环境的就是你学到了。请看下面的小例子:

清单1.1

String abc = "abc";
char data[] = {'a','b','c'};
String abcStr = new String(data);
System.out.println("abc");
String cde = "cde";
System.out.println("abc" + cde);
String c = "abc".substring(2,3);
String d = cde.substring(1, 2);
System.out.println(c);
System.out.println(d);

泥瓦匠,你不是在忽悠我吗?这么简单的程序,你想说什么。说实在确实是基础,但基础扎实才能有更高的突破。就想泥瓦匠默默为自己,为家人为未来打基础。

可以见,String是不需要用new来创建一个新对象的类。它是不可变的(Constant),其值(像"abc"创建后,不可改变)。自然,String其实实现了基本类型char的序列的功能,因此中文名“字符串”。这里大家有可能疑惑,泥瓦匠就找到JDK源码证据给你们看:

这段代码是来自JDK1.7源码里面的,char型value数组的形式组成了String类的内容。其操作就是针对value数组操作。这样想是否豁然开朗,然后心里自喜“so easy”。其实难点下面,大家慢慢看下去。

1.3 String字符串操作

依旧看代码清单1.1,Java提供了一个特殊的连接操作符(concatenation operator)+ 用于直接来拼接字符串。其中操作常用的方法罗列如下:

方法                作用
s.length()                        返回s字符串长度

s.charAt(1)                       返回s字符串中下标为1的字符

s.substring(0, 2)                 返回s字符串中下标0到2的子字符串

s.indexOf("nsg")                  返回子字符串"nsg"的下标

s.startsWith(" ")                 判断s是否以空格开始

s.endsWith("end")                 判断s是否以"end"结束

跟着泥瓦匠运行下清单1.1,你可以看到打印出来:

abc
abccde
c
d

很简单的发现答案就是我们心目中想要的。这里我不打算很详细的解释api,这样会失去文章的趣味和读者的兴趣。我喜欢用例子来引导出,对于String在我项目和经验中的总结。

下面就以我们substring()方法来解释下源码,可以看到其中实现的细节:

逻辑大致归纳如下:

1. 首先判断传入值得可靠性,判断传入的开始和结束,还有子字符串的长度。这是在每个系统开发需要在细节中关心的。关心细节,才能干大事。

2. 然后判断是不是原String,是的话,直接返回this,如果不是就new一个子串。这个构造函数实现也不难,如果有兴趣可以看看,其实也就是实现char数组的拷贝。

就String基础部分泥瓦匠就介绍这里,在这里我们可以得出的结论是:设计源于生活,源于简单。像一个方法和一个类结构的设计,单一简单。所以我们学习以后设计方法要简单,耦合度不能太高。

到这里,读者可以先听首歌,比如什么的钢琴曲,什么月光曲。休息会继续讲。

1.3  == 与 equals 不是亲家

有人看到这个会大吃一惊,然后质问说“老师说,String 和 == 没关系,你是不是瞎扯淡”。哈哈,我只能说,只不过修行在实际。举个小栗子吧。

看下面清单1.2

static String b = "ab";
private static void test(){
String a = "a" + "b";
System.out.println(a == b);

}

看到这里,你是否觉得这么简单,心里想着肯定是“false,泥瓦匠真逗。”运行下,结果却是

true

结论: 关于
== ,我们要知道 == 用于匹配内存单元的内容。Java中对比的就是两个内存单元的内容(其实就是一串数字)。至于八个基本类型直接比较值。例如清单1.2,比较的是两个引用,两个引用对比的是引用的对象的逻辑值(也就是两个对象对应内存单元的内容)。

泥瓦匠的记忆宫殿:就是相当于 泥瓦匠和我的亲姐姐比较我们两个的爸爸,毕竟的爸爸都是那一个,当然是true。

"a" + "b"的生命是怎么样的呢?网络牛人有些反编译会发现其实是类似下面的过程:

StringBuilder temp = new StringBuilder();
temp.append("a").append("b");
return temp.toString();

究竟JVM是怎么把"a" + "b"和"ab"知道他们是一样的呢。我们回顾下,因为String是不可变类,也就是静态Java语言的特点。这涉及到JVM的优化方案。但JVM很死板,它不会很人工智能的像人脑,它会的只是它能处理的,所以好好了解JVM有助于开发更好的额程序。比如定义一个ab字符串常量够了,它不会在再 子串 a b。这就是它的优化方案。

泥瓦匠的记忆宫殿:这就是偷懒思想,今天我要见我爸爸,明天姐姐要见爸爸。合并下呗,后天一起去见爸爸。

就此我们可以得出结论:编译器给我带来它对程序的优化,为了提升程序的效率和内存资源等,所以我们可以明白,如何掌握其特性写出更好的代码。

1.4 也不难 泥瓦匠再出奇招

补充个例子,看看你们真的掌握没?

private static String getA(){ return "a";}
private static void test2(){
String a = "a";
String b = a + "b";
String e = getA() + "b";
System.out.println(b == "ab");
System.out.println(e == "ab");
}

这涉及了JVM的优化,答案是:

false
false

如果猜到了,证明你真懂了。泥瓦匠还是耐心的讲解下:

第一个false:b是包括了一个常量和一个引用的值,所以。

第二个false: 你也可以猜到外部方法的常量在本方法只是一个引用。所以。

结论如下:我们现在如果不知道JVM怎么在编译时期优化,我们就慢慢了解它们。

1.5 equals碰到n长字符串呢?

这个例子不好写,泥瓦匠就带你们看看源码等。equals并不陌生,相信大家的系统里面有很多设计到这个方法。

其实equals是实现了Object的equals方法,而在Object的equals:

它其实就是实现了 == 比较内存的内容。

Q:泥瓦匠,为什么String要重写Object,这不导致意义不一致了吗?

A:非也非也,String是String,String用来处理的事物逻辑和业务和Object完全不同。equals顾名思义等于,等于不表示完全相等。而是一样或者相似。正所谓业务看情况,比如泥瓦匠处理得金融数据一般小数点10几位,那请问你还会在乎小数点10位后的的值吗,当它们比较的时候,你不会在乎,所以这就是相似。

那么我们就看一下String实现的equals方法:

简单描述下,就是遍历两个数组最好的条件下,就是要么长度不同,要么前面几个不同。直接返回false。

Q:如果碰到大字符串这就悲剧了。所以一些大字符串怎么处理呢。

A:其实实际中,大字符串也难见。但说处理的话,大字符串匹配可以考虑分割匹配或是啥的这里就不展开了。

Q:equals重写 和 hashCode 有关系吗?

A:这里我们要先说明hashCode方法提供对象的hashCode值,返回与默认的System.identityHashCode()一致。其广泛用于集合框架。到后面我也会讲到。hashCode的源于计算机的比较源于数字,而不是对象。所以一个hashCode值标识一个对象,产生了对象相关的很多算法。但是默认的hashCode犯法会发起对本地的调用开销很大。

好累好累,泥瓦匠喝口水,讲完下面这个,我们String到此结束。

1.6 intern妙用

intern大家就有点陌生了。咱们首先得普及下常量池的概念,常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。然后看看下面的例子:

private static void test5(){
    String a = "a";
    String b = a + "b";
    String c = "ab";
    String d = new String("ab");
    System.out.println(c == d.intern());
    System.out.println(b.intern() == d.intern());
}

当调用 intern()方法时,JVM 会在这个常量池中通过 equals()方法查找是否存在等值的 String,如果存在,则直接返回常量池中这个 String 对象的地址;没有找到,则会创建等值的字符串,返回其地址。

思考提升:这有什么用呢?

答曰:可以用于一些常量的存储比较,枚举(枚举底层就是字符串,哈哈)。当想常量比常量,多数的情况下,考虑效率则选择 intern()而不是equals。

总结

String是Java基础组件重要的一部分。我慢慢的总结的J2EE Java的组件体系,然后增加内容进去。还是那句话,泥瓦匠想说:

如以上文章或链接对你有帮助的话,别忘了在文章按钮或到页面右下角点击 “赞一个” 按钮哦。你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章

时间: 2024-10-13 11:56:36

[Java 泥水匠] Java Components 之一:Java String (肯定有你不懂的)的相关文章

[Java 泥水匠] Java Components 之二:算法篇之项目实践中的位运算符(有你不懂的

作者:泥沙砖瓦浆木匠 网站:http://blog.csdn.net/jeffli1993 个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节. 交流QQ群:[编程之美 365234583]http://qm.qq.com/cgi-bin/qm/qr?k=FhFAoaWwjP29_AonqzL0rpdQAjjqlHQQ 如果我的帮到了你,是否乐意捐助一下或请一杯啤酒也好呢?有你支持,干的更好~ 点这参与众筹 我的支付宝:13958686678 2.1 前言 自从上篇[Java 泥水

Java基础3:深入理解String及包装类

Java基础3:深入理解String及包装类 String的连接 @Testpublic void contact () {    //1连接方式    String s1 = "a";    String s2 = "a";    String s3 = "a" + s2;    String s4 = "a" + "a";    String s5 = s1 + s2;    //表达式只有常量时,编译

全面解释java中StringBuilder、StringBuffer、String类之间的关系

http://www.jb51.net/article/33398.htm String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间,StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象,StringBuffer和StringBuilder类功能基本相似 1. String 类  String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不

不使用java内置函数,将String字符串转换为int类型

package com.test; public class AtoiTest { public static void main(String[] args) throws Exception { String s = "-011134"; System.out.println("转换前的字符串:" + s); System.out.println("atoi1转换后的字符串:" + atoi1(s)); System.out.println(

java基础知识回顾之---java String final类之intern方法

public class StringObjectDemo { /** * @param args */ public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " ");//true System.out.print((Other.hello == hel

java基础知识回顾之---java String final类普通方法的应用之“按照字节截取字符串”

/*需求:在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符.但对应的字节数不同,一个汉字占两个字节.定义一个方法,按照最大的字节数来取子串.如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,那么半个就要舍弃.如果去四个字节就是“ab你”,取五个字节还是“ab你”.*/ 代码:其实是一个解码和编码的问题,要明白UTF-8码表和GBK码表的区别,UTF-8中用三个字节代表一个汉字,GBK使用2个字节代表一个汉字. 且在码表中都是用数字存放这些汉字.

java基础知识回顾之---java String final类 容易混淆的java String常量池内存分析

/** *   栈(Stack) :存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放  在常量池中). 堆(heap):存放所有new出来的对象. *   静态存储:存放静态成员(static定义的). 常量池(constant pool):在堆中分配出来的一块存储区域,存放储显式的String常量和基本类型常量(float.int等).另外,可以存储不经常改变的东西 *                       p

java基础知识回顾之---java String final类普通方法的应用之字符串数组排序

/* * 1,给定一个字符串数组.按照字典顺序进行从小到大的排序. * {"nba","abc","cba","zz","qq","haha"} *  * 思路: * 1,对数组排序.可以用选择,冒泡都行. * 2,for嵌套和比较以及换位. * 3,问题:以前排的是整数,比较用的比较运算符,可是现在是字符串对象. *   字符串对象怎么比较呢?爽了,对象中提供了用于字符串对象比较的功能

JAVA学习记录(一)——Number\Character\String\数组\Date\Calendar\GregorianCalendar

jdk环境配置 Java Number类 装箱拆箱 Number类的成员方法 Java Character类 简介 Character 方法 Java String类 创建字符串 字符串长度length 连接字符串 创建格式化字符串 String 方法 Java StringBuffer和StringBuilder类 StringBuffer 方法 Java 数组 声明数组变量 创建数组 遍历数组 for foreach Arrays 类 Java日期时间 Date 构造函数 日期方法 获取当前

第三次博客作业package com.fry; //导入java.util.Arrays; import java.util.Arrays; public class Demo1 { public static void main(String[]args){ //创建对象,对象名为hello Demo1 hello =new Demo1(); //调用方法并将返回值保存在变量中

1.  某网站管理系统,用户注册时,电话号码为可选输入项,输入格式为:区号-电话号码—分机号,中间用“-”隔开.以下为jsp页面上的设计,且并未对输入做任何控制. 假设系统现在需要取出中间的电话号码部分,代码如下: /** * * 该方法根据用户输入取出中间的电话号码部分 * @param strPhoneNum 电话号码,如:“0591-83279988—002” * @return 返回号码部分,如:“83279988” */ public String getPhoneNumber(Str