文章目录

  • Pre
  • Q1: String 是如何实现的?
  • Q2: String 有哪些重要的方法?
    • 构造函数
    • equals()
    • compareTo()
    • 【equals() vs compareTo() 】
    • 其他重要方法
  • Q3: 为什么 String 类型要用 final 修饰
  • Q4: == 和 equals 的区别是什么
  • Q5: String 和 StringBuilder、StringBuffer 有什么区别
  • Q6: String 类型在 JVM中是如何存储的?编译器对 String 做了哪些优化

Pre

Java Version : 主流版本JDK 8


Q1: String 是如何实现的?

看到了吧 , 底层存储是 char 数组

public final class String  implements java.io.Serializable, Comparable<String>, CharSequence {// the value is used for character storage    存储字符串的值private final char value[];// Cache the hash code for the string  缓存字符串的 hash codeprivate int hash; // Default to 0// ......
}

Q2: String 有哪些重要的方法?

构造函数

挑几个比较重要的

// String 为参数的构造方法
public String(String original) {this.value = original.value;this.hash = original.hash;
}
// char[] 为参数构造方法
public String(char value[]) {this.value = Arrays.copyOf(value, value.length);
}
// StringBuffer 为参数的构造方法
public String(StringBuffer buffer) {synchronized(buffer) {this.value = Arrays.copyOf(buffer.getValue(), buffer.length());}
}
// StringBuilder 为参数的构造方法
public String(StringBuilder builder) {this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

这里需要提一下的是: 以 StringBuffer 和 StringBuilder 为参数的构造函数容易被忽略,因为String 、 StringBuffer、StringBuilder 这三种数据类型, 通常都是单独使用的哇。 知道就行,反正平常也不这么写

还有其他构造函数 ,大家可以自行看一下


equals()

比较两个字符串是否相等

来看下源码

    public boolean equals(Object anObject) {// 如果是对象引用,直接返回true if (this == anObject) {return true;}// 类型判断  如果不是String类型则直接返回 falseif (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {// 把两个字符串都转换为 char 数组对比char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) { // 循环比对两个字符串的每一个字符if (v1[i] != v2[i])  // 如果其中有一个字符不相等就直接返回false,否则继续对比,直接到结束return false;i++;}return true;}}return false;}

equals() 是String 类型重写的 Object 中的 方法,Object#equals() 方法需要传递一个 Object 类型的参数值所以才有了上面的instanceof 类型判断 。 当判断参数为 String 类型之后,会循环对比两个字符串中的每一个字符,当所有字符都相等时返回 true,否则则返回 false。

【Object#equals()】

 public boolean equals(Object obj) {return (this == obj);  // 仅判断的对象引用,即比较的是对象在内存中的地址}

【instanceof 用法】

Object a= "123";
Object b= 123;
System.out.println(a instanceof String); //  true
System.out.println(b instanceof String); //  false

另外还有一个 equalsIgnoreCase(), 忽略字符串的大小写之后进行字符串对比。


compareTo()

比较两个字符串

   public int compareTo(String anotherString) {int len1 = value.length;int len2 = anotherString.value.length;int lim = Math.min(len1, len2); // 取两个字符串中长度最短的那个字符串的长度  char v1[] = value;char v2[] = anotherString.value;int k = 0;while (k < lim) {  // 对比每一个字符char c1 = v1[k];char c2 = v2[k];if (c1 != c2) {// 有字符不相等时返回差值 return c1 - c2;  }k++;}return len1 - len2;}

从源码总可以看到compareTo() 方法会循环对比所有的字符,当两个字符串中有任意一个字符不相同时,则 return c1 - c2

举个例子

“53334433”.compareTo(“3”) ----> 2 【取最小长度,第一个字符 5 和 3 比,转成char 比较, 不相等 返回 5 - 3 = 2】

再来个例子: 两个字符串分别存储的是 1 和 2,返回的值是 -1;如果存储的是 1 和 1,则返回的值是 0 ,如果存储的是 2 和 1,则返回的值是 1。

还有个compareToIgnoreCase 忽略大小写后比较两个字符串。


【equals() vs compareTo() 】

可以看出 compareTo() 方法和 equals() 方法都是用于比较两个字符串的,但它们有两点不同:

  • equals() 可以接收一个 Object 类型的参数,而 compareTo() 只能接收一个 String 类型的参数
  • equals() 返回值为 Boolean,而 compareTo() 的返回值则为 int

它们都可以用于两个字符串的比较,当 equals() 方法返回 true 时,或者是 compareTo() 方法返回 0 时,则表示两个字符串完全相同


其他重要方法

  • indexOf():查询字符串首次出现的下标位置
  • lastIndexOf():查询字符串最后出现的下标位置
  • contains():查询字符串中是否包含另一个字符串
  • toLowerCase():把字符串全部转换成小写
  • toUpperCase():把字符串全部转换成大写
  • length():查询字符串的长度
  • trim():去掉字符串首尾空格
  • replace():替换字符串中的某些字符
  • split():把字符串分割并返回字符串数组
  • join():把字符串数组转为字符串

Q3: 为什么 String 类型要用 final 修饰

从源码中可以知道String是final修饰的?

为啥子嘞?

高司令以前回答过: 他会更倾向于使用 final,因为它能够缓存结果,当你在传参时不需要考虑谁会修改它的值;如果是可变类的话,则有可能需要重新拷贝出来一个新值进行传参,这样在性能上就会有一定的损失。

String 类设计成不可变的另一个原因是安全,当你在调用其他方法时,比如调用一些系统级操作指令之前,可能会有一系列校验,如果是可变类的话,可能在你校验过后,它的内部的值又被改变了,这样有可能会引起严重的系统崩溃问题,这是迫使 String 类设计成不可变类的一个重要原因。

总之,使用 final 修饰的第一个好处是安全;第二个好处是高效

我们以JVM中的常量池来举个例子

String s1 = "java";
String s2 = "java";

只有字符串是不可变时,我们才能实现字符串常量池。

字符串常量池可以为我们缓存字符串,这样的话不用每次都去开辟一块内存地址存放,自然就提高了运行效率。

如果String是可变的,那字符串常量池就歇菜了。。。。。


Q4: == 和 equals 的区别是什么

【==】

  • 对于基本数据类型来说, 比较 “值”是否相等的
  • 对于引用类型来说, 比较引用地址是否相同的

Object#equals() 其实就是 ==

public boolean equals(Object obj) {return (this == obj);
}

String#equal这是重写了父类Object的equals方法,把它修改成了比较两个字符串的值是否相等,分析如上。


Q5: String 和 StringBuilder、StringBuffer 有什么区别

简单来说:

  • String 不可变 ,正是因为不可变,所以字符串在拼接时,效率低,所以才有了下面两个
  • StringBuffer 线程安全
  • StringBuilder 线程不安全

String 类型是不可变的,所以在字符串拼接的时候如果使用 String 的话性能会很低。

因此我们就需要使用另一个数据类型 StringBuffer,它提供了 append 和 insert 方法可用于字符串的拼接,它使用 synchronized 来保证线程安全

@Override
public synchronized StringBuffer append(Object obj) {toStringCache = null;super.append(String.valueOf(obj));return this;
}
@Override
public synchronized StringBuffer insert(int offset, String str) {toStringCache = null;super.insert(offset, str);return this;
}  

因为它使用了 synchronized 来保证线程安全,所以性能不是很高。

于是在 JDK 1.5 就有了 StringBuilder,它同样提供了 append 和 insert 的拼接方法,但它没有使用 synchronized 来修饰,因此在性能上要优于 StringBuffer,所以在非并发操作的环境下可使用 StringBuilder 来进行字符串拼接。

      @Overridepublic StringBuilder append(String str) {super.append(str);return this;}@Overridepublic StringBuilder insert(int offset, String str) {super.insert(offset, str);return this;}

当然了,append 和 insert的方法入参有很多,这里仅仅列举出了一个,主要是让你看下 synchronized实现上的区别。


Q6: String 类型在 JVM中是如何存储的?编译器对 String 做了哪些优化

String 常见的创建方式有两种

  • new String()
  • 直接赋值

直接赋值的方式会先去字符串常量池中查找是否已经有此值,如果有则把引用地址直接指向此值,否则会先在常量池中创建,然后再把引用指向此值;

new String() 一定会先在堆上创建一个字符串对象,然后再去常量池中查询此字符串的值是否已经存在,如果不存在会先在常量池中创建此字符串,然后把引用的值指向此字符串

举个例子

String s1 = new String("Java");
String s2 = s1.intern();
String s3 = "Java";
System.out.println(s1 == s2); //  ------> false
System.out.println(s2 == s3); //  ------>  true

JDK 1.7 之后把永久代代换成的元空间,把字符串常量池从方法区移到了 Java 堆上

除此之外编译器还会对 String 字符串做一些优化,例如以下代码

String s1 = "Ja" + "va";
String s2 = "Java";
System.out.println(s1 == s2);

输出 true

javap -c 反汇编看一下

从编译代码 #2 可以看出,代码 “Ja”+“va” 被直接编译成了 “Java” ,因此 s1==s2 的结果才是 true,这就是编译器对字符串优化的结果。


Java - String源码解析及常见面试问题相关推荐

  1. Java String源码解析

    String类概要 所有的字符串字面量都属于String类,String对象创建后不可改变,因此可以缓存共享,StringBuilder,StringBuffer是可变的实现 String类提供了操作 ...

  2. 程序兵法:Java String 源码的排序算法(一)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 <程序兵法:Java Str ...

  3. Java Executor源码解析(7)—Executors线程池工厂以及四大内置线程池

    详细介绍了Executors线程池工具类的使用,以及四大内置线程池. 系列文章: Java Executor源码解析(1)-Executor执行框架的概述 Java Executor源码解析(2)-T ...

  4. Java Executor源码解析(3)—ThreadPoolExecutor线程池execute核心方法源码【一万字】

    基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 上一篇文章中,我们介绍了:Java Executor源码解析(2)-ThreadPoolExecutor ...

  5. Java的String为什么不可变?(String源码解析)

    String的源码解析 public final class String{private final char value[];//容器,存放字符串的private int hash;//哈希值pr ...

  6. Java SPI 源码解析及 demo 讲解

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:Java实现QQ登录和微博登录个人原创+1博客:点击前往,查看更多 作者:JlDang 来源:https://s ...

  7. Java HashSet源码解析

    本解析源码来自JDK1.7,HashSet是基于HashMap实现的,方法实现大都直接调用HashMap的方法 另一篇HashMap的源码解析文章 概要 实现了Set接口,实际是靠HashMap实现的 ...

  8. Java Thread 源码解析

    Thread 源码解析 线程的方法大部分都是使用Native使用,不允许应用层修改,是CPU调度的最基本单元.线程的资源开销相对于进程的开销是相对较少的,所以我们一般创建线程执行,而不是进程执行. T ...

  9. Java 正则表达式源码解析

    使用方法 Pattern p = Pattern.compile("a*");Matcher m = p.matcher("aaaa");if (m.find( ...

最新文章

  1. mysql设置约束l命令_2、MYSQL 基本数据库命令及约束
  2. 华为服务器型号查询,服务器设备型号查询
  3. vuex之state-状态对象的获取方法(三)
  4. linux php源码包 安装openssl 和curl 扩展
  5. ubuntu 16.4 安装postgreSQL,使C++链接到数据库
  6. Chrome跨域问题
  7. VUE.JS 组件化开发实践
  8. php 怎么解析文本,PHP解析自定义纯文本数据库
  9. 【华为云技术分享】实战案例丨代码优化:如何去除context中的warning?
  10. cnn训练出现的问题
  11. 卡巴斯基2014激活码授权文件KEY
  12. 升降压型电感电容计算
  13. 计算机二级 MSOffice 考试历年真题精选
  14. 知名的医药管理软件有哪些,说说看
  15. java对象的哈希值_对象的哈希值
  16. 微信登陆失败redirect_uri 域名与后台配置不一致 10003(thinkphp)
  17. D3 二维图表的绘制系列(十七)树图
  18. 【图】Excel快捷键大全+函数公式 职场必备
  19. 【Matplotlib设置】Python绘图全局字体改为 Times New Roman
  20. java语音播报天气_语音播报实时天气

热门文章

  1. Linux下C语言程序的内存布局(内存模型)
  2. tf.data.Dataset 用法
  3. python海伦公式_少儿编程Python第2课-if语句(海伦公式)
  4. 随时找到数据流中的中位数
  5. Linux疑难杂症解决方案100篇(十一)-常用Linux命令,助力工作更轻松便捷
  6. 说了这么久中台,那你知道中台是什么?在治什么病吗?
  7. 补贴背后的商业竞争,你真的懂吗?
  8. 非常值得收藏的 IBM SPSS Modeler 算法简介
  9. WebServices 简介
  10. MATLAB中的varargin,varargout在函数中运用