String的Intern()方法,详解字符串常量池!
文章目录
一、String的基本特性
1.String字符串,使用一对""引起来表示。
2.String声明为final的,不可被继承。
3.String实现了Serializable接口,表示字符串是支持序列化的。实现了Comparable接口,表示String可以比较大小。
4.String在jdk8及以前内部定义了final char[] value用于存储字符串数据。jdk9时改为byte[]。
5.String代表不可变的字符序列。简称:不可变性。
(1)当字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
(2)当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
(3)当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
6.通过字面量的方式(区别于new,String a = ‘abc’;)给一个字符串赋值,此时字符串值声明在字符串常量池中。
7.字符串常量池中是不会存储相同内容的字符串的。
8.String的String Pool是一个固定大小的HashTable,默认值大小长度是1009.如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降。
9.使用-XX:StringTableSize可以设置StringTable的长度。
10.在jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。StringTableSize设置没有要求。
11.在jdk7中,StringTable的长度默认值是60013,StringTable的设置没有要求。
12.jdk8中,StringTable的长度默认值是60013,1009是可设置的最小值。
为什么jdk9String由char[]改为了byte[]去存?
主要还是节省空间。
JDK9 之前的库的 String 类的实现使用了 char 数组来存放字符串,char 占用16位,即两字节。
研究发现String是堆空间的主要部分,同时大部分String都是一些拉丁文,一个字节就能够撑得下,如果使用char的话,因为char占两个字节,所以有一半的空间被浪费掉。
所以,由char[]改为了byte[],再补一个字符标码集的标识coder;如果是ISO-8859-1、Latin-1就用一个byte去存,如果是UTF-16还是用两个字节去存。
结论:String再也不用char[]来存储了,改成了byte[]加上编码标记,节约了一些空间。
String存储结构发生了变更,StringBuffer和StringBuilder是否改变了?
基于String的类,比如StringBuilder、StringBuffer以及HotSpot虚拟机内部的一些类也进行了修改。
实例(因为String不可变性,原来地址的值是无法被改变的):
二、String的内存分配
1.在Java语言中有8种基本数据类型和一种比较特殊的类型String。这些类型为了使它们在运行过程中速度更快、更节省内存,都提供了一种常量池的概念。
2.常量池就类似一个Java系统级别提供的缓存。8种基本数据类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种。
(1)直接使用双引号声明出来的String对象会直接存储在常量池中。(String a = “aaa”;)
(2)如果不是用双引号声明的String对象,可以使用String提供的intern()方法。
3.Java 6及以前,字符串常量池存放在永久代。
4.Java7中Oracle的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内。
所有的字符串都保存在堆(Heap)中,和其他普通对象一样,这样可以让你在进行调优应用时仅需要调整堆大小就可以了。
字符串常量池概念原本使用得比较多,但是这个改动使得我们又足够的理由让我们重新考虑在java7中使用String.intern()。
5.java8永久代变为元空间,字符串常量还是在堆。
为什么要将StringTable进行调整,从永久代->堆?
1.永久代空间有限,将字符串放在永久代容易报OOM-永久代内存溢出。
2.永久代回收频率很低。
三、String的基本操作
java语言规范里要求完全相同的字符串字面量,应该包含同样的Unicode字符序列(包含同一份码点序列的常量),并且必须是指向同一个String类实例。
实例1:
debug下,可以逐行查看字符串常量池,首次加载1-10之后,再次打印就不会再往字符串常量池里加1-10字符串了:
实例2:
line3、line4的new的Object()、Memory()都是指向堆空间。
line6的param也指向堆空间Object()。
line7的param的toString()方法返回一个字符串,字符串存在字符串常量池里。
四、字符串拼接操作
1.常量与常量的拼接结果在常量池,原理是编译期优化。
2.常量池中不会存在相同内容的常量。
3.只要其中有一个是变量,结果就在堆中。变量拼接的原理是StringBuilder。
4.如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。
实例1:(常量与常量的拼接结果在常量池)
反编译结果:
实例2:(拼接其中有一个是变量,结果就在堆中,相当于新new的String)
intern()方法就是判断字符串常量池中是否存在这个字符串,如果存在则返回常量池中字符串地址,如果不存在就在常量池中加载字符串并返回此地址。
实例3:(字符串变量拼接底层是用StringBuilder)
根据字节码指令可以看出,拼接字符串变量,先new了StringBuilder,然后使用append操作,然后调用toString()方法。
StringBuilder的toString()方法,约等于new String(“”)。具体继续往下看。
(补充:jdk1.5之后,拼接字符串使用StringBuilder,jdk1.5之前使用StringBuffer)
实例4:
字符串拼接操作,不一定是用的StringBuilder。如果拼接符号左右两边都是字符串常量或者常量引用(final修饰),则仍然使用编译期优化,非StringBuilder方式。
所以,针对于final修饰的类、方法、基本数据类型、引用数据类型的结构时,能使用上final的时候建议使用上。
反编译:
实例5:练习
实例6:多个字符串拼接,其实也只new了一个StringBuilder
实例7:for循环中拼接字符串
可以看出,for每循环一次就会new一个StringBuilder。所以,拼接字符串最好还是直接使用StringBuilder,而不是“+”。
总结:
字符串拼接最好使用StringBuilder调用append来拼接。
使用加号“+”拼接,会new一个StringBuilder,并且在最后调用toString方法时还会new String()。
内存中由于创建了较多的StringBuilder和String对象,还有一方面是内存占用,调用GC还会额外花费时间。
所以,字符串拼接直接使用StringBuilder会大大提高性能,尤其是多个字符串拼接。
改进的空间:
如果开发中基本确定拼接后的字符串长度不高于某个限定值,可以使用StringBuilder(int highlevel)构造器来进行创建StringBuilder。
五、intern()的使用
以上的解释:
如果字符串常量池中,通过queals方法对比的时候,已经包含了这个字符串,接下来池子中的字符串将会被返回。否则就会将这个字符串加到池子里,并且返回这个字符串实例。
假如说s和t是相同的,以下两种情况结果都为true。
s.intern() == t.intern()
s.equals(t)
1.如果不是用双引号声明的String对象,可以使用String提供的intern方法:intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
比如:String s = new String(“I love you”).intern();
2.也就是说,如果在任意字符串上调用String.intern方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。因此,下列表达式的值必定是true:
(“a” + “b” + “c”).intern() == “abc”;
3.通俗点讲,Interned String就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意,这个值会被存放在字符串内部池(字符串常量池)(String Intern Pool)。
如何保证变量s指向字符串常量池中的数据?
1.String s = “abc”; // 字面量方式定义。
2.String s = new String(“abc”).intern();
String s = new StringBuilder(“abc”).toString().intern(); //调用intern方法。
new String(“abc”)会创建几个对象?
1.创建了两个对象
一个是通过new关键字在堆空间创建的。
另一个是ldc创建的字符串常量池中的对象。
2.创建了5个对象
第一个new 了StringBuilder对象(1)。
第二个new了String(4)。
第三个常量池中的a(6)。
第四个new了String(9)。
第五个常量池中的b(11)。
第六个,StringBuilder的toString()方法,也会new一个String(toString方法的调用,并不会将字符串放到字符串常量池中)。
StringBuilder的toString()方法:
toString方法的调用,并不会将字符串放到字符串常量池中。
我们并没有在字节码指令中发现ldc命令,所以并没有将字符串放到字符串常量中。
经典题目(99%的人会做错!)
结果:
jdk6:false、false。
jdk7和8:false、true。
解释:
Strign s = new String(“1”);我们已经知道,它不仅会new一个String对象在堆中,而且还会在字符串常量池中放上"1"。
所以调用s.intern();并没有什么实际意义,因为字符串常量池中已经有"1"了。
而且s指向的是new String(“1”)的地址,而不是常量池中的地址。
如果s.intern() 改为s = s.intern(); 那么s == s2就是true了。
s3的地址记录的实际是new String(“11”)。此时,字符串常量池中并不存在"11",只存在"1",因为字符串拼接使用StringBuilder之后调用的toString()方法,并不会将结果存在字符串常量池中。
s3.intern();会将"11"存在字符串常量池中。jdk6中就是创建了一个常量池中新的对象"11",也就有新的地址;jdk7和8常量池放到堆中了,这里s3调用intern()方法,这里由于空间节省,常量池中并不会创建"11",而是创建了一个指向堆空间new String(“11”)的地址。
String s4 = “11”; s4指向的地址是上一行代码在常量池中存放的"11"。
jdk7和8中,本质上s3和s4指向了堆中同一个地址。
详细过程:
拓展:
原因:
String s4 = “11”; 是实打实的在字符串常量池中创建了"11",而不是一个引用了。
s3.intern(); 因为常量池中已经有"11"了。
此时s3指向堆中的new String(“11”); s4指向常量池中的"11",s5也指向常量池中的"11"。
总结String的intern()的使用:
1.jdk1.6中,将这个字符串对象尝试放入字符串常量池。
如果串池中有,则并不会放入,返回已有的串池中的对象的地址。
如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址。
2.jdk1.7起,将这个字符串对象尝试放入串池。
如果串池中有,则并不会放入,返回已有的串池中的对象地址。
如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址。
intern练习1:
jdk6:true、false
jdk7:true、true(jdk7在串池并没有创建"ab"对象,而是创建了一个引用,指向堆中的new String(“ab”)的地址)
在代码前面加上String x = “ab”;
intern练习2:
第一种情况是false,第二种情况是true。
String s1 = new String(“ab”); 会在堆中new 一个String,并且也会在字符串常量池中新建一个"ab"。
String s1 = new String(“a”) + new String(“b”); 会在字符串常量池中新建"a"、“b”,拼接字符串使用StringBuilder,其toString()方法并不会在常量池中创建"ab"。而且调用s1.intern()之后,jdk7由于堆中已经有ab字符串了,在字符串常量池只创建了一个该对象的引用。
六、intern()效率测试
空间角度(jdk1.8)
使用jvisualvm查看结果:
不使用intern:
使用intern:
总结:使用intern更节省内存空间(尤其是程序中有大量重复的字符串时),并且执行速度还快。
七、StringTable的垃圾回收
使用jvm参数:
-XX:+PrintStringTableStatistics,打印字符串常量池信息。
main函数中什么也不写的字符串常量池打印信息:
执行以下代码:
执行以下代码:
我们会发现发生了GC,并且字符串常量池数据量降了下来
八、G1中的String去重操作
1.背景:对许多Java应用(有大的也有小的)做的测试得出以下结果:
堆存活数据集合里面String对象占了25%。
堆存活数据集合里面重复的String对象有13.5%。
String对象的平均长度是45。
2.许多大规模的Java应用的瓶颈在于内存,测试表明,在这些类型的应用里面,Java堆中存活的数据集合差不多25%是String对象。更进一步,这里面差不多一半String对象是重复的,重复的意思是说:string1.equals(string2)=true。堆上存在重复的String对象必然是一种内存的浪费。这个项目将在G1垃圾收集器中实现自动持续对重复的String对象进行去重,这样就能避免浪费内存。
实现
1.当垃圾收集器工作的时候,会访问堆上存活的对象。对每一个访问的对象都会检查是否是候选的要去重的String对象。
2.如果是,把这个对象的一个引用插入到队列中等待后续的处理。一个去重的线程在后台运行,处理这个队列。处理队列的一个元素意味着从这个队列删除这个元素,然后尝试去重它引用的String对象。
3.使用一个hashtable来记录所有的被String对象使用的不重复的char数组。当去重的时候,会查这个hashtable,来看堆上是否已经存在一个一模一样的char数组。
4.如果存在,String对象会被调整引用那个数组,释放对原来的数组的引用,最终会被垃圾收集器回收掉。
5.如果查找失败,char数组会被插入到hashtable,这样以后的时候就可以共享这个数组了。
命令行选项
UseStringDeduplication(bool):开启String去重,默认是不开启的,需要手动开启。
PrintStringDeduplicationStatistics(bool):打印详细的去重统计信息。
StringDeduplicationAgeThreshold(uintx):达到这个年龄的String对象被认为是去重的候选对象。
相关文章:

并发编程下的集合:数组寻址、LinkedList、HashMap、ConcurrentHashMap
如果发现hash取模后的数组索引位下无元素则直接新增,若不是空那就说明存在hash冲突,则判断数组索引位链表结构中的第一个元素的key以及hash值是否与新的key一致则直接覆盖,若不一致则判断当前的数组索引下的链表结构是否为红黑树,若为红黑树则走红黑树的新增方法,若不为红黑树则遍历当前链表结构,遍历中发现某个节点元素的next为null是则直接将新元素指针与next进行关联,若在遍历到next为空前判断到,某个节点的key以及key的hash值与新的key与新的keyhash值一致时则走覆盖。

【日常开发之插件篇】IDEA plugins 神器助我!!
今早因为老代码的一些bug让我突然觉得Idea的一些插件特别好用,我准备将我平时所用到的一些插件做个推荐以及记录。

【日常开发之FTP】Windows开启FTP、Java实现FTP文件上传下载
FTP是一个专门进行文件管理的操作服务,一般来讲可以在任意的操作系统之中进行配置,但是如果考虑到简便性,一般来讲可以直接在Linux系统下进行安装。FTP (File Transfer Protocol、文件传输协议)是TCP/IP协议中的一部分,属于应用层协议。使用FTP最主要的功能是对文件进行管理,所以在FTP内部对于文件支持有两种传输模式:文本模式(ASCII、默认)和二进制模式(Binary),通常文本文件使用ASCIl模式,而对于图片、视频、声音、压缩等文件则会使用二进制的方式进行传输。

【Linux之升华篇】Linux内核锁、用户模式与内核模式、用户进程通讯方式
alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。字符设备描述符 struct cdev,cdev_alloc()用于动态的分配 cdev 描述符,cdev_add()用于注。外,还支持语义符合 Posix.1 标准的信号函数 sigaction(实际上,该函数是基于 BSD 的,BSD。从最初的原子操作,到后来的信号量,从。(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的。

【Mongdb之数据同步篇】什么是Oplog、Mongodb 开启oplog,java监听oplog并写入关系型数据库、Mongodb动态切换数据源
oplog是local库下的一个固定集合,Secondary就是通过查看Primary 的oplog这个集合来进行复制的。每个节点都有oplog,记录这从主节点复制过来的信息,这样每个成员都可以作为同步源给其他节点。Oplog 可以说是Mongodb Replication的纽带了。

【日常开发之Windows共享文件】Java实现Windows共享文件上传下载
下拉框选择你选择的用户点击添加,然后共享确定。创建一个文件夹然后点击属性界面,点击共享。maven版本存在于SMB协议的兼容问题。首先开启服务,打开控制面板点击程序。点击启用或关闭Windows功能。我这边是专门创建了一个用户。SMB1.0选中红框内的。

CXFServlet类的作用
CXFServlet是Apache CXF框架中的一个核心组件,用于处理HTTP请求并将它们转换为Web服务调用。通过配置CXFServlet,你可以轻松地部署和管理SOAP和RESTful Web服务。

@Scheduled注解的scheduler属性什么作用
注解是 Spring Framework 提供的一种机制,用于定义计划任务,即周期性执行的任务。 注解可以应用于方法上,以指示 Spring 容器在特定的时间间隔或按照某种调度规则来调用该方法。 属性是 注解的一个可选属性,它的作用是允许开发者指定一个自定义的 对象来控制任务的调度方式。默认情况下, 注解使用 Spring 内部的 来执行任务,但如果需要更高级的定制化需求,可以通过 属性指定一个自定义的 实现。自定义调度器:共享调度器资源:高级调度需求:假设你想使用 作为调度器,并且希望所有带有

过滤器、拦截器、aop的先后顺序和作用范围&拦截器preHandle(),postHandle(),afterComplation()方法执行顺序
在Spring框架中,过滤器(Filter)、拦截器(Interceptor)和面向切面编程(AOP)都是用于处理请求和处理流程的组件,但它们的作用范围和触发时机有所不同。下面我会解释这三者的先后顺序和作用范围。执行顺序:请注意,这个顺序可能因具体的配置和使用的技术而有所不同。在实际应用中,建议根据项目的具体需求来合理配置和使用这些组件。拦截器执行流程图:实现拦截器需要实现这个接口,这个 接口中有三个默认方法,这三个方法的执行顺序:我们实现接口然后重写这三个方法,就会在对应的时机被自动执行。这里就是调用处理

Zookeeper概要、协议、应用场景
Zoopkeeper提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型,作为分布式系统的沟通调度桥梁。

spring.factories文件的作用
即spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器中。在Spring Boot启动时,它会扫描classpath下所有的spring.factories文件,加载其中的自动配置类,并将它们注入到Spring ApplicationContext中,使得项目能够自动运行。spring.factories文件是Spring Boot自动配置的核心文件之一,它的作用是。

Spring事务七大传播机制与五个隔离级别,嵌套事务
如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。

常见的七种加密算法及实现
**数字签名**、**信息加密** 是前后端开发都经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、`oauth` 等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的 **签名加密算法** 来达到业务目标。这里简单的给大家介绍几种常见的签名加密算法和一些典型场景下的应用。## 正文### 1. 数字签名**数字签名**,简单来说就是通过提供 **可鉴别** 的 **数字信息** 验证 **自身身份** 的一种方式。一套 **数字签名** 通常定义两种 **互补

7min到40s:SpringBoot 启动优化实践
然后重点排查这些阶段的代码。先看下。

SpringBoot系列教程之Bean之指定初始化顺序的若干姿势
之前介绍了@Order注解的常见错误理解,它并不能指定 bean 的加载顺序,那么问题来了,如果我需要指定 bean 的加载顺序,那应该怎么办呢?本文将介绍几种可行的方式来控制 bean 之间的加载顺序。

在Java中使用WebSocket
WebSocket是一种协议,用于在Web应用程序和服务器之间建立实时、双向的通信连接。它通过一个单一的TCP连接提供了持久化连接,这使得Web应用程序可以更加实时地传递数据。WebSocket协议最初由W3C开发,并于2011年成为标准。

3种方案,模拟两个线程抢票
在多线程编程中,资源竞争是一个常见的问题。资源竞争发生在多个线程试图同时访问或修改共享资源时,可能导致数据不一致或其他并发问题。在模拟两个线程抢票的场景中,我们需要考虑如何公平地分配票,并确保每个线程都有机会成功获取票。本篇文章将通过三种方式来模拟两个线程抢票的过程,以展示不同的并发控制策略。使用 Synchronized 来确保一次只有一个线程可以访问票资源。使用 ReentrantLock 来实现线程间的协调。使用 Semaphore 来限制同时访问票的线程数量。

替代Druid,HakariCP 为什么这么快?
这次源码探究,真的感觉看到了无数个小细节,无数个小优化,积少成多。平时开发过程中,一些小的细节也一定要“扣”。

Java中volatile 的使用场景有哪些?
volatile是一种轻量级的同步机制,它能保证共享变量的可见性,同时禁止重排序保证了操作的有序性,但是它无法保证原子性。所以使用volatilevolatile。

JDK22 正式发布了 !
Java 22 除了推出了新的增强功能和特性,也获得 Java Management Service (JMS) 的支持,这是一项新的 Oracle 云基础设施远程软件服务(Oracle Cloud Infrastructure, OCI) 原生服务,提供统一的控制台和仪表盘,帮助企业管理本地或云端的 Java 运行时和应用。使包含运行时计算值的字符串更容易表达,简化 Java 程序的开发工作,同时提高将用户提供的值编写成字符串,并将字符串传递给其他系统的程序的安全性。支持开发人员自由地表达构造器的行为。

Jackson 用起来!
你可以创建自定义序列化器和反序列化器以自定义特定字段或类的序列化和反序列化行为。为此,请创建一个实现或接口的类,并在需要自定义的字段或类上使用和注解。@Override// ...其他代码...优势性能优异:Jackson在序列化和反序列化过程中表现出优秀的性能,通常比其他Java JSON库更快。灵活性:通过注解、自定义序列化器/反序列化器等功能,Jackson提供了丰富的配置选项,允许你根据需求灵活地处理JSON数据。易于使用:Jackson的API设计简洁明了,易于学习和使用。

拜托!别再滥用 ! = null 判空了!!
另外,也许受此习惯影响,他们总潜意识地认为,所有的返回都是不可信任的,为了保护自己程序,就加了大量的判空。如果你养成习惯,都是这样写代码(返回空collections而不返回null),你调用自己写的方法时,就能大胆地忽略判空)这种情况下,null是个”看上去“合理的值,例如,我查询数据库,某个查询条件下,就是没有对应值,此时null算是表达了“空”的概念。最终,项目中会存在大量判空代码,多么丑陋繁冗!,而不要返回null,这样调用侧就能大胆地处理这个返回,例如调用侧拿到返回后,可以直接。

详解Java Math类的toDegrees()方法:将参数从弧度转换为角度
Java Math 类的 toDegrees() 方法是将一个角度的弧度表示转换为其度表示,返回值为double类型,表示从弧度数转换而来的角度数。这就是Java Math 类的 toDegrees() 方法的攻略。我们已经了解了该方法的基本概念、语法、注意事项以及两个示例。希望这篇攻略对你有所帮助。

SpringBoot接口防抖(防重复提交)的一些实现方案
作为一名老码农,在开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。啥是防抖所谓防抖,一是防用户手抖,二是防网络抖动。

公司新来一个同事:为什么 HashMap 不能一边遍历一边删除?一下子把我问懵了!
前段时间,同事在代码中KW扫描的时候出现这样一条:上面出现这样的原因是在使用foreach对HashMap进行遍历时,同时进行put赋值操作会有问题,异常ConcurrentModificationException。于是帮同简单的看了一下,印象中集合类在进行遍历时同时进行删除或者添加操作时需要谨慎,一般使用迭代器进行操作。于是告诉同事,应该使用迭代器Iterator来对集合元素进行操作。同事问我为什么?这一下子把我问蒙了?对啊,只是记得这样用不可以,但是好像自己从来没有细究过为什么?

每天一个摆脱if-else工程师的技巧——优雅的参数校验
在日常的开发工作中,为了程序的健壮性,大部分方法都需要进行入参数据校验。最直接的当然是在相应方法内对数据进行手动校验,但是这样代码里就会有很多冗余繁琐的if-else。throw new IllegalArgumentException("用户姓名不能为空");throw new IllegalArgumentException("性别不能为空");throw new IllegalArgumentException("性别错误");

SpringBoot请求转发与重定向
但是可能由于B网址相对于A网址过于复杂,这样搜索引擎就会觉得网址A对用户更加友好,因而在重定向之后任然显示旧的网址A,但是显示网址B的内容。在平常使用手机的过程当中,有时候会发现网页上会有浮动的窗口,或者访问的页面不是正常的页面,这就可能是运营商通过某种方式篡改了用户正常访问的页面。重定向,是指在Nginx中,重定向是指通过修改URL地址,将客户端的请求重定向到另一个URL地址的过程,Nginx中实现重定向的方式有多种,比如使用rewrite模块、return指令等。使用场景:在返回视图的前面加上。

SSO 单点登录和 OAuth2.0 有何区别?
此方法的缺点是它依赖于浏览器和会话状态,对于分布式或者微服务系统而言,可能需要在服务端做会话共享,但是服务端会话共享效率比较低,这不是一个好的方案。在单点登录的上下文中,OAuth 可以用作一个中介,用户在一个“授权服务器”上登录,并获得一个访问令牌,该令牌可以用于访问其他“资源服务器”上的资源。首先,SSO 主要关注用户在多个应用程序和服务之间的无缝切换和保持登录状态的问题。这种方法通过将登录认证和业务系统分离,使用独立的登录中心,实现了在登录中心登录后,所有相关的业务系统都能免登录访问资源。

TCP协议-TCP连接管理
TCP协议是 TCP/IP 协议族中一个非常重要的协议。它是一种面向连接、提供可靠服务、面向字节流的传输层通信协议。TCP(Transmission Control Protocol,传输控制协议)。

接口响应慢?那是你没用 CompletableFuture 来优化!
大多数程序员在平时工作中,都是增删改查。这里我跟大家讲解如何利用CompletableFuture优化项目代码,使项目性能更佳!