java tostring底层_String底层解析

本文详细探讨了Java中String类的内部结构、构造方法、equals()和compareTo()的对比,以及常用的字符串操作方法。讲解了String为何被设计为final类、与StringBuilder/StringBuffer的区别,并介绍了字符串创建的两种方式及其在内存中的表现。此外,还提到了编译器对String字符串拼接的优化。
摘要由CSDN通过智能技术生成

目录

关于String

String类型的底层代码是Java所有底层代码中相对来说比较简单、好理解。同时,String底层的源码又是面试官经典的“面试第一题”。“良好的开端就是成功的一半”,因此这个问题回答的好坏会很大程度影响你接下来的面试。我会在这篇博客中梳理几个面试中频率较高的知识点。

String内部结构

String内部存储结构为char数组,有两个私有变量,一个是char[],哈希值。

具体结构:

class String implements Serializable,Comparable,CharSequence {

//用于存储字符串的值

private char value[];

//缓字符串的hash code

private int hash;

}

String的构造方法

String的构造方法有四个,前两个比较常见,参数是String字符串和char[]数组。

后两个是容易被忽略的小透明,参数是StringBuffer和StringBuilder

1.String为参数的构造方法

//String为参数的构造方法

public String(String original){

this.value = original.value;

this.hash = original.hash;

}

2.char[] 为参数的构造方法

//char[]为参数的构造方法

public String(char value[]){

this.value = Arrays.copyOf(value,value.length);

}

3.StringBuffer为参数的构造方法

//StringBuffer为参数的构造方法

public String(StringBuffer buffer){

synchronized(buffer){

this.value=Array.copyOf(buffer.getValue(),buffer.length())

}

}

4.StringBuilder为参数的构造方法

//StirngBuilder为参数的构造方法

public String(StringBuilder builder){

this.value = Arrays.copyOf(builder.getValue(),builder.length());

}

String中的对比——equals()和compareTo()的对比

equals()方法

String中的equals()方法是重写了Object类中的equals()方法,其本质是利用了“==”。

equals()方法先检验对比双方的引用地址是否相等(“==”),如果地址相等,对比双方自然相等,返回true。然后检验需要对比的对象是否为String类型(“instanceof”),如果不是则返回false。之后对比两个字符串的长度是否对等(“==”),最后将两个字符串都转化为char[]形式,循环对比每一个字符。

源码:

public boolean equals(Object anObject){

//对象引用相同直接返回true

if(this==anObject){

return true;

}

//判断需要对比的值是否为String类型,如果不是则返回false

if(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){

//如果其中有一个字符不相等就true false,否则继续对比

if(v1[i]!=v2[i])

return false;

i++;

}

return true;

}

}

return false;

}

compareTo()方法

compareTo()方法不涉及地址的对比,它只是循环比较所有的字符串。当两个字符串中有任意一个字符不相同的时候,返回两个字符的差值。如果长度不对等则返回两个字符串长度的差值。

源码:

public int compareTo(String anotherString){

int len1 = value.length;

int len2 = anotherString.value.length;

//获取到两个字符串长度最短的那个int值

int lim = Math.min(len1,len2);

char v1[]=value;

char v2[]=anotherString.value;

int k=0;

//对比每一个字符

while(k

char c1=v1[k];

char c2=v2[k];

if(c1!=c2){

//有字符不相等就返回差值

return c1-c2;

}

k++;

}

//检验长度

return len1-len2;

}

小结:String中compareTo()和equals()方法的异同点

不同点:

equals()可以接收一个Object类型的参数,而compareTo()只能接收String类型的参数

equals()返回值为Boolean,而compareTo()的返回值则为int

相同点:

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

String的常用方法清单

indexOf():查询字符串首次出现的下标位置

lastIndexOf():查询字符串最后出现的下标位置

contains(): 查询字符串中是否包含另一个字符串

toLowerCase(): 把字符串全部转换成小写

toUpperCase(): 把字符串全部转换成大写

length(): 查询字符串的长度

trim(): 去掉字符串首尾空格

replace():替换字符串中的某些字符

split(): 把字符串分割并返回字符串数组

join(): 把字符串数组转为字符串

关于equals()方法:“==”和equals()的区别?

“==”:

对于基本数据类型来说,是比较两者的值是否相等

对于引用数据类型来说,是用于比较两者引用地址是否相等

equals():

String类型的equals()是重写了Object类中的equals()方法,他的基本实现是靠的“==”

Object类中的equal方法:

public boolean equals(Object obj){

return (this==obj);

}

为什么用final修饰String类?

对于这个问题,Java之父James Gosling在一次记者采访中说明过,大体的原因如下:

1.安全

迫使String类被设计成不可变类的一个原因就是安全。(在进行系统的校验工作中,如果设为可变类,就有可能会出现严重的系统崩溃问题。)

举例:字符串常量池

2.高效

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

String和StringBuilder、StringBuffer的区别

首先,考虑到安全和性能问题,String类是不可变的,所以在字符串拼接的时候如果使用String的话性能会很低。因此就需要使用另一个数据类型StringBuffer。

StringBuffer:

它提供了append和insert方法可用于字符串的拼接

它使用了synchronized来保证线程安全

StringBuffer中append()方法:

public synchronized StringBuffer append(Object obj){

toStringCache = null;

super.append(String.valueOf(obj));

return this;

}

public synchronized StringBuffer append(String str){

toStringCache = null;

super.append(String.valueOf(str));

return this;

}

但是由于StringBuffer保证了线程的安全,所以性能上来讲 —— 很低。

于是在JDK1.5中推出了线程不安全,但是性能相对而言较高的StringBuilder。其功能和StringBuffer一样。

String两种创建方法的异同

我们先来看看创建String的两种方式:

方式一:

String s1 = "java"; //直接赋值

方式二:

String s2 = new String("java"); //对象创建

这两种方法都可以创建字符串,但是两个方法在JVM的存储区域截然不同

方法一:

jvm会先到字符串常量池中寻找是否有相同的字符串,如果有就返回常量句柄;如果没有该字符串,就先在常量池中创建此字符串,然后再返回常量句柄。

方法二:

直接在堆中创建此字符串,只有调用intern()才会放入字符串常量池中。

举例:

String s1 = new String("java");

String s2 = s1.intern();

String s3 = "java";

System.out.println(s1==s2); //false

System.out.println(s2==s3); //true

(s2,s3指向堆中常量池的“java”,s1指向堆中的“java”对象)

== 补充:jdk1.7之后把永生代换成了元空间,把字符串常量池从方法区移到了java堆上 ==

编译器对String字符串的优化

我们经常会用“+”来拼接两个字符串。即使是这样拼接的字符串,在进行比较时,也不会出现和结果相同字符串不对等的情况。这是编译器对于String的优化。

举例:

String s1 = "ja" + "va";

String s2 = "java";

System.out.println(s1==s2); //true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值