不是报错,只是soanrqube检测觉得这样子不规范,这个也是阿里巴巴java编程手册中的一个规定,里面说法是不允许使用魔法值
java中,在使用sonarqube的时候,string类型的代码不允许直接使用未经定义的常量,什么是未经定义的产量呢?下面举个例子:
//这个会报未经定义的常量 String value = "常量";
一开始想到的解决方案是改成下面这个:
//这样子就是定义了这个变量了,sonarqube也没有报错了 String value = new String("常量");
但是里面有一个问题,那就是浪费内存。
浪费内存是,里面有两个对象,String value只是一个字符串变量,没有产生对象,new String(“常量”)不是原子操作,把它拆分为"常量"和new String(),首先会在字符串的常量池中查找有没有这个字符串"常量",没有找到就会创建一个字符串对象"常量"在元空间(JDK1.8 , 谢谢评论区指正~),然后new String 会把这个字符串对象拷贝一份到堆中,返回这个对象的引用。
除此之外,我知道String是不可变的,但是那是创建了之后不可变,也就是线程安全,那么我的疑问是在创建的时候是线程安全的么?以下是关于这个方面的个人理解:
之所以说线程不安全,这里引入两个概念:原子操作,指令重排。
1.原子操作
- 原子操作,可以理解为不可分割的操作,就是它已经小到不可以再切分为多个操作进行,那么在计算机中要么它完全执行了,要么它完全没有执行,它不会存在执行到中间状态,可以理解为没有中间状态。比如:赋值语句就是一个原子操作:
n = 1; //这是一个原子操作
假设n的值以前是0,那么这个操作的背后就是要么执行成功n等于1,要么没有执行成功n等于0,不会存在中间状态,就算是并发的过程中也是一样的。
下面看一句不是原子操作的代码:
int n =1;//不是原子操作
**原因:**这个语句中可以拆分为两个操作,1.声明变量n,2.给变量赋值为1,从中我们可以看出有一种状态是n被声明后但是没有来得及赋值的状态,这样的情况,在并发中,如果多个线程同时使用n,那么就会可能导致不稳定的结果。
2.指令重排
所谓指令重排,就是计算机会对我们代码进行优化,优化的过程中会在不影响最后结果的前提下,调整原子操作的顺序。比如下面的代码:
int a ; // 语句1 a = 1 ; // 语句2int b = 2 ; // 语句3int c = a + b ; // 语句4
正常的情况,执行顺序应该是1234,但是实际有可能是3124,或者1324,这是因为语句3和4都没有原子性问题,那么就有可能被拆分成原子操作,然后重排.
那么回到我们上面那个代码
String value = new String("常量"); //这不是原子操作,就是可以拆分,事实上先定义了一个String变量,然后创建了对象"常量",然后拷贝对象到堆中,将对象引用指向value。
上面代码如果在多线程中,有可能触发指令重排,如果有线程创建了对象,还没有将引用指过去,其他线程也就有可能创建多个这样的常量,有可能会导致其他问题出现。【如有错误,还请指教】
正确的改动方法:
1.使用常量,将项目的常量都抽取到一个class文件中:
2.使用format方法:
public class ConstantFile{ public static final String name = "常量";//方法1 public void myMethod(){ String.format("常量,%s,%s",args1,args2);//方法2 }}
3.使用枚举类型:
public enum ConstantFile { USA("美国",1),CHINA("中国",2); private String name; private int index; private ConstantFile(String name,int index){ =name; this.index=index; } public String getName() { return name; } public void setName(String name) { = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; }}