资讯专栏INFORMATION COLUMN

面试别再问我String了

leon / 1007人阅读

摘要:阅读原文面试别再问我了字符串广泛应用在编程中,在中字符串属于对象,提供了类来创建和操作字符串。测试此字符串是否以指定的后缀结束。当执行此句时,因为对应的实例已经存在于字符串常量池中,所以会将此实例复制到会在堆中并返回引用地址。

阅读原文:面试别再问我String了

字符串广泛应用 在Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。

String 简介

String定义:

public final class String
    implements java.io.Serializable, Comparable, CharSequence {}
为什么设计为不可变类呢?
String设计为不可变类主要考虑到:效率和安全。

效率:1.在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。2.缓存hashcode,String不可变,所以hashcode不变,这样缓存才有意义,不必重新计算。

安全:String常被作为网络连接,文件操作等参数类型,倘若可改变,会出现意想不到的结果。

测试掌握程度

为了不浪费你的时间,请看下面的题目,若你一目了然,可以跳过本文了。

public class Test {
    public static void main(String[] args) {
        String str1 = "HelloFlyapi";
        String str2 = "HelloFlyapi";
        String str3 = new String("HelloFlyapi");
        String str4 = "Hello";
        String str5 = "Flyapi";
        String str6 = "Hello" + "Flyapi";
        String str7 = str4 + str5;

        System.out.println("str1 == str2 result: " + (str1 == str2));

        System.out.println("str1 == str3 result: " + (str1 == str3));

        System.out.println("str1 == str6 result: " + (str1 == str6));

        System.out.println("str1 == str7 result: " + (str1 == str7));

        System.out.println("str1 == str7.intern() result: " + (str1 == str7.intern()));

        System.out.println("str3 == str3.intern() result: " + (str3 == str3.intern()));
    }
}

String 的创建方式

从上面的题中你会知道,String的创建方式有两种:

直接赋值

此方式在方法区中字符串常量池中创建对象

String str = "flyapi";

构造器

此方式在堆内存创建对象

String str = new String();

分析

要理解String,那么要了解JVM内存中的栈(stack)、堆(heap)和方法区。简要图如下:

str1 == str2

String str1 = "HelloFlyapi";
String str2 = "HelloFlyapi";

System.out.println(str1 == str2); // true

当执行第一句时,JVM会先去常量池中查找是否存在HelloFlyapi,当存在时直接返回常量池里的引用;当不存在时,会在字符创常量池中创建一个对象并返回引用。

当执行第二句时,同样的道理,由于第一句已经在常量池中创建了,所以直接返回上句创建的对象的引用。

str1 == str3

String str1 = "HelloFlyapi";
String str3 = new String("HelloFlyapi");

System.out.println(str1 == str3); // false

执行第一句,同上第一句。

执行第二句时,会在堆(heap)中创建一个对象,当字符创常量池中没有‘HelloFlyapi’时,会在常量池中也创建一个对象;当常量池中已经存在了,就不会创建新的了。

str1 == str6

String str1 = "HelloFlyapi";
String str6 = "Hello" + "Flyapi";

System.out.println(str1 == str6); // true

由于"Hello"和"Flyapi"都是常量,编译时,第二句会被自动编译为‘String str6 = "HelloFlyapi";’

str1 == str7

String str1 = "HelloFlyapi";
String str4 = "Hello";
String str5 = "Flyapi";
String str7 = str4 + str5;

System.out.println(str1 == str7); // false

其中前三句变量存储的是常量池中的引用地址。

第四句执行时,JVM会在堆(heap)中创建一个以str4为基础的一个StringBuilder对象,然后调用StringBuilder的append()方法完成与str5的合并,之后会调用toString()方法在堆(heap)中创建一个String对象,并把这个String对象的引用赋给str7。

常用方法

下面是 String 类支持的方法,更多详细,参看 Java String API 文档:

方法 描述
char charAt(int index) 返回指定索引处的 char 值。
int compareTo(Object o) 把这个字符串和另一个对象比较。
int compareTo(String anotherString) 按字典顺序比较两个字符串。
boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结束。
boolean equals(Object anObject) 将此字符串与指定的对象比较。
boolean equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较,不考虑大小写。
byte[] getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
byte[] getBytes(String charsetName) 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。
int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
int indexOf(String str) 返回指定子字符串在此字符串中第一次出现处的索引。
int indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
String intern() 返回字符串对象的规范化表示形式。
int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。
int lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。
int lastIndexOf(String str) 返回指定子字符串在此字符串中最右边出现处的索引。
int lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
int length() 返回此字符串的长度。
boolean matches(String regex) 告知此字符串是否匹配给定的正则表达式。
String replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String replaceAll(String regex, String replacement) 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。
String[] split(String regex, int limit) 根据匹配给定的正则表达式来拆分此字符串。
boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开始。
boolean startsWith(String prefix, int toffset) 测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串。
String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串。
char[] toCharArray() 将此字符串转换为一个新的字符数组。
String toLowerCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
String toUpperCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
String trim() 返回字符串的副本,忽略前导空白和尾部空白。
String相关

由于String的不可变性导致,字符串变更时效率低下,在之后得JDK版本中出现了StringBuilder和StringBuffer.

可变性 线程安全
String 不可变 安全
StringBuffer 可变 安全
StringBuilder 可变 非安全

使用选择

当有少量连接操作时,使用String

当单线程下有大量连接操作时,使用StringBuilder

当多线程下有大量连接操作时,使用StringBuffer

常见String面试题

String str = new String("abc")创建了多少个实例?

这个问题其实是不严谨的,但面试一般会遇到,所以我们要补充来说明。

类的加载和执行要分开来讲:
创建了两个

当加载类时,"abc"被创建并驻留在了字符创常量池中(如果先前加载中没有创建驻留过)。

当执行此句时,因为"abc"对应的String实例已经存在于字符串常量池中,所以JVM会将此实例复制到会在堆(heap)中并返回引用地址。

通过字节码我们可以看到:

源码:String str = new String("abc")

字节码:

    Code:
       0: new           #2                  // class java/lang/String
       3: dup
       4: ldc           #3                  // String abc
       6: invokespecial #4                  // Method java/lang/String."":(Ljava/lang/String;)
       9: astore_1
      10: return

执行时仅(#2)创建了一个对象。

关于这个面试题,可以看看一个超大牛的回答:http://rednaxelafx.iteye.com/...

本文优先发布于微信公众号:码上实战 和 GitHub 上。

GitHub flyhero

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73991.html

相关文章

  • 面试被问烂的 Spring IOC(求求你再问)

    摘要:例如资源的获取,支持多种消息例如的支持,对多了工具级别的支持等待。最上面的知道吧我就不讲了。生命周期事件回调等。他支持不同信息源头,支持工具类,支持层级容器,支持访问文件资源,支持事件发布通知,支持接口回调等等。 广义的 IOC IoC(Inversion of Control) 控制反转,即不用打电话过来,我们会打给你。 两种实现: 依赖查找(DL)和依赖注入(DI)。 IOC 和...

    denson 评论0 收藏0
  • 小而有料的final

    摘要:阅读原文小而有料的想听文章核心内容语音对话版,请到微信公众号码上实战,阅读原文这块肉虽然小,但不可不知,因为不仅面试可能会问,实际中也常使用。参考书籍编程思想深入理解虚拟机更多精彩技术文章尽在微信公众号码上实战 阅读原文:小而有料的final(想听文章核心内容语音对话版,请到微信公众号——码上实战,阅读原文) final这块肉虽然小,但不可不知,因为不仅面试可能会问,实际中也常使用。就...

    Baoyuan 评论0 收藏0
  • 不要再问我this的指向问题

    摘要:所以构造函数里的指的就是将要被出来的新对象。希望看完这篇文章之后,再有人问指向的问题,你可以嘴角微微上扬,冷笑一声不要再问我的指向问题了。 this的指向已经是一个老生常谈的问题,每逢面试都要去复习复习,近来巩固js的基础,决心彻底掌握这个知识点,一劳永逸。说明一下,为了不影响大家的思考过程,下面的代码都不会去注释答案,想知道答案,只需要去控制台执行一下。 四类场景逐一击破 首先,分析...

    nifhlheimr 评论0 收藏0
  • 拜托!面试请不要再问我Spring Cloud底层原理!

    摘要:不过大多数讲解还停留在对功能使用的层面,其底层的很多原理,很多人可能并不知晓。每个线程池里的线程就仅仅用于请求那个服务。 欢迎关注微信公众号:石杉的架构笔记(id:shishan100) 每日更新!精品技术文章准时送上! 目录 一、业务场景介绍 二、Spring Cloud核心组件:Eureka 三、Spring Cloud核心组件:Feign 四、Spring Cloud核心组件:R...

    wums 评论0 收藏0
  • 拜托!面试请不要再问我Spring Cloud底层原理!

    摘要:不过大多数讲解还停留在对功能使用的层面,其底层的很多原理,很多人可能并不知晓。每个线程池里的线程就仅仅用于请求那个服务。 欢迎关注微信公众号:石杉的架构笔记(id:shishan100) 每日更新!精品技术文章准时送上! 目录 一、业务场景介绍 二、Spring Cloud核心组件:Eureka 三、Spring Cloud核心组件:Feign 四、Spring Cloud核心组件:R...

    wangjuntytl 评论0 收藏0

发表评论

0条评论

leon

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<