Java中关于String类型的一些思考
作为初学者在学习Java的时候,变量类型是不可避免会遇到的,在以往我们的印象中字符串String都是作为基本类型而存在的,但是在Java中String类型确是一个实实在在的引用类型,是可以通过new关键字来实例化的,只不过我们在使用的过程中很少使用这种方式去操作,但这并不能否定他是一个引用类型。然而在使用String类型的过程中,也发现了一些有意思的现象,下面就让我们来具体看看这个String类型是如何在基本类型中脱颖而出的。
String类型的内部实现
使用基本类型的时候,在对其赋予不同值的前后,变量的地址会发生变化,所以基本数据类型一旦赋值便不可再次更改。Java中并不存在字符串基本类型,只存在一个叫String的引用类型,但这个引用类型却可以当成和基本类型字符串一样使用,这是如何办到的呢?我们先来看一下String类型的源码实现(在Eclipse中使用Ctrl+鼠标点击即可查看String类型的源代码,用起来还是比较方便的)
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
/**
* The value is used for character storage.
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*
* Additionally, it is marked with {@link Stable} to trust the contents
* of the array. No other facility in JDK provides this functionality (yet).
* {@link Stable} is safe here, because value is never null.
*/
@Stable
private final byte[] value;
打开源码我们发现存在上述的片段,里面有很关键的一句代码private final byte[] value;
,就是这句代码导致存储的字节数组不可以被修改,因为有final关键字修饰,至于为何被final修饰即可保证不被改变,需要详细查看下final的实现机制,在这里不做过多解释。
有趣现象:String类型之间的==操作
在使用String类型的过程中,经常会涉及到判断两个字符串是否相等这种操作,而==则是其中的一种实现方式,下面我们来看下使用这种方式会引发哪些有趣的现象
public static void main(String[] args) {
String str = new String("abc");
String str1 = "abc";
System.out.println(str==str1); //false
String str2 ="abc";
System.out.println(str2==str1); //true
}
看下这段代码的输出结果,是否感到有些疑问,为何同样的字符串在进行操作的时候结果确是两个完全不一样的呢。首先我们就要从运算符下手了,需要知道到底是如何判断两个变量是否相等的,它的依据又是什么。这里我直接揭秘答案吧,在Java中操作实际比较的是两个对象的值,知道了这一点我们先来看第一个输出的结果为什么是false。str在进行初始化的时候使用了new关键字的形式来实例化。在Java中每次new对象都会开辟新的地址空间,str在new实例化的过程中完成了两件事:为new String对象和"abc" 开辟地址,并将String内部字节数组指向"abc"的地址,返回new String对象的地址给str变量;而str1则是直接将"abc"的地址赋值给了它,答案就显而易见了,两个变量虽然值是一样的但是地址却不一样,这就导致输出结果为false。那为什么str1和str2的地址又是相等的呢?在正常情况下,每有一个新变量的时候都会重新开辟地址空间,虽然str2的变量值与str1的相同,也会是为这个新的"abc"开辟新的地址空间的,但是java中的JVM在内存管理的时候优化了这一点,在对str2赋值的时候发现"abc"已经存在了,没必要在重复创建了,所以直接就将其的地址返回给了str2,这才导致str1和str2变量值的==操作返回为true(关于String类型的地址是如何分配的会涉及到常量池,属于JVM关于内存地址的分配和管理问题,感兴趣的朋友可以去看看,对于理解一些原理和现象还是很有用的)。
有趣现象:String类型之间的equals操作
出了上述现象中的==操作可以用来判断两个对象是否相等,还有equals方法也可以用来判断两个对象是否相等,那么使用equals的方式结果又会是如何呢?
public static void main(String[] args) {
String str = new String("abc");
String str1 = "abc";
System.out.println(str.equals(str1)); //true
String str2 ="abc";
System.out.println(str2.equals(str1)); //true
}
老规矩我们先来搞清楚equals方法是什么,是如何进行判等操作的?在“万事万物皆对象”的Java语言当中,几乎所有的类都继承自Object,并在Object中提供了一些公共的属性或方法,equals就在其中,下面我看来看下源码(同样是按住Ctrl+鼠标点击)
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
/**
* Constructs a new object.
*/
@HotSpotIntrinsicCandidate
public Object() {}
@HotSpotIntrinsicCandidate
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
为了看起来方便我去掉了部分的注释。咦?在equals的内部实现竟然是使用==操作来判断两个对象是否相等的,既然是这样,那结果应该是第一个现象相同才对啊!是的,单纯看Object类的实现的确是这样的,但是Object作为父类被继承,其中的方法就有可能被重写,之所以会出现如上现象就是因为String 类型重写了这个方法,我们来看下源码,到底是如何实现的。
//String类中的部分代码片段
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
//StringLatin1类中的部分代码片段
final class StringLatin1 {
@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
//StringUTF16类中的部分代码片段
final class StringUTF16 {
@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
int len = value.length >> 1;
for (int i = 0; i < len; i++) {
if (getChar(value, i) != getChar(other, i)) {
return false;
}
}
return true;
}
return false;
}
终于找到原因了,原来重写后的equals方法并不进行地址判断而是对其所存储的值进行遍历,既然对象内存储的值都是相同的那么自然就是相等,返回true也是意料之中。
总结
我们粗略的看了下String在实际应用中不同的用法所展现出来的不同结果并阐述了现象的原因,因此我们在日常使用的过程中,对String类型的判等操作需要使用equals来实现,以免发生预期以外的结果。为什么Java会把String类型设置为引用类型呢?在逐渐接触Java的过程中也开始慢慢的理解了,在Java中存在8大基础数据类型(int,double,long,byte等),但是对基础类型之间的转换操作基础类型是不提供这些方法的,所以Java又提供了每个基础数据类型对应的引用类型(Integer,Long,Double,Byte等)来包装基础数据类型并对其扩充方法,包括数据类型之间的相互转换等。而这些对应的引用类型在使用的过程中却可以自动拆装箱来达到和基本类型一样的使用方式。
Java中关于String类型的一些思考的更多相关文章
- java中关于String 类型数据 的存储方式
Constant Pool常量池的概念: 在讲到String的一些特殊情况时,总会提到String Pool或者Constant Pool,但是我想很多人都不太 明白Constant Pool到底是个 ...
- Java中的String类型
1.基本类型和引用类型 在C语言里面,是有指针这么一个变量类型的,指针变量保存的就是所要指向内容的地址.在Java里面,没有了指针的这么个说法,而是换了一个词:引用类型变量. 先说Java里面的基本类 ...
- Java中关于String类型的10个问题
1. 如何比较两个字符串?用“=”还是equals 简单来说,“==”是用来检测俩引用是不是指向内存中的同一个对象,而equals()方法则检测的是两个对象的值是否相等.只要你想检测俩字符串是不是相等 ...
- Java 中转换为String类型的四种方法
1. 使用 String 的构造方法,用于 byte[], char[], StringBuffer, StringBuilder 类型 2. 使用 String 的静态方法 valueOf() 推荐 ...
- java 中的String类型数据添加双引号
转义符 \ 加上引号 \" <?xml version="1.0"encoding="GBK"?> String temp = &qu ...
- 理解Java中的字符串类型
1.Java内置对字符串的支持: 所谓的内置支持,即不用像C语言通过char指针实现字符串类型,并且Java的字符串编码是符合Unicode编码标准,这也意味着不用像C++那样通过使用string和w ...
- Java中的String到底占用多大的内存空间?你所了解的可能都是错误的!!
写在前面 最近小伙伴加群时,我总是问一个问题:Java中的String类占用多大的内存空间?很多小伙伴的回答着实让我哭笑不得,有说不占空间的,有说1个字节的,有说2个字节的,有说3个字节的,有说不知道 ...
- c/c++中关于String类型的思考
首先说明:String并不是一种内置类型,因此任何通过String声明出来的实例都不是一个变量,不同于内置类型因此String仅仅能称之为一种特殊的型别,没错String是一个类类型. 一般来说c语言 ...
- Java中的Bigdecimal类型运算
Java中的Bigdecimal类型运算 双精度浮点型变量double可以处理16位有效数.在实际应用中,需要对更大或者更小的数进行运算和处理.Java在java.math包中提 供的API类BigD ...
随机推荐
- Python - Django - 页面上展示固定的页码数
如果页数太多的话,全部显示在页面上就会显得很冗杂 可以在页面中显示规定的页码数 例如: book_list.html: <!DOCTYPE html> <html lang=&quo ...
- didMoveToSuperview方法认识和使用
由来: 今天给项目添加新功能——点击弹出阳历,阴历日期选择. 弹出日期选择是弹出的控制器,里面的日期选择控件是封装的View,View使用Xib画的, 遇到的问题是:控制器传数据给View,在awak ...
- IIS7(Windows7)下最简单最强安装多版本PHP支持环境
最近调试程序,要在PHP5.2和5.3之间换来换去,而习惯了windows下的开发,就琢磨怎么在iis下安装多版本支持,赫然发现其实微软都为我们准备了好工具. 微软对PHP的支持越来越强,这点在IIS ...
- SQL Delta实用案例介绍,很好的东西,帮了我不少忙
SQL Delta实用案例介绍 概述 本篇文章主要介绍SQL DELTA的简单使用.为了能够更加明了的说明其功能,本文将通过实际项目中的案例加以介绍. 主要容 SQL DELTA 简介 ...
- SPSS 2019年10月24日 今日学习总结
2019年10月24日今日课上内容1.SPSS掌握基于键值的一对多合并2.掌握重构数据3.掌握汇总功能 内容: 1.基于键值的一对多合并 合并文件 添加变量 合并方法:基于键值的一对多合并 变量 2. ...
- WXSS选择器
- BGP 实验
一.环境准备 1. 软件:GNS3 2. 路由:c7200 二.实验操作 实验要求: 1. 掌握 BGP 的基本配置方法. 2. 掌握如何查看 BGP 的各种配置信息. 3. 掌握基于回环口的 BGP ...
- TCP/IP学习笔记7--TCP/IP模型通信例子学习
"一位如蝴蝶般美丽的女子向我飞来,翩翩的舞姿如同云端轻盈的叶儿." -------------------------------------------------------- ...
- PHP正则匹配价格
/** * 匹配价格 * @param $price * @return bool */ public static function checkPrice($price) { // 不能小于0 if ...
- LeetCode 637. 二叉树的层平均值(Average of Levels in Binary Tree)
637. 二叉树的层平均值 637. Average of Levels in Binary Tree LeetCode637. Average of Levels in Binary Tree 题目 ...