【Java进阶篇】String中 intern 的原理是什么?

在这里插入图片描述

String中 intern 的原理

  • ✔️ 典型解析
    • ✔️小思考(回顾)
  • ✔️字面量
  • ✔️intern
  • ✔️ intern原理
    • ✔️a和1有什么不同
    • ✔️答案


✔️ 典型解析


字符串常量池中的常量有两种来源:


1、 字面量会在编译期先进入到Class常量池,然后再在运行期进去到字符串池


2、在运行期通过intern将字符串对象手动添加到字符串常量池中


✔️小思考(回顾)


💡String a = “ab”; String b = “a” + “b”; a == b 吗?


在Java中,对于字符串使用 == 比较的是字符串对象的引用地址是否相同。


因为a和b都是由字面量组成的字符串,它们的引用地址在编译时就已经确定了,并且在编译之后,会把字面量直接合在一起。因此,a == b的结果为true,因为它们指向的是同一个字符串对象。


本站跳转博主博文:String 、StringBuffer、StringBuilder 三者区别


✔️字面量


在计算机科学中,字面量 (literal) 是用于表达源代码中一个固定值的表示法 (notation) 。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如: 整数、浮点数以及字符串,而有很多也对布尔类型和字符类型的值也支持字面量表示,还有一些甚至对枚举类型的元素以及像数组、记录和对象等复合类型的值也支持字面量表示法。


以上是关于计算机科学中关于字面量的解释,并不是很容易理解。说简单点,字面量就是指由字母、数字等构成的字符串或者数值。


字面量只可以右值出现,所谓右值是指等号右边的值,如: int a=123这里的 a 为左值,123为右值在这个例子中 123 就是字面量。


int a = 123;
String s = "xinbaobaba";

上面的代码事例中,123 和 xinbaobaba 都是字面量。


JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串常量池。


在JVM运行时区域的方法区中,有一块区域是运行时常量池,主要用来存储编译期生成的各种字面量和符号引用。


了解 Class文件结构或者做过 Java 代码的反编译的朋友可能都知道,在java代码被iavac编译之后,文件结构中是包含一部分Constant pool 的。比如以下代码:


public static void main(String[] args) {String s ="xinbaobaba";
}

经过编译后,常量池内容如下:


Classfile /F:/Java/WhileAndFor.classLast modified 202412; size 290 bytesSHA-256 checksum f92f9977740415349cbf9e3ac5d853f97989c05ac8ebc768a9f0929d3382b39bCompiled from "WhileAndFor.java"
public class WhileAndForminor version: 0major version: 61flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #9                          // WhileAndForsuper_class: #2                         // java/lang/Objectinterfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:#1 = Methodref          #2.#3          // java/lang/Object."<init>":()V#2 = Class              #4             // java/lang/Object#3 = NameAndType        #5:#6          // "<init>":()V#4 = Utf8               java/lang/Object#5 = Utf8               <init>#6 = Utf8               ()V#7 = String             #8             // xinbaobaba#8 = Utf8               xinbaobaba#9 = Class              #10            // WhileAndFor#10 = Utf8               WhileAndFor#11 = Utf8               Code#12 = Utf8               LineNumberTable#13 = Utf8               main#14 = Utf8               ([Ljava/lang/String;)V#15 = Utf8               SourceFile#16 = Utf8               WhileAndFor.java
{public WhileAndFor();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 5: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=1, locals=2, args_size=10: ldc           #7                  // String xinbaobaba2: astore_13: returnLineNumberTable:line 8: 0line 9: 3
}
SourceFile: "WhileAndFor.java"

上面的class文件中的常量池中,比较重要的几个内容:


 #6 = Utf8               ()V#8 = Utf8               xinbaobaba#10 = Utf8               WhileAndFor

上面的几个常量中, v就是前面提到的符号引用,而 xinbaobaba 就是前面提到的字面量


而Class文件中的常量池部分的内容,会在运行期被运行时常量池加载进去。


✔️intern


intern 的作用是这样的:


如果字符串池中已经存在一个等于该字符串的对象,intern() 方法会返回这个已存在的对象的引用


如果字符串池中没有等于该字符串的对象,intern() 方法会将该字符串添加到字符串池中,并返回对新添加的字符串对象的引用。


String s = new String("xinbao") + new String("baba");
s .intern();

所以,无论何时通过 intern()方法获取字符串的引用,都会得到字符串池中的引用,这样可以确保相同的字符串在内存中只有一个实例。


很多人以为知道以上信息,就算是了解 intern 了,那么请回答一下这个问题:


public static void main(String[] args) {String s1 = new String("x");s1.intern();String s2 = "x" ;System.out.println(s1 == s2); // falseString s3 = new String("a") + new String("a");s3.intern();String s4 = "aa";System.out.println(s3 == s4);// true
}

大家可以在JDK 1.7以上版本中尝试运行以上两段代码,就会发现,s1 == s2的结果是 false,但是s3 == s4的结果是 true。


这是为什么呢? ( 后文所有case均基于JDK 1.8运行 )


✔️ intern原理


了解其原理,我们继续分析上面的代码:


public static void main(String[] args) {String s1 = new String("x"); // ①s1.intern();//②String s2 = "x" ;//③System.out.println(s1 == s2); // ④           falseString s3 = new String("a") + new String("a"); //⑤s3.intern(); //⑥String s4 = "aa"; //⑦System.out.println(s3 == s4);// ⑧           true
}

这个类被编译后,Class常量池中应该有 “a” 和 “aa” 这两个字符串,这两个字符串最终会进到字符串池。但是,字面量 “a” 在代码①这一行,就会被存入字符串池,而字面量"aa"则是在代码⑦这一行会存入字符串池。


以上代码的执行过程:


第①行,,new 一个 String 对象,并让 s1指向他

第②行,,对 s1执行 intern,但是因为"a"这个字符串已经在字符串池中,所以会直接返回原来的引用,但是并没有赋值给任何一个变量。

第③行,s2指向常量池中的 "a” 。

所以,s1 和 s2并不相等!

第⑤行,new 一个 String 对象,并让 s3 指向他

第⑥行,对s3 执行 intern,但是目前字符串池中还没有"aa"这个字符串,于是会把 <s3指向的String对象的引用> 放入 <字符串常量池>

第⑦行,因为"aa"这个字符串已经在字符串池中,所以会直接返回原来的引用,并赋值给 s4

所以,s3和 s4 相等!


而,如果我们对代码稍微做一下修改:


String s = "aa"; //①
String s3 = new String("a") + new String("a");// ②
s3.intern();// ③
String s4 ="aa";
System.out.printIn(s3 == s4);// ④

以上代码得到的结果是 : false


第①行,创建一个字符串aa,并且因为它是字面量,所以把他放到字符串池

第②行,new一个 String 对象,并让 s3 指向他

第③行,对 s3 执行 intern,但是目前字符串池中已经有 “aa” 这个字符串,所以会直接返回s的引用但是并没有对 s3 进行赋值

第④行,因为 ”aa" 这个字符串已经在字符串池中,所以会直接返回原来的引用,即 s 的引用,并赋值给 s4; 所以,s3和 s4不相等。


✔️a和1有什么不同


关于这个问题,我们还有一个变型,可以帮大家更好的理解intern,请大家分别在JDK 1.8和JDK 11及以上的版本中执行以下代码:


String s3 = new String("1") + new String("1");// ①
s3.intern(); // ②
String s4 = "11";
System.out.println(s3 == s4); //③

你会发现,在JDK 1.8中,以上代码得到的结果是true,而JDK 11及以上的版本中结果却是false。


那么,再稍作修改呢? 在目前的所有JDK版本中,执行以下代码 :


String s3 = new String("3") + new String("3");//①
s3.intern();//②
String s4 = "33";
System.out.println(s3 == s4);// ③

得到的结果也是true,知道什么原因吗?

✔️答案

出现上述现象,肯定是因为在JDK 11 及以上的版本中,"11"这个字面量已经被提前存入字符串池了。那什么时候存进去的呢? (这个问题,全网应该没人提过)


经过我的攻克,终于发现端倪,就在下面代码中:


/** Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.*********************/package com.sun.tools.javac.code;import java.util.*;import javax.lang.model.SourceVersion;
import static javax.lang.model.SourceVersion.*;import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.Error;
import com.sun.tools.javac.util.JCDiagnostic.Fragment;import static com.sun.tools.javac.main.Option.*;/** The source language version accepted.**  <p><b>This is NOT part of any supported API.*  If you write code that depends on this, you do so at your own risk.*  This code and its internal interfaces are subject to change or*  deletion without notice.</b>*/
public enum Source {/** 1.0 had no inner classes, and so could not pass the JCK. */// public static final Source JDK1_0 =              new Source("1.0");/** 1.1 did not have strictfp, and so could not pass the JCK. */// public static final Source JDK1_1 =              new Source("1.1");/** 1.2 introduced strictfp. */JDK1_2("1.2"),/** 1.3 is the same language as 1.2. */JDK1_3("1.3"),/** 1.4 introduced assert. */JDK1_4("1.4"),/** 1.5 introduced generics, attributes, foreach, boxing, static import,*  covariant return, enums, varargs, et al. */JDK5("5"),/** 1.6 reports encoding problems as errors instead of warnings. */JDK6("6"),/** 1.7 introduced try-with-resources, multi-catch, string switch, etc. */JDK7("7"),/** 1.8 lambda expressions and default methods. */JDK8("8"),/** 1.9 modularity. */JDK9("9"),/** 1.10 local-variable type inference (var). */JDK10("10"),/** 1.11 covers the to be determined language features that will be added in JDK 11. */JDK11("11");private static final Context.Key<Source> sourceKey = new Context.Key<>();public static Source instance(Context context) {Source instance = context.get(sourceKey);if (instance == null) {Options options = Options.instance(context);String sourceString = options.get(SOURCE);if (sourceString != null) instance = lookup(sourceString);if (instance == null) instance = DEFAULT;context.put(sourceKey, instance);}return instance;}public final String name;private static final Map<String,Source> tab = new HashMap<>();static {for (Source s : values()) {tab.put(s.name, s);}tab.put("1.5", JDK5); // Make 5 an alias for 1.5tab.put("1.6", JDK6); // Make 6 an alias for 1.6tab.put("1.7", JDK7); // Make 7 an alias for 1.7tab.put("1.8", JDK8); // Make 8 an alias for 1.8tab.put("1.9", JDK9); // Make 9 an alias for 1.9tab.put("1.10", JDK10); // Make 10 an alias for 1.10// Decline to make 1.11 an alias for 11.}private Source(String name) {this.name = name;}public static final Source MIN = Source.JDK6;private static final Source MAX = values()[values().length - 1];public static final Source DEFAULT = MAX;public static Source lookup(String name) {return tab.get(name);}public Target requiredTarget() {if (this.compareTo(JDK11) >= 0) return Target.JDK1_11;if (this.compareTo(JDK10) >= 0) return Target.JDK1_10;if (this.compareTo(JDK9) >= 0) return Target.JDK1_9;if (this.compareTo(JDK8) >= 0) return Target.JDK1_8;if (this.compareTo(JDK7) >= 0) return Target.JDK1_7;if (this.compareTo(JDK6) >= 0) return Target.JDK1_6;if (this.compareTo(JDK5) >= 0) return Target.JDK1_5;if (this.compareTo(JDK1_4) >= 0) return Target.JDK1_4;return Target.JDK1_1;}/*** Models a feature of the Java programming language. Each feature can be associated with a* minimum source level, a maximum source level and a diagnostic fragment describing the feature,* which is used to generate error messages of the kind {@code feature XYZ not supported in source N}.*/public enum Feature {DIAMOND(JDK7, Fragments.FeatureDiamond, DiagKind.NORMAL),MULTICATCH(JDK7, Fragments.FeatureMulticatch, DiagKind.PLURAL),IMPROVED_RETHROW_ANALYSIS(JDK7),IMPROVED_CATCH_ANALYSIS(JDK7),MODULES(JDK9, Fragments.FeatureModules, DiagKind.PLURAL),TRY_WITH_RESOURCES(JDK7, Fragments.FeatureTryWithResources, DiagKind.NORMAL),EFFECTIVELY_FINAL_VARIABLES_IN_TRY_WITH_RESOURCES(JDK9, Fragments.FeatureVarInTryWithResources, DiagKind.PLURAL),BINARY_LITERALS(JDK7, Fragments.FeatureBinaryLit, DiagKind.PLURAL),UNDERSCORES_IN_LITERALS(JDK7, Fragments.FeatureUnderscoreLit, DiagKind.PLURAL),STRINGS_IN_SWITCH(JDK7, Fragments.FeatureStringSwitch, DiagKind.PLURAL),DEPRECATION_ON_IMPORT(MIN, JDK8),SIMPLIFIED_VARARGS(JDK7),OBJECT_TO_PRIMITIVE_CAST(JDK7),ENFORCE_THIS_DOT_INIT(JDK7),POLY(JDK8),LAMBDA(JDK8, Fragments.FeatureLambda, DiagKind.PLURAL),METHOD_REFERENCES(JDK8, Fragments.FeatureMethodReferences, DiagKind.PLURAL),DEFAULT_METHODS(JDK8, Fragments.FeatureDefaultMethods, DiagKind.PLURAL),STATIC_INTERFACE_METHODS(JDK8, Fragments.FeatureStaticIntfMethods, DiagKind.PLURAL),STATIC_INTERFACE_METHODS_INVOKE(JDK8, Fragments.FeatureStaticIntfMethodInvoke, DiagKind.PLURAL),STRICT_METHOD_CLASH_CHECK(JDK8),EFFECTIVELY_FINAL_IN_INNER_CLASSES(JDK8),TYPE_ANNOTATIONS(JDK8, Fragments.FeatureTypeAnnotations, DiagKind.PLURAL),ANNOTATIONS_AFTER_TYPE_PARAMS(JDK8, Fragments.FeatureAnnotationsAfterTypeParams, DiagKind.PLURAL),REPEATED_ANNOTATIONS(JDK8, Fragments.FeatureRepeatableAnnotations, DiagKind.PLURAL),INTERSECTION_TYPES_IN_CAST(JDK8, Fragments.FeatureIntersectionTypesInCast, DiagKind.PLURAL),GRAPH_INFERENCE(JDK8),FUNCTIONAL_INTERFACE_MOST_SPECIFIC(JDK8),POST_APPLICABILITY_VARARGS_ACCESS_CHECK(JDK8),MAP_CAPTURES_TO_BOUNDS(MIN, JDK7),PRIVATE_SAFE_VARARGS(JDK9),DIAMOND_WITH_ANONYMOUS_CLASS_CREATION(JDK9, Fragments.FeatureDiamondAndAnonClass, DiagKind.NORMAL),UNDERSCORE_IDENTIFIER(MIN, JDK8),PRIVATE_INTERFACE_METHODS(JDK9, Fragments.FeaturePrivateIntfMethods, DiagKind.PLURAL),LOCAL_VARIABLE_TYPE_INFERENCE(JDK10),IMPORT_ON_DEMAND_OBSERVABLE_PACKAGES(JDK1_2, JDK8);enum DiagKind {NORMAL,PLURAL;}private final Source minLevel;private final Source maxLevel;private final Fragment optFragment;private final DiagKind optKind;Feature(Source minLevel) {this(minLevel, null, null);}Feature(Source minLevel, Fragment optFragment, DiagKind optKind) {this(minLevel, MAX, optFragment, optKind);}Feature(Source minLevel, Source maxLevel) {this(minLevel, maxLevel, null, null);}Feature(Source minLevel, Source maxLevel, Fragment optFragment, DiagKind optKind) {this.minLevel = minLevel;this.maxLevel = maxLevel;this.optFragment = optFragment;this.optKind = optKind;}public boolean allowedInSource(Source source) {return source.compareTo(minLevel) >= 0 &&source.compareTo(maxLevel) <= 0;}public boolean isPlural() {Assert.checkNonNull(optKind);return optKind == DiagKind.PLURAL;}public Fragment nameFragment() {Assert.checkNonNull(optFragment);return optFragment;}public Fragment fragment(String sourceName) {Assert.checkNonNull(optFragment);return optKind == DiagKind.NORMAL ?Fragments.FeatureNotSupportedInSource(optFragment, sourceName, minLevel.name) :Fragments.FeatureNotSupportedInSourcePlural(optFragment, sourceName, minLevel.name);}public Error error(String sourceName) {Assert.checkNonNull(optFragment);return optKind == DiagKind.NORMAL ?Errors.FeatureNotSupportedInSource(optFragment, sourceName, minLevel.name) :Errors.FeatureNotSupportedInSourcePlural(optFragment, sourceName, minLevel.name);}}public static SourceVersion toSourceVersion(Source source) {switch(source) {case JDK1_2:return RELEASE_2;case JDK1_3:return RELEASE_3;case JDK1_4:return RELEASE_4;case JDK5:return RELEASE_5;case JDK6:return RELEASE_6;case JDK7:return RELEASE_7;case JDK8:return RELEASE_8;case JDK9:return RELEASE_9;case JDK10:return RELEASE_10;case JDK11:return RELEASE_11;default:return null;}}
}

xdm,在JDK 11 的源码中,定义了”11"这个字面量,那么他会提前进入到字符串池中,那么后续的 intern 的过程就会直接从字符串池中获取到这个字符串引用。


按照这个思路,大家可以在JDK 11中执行以下代码:


String s3 = new String("1") + new String("1");
s3.intern();
String s4 ="11";
System.out.println(s3 == s4);String s3 = new String("1") + new String("2");
s3 .intern();
String s4 ="12";
System.out.println(s3 == s4);

得到的结果就是false和true


或者我是在JDK 21中分别执行了以下代码:


String s3 = new String("2") + new String("1");
s3 .intern();
String s4 = "21";
System.out.printn(s3 == s4);String s3 = new String("2") + new String("2");
s3.intern();
String s4 = "22";
System.out.println(s3 == s4);

得到的结果就也是false和true

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

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

相关文章

milvus学习(一)cosin距离和欧式距离

参考&#xff1a;https://blog.csdn.net/qq_36560894/article/details/115408613 归一化以后的cosin距离和欧式距离可以相互转化&#xff0c;未归一化的不可以相互转化&#xff08;因为距离带单位&#xff09;。

C++多态性——(2)联编

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 成功的秘诀就在于多努力一次&#xff…

自动化测试中,如何增加失败重试机制!

01、前言 在执行自动化测试用例时&#xff0c;会发现有时候用例失败并非代码问题&#xff0c;而是由于服务正在发版&#xff0c;导致请求失败&#xff0c;从而降低了自动化用例的稳定性&#xff0c;最后还要花时间定位到底是自身case的原因还是业务逻辑问题&#xff0c;还是其…

电子招标采购系统源码之从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理

​随着市场竞争的加剧和企业规模的扩大&#xff0c;招采管理逐渐成为企业核心竞争力的重要组成部分。为了提高招采工作的效率和质量&#xff0c;我们提出了一种基于电子化平台的解决方案。该方案旨在通过电子化招投标&#xff0c;使得招标采购的质量更高、速度更快&#xff0c;…

宣传照(私密)勿转发

精美的海报通常都是由UI进行精心设计的&#xff0c;现在有100 件商品需要进行宣传推广&#xff0c;如果每个商品都出一张图显然是不合理的&#xff0c;且商品信息各异。因此需要通过代码的形式生成海报。对此&#xff0c;我也对我宣传一波&#xff0c;企图实现我一夜暴富的伟大…

贪心算法part04 860柠檬水找零 406根据身高重建队列

860 柠檬水找零 406 根据身高重建队列 452 用最少数量的箭引爆气球

六、HTML 段落

HTML 可以将文档分割为若干段落。 一、HTML 段落 段落是通过 <p> 标签定义的。 <p>这是一个段落 </p> <p>这是另一个段落</p> 注意&#xff1a;浏览器会自动地在段落的前后添加空行。&#xff08;</p> 是块级元素&#xff09; 二、不…

[蓝桥杯知识学习] 树链

DFS序 什么是DFS序 怎么求DFS序 进入操作&#xff0c;将有计数 出&#xff1a;可以理解为&#xff0c;没有孩子可以去了&#xff08;不能&#xff0c;向下行动&#xff1a;对应于程序里的入栈&#xff09;&#xff0c;所以回到父结点&#xff08;向上行动&#xff0c;对应于程…

SCENIC+:增强子和基因调控网络的单细胞多组学推理

SCENIC&#xff1a;增强子和基因调控网络的单细胞多组学推理 摘要IntroductionSCENIC 使用超过 30,000 个 TF 基序来预测 eGRNSCENIC 在 PBMC 多组数据上的图示SCENIC prioritizes functional enhancers &#xff08;SCENIC 优先考虑功能增强剂&#xff09;SCENIC simulates ph…

从0到1实战,快速搭建SpringBoot工程

目录 一、前言 二、准备工作 2.1 安装JDK 2.2 安装Maven 2.3 下载IDEA 三、从0到1搭建 3.1 创建SpringBoot工程 3.2 运行SpringBoot工程 四、总结 一、前言 SpringBoot是一个在Spring框架基础上构建的开源框架&#xff0c;不仅继承了Spring框架原有的优秀特性&#x…

Selenium自动化测试之学会元素定位

这是我经常被问到的一个问题&#xff0c;也是我很讨厌回答的问题&#xff0c;因为要想回答这个问题需要知道上下文。什么样式的元素&#xff0c;有哪些属性&#xff0c;是否有表单嵌套&#xff0c;都尝试了哪些定位方法。。。而且没几个提问者能事先详细的说明这些。哪儿像提bu…

centos7通过systemctl启动springboot服务代替java -jar方式启动

背景&#xff1a;以前启动jar包都是通过java -jar&#xff0c;后面了解到一种注册服务式启动jar包的方式&#xff0c;所以做个记录。 注意&#xff1a;我在写该篇博客的时候&#xff0c;给脚本代码都加了#注释来解释该行脚本命令的意思&#xff0c;所以可能出现复制该篇博客脚本…

Android 13 - Media框架(29)- MediaCodec(四)

上一节我们了解了如何通过 onInputBufferAvailable 和 getInputBuffer 获取到 input buffer index&#xff0c;接下来我们一起学习上层如何拿到buffer并且向下写数据的。 1、获取 input Buffer 获取 MediaCodec 中的 buffer 有两种方式&#xff0c;一种是调用 getInputBuffers…

Jsonpath - 数据中快速查找和提取的强大工具

JSON&#xff08;JavaScript Object Notation&#xff09;在现代应用程序中广泛使用&#xff0c;但是如何在复杂的JSON数据中 查找和提取所需的信息呢&#xff1f; JSONPath是一种功能强大的查询语言&#xff0c;可以通过简单的表达式来快速准确地定位和提取JSON数据。本文将介…

如何将内容转化为流量?媒介盒子分享

软文营销就是将内容转为流量的常见做法&#xff0c;但是有许多企业在做内容时往往一头雾水导致效果不佳&#xff0c;做内容不是光靠写就可以的&#xff0c;还需要做好选题、类型、结构等&#xff0c;今天媒介盒子就来和大家聊聊&#xff1a;如何将内容转化为流量。 一、 确定内…

【SpringBoot开发】之商城项目案例(购物车相关操作)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发之商城项目系列》。&#x1f3af…

2023最新租号平台系统源码支持单独租用或合租使用

这是一款租号平台源码&#xff0c;采用常见的租号模式。目前网络上还很少见到此类类型的源码。 平台的主要功能如下&#xff1a; 支持单独租用或采用合租模式&#xff1b; 采用易支付通用接口进行支付&#xff1b; 添加邀请返利功能&#xff0c;以便站长更好地推广&#xf…

每天刷两道题——第三天

1.1两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09; 输入&#xff1a;[1,2,3,4] 输出&#xff1a;[2,1,4,3…

祝贺首届兽医专业学位研究生创新创业大赛圆满闭幕

为进一步贯彻落实科教兴国、农业强国发展战略&#xff0c;适应兽医行业、产业发展需求&#xff0c;提高兽医专业学位研究生实践创新能力&#xff0c;推动兽医领域高层次应用型人才培养供给侧改革&#xff0c;促进兽医专业学位教育高质量发展&#xff0c;12月28日由中国学位与研…

什么是分布式锁

想象一下&#xff0c;如果没有分布式锁&#xff0c;多个分布式节点同时涌入一个共享资源的访问时&#xff0c;就像一群饥肠辘辘的狼汇聚在一块肉前&#xff0c;谁都想咬一口&#xff0c;最后弄得肉丢了个精光&#xff0c;大家都吃不上。 而有了分布式锁&#xff0c;就像给这块肉…