String 和 Long 的底层源码实现

1.1 String类,底层实现
public final class String 
implements java.io.Serializable, Comparable<String>, CharSequence {    
   private final char value[]; 
   ...
}

可以看出来两点:

  1. final 修饰的类,不能被继承,也就是说任何对 String 的操作方法,都不会被继承覆写;
  2. String 中保存数据的是一个 char 的数组,是被 final 修饰的,也就是说 value 一旦被赋值,内存地址是绝对无法修改的,而且 value 的权限是 private 的,外部绝对访问不到,String 也没有开放出可以对 value 进行set赋值的方法,所以说 value 一旦产生,内存地址就根本无法被修改。
    以上两点就是 String 不变性的原因,充分利用了 final 关键字的特性,如果你自定义类时,希望也是不可变的,也可以模仿 String 的这两点操作。
1.2 字符串乱码

由于在二进制转化操作时,并没有强制规定文件编码,而不同的环境默认的文件编码不一致导致了中文乱码问题。
如:

String str ="nihao 你好 喬亂"; 
// 字符串转化成 byte 数组 
byte[] bytes = str.getBytes("ISO-8859-1"); 
// byte 数组转化成字符串 
String s2 = new String(bytes,"ISO-8859-1"); 
log.info(s2); 
// 结果打印为: nihao ?? ??

对于 String 来说,getBytes 和 new String 两个方法都会使用到编码,我们把这两处的编码替换成 UTF-8 后,打印出的结果就正常了

1.3 首字母大小写

在反射场景下面,我们也经常要使类属性的首字母小写,这时候我们一般都会这么做:

name.substring(0, 1).toLowerCase() + name.substring(1); 

substring 有两个方法:

  1. public String substring(int beginIndex, int endIndex) beginIndex:开始位置,endIndex:
    结束位置;
  2. public String substring(int beginIndex) beginIndex:开始位置,结束位置为文本末尾。
  3. substring 方法的底层使用的是字符数组范围截取的方法 : Arrays.copyOfRange(字符数组, 开始 位置, 结束位置); 从字符数组中进行一段范围的拷贝。
1.4 String中相等判断equal

equals 和 equalsIgnoreCase。后者判断相等时,会忽略大小写, 近期看见一些面试题在问:如果让你写判断两个 String 相等的逻辑,应该如何写

public boolean equals(Object anObject) { 
// 判断内存地址是否相同 
if (this == anObject) { 
return true; 
}
// 待比较的对象是否是 String,如果不是 String,直接返回不相等 
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; 
}

比较两个对象是否相同的一种思路:我们可以从两者的底层结构出发,这样可以迅速想到一种贴合实际的思路和方法,就像 String 底层的数据结构是 char的数组一样,判断相等时,就挨个比较 char 数组中的字符是否相等即可。

1.5 替换、删除

replace 有两个方法,一个入参是 char,一个入参是 String,前者表示替换所有字符,如:name.replace('a','b') ,后者表示替换所有字符串,如: name.replace("a","b") ,两者就是单引号和多引号的区别。

public void testReplace(){ 
String str ="hello word !!"; 
log.info("替换之前 :{}",str); 
str = str.replace('l','d'); 
log.info("替换所有字符 :{}",str); 
str = str.replaceAll("d","l"); 
log.info("替换全部 :{}",str); 
str = str.replaceFirst("l",""); 
log.info("替换第一个 l :{}",str); }
//输出的结果是: 
替换之前 :hello word !! 
替换所有字符 :heddo word !! 
替换全部 :hello worl !! 
替换第一个 :helo worl !!
1.6 拆分和合并

拆分我们使用 split 方法,该方法有两个入参数。第一个参数是我们拆分的标准字符,第二个参 数是一个 int 值,叫 limit,来限制我们需要拆分成几个元素。

String s ="boo:and:foo"; 
// 我们对 s 进行了各种拆分,演示的代码和结果是: 
s.split(":") 结果:["boo","and","foo"] 
s.split(":",2) 结果:["boo","and:foo"] 
s.split(":",5) 结果:["boo","and","foo"] 
s.split(":",-2) 结果:["boo","and","foo"] 
s.split("o") 结果:["b","",":and:f"] 
s.split("o",2) 结果:["b","o:and:foo"]

那如果字符串里面有一些空值呢,拆分的结果如下:

String a =",a,,b,"; a.split(",") 
结果:["","a","","b"]

但 Guava(Google 开源的技术工具) 提供了一些可靠的工具类,可以帮助我们快速去掉空值,如下:

List<String> list = Splitter.on(',') 
                    .trimResults()// 去掉空格 
                    .omitEmptyStrings()// 去掉空值 
                    .splitToList(a); 
log.info("Guava 去掉空格的分割方法:{}",JSON.toJSONString(list)); 
// 打印出的结果为: ["a","b c"]
1.7 组合使用String.join()方法
String result = String.join("-",“a”,“b”,“c”,“d”);
输出结果如下:a-b-c-d
也可使用如下方式:
String[] arr = {“a”,“b”,“c”,“d”};
String result = String.join("-",arr);
输出结果如下:a-b -c-d

如果 join 的是一个 List,无法自动过滤掉 null 值。
而 Guava 正好提供了 API,解决上述问题,我们来演示一下:

// 依次 join 多个字符串,Joiner 是 Guava 提供的 API 
Joiner joiner = Joiner.on(",").skipNulls(); 
String result = joiner.join("hello",null,"china"); 
log.info("依次 join 多个字符串:{}",result); 
List<String> list = Lists.newArrayList(new String[]{"hello","china",null}); 
log.info("自动删除 list 中空值:{}",joiner.join(list)); 
// 输出的结果为; 
依次 join 多个字符串:hello,china 
自动删除 list 中空值:hello,china
1.8 Long的缓存

Long 最被我们关注的就是 Long 的缓存问题,Long 自己实现了一种缓存机制,缓存了从 -128 到 127 内的所有 Long 值,如果是这个范围内的 Long 值,就不会初始化,而是从缓存中拿,缓存初始化源码如下:

private static class LongCache { 
private LongCache(){} 
// 缓存,范围从 -128 到 127,+1 是因为有个 0 
static final Long cache[] = new Long[-(-128) + 127 + 1]; 
// 容器初始化时,进行加载 
static { 
  // 缓存 Long 值,注意这里是 i - 128 ,所以再拿的时候就需要 + 128 
  for(int i = 0; i < cache.length; i++) 
     cache[i] = new Long(i - 128); 
} 
}
1.9 为什么使用 Long 时,大家推荐多使用 valueOf 方法,少使用 parseLong 方法

答:因为 Long 本身有缓存机制,缓存了 -128 到 127 范围内的 Long,valueOf 方法会从缓存中去拿值,如果命中缓存,会减少资源的开销,parseLong 方法就没有这个机制。

2.0 如何解决 String 乱码的问题

答:乱码的问题的根源主要是两个:字符集不支持复杂汉字、二进制进行转化时字符集不匹配,
所以在 String 乱码时我们可以这么做:

  1. 所有可以指定字符集的地方强制指定字符集,比如 new String 和 getBytes 这两个地方;
  2. 我们应该使用 UTF-8 这种能完整支持复杂汉字的字符集。
2.1 为什么大家都说 String 是不可变的

答:主要是因为 String 和保存数据的 char 数组,都被 final 关键字所修饰,所以是不可变的,具体细节描述可以参考上文。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351