Java引用类型String源码解析

news2024/9/26 1:26:56

目录

String解析

final的作用

String是否有长度限制

StringBuffer解析

StringBuilder解析

关键字、操作类相关


引用数据类型非常多大致包括:类、 接口类型、 数组类型、 枚举类型、 注解类型、 字符串型。String类型就是引用类型。

String解析

JVM运行时会分配一块空间给String,字符串的分配和其他对象分配一样,需要消耗高昂的时间和空间,JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化,使用字符串常量池,创建字符串常量时,JVM先检查字符串常量池中有没有,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。String类是由final关键字修饰的,字符串具有不可变性,常量池中不会存在两个相同的字符串。

      public class App {
          public static void main(String[] args) {
              String a = "111";
              a = "222";
              System.out.println(a);
          }
      }

引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。所以上面String a = “111”,表达的是变量a里保存了“111”这个对象的引用地址,变量是可以变的,不能变的是“111”。a="222",先去JVM常量池中查找,如果常量池中存在,就直接把对象的引用地址赋给a,如果不存在就重新创建一个对象,然后把对象的引用地址赋给a。

final的作用

当用final修饰一个类时,表明这个类不能被继承。final修饰的类中的成员变量可以根据需要设为final(类中所有成员方法都会被隐式地指定为final方法)。final修饰的方法表示此方法已经是“最后的、最终的”含义,即此方法不能被重写,但可以重载。重写的前提是子类可以从父类中继承此方法,如果父类中final修饰方法同时访问控制权限为private,会导致子类中不能直接继承到此方法,此时可以在子类中定义相同的方法名和参数(类的private方法会隐式地被指定为final方法)。当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化。如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。另外final修饰一个成员变量必须要初始化。有两种初始化方式(在声明的时候给其赋值,在其类的所有构造方法中为其赋值)

    public class FinalDemo {
        private final String name;//1

        public FinalDemo(String name) {//2
            this.name = name;//3
        }

        public FinalDemo() {//4
        }
    }

1处不会通过编译,要先给name初始化值才可以通过编译。

181340de35f4409bb3a0b186bf98ed2c.png

String几个常用方法源码

       public String concat(String str) {
              int otherLen = str.length();
              if (otherLen == 0) {
                  //啥都没有,就直接把当前字符串给你
                  return this;
              }
              int len = value.length;
              char buf[] = Arrays.copyOf(value, len + otherLen);
              str.getChars(buf, len);
              //看到了吗?返回的居然是新的String对象
              return new String(buf, true);
          }
          void getChars(char dst[], int dstBegin) {
              System.arraycopy(value, 0, dst, dstBegin, value.length);
          }    
          public String replace(char oldChar, char newChar) {
              //如果两个是一样的,那就必要替换了,所以返回this
              if (oldChar != newChar) {
                  int len = value.length;
                  int i = -1;
                  //把当前的char数组复制给val,然后下面基于val来操作
                  char[] val = value; 
                  while (++i < len) {
                      if (val[i] == oldChar) {
                          break;
                      }
                  }
                  if (i < len) {
                      //创建一个新的char数组
                      char buf[] = new char[len];
                      for (int j = 0; j < i; j++) {
                          buf[j] = val[j];
                      }
                      while (i < len) {
                          char c = val[i];
                          buf[i] = (c == oldChar) ? newChar : c;
                          i++;
                      }
                      //创建一个新的String对象
                      return new String(buf, true);
                  }
              }
              return this;
          }
          public String substring(int beginIndex, int endIndex) {
              if (beginIndex < 0) {
                  throw new StringIndexOutOfBoundsException(beginIndex);
              }
              if (endIndex > value.length) {
                  throw new StringIndexOutOfBoundsException(endIndex);
              }
              int subLen = endIndex - beginIndex;
              if (subLen < 0) {
                  throw new StringIndexOutOfBoundsException(subLen);
              }
              //正常返回的都是新new出来的String对象
              return ((beginIndex == 0) && (endIndex == value.length)) ? this
                      : new String(value, beginIndex, subLen);
          }
          public String trim() {
              int len = value.length;
              int st = 0;
              char[] val = value;    /* avoid getfield opcode */
              while ((st < len) && (val[st] <= ' ')) {
                  st++;
              }
              while ((st < len) && (val[len - 1] <= ' ')) {
                  len--;
              }
              //如果是该字符串中包含了空格,调用substring方法,否则就是啥都没干原本返回
              //就是如果字符串里有空格,那么还是新生一个String对象返回
              return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
          }

无论是concat、replace、substring还是trim方法的操作都不是在原有的字符串上进行的而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,任何变化性的操作都会生成新的对象。

    public class App {
        public static void main(String[] args) {
            String a = "111";
            String a1 = "111";

            String b = new String("111");

            //对象地址是同一个 
            //==比的是变量的值(值:指向内容的引用地址),equals比的是变量的内容
            System.out.println(a==a1);
            //对象内容是一样的
            System.out.println(a.equals(a1));
            //对象地址不一样
            System.out.println(a==b);
            //对象内容是一样的
            System.out.println(a.equals(b));
        }
    }

结果解析 

输出结果:true true false true 
第一个输出true,a和a1两个变量保存的引用地址是同一个。
第二个输出true,a和a1引用地址中内容是一样的。
String a = "111"在JVM申请内存存放"111"对应的对象,当String a1="111"的时候,先去JVM里寻找是否存在"111",如果存在直接把对象的引用地址给a1。此时的a和a1都保存着同一个引用地址。String b = new String("111")创建一个对象然后把对象引用地址赋给变量b,先去JVM里找 "111",找到了直接存放引用地址。找不到创建一个对象然后把引用地址给String的有参构造方法里。所以第三个中输出false,因为a和b所保存的对象引用是不一样的。
最后一个输出true。那是因为两个变量所保存的引用地址中的内容都是“111”。

String是否有长度限制

在Java中String是有长度限制的,在JVM编译中有规范。String长度限制的场景:将某固定文件转码成Base64的形式用字符串存储,运行时需要的时候在转回来,文件比较大。String a = "ssssssss..."构造的10万个字符的字符串,编译之后虚拟机提示报错,提示字符串长度过长。字符串的内容是由一个字符数组 char[] 来存储的,由于数组的长度及索引是整数且String类中返回字符串长度的方法length()返回值也是int ,通过int类型对应的包装类Integer源码中可以看到其长度最大限制为2^31 -1,说明数组的长度是0~2^31-1,那么大小就是(2^31-1 = 2147483647 = 2GB)。 但是通过翻阅java虚拟机手册对class文件格式的定义以及常量池中对String类型的结构体定义,对于索引定义了u2,就是无符号占2个字节,2个字节可以表示的最大范围是2^16 -1 = 65535, 但是JVM需要1个字节表示结束指令,所以这个范围就为65534了。超出这个范围在编译时期是会报错。


关键字、操作类相关

final关键字:final修饰的类叫最终类,该类不能被继承。final 修饰的方法不能被重写。final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

操作字符串的类有:String、StringBuffer、StringBuilder。String和StringBuffer、StringBuilder的区别在于String声明的是不可变的对象,每次操作都会生成新的String对象,然后将指针指向新的 String对象,而StringBuffer、StringBuilder可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用String。 StringBuffer和StringBuilder最大的区别在于StringBuffer 是线程安全的而StringBuilder是非线程安全的,StringBuilder的性能高于StringBuffer,在单线程环境下推荐使用StringBuilder,多线程环境下推荐使用StringBuffer。String str="i"与String str=new String("i")区别,String str="i"的方式,Java 虚拟机会将其分配到常量池中,String str=new String("i") 则会被分到堆内存中。

字符串反转

              使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
              // StringBuffer reverse
              StringBuffer stringBuffer = new StringBuffer();
              stringBuffer. append("abcdefg");
              System. out. println(stringBuffer. reverse()); // gfedcba
              // StringBuilder reverse
              StringBuilder stringBuilder = new StringBuilder();
              stringBuilder. append("abcdefg");
              System. out. println(stringBuilder. reverse()); // gfedcba

 String类的常用方法:

              indexOf():返回指定字符的索引。
              charAt():返回指定索引处的字符。
              replace():字符串替换。
              trim():去除字符串两端空白。
              split():分割字符串,返回一个分割后的字符串数组。
              getBytes():返回字符串的 byte 类型数组。
              length():返回字符串长度。
              toLowerCase():将字符串转成小写字母。
              toUpperCase():将字符串转成大写字符。
              substring():截取字符串。
              equals():字符串比较。


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1236377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter:多线程Isolate的简单使用

在flutter中如果要使用线程&#xff0c;需要借助Isolate来实现。 简介 在Flutter中&#xff0c;Isolate是一种轻量级的线程解决方案&#xff0c;用于在应用程序中执行并发任务。Isolate可以被认为是独立于主线程的工作单元&#xff0c;它们可以在后台执行任务而不会阻塞应用程…

8.Gin 自定义控制器

8.Gin 自定义控制器 前言 在上一篇路由文件抽离的过程中&#xff0c;我们发现接口的业务逻辑还写在路由配置中&#xff0c;如下&#xff1a; 1696385129126 但是如果业务逻辑比较多&#xff0c;如果写在路由之中&#xff0c;肯定不合适。 我们可以将业务逻辑抽离&#xff0c;单…

【C++进阶之路】第八篇:智能指针

文章目录 一、为什么需要智能指针&#xff1f;二、内存泄漏1.什么是内存泄漏&#xff0c;内存泄漏的危害2.内存泄漏分类&#xff08;了解&#xff09;3.如何检测内存泄漏&#xff08;了解&#xff09;4.如何避免内存泄漏 三、智能指针的使用及原理1.RAII2.智能指针的原理3.std:…

【兔子王赠书第8期】AI短视频制作一本通: 文本生成视频+图片生成视频+视频生成视频

文章目录 写在前面推荐图书关键点内容简介作者简介推荐理由写在后面 写在前面 1本书精通AI短视频制作&#xff0c;文本生成视频图片生成视频视频生成视频AI短视频应用&#xff01;高效视频制作技巧&#xff0c;助你快速成长为行业大咖&#xff01; 推荐图书 《AI短视频制作一…

Stable Diffusion XL网络结构-超详细原创

强烈推荐先看本人的这篇 Stable Diffusion1.5网络结构-超详细原创-CSDN博客 1 Unet 1.1 详细整体结构 1.2 缩小版整体结构 以生成图像1024x1024为例&#xff0c;与SD1.5的3个CrossAttnDownBlock2D和CrossAttnUpBlock2D相比&#xff0c;SDXL只有2个&#xff0c;但SDXL的Cros…

springboot集成nacos作配置中心,动态配置不生效

总体概要 springboot3.0&#xff0c;nacos&#xff0c;jdk17使用nacos配置中心&#xff0c;热更新&#xff0c;使配置动态生效 本文主要介绍springboot怎么集成nacos作为配置中心&#xff0c;使其配置在不重启服务的情况下&#xff0c;怎么生效的。 所用组件及其版本 组件版本…

【ArcGIS Pro微课1000例】0034:矢量数据几何校正案例(Spatial Adjustment)

本案例讲解矢量数据几何校正&#xff0c;根据一个矢量数据去校正另外一个矢量数据。 文章目录 一、加载实验数据二、空间校正三、注意事项 一、加载实验数据 在ArcGIS Pro中加载数据效果如下&#xff1a; design&#xff1a;需要校正的数据图层planroadcenter&#xff1a;目标…

118.184.158.111德迅云安全浅谈如何避免网络钓鱼攻击

随着互联网的不断发展&#xff0c;网络钓鱼攻击也越来越猖獗&#xff0c;给个人和企业带来了巨大的经济损失和安全威胁。本文对如何防范网络钓鱼攻击提出的一些小建议 希望对大家有所帮助。 1.防止XSS&#xff08;跨站脚本攻击&#xff09;攻击 XSS攻击指的是攻击者在网站中注入…

每日一练:组合不重复的四位数字

问题&#xff1a;有四个数字“1、2、3、4”&#xff0c;能组成多少个互不相同且无重复数字的四位数&#xff1f;各是多少&#xff1f; 程序分析 可填在千位、百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去掉不满足条件的排列。 实现方法1 1&#xff09;使用四…

909-2014-T1

文章目录 1.原题2.算法思想3.关键代码4.完整代码5.运行结果 1.原题 为带表头的单链表类Chain编写一个成员函数Reverse&#xff0c;该函数对链表进行逆序操作&#xff08;将链表中的结点按与原序相反的顺序连接&#xff09;&#xff0c;要求逆序操作就地进行&#xff0c;不分配…

Linux学习第44天:Linux 多点电容触摸屏实验:难忘记第一次牵你手的温存

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图内容如下&#xff1a; 二、硬件原理图分析 三、实验程序编写 1、修改设备树 1&#xff09;、添加FT5426所使用的IO 一个复位 IO、一个中断 IO、…

【C++进阶之路】第五篇:哈希

文章目录 一、unordered系列关联式容器1.unordered_map&#xff08;1&#xff09;unordered_map的介绍&#xff08;2&#xff09;unordered_map的接口说明 2. unordered_set3.性能对比 二、底层结构1.哈希概念2.哈希冲突3.哈希函数4.哈希冲突解决&#xff08;1&#xff09;闭散…

【Python3】【力扣题】338. 比特位计数

【力扣题】题目描述&#xff1a; 题解&#xff1a;从0到n的整数&#xff0c;逐一统计二进制中1的个数&#xff0c;记录在一个新列表中。 【Python3】代码&#xff1a; 1、解题思路&#xff1a;Python函数。 知识点&#xff1a;bin(...)&#xff1a;转为二进制字符串&#xff…

秋招JAVA面经总结

面试的范围是Java基础+Java并发+Java框架+mysql+网络。 Java基础 重载与重写有什么区别? 重载(Overloading)指的是在同一个类中,可以有多个同名方法,它们具有不同的参数列表(参数类型、参数个数或参数顺序不同),编译器根据调用时的参数类型来决定调用哪个方法。 重写…

用VS编译ROS包

扩展安装 在扩展中搜索并安装ROS、C、python、CMake和CMake Tools。 打开工作空间 文件→打开文件夹 新建功能包 右键src文件夹&#xff0c;选择新建功能包&#xff08;通常是最后一条命令&#xff09; 编译 如果需要新建终端的话&#xff0c;就点击下图中的加号 创建la…

Java,数据结构与集合源码,数据结构概述

目录 数据结构概念&#xff1a; 数据结构的研究对象&#xff1a; 研究对象一&#xff0c;数据间逻辑关系&#xff1a; 研究对象二&#xff0c;数据的存储结构&#xff08;或物理结构&#xff09;&#xff1a; 研究对象三&#xff1a;运算结构 数据结构的相关介绍&#xff…

泵类设备常见的5种故障及监测方法

在各种工业领域中&#xff0c;泵是一种关键设备&#xff0c;用于输送液体或气体。然而&#xff0c;泵类设备常常会面临各种故障&#xff0c;这可能导致生产停顿和生产效率下降。为了及时监测并解决这些故障&#xff0c;设备状态监测系统成为一种重要的工具。本文将介绍泵类设备…

CSDN专栏设置

文章目录 一、规则1.1、专栏数量与等级关联1.2、等级与积分关联1.3、积分1.3.1、积分获取1.3.2、积分被扣 二、配置2.1、入口2.2、新建2.2.1、一级专栏2.2.2、二级专栏 2.3、快捷编辑2.4、拖拽 一、规则 写了一阵子CSDN博客后&#xff0c;发现自己新增专栏的时候提示不能再新增…

kubeadm join 192.168.10.16:6443 --token xxx报错Failed to request cluster-info

1、node节点执行 kubeadm join 192.168.10.16:6443 --token hak4zi.hrib9uv4p62t1uok --discovery-token-ca-cert-hash sha256:4337638eef783ee6a66045ad699722079e071c2dfbaa21e37d3174f04d58ea97 --v2 报错 [discovery] Failed to request cluster-info, will try again: G…

华为云cce中环境变量的使用

如上图&#xff0c;cce中的环境变量可配置。 配置后的这些参数怎么用呢&#xff1f; 我们可以在docker打包前在springboot的配置文件中配置&#xff0c;cce在启动的时候会调用环境变量中的设置。 如上图&#xff0c;配置的东西以key值标记&#xff0c;冒号后面的是默认配置项…