Java基础之字符串&equals、==、包装类、常量池

2023-05-16

在java中有三中对字符串的操作方式。

注:文章只注明思路原理。不注明方法,看API就行了。文章就涉及到啥写啥了,哈哈,瞅着可能乱一点。但是这么写就很舒服

1、String                  常量 效率较低  指的是多进行字符串拼接操作时的效率 final

2、StringBuffer        变量 效率其次  多线程安全

3、StringBuilder      变量 效率最快  多线程不安全

先看看String这个类。这个类被final修饰,所以不能被继承。

对于String方法的改变操作方法都是new了一个新的对象。原对象保存不变。

 先说一下String中的equals。下面是源代码

 

可以看到。976行方法入参是个Object,如果是这样的  str.equals(str);那他执行的时候977行就是this==anObject 直接返回,因为是同一个对象。接下来 980行,判断是不是String及其子类的引用。没有子类,这里判断得就是这个引用是不是String的。然后对比字符串中的char[] 每个索引的元素的相同就返回true。

注:要区分一下哦。引用和对象不是一个东西。引用是存放在栈中的,内存地址固定。而对象是存在于堆中的。

注:instanceOf是判断这个对象是不是右边类的实例。

java存放数据的地方有如下这么几个地方(JVM的东西java基础搞定了在写笔记)

1.寄存器。也就是最快的地方,属于处理器的内部。比内存还快。

2.栈。仅次于寄存器。入栈就是开拓一个新的内存地址。出栈便是释放内存地址。有个类似指针的东西来控制访问、存的是基本数据类型的值   对象的引用。 真正的对象,也就是Object的子类对象存放的位置是堆内存中(以及常量池)。线程私有。

3.堆。存放的是对象。也就是最占地方的。线程共享。

4.常量池。也就是不可变的字符串。这个是线程共享的。

5.程序计数器PC。存放的是下一条指令的地址。遍历的是字节码文件。

6.静态区。在程序初始化时会最先初始化静态变量。有特定位置存放静态数据。而不是堆栈中。

说一下==和equals的区别以及hashcode吧。

一、 ==

1、首先 == 这个操作符在基本数据类型中可以理解成值是否相等,对于 int a = 10;  double b = 10.0  a==b  true

==会将a和b转成相同的数据类型进行比较。

byte boolean  char  short  int float long double       boolean取值为false,true默认是false

1  、 1、           2、     2、 4、 4、  8、    8   byte字节  取值范围就是 2的字节数*8(一个字节=8B)次幂,再看符号位。

对于char无论是中文还是英文一个字符就是占2个字节。

对于整型。+ 操作符会转成int操作。  byte b = (byte) 128 ; 这个会溢出的。取补码,也就是二进制取反+1;  输出-128

int a = 1000;
long b = 1000;
double c = 1000.0;
double d = a;
int e = (int) c;    //高向低转需要强转。

2、对于Object的子类,==表示的是是否为同一个对象。即存放在堆内存中的地址是否相等。

二、equals

首先equals是只有对象才能用的。基本数据类型不能用(不要混淆基本数据类型和包装类),因为这个是Object的方法。

下面是Object最初的equals方法。子类需要重写来定义相等的情况

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

可以看出。原始的equals相当于==

说到equals,把包装类顺便也说了吧。0.0   包装类与基本数据类型最大的区别就是它允许存NULL值

首先对于包装类型。对于数值型都有一个父类就是Number.class。  加上Character  Boolean 构成

public static boolean parseBoolean(String s) {
    return ((s != null) && s.equalsIgnoreCase("true"));
}
public Short(String s) throws NumberFormatException {
    this.value = parseShort(s, 10);
}

有个共同的特征。除了Character的构造是char,其余的构造都能传String,然后在parse转换。

而对于转成基本数据类型。下面是Short的源码、

int a = 10;
Integer aa = Integer.valueOf(a);  //包装类与基本数据类型的转换。 相当于自动装箱。

Integer b = new Integer(10);

int bb = b;    // 相当于Integer.intValue()  自动拆箱。   Integer内部有个int类型的value变量。这个方法把这个value返回了

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
public int intValue() {
    return value;
}

对于包装类中的equals。

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
       return value == ((Integer)obj).intValue();//value变量是类内部的int型 再把入参强转+拆箱。再用基本数据类型==比较
    }
    return false;
}

所以包装类 == 是比较是否为同一个对象(或者在常量池范围内)。equals就是看值相不相等。而包装类与基本数据类型的比较就是先拆箱再比较。对于包装类的常量池,每个类不一样,统共就那几个。Integer a = 127; Integer b = 127 true; 换成128就是false

Byte--Long -128~127  Character 0~127  Float Double没有。 Boolean true false

而上文的double,int案例就不存在了。因为equals会判断是否是integer类的实例。

个人认为:基本数据类型是存在栈中的,比较快。而包装类为基本数据类型提供了基于对象的操作。

三、hashCode   参考:Java hashCode() 和 equals()的若干问题解答 - 如果天空不死 - 博客园

1.没涉及到Hash存储结构。此时Hashcode没啥实际意义。没涉及到Hash的情况equals等就ok了。

2.涉及到HashMap等存储结构。Hashcode的值相等时还得equals相等才能算作等。光Hashcode值相等还有哈希冲突的情况。

泛型作为key时,必须Hashcode和equals都重写、hashcode要是不等,就相当于存到俩个位置了。Hash相等再判断equals

看下HashMap中node的equals源码吧。

public final boolean equals(Object o) {
    if (o == this)
        return true;
    if (o instanceof Map.Entry) {
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        if (Objects.equals(key, e.getKey()) &&     //注意这个是Objects不是Object
            Objects.equals(value, e.getValue()))
            return true;
    }
    return false;
}
public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));   //此时比较equals就是泛型重写的equals了。

接着回到String

emmm。这个时候得提一下对象的创建方式

对象有如下这么几个创建方式

1.new 这个很容易,只要构造方法不是private。外部就能随便new(不要抬杠说protected..)

2.反射(下篇文章会介绍) Class.newInstance。或者用Class的构造来创建

3.clone对于实现Cloneable接口的类可以使用这个方法来创建对象。

4.反序列化。在反序列化的时候可以用ObjectInputStream来读取对象。转换成指定的类。

String s = "abc";   String s = new String("abc"); 这是String的创建方式。比较特殊

String s = "abc"; 这个是将s这个引用存放到栈内存中,并在常量池中查找"abc"。查到就返回,没查到就new一个放进常量池。这种如果常量池中存在就不必创建一个新对象了。常量池用来存储运行中创建的字符。String有个intern方法调常量池的引用

String s = new String("abc");这个就是在堆中创建这么一个对象。堆中不要求唯一,创建就开辟一块新地址。

对于这个例子。String s = "abc"; String s1 = "a"+"bc";    s==s1  true   //本处拼接没有用原生引用。是常量池中引用拼接

对于这个例子。String s = "a"; String s1 = "bc"; String s2 = "abc";  s2==(s+s1); 返回的是false;因为s+s1是原生引用拼接。jvm在编译阶段会进行优化,生成一个StringBuffer对象,对每个+连起来的做append操作,最后toString成一个新的对象,放堆里。下面讲一下反编译过程。

下图为StringBuffer的toString()方法。不用管别的行,看673行就知道new了一个新的对象扔堆里了。

tips:堆里面的对象没有被引用,慢慢就被GC回收了。(jvm文章会详细说明)

对于如上这个操作,我们可以用javap反编译来解释。

先放一下源文件代码给大家瞅一眼

在class文件处。shift+鼠标右键打开终端。我把system的部分解释去掉了。

这俩图已经表示的很明显了。

简单总结一下。对于String的equals比较各个位置的字符是否都相等。 == 在没有new或者栈引用拼接时候就相等。

这个是String中重写的Hashcode方法

接下来说一下String中的getBytes方法。因为这个面试时候有被问过。 对于编码就不做过多介绍了。在写代码时候涉及到编码时一定要指明编码格式。否则放到另一个平台就会乱码。windows下GBK ,unix默认是iso

可以看到。一个中文字符GBK占2,utf-8占3,ISO占1

关于String还有最后一个问题。使用final是为什么。

首先final是   private final char value[];  来实现的、引用不可变。

String s = "abc";  s= "def"; 这个是换栈中s位置存放的常量池中的引用。而自身引用是不变的。变得是引用中存放的东西。

String设计成final的为了保证线程安全。并且使用常量池,使操作的效率变高。

上图是对比。对于StringBuffer,不想把之前的值改变。但是事与愿违。

关于String介绍的差不多了。接下来说说StringBuffer和StringBuilder

对于三者的区别。

1.执行速度  StringBuilder>StringBuffer>String 

2.StringBuffer是线程安全的,StringBuilder不是。而String是常量。

多线程用StringBuffer,总操作字符用StringBuffer或者StringBuider。字符操作少用String。(我代码里还没用过StringBuilder。。)

StringBuffer的操作保证线程安全,方法都带了个synchronized

他俩都继承于AbstractStringBuilder 2倍+2扩容。一个线程安全一个不安全,一个快一个稍慢。

void expandCapacity(int minimumCapacity) {    //AbstractStringBuilder 的扩容方法
    int newCapacity = value.length * 2 + 2;
    if (newCapacity - minimumCapacity < 0)
        newCapacity = minimumCapacity;
    if (newCapacity < 0) {
        if (minimumCapacity < 0) // overflow
            throw new OutOfMemoryError();
        newCapacity = Integer.MAX_VALUE;
    }
    value = Arrays.copyOf(value, newCapacity);
}

方法都差不多。看看API就好了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java基础之字符串&equals、==、包装类、常量池 的相关文章

随机推荐

  • Kibana学习&理解

    注 xff1a 本篇的kibana基于7 5 1版本 Kibana是什么 xff1f kibana是一个数据可视化平台 展示与分析 将es里面的东西通过各种图表展示出来 xff0c 还可以执行es的各种搜索 amp 监控 Kibana环境搭
  • filebeat学习

    注 xff1a 本篇基于filebeat7 5 2 filebeat是什么 xff1f Filebeat 是用于转发和集中日志数据的轻量级传送程序 作为服务器上的代理安装 xff0c Filebeat 监视您指定的日志文件或位置 xff0c
  • Git Flow 用法

    git flow 工作流程 如下图所示 master 分支 master 分支主要方稳定 随时可上线的版本 这个分支只能从别的分支上合并过来 xff0c 一般来讲 xff0c 从develop 上合并 xff0c 或者从hotfix分支上合
  • Qt父窗口与子窗口间的焦点传递问题的完美解决

    使用activateWindow 或者raise 参考文章 xff1a https blog csdn net Hoarce article details 107215868 http www manongjc com detail 19
  • Git 工作中的一些命令操作

    本篇为工作中 git 使用过程中的一些操作记载 xff0c 不定期更新 目录 1 git 推本地代码到远程 2 git 放弃修改 commit 撤销远程提交记录 3 git pull push fetch 4 git关联本地与远程分支 5
  • php如何使用S3

    本篇是新手使用PHP调aws的s3服务的一些心得 一 关于AWS S3 s3是一个文件存储服务 xff0c 当需要做成服务来进行微服务调用 xff0c 或者终端服务端文件交流使用s3是一个非常不错的选择 aws各种常见的语言例如 xff1a
  • MySQL相关面试题

    1 MySQL text长度 mysql的text是65535的字节限制 xff0c 而pg是不限制的 2 覆盖索引 聚簇索引 xff08 https blog csdn net alexdamiao article details 519
  • Redis相关面试题

    1 缓存是什么 xff1f 缓存分为本地缓存和分布式缓存 以Java为例 xff0c guava实现的就是本地缓存 xff0c 生命周期随JVM销毁而结束 起多个服务实例 xff0c 就有多份缓存 xff0c 不具有一致性 redis和me
  • 如何断网安装docker

    docker rpm安装 不能联网情况 生产环境可能是不能联网的 xff0c 当我们需要用到docker 或其他组件 的时候 xff0c 就需要借助能联网的环境下载好rpm包 xff0c 然后去操作系统服务器装下载好的docker RPM包
  • docker相关

    优势 xff1a 1 启动快 传统的虚拟机技术启动应用服务往往需要数分钟 xff0c 而 Docker 容器应用 xff0c 由于直接运行于宿主内核 xff0c 无需启动完整的操作系统 xff0c 因此可以做到秒级 甚至毫秒级的启动时间 大
  • Liunx下源代码安装&&make&&makefile

    Linux下安装软件的方式分为源代码安装和二进制安装 源代码安装 xff0c 即使用应用程序源代码进行编译安装二进制安装 xff0c 例如red hat发行的 rpm包 debian发行的 deb包 源代码安装 用c语言为例 include
  • Linux下rpm&yum&apt-get

    RPM简介 RPM命名 RedHat Package Manager xff0c 简称则为RPM 属于Red Hat阵营的 xff0c 与其并列的则是debian centos中大部分我们安装都是使用yum install xff0c 而d
  • Java面试题复习整理(多线程)

    文章目录 1 aio nio bio epoll select2 reactor模式介绍Reactor软件工程java代码总结 3 Java中的cas乐观锁4 自旋锁是什么 xff1f 自旋锁 amp amp 自适应自旋锁 amp amp
  • Grpc&&protocol buffer结合提供grpc服务

    Grpc amp amp protocol buffer 关于下载 xff1a 首先下载一个protobuf 对于mac系统就brew install protobuf 就可以了 然后可以 protoc version 看下安装的版本号 x
  • 指针p,*p,&p之间的区别

    假设我们定义一个指针p 那么会经常使用到三个符号 xff1a 1 xff0c p xff1b p是一个指针变量的名字 xff0c 表示此指针变量指向的内存地址 xff0c 如果使用 p来输出的话 xff0c 它将是一个16进制数 2 xff
  • 自己经历的Java面试题(附答案)

    本篇记录一下自己面试的一些中大厂的 1 3年Java开发的面试题以及自己对题目理解的答案 xff08 结合网上查的资料 xff09 部分可能更新的不及时 xff0c 有问题可以评论区讨论 面试题不分先后 1 hashmap rehash 过
  • Redisson相关使用&&分布式锁浅析

    注 xff1a 本篇的redisson版本基于3 13 3 xff1b 本篇的demo将我写的源代码贴了出来 xff0c 每个方法都有清晰的注释 xff0c 分布式锁相关的代码以及验证是我手动验证Redis中key状态来判断的 文章目录 简
  • SpringBoot实战elasticSearchAPI微服务开发

    文章目录 前言启动一个ElasticSearchSpringBoot引入ElasticSearch索引创建 amp amp 更新插入删除 写 操作ElasticSearch的API查询操作源码参考 前言 本文准备记录一下ElasticSea
  • Leetcode链表刷题思路汇总

    链表 链表相关的题 xff0c 最快的入门方法就是做题的时候画图 标注A的next节点是哪里 xff0c 单链表的遍历只能是单向的 xff0c 从头结点到尾结点 例如 xff1a 给你一个链表的head头 xff0c 让你返回最后元素的值
  • Java基础之字符串&equals、==、包装类、常量池

    在java中有三中对字符串的操作方式 注 xff1a 文章只注明思路原理 不注明方法 xff0c 看API就行了 文章就涉及到啥写啥了 xff0c 哈哈 xff0c 瞅着可能乱一点 但是这么写就很舒服 1 String 常量 效率较低 指的