java常见面试题分析之-字符串比较-延伸到final类型

Raft算法
• 阅读 1624

文章内容包含:

  1. 关于字符串==比较的几种情况总结
  2. 关于final类型变量的初始化时机的总结

字符串==比较
import lombok.extern.slf4j.Slf4j;

/**
 * @Author weijun.nie
 * @Date 2020/4/18 21:29
 * @Version 1.0
 */
@Slf4j
public class StringDemo {

    public static void main(String[] args) {
        String hash = "hash";
        final String hashFinal = "hash";
        String b = "niewj";

        String a = "niewjhash";
        String c = "niewj";

        String d = "niewj" + "hash";
        String e = "niewj" + hash;
        String f = "niewj" + hashFinal;
        
        final String hashFinal2 = getByMethod();
        String g = "niewj" + hashFinal2;
        
        log.info("字符串常量 比较: b==c -> {}", b == c); // true
        log.info("字符串常量 和 两个常量的+连接: a==d -> {}", a == d); // true
        log.info("字符串常量 和 一个常量连接一个变量: a==e -> {}", a == e); // false
        log.info("字符串常量 和 一个常量连接一个变量: a==e.intern() -> {}", a == e.intern()); // true
        log.info("字符串常量 和 一个常量连接一个final变量: a==f -> {}", a == f); // true
        log.info("字符串常量 和 一个常量连接一个调用方法获取的final变量: a==g -> {}", a == g); //false
    }
    
    private static String getByMethod() {
        return "hash";
    }
}
1[main]- 字符串常量 比较: b==c -> true
2[main]- 字符串常量 和 两个常量的+连接: a==d -> true
3[main]- 字符串常量 和 一个常量连接一个变量: a==e -> false
4[main]- 字符串常量 和 一个常量连接一个变量: a==e.intern() -> true
5[main]- 字符串常量 和 一个常量连接一个final变量: a==f -> true
6[main]- 字符串常量 和 一个常量连接一个调用方法获取的final变量: a==f -> false

分析总结:

1. 对于字符串常量的声明, 都会推送到字符串常量池中去缓存: 比如 String b = "niewj"; String c = "niewj"; 此时, 比较两个字符串reference(也就是比较他们指向的常量字符串的内存地址), 自然都是相同的缓存;
2. 对于声明的两个常量字符串"+" 操作 String d = "niewj" + "hash"; 编译器也会做连接优化, 将其连接结果对应到字符串常量池中去比对缓存; 有则引用之; 所以此处的 a==d;
3. 字符串常量非final字符串引用"+", 编译器并不会提前优化, 而是需要计算得出, 并不会在 字符串常量池 中对应; 所以 a==e 为false;
4. 字符串String类的intern()方法的说明: 我们可以看到这样的说明: String s = "abc" (1)如果池中已经有了字符串s引用的字符串(根据equals方法比较:也就是比较字符串内容), 则直接返回池中的字符串引用; (2)如果池中没有, 那么intern会生成"abc"放入池中, 并返回此串引用; (3)如果两个字符串内容相同, s1.intern()==s2.intern()肯定是true;
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.
All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.

public native String intern();

5. 关于final的字符串: final的字符串变量, 在声明时都会被放入到字符串常量池中; 不仅如此, 常量字符串和final类型字符串的 "+"操作也可以视为是 两个常量字符串的 "+" 操作, 也会到池中对取缓存; 为什么呢? final类型的变量, 只能初始化1次, 而且在使用的时候, 要求必须是已经初始化过了; 所以在 "+"操作的时候, 编译器已经能确认是有值了, 可以作为常量来对待, 因此作为常量字符串对待了;
6. 虽然也是final的字符串变量, 但是是通过调用方法获取的final值, 编译器无法在编译时预知, 所以不会进行优化, 所以在 "+"操作的时候, 编译器无法确定, 因此没有同步到池;
final类型变量的初始化时机

另外关于final变量初始化时机的总结:
final变量有3中:

(1) class中声明的实例final变量;

三种赋值时机:

  1. 直接赋值: 声明即赋值`class Const{ final int a=20; }
  2. 构造方法赋值: 声明时不赋值, 在构造方法中初始化赋值: class Const{ final int a; public Const(){a = 20;}}
  3. 实例块/初始化块赋值: 声明时不赋值,初始化块赋值:class Const{final int a; {a = 20;}}
(2) class中声明的static的final变量;

两种赋值时机:

  1. 直接赋值: 声明即赋值: class Const{static final int a = 20;}
  2. 静态块赋值: 声明时不赋值,静态块赋值: class Const{static final int a; static{a = 20;}}
(3) method中声明的final的变量;

一种:使用前初始化过即可; 特别注意的情景有一种,就是声明不初始化, 也可以编译过: 因为没有使用:

  1. public void test(){final int a;} 可以编译过, 因为后面没有对a变量的读取;

-->引申: 字符串常量池:关于字符串常量池的内存区域, 参见前面的整理: jvm字符串常量池在什么内存区域?

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Karen110 Karen110
3年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Stella981 Stella981
3年前
JS 苹果手机日期显示NaN问题
问题描述newDate("2019122910:30:00")在IOS下显示为NaN原因分析带的日期IOS下存在兼容问题解决方法字符串替换letdateStr"2019122910:30:00";datedateStr.repl
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Stella981 Stella981
3年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这