String是如何保证不变的?反射为什么可以改变String的值?

news2024/11/17 7:37:46

String是如何保证不变的?反射为什么可以改变String的值?

1. String字符串的源码分析

String 字符串到底能不能改变已经是老生常谈的问题了,但是在面试环节中,依然能够难住不少人。

下面我们根据 JDK1.8 版本下的String源码进行分析,一步一步的了解String字符串的不可变性。

image-20230214154817472

String底层是使用final修饰的字符数组 value[] 来存储字符,而数组是引用类型,引用类型的值是内存中的地址,地址在初始化之后不可变,所以String的值不可变。

2. 通过反射改变字符串的值

虽然final修饰的数组地址不可改变,但是地址指向的值(堆内存中)是可以改变的,String没有对外提供相应的方法来更改值,但是可以通过反射实现。

import java.lang.reflect.Field;

public class stringDemo {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String s = "abc";
        Class clz = s.getClass();
        //需要使用getDeclaredField(), getField()只能获取公共成员字段
        Field field = clz.getDeclaredField("value");
        field.setAccessible(true);

        char[] ch =(char[])field.get(s);
        ch[1] = '8';
        System.out.println(s);
    }
}

打印结果:

image-20230216105045200

(在实际开发中,很少需要通过反射来修改String的值。这里只是提供一种思路,在某些情况下可以帮助我们解决一些实际问题。)

上面我们通过案例知道了,字符串的字符数组可以通过反射进行修改,导致字符串的“内容”发生了变化。但即使是内容发生了改变,它的hash值也是不会改变的:

import java.lang.reflect.Field;

public class stringDemo {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String s = "abc";
        System.out.println("str=" + s + "," +  s.hashCode());
        Class clz = s.getClass();
        //需要使用getDeclaredField(), getField()只能获取公共成员字段
        Field field = clz.getDeclaredField("value");
        field.setAccessible(true);

        char[] ch =(char[])field.get(s);
        ch[1] = '8';
        System.out.println("str=" + s + "," + s.hashCode());
    }
}

运行结果:

image-20230216110228618

从上述结果可以知道,String 字符串对象的 value 数组的元素是可以被修改的,但是hash值没有发生改变,也就是说对象没有变。

但是字符串两次打印出来的结果不一样,计算的hash值为什么是一样的呢?

/** Cache the hash code for the string */
    private int hash; // Default to 0
/**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using {@code int} arithmetic, where {@code s[i]} is the
     * <i>i</i>th character of the string, {@code n} is the length of
     * the string, and {@code ^} indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

发现在第一次调用 hashCode 函数之后,字符串对象内通过 hash 这个属性缓存了 hashCode的计算结果(只要缓存过了就不会再重新计算),因此第二次打印hash值和第一次相同。

3. 如何理解String字符串的不可变性呢?

首先将 String 类声明为 fianl 保证不可继承。

然后,所有修改的方法都返回新的字符串对象,保证修改时不会改变原始对象的引用。

image-20230218093416810

image-20230218093514172

。。。。。。

接下来我们来分析下面这段代码,

import java.lang.reflect.Field;

public class stringDemo {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String s = "abc";
        System.out.println("str=" + s + "," +  s.hashCode());
        Class clz = s.getClass();
        //需要使用getDeclaredField(), getField()只能获取公共成员字段
        Field field = clz.getDeclaredField("value");
        field.setAccessible(true);

        char[] ch =(char[])field.get(s);
        ch[1] = '8';
        System.out.println("str=" + s + "," + s.hashCode());
        System.out.println("abc");
    }
}

运行结果:

image-20230218093817638

是不是很神奇,明明打印的是System.out.println(“abc”);但是得到的结果是:a8c。其实道理也很简单,是因为字符串字面量都指向字符串池中的同一个字符串对象(本质是池化的思想,通过复用来减少资源占用来提高性能),通过官方的解释可以明确这一点:

官方地址:https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.5

image-20230218095010665

A string literal is a reference to an instance of class String (§4.3.1, §4.3.3).
字符串字面量是指向字符串实例的一个引用。

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are “interned” so as to share unique instances, using the method String.intern.
字符串字面量都指向同一个字符串实例。
因为字面量字符串都是常量表达式的值,都通过String.intern共享唯一实例。

image-20230218095145221

以上可以分析得到:对象池中存在,则直接指向对象池中的字符串对象,否则创建字符串对象放到对象池中并指向该对象。

因此可以看出,字符串的不可变性是指引用的不可变。

虽然 String 中的 value 字符数组声明为 final,但是这个 final 仅仅是让 value的引用不可变,而不是为了让字符数组的字符不可替换。

由于开始的 abc 和最后的 abc 属于字面量,指向同一个字符串池中的同一个对象,因此对象的属性修改,两个地方打印都会受到影响。

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

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

相关文章

微信Hook逆向-获取登录二维码

文章目录前言一、打开Pc微信&#xff0c;切换到二维码界面二、解析当前二维码内容三、利用Cheat Enginer软件扫描二维码解析文本四、寻找静态偏移五.代码获取二维码网址前言 微信二维码可以Hook获取,也可以通过找到静态偏移的方式读取 提示&#xff1a;以下是本篇文章正文内容…

力扣(LeetCode)240. 搜索二维矩阵 II(C++)

题目描述 枚举 枚举整个矩阵&#xff0c;找到等于 target 的元素&#xff0c;则 return true &#xff0c;否则 return false。 class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int n matrix.size(), m matrix[0]…

DataWhale 大数据处理技术组队学习task2

三、Hadoop分布式文件系统 1. 产生背景 数据量越来越大&#xff0c;一台独立的计算机已经无法存储所有的数据---->将大规模的数据存储到成百上千的计算机中------为了解决数据管理以及维护极其繁琐与低效------>分布式文件系统 分布式文件系统是管理网络中跨多台计算机…

基于SSM框架的狼途汽车门店管理系统的设计与实现

基于SSM框架的狼途汽车门店管理系统的设计与实现 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、…

AutoCAD学习之基本操作学习笔记

AutoCAD学习 基本操作&#xff08;23.2.15~23.2.17&#xff09; CtrlN 新建一个CAD文档F7 删除格栅F3 对象捕捉&#xff08;很重要啊&#xff0c;如果一直开着&#xff0c;操作起来很费劲。&#xff09;&#xff0c;需要关掉&#xff0c;注意使用snipaste&#xff0c;会不停地…

QT 文件监视系统QFileSystemWatcher监视目录的改变directoryChanged和监视文件的改变fileChanged

QT 文件监视系统QFileSystemWatcher监视目录的改变相关操作说明mainwindow.hmainwindow.cpp调试结果相关操作说明 添加头文件 Header: #include qmake: QT core bool QFileSystemWatcher::addPath(const QString &path)如果路径存在&#xff0c;则会向文件系统监视器添…

Prometheus Docker安装及监控自身

前提环境&#xff1a; Docker环境 涉及参考文档&#xff1a; 安装Prometheus开始 Prometheusnode_exporter Agent组件 一、部署Prometheus 1、启动容器将文件拷贝出来 docker run -d prom/prometheus2、容器将文件拷贝出来 docker cp 容器ID:/usr/share/prometheus/conso…

深度学习笔记:误差反向传播(1)

1 计算图 计算图使用图&#xff08;由节点和边构成的图&#xff09;来表达算式。 如图&#xff0c;我们用节点代表运算符号&#xff0c;用边代表传入的参数&#xff0c;即可算出购买苹果和橘子的总价格。 2 计算图的局部计算 局部计算意味着每个节点只处理和其相关的运算&…

网页设计html期末大作业

网页设计html期末大作业网页设计期末大作业-自制网站大一期末作业&#xff0c;外卖网站设计网页设计期末大作业-精美商城-首页框架网页设计期末大作业-自制网站 有导航栏&#xff0c;轮播图&#xff0c;按钮均可点进去&#xff0c;如下图所示 点我下载资源》》》》 大一期末…

linux ubuntu查日志信息以及错误排查

目录 一、linux的日志文件 1、常用日志文件 2、其他日志文件 二、历史日志的查看 1、查看Logrotate的配置信息 2、查看日志配置 一、linux的日志文件 Linux系统中最有趣的(可能也是最重要的)目录之一是/var/log。根据文件系统层次结构标准&#xff0c;在系统中运行的大多数…

java面试题-泛型异常反射

泛型1.什么是泛型&#xff1f;Java是一种强类型语言&#xff0c;数据类型在编译时必须确定。如果我们想要在代码中使用不同类型的数据&#xff0c;那么就需要为每种类型分别写出相应的代码。这样会导致代码冗长、重复&#xff0c;也不便于维护。为了解决这个问题&#xff0c;Ja…

嵌入式Linux入门级板卡的神经网络框架ncnn移植与测试-米尔i.MX6UL开发板

本篇测评由电子发烧友的优秀测评者“ALSET”提供。 米尔 MYD-Y6ULX-V2 开发板&#xff0c;基于 NXP i.MX6UL/i.MX6UL L处理器&#xff0c;该开发板被米尔称之为经典王牌产品。本次测试目标是在此开发板上进行神经网络框架ncnn的移植与测试开发&#xff0c;测试ncnn在此开发板上…

高可用的“异地多活”架构设计

前言 后台服务可以划分为两类&#xff0c;有状态和无状态。高可用对于无状态的应用来说是比较简单的&#xff0c;无状态的应用&#xff0c;只需要通过 F5 或者任何代理的方式就可以很好的解决。后文描述的主要是针对有状态的服务进行分析。 服务端进行状态维护主要是通过磁盘…

Orin 安装CUDA CUDNN TensorRT Opencv和输入法的环境配置

有两种方法可以安装CUDA环境 第一种方法-用命令按照 在刷机完成的Orin&#xff0c;执行如下命令&#xff1a; sudo apt update sudo apt upgrade sudo apt install nvidia-jetpack -y注释–如果在执行第三行命令&#xff0c;报错的话&#xff0c;先查看nvidia-l4t-apt-so…

初识K8s

概览 k8s 概念和架构从零搭建K8s 集群k8s 核心概念搭建集群监控平台搭建高可用k8s集群集群环境 实际部署项目 k8s 概念和架构 1、K8S概述和特性 概述&#xff1a; k8s是谷歌在2014年开源的容器化集群管理系统使用k8s进行容器化应用部署使用k8s利于应用扩展k8s目标实施让部…

AcWing3416.时间显示——学习笔记

目录 题目 代码 AC结果 思路 关键步骤 题目 3416. 时间显示 - AcWing题库https://www.acwing.com/problem/content/description/3419/ 代码 import java.util.Scanner;public class Main {public static void main(String[] args){Scanner input new Scanner(System.in…

Rust学习入门--【15】Rust 所有权

系列文章目录 Rust 语言是一种高效、可靠的通用高级语言&#xff0c;效率可以媲美 C / C 。本系列文件记录博主自学Rust的过程。欢迎大家一同学习。 Rust学习入门–【1】引言 Rust学习入门–【2】Rust 开发环境配置 Rust学习入门–【3】Cargo介绍 Rust学习入门–【4】Rust 输…

Maven的安装和配置

Maven的安装 1.1Maven是什么&#xff1f; 是阿帕奇的&#xff0c;就是代替原先手动导入jar包的方式 1.官方介绍 视频&#xff1a;百度百科-验证Maven是一款服务于Java平台的自动化构建工具。Maven 作为 Java 项目管理工具&#xff0c;它不仅可以用作包管理&#xff0c;还有许多…

DIDL4_前向传播与反向传播(模型参数的更新)

前向传播与反向传播前向传播与反向传播的作用前向传播及公式前向传播范例反向传播及公式反向传播范例小结前向传播计算图前向传播与反向传播的作用 在训练神经网络时&#xff0c;前向传播和反向传播相互依赖。 对于前向传播&#xff0c;我们沿着依赖的方向遍历计算图并计算其路…

# AutoSar一文概览

1.什么是AutoSar ​ AUTOSAR全称为“AUTomotive Open System ARchitecture”&#xff0c;译为“汽车开放系统体系结构”&#xff1b;AUTOSAR是由 全球各大汽车整车厂、汽车零部件供应商、汽车电子软件系统公司联合建立的一套标准协议、软件架构。 2.为什么汽车行业要定义一个…