我得到了以下代码,我被问到哪个选项有以下模式:
XXXX-XXXX-XXXX-2324
...
以下代码:
public class CCMark {
public static String maskCC(String creditCard){
String x = "XXXX-XXXX-XXXX-";
//line 1
}
public static void main(String[] args) {
System.out.println(maskCC("1234-5678-1234-2324"));
System.out.println(maskCC("4567-5678-1234-5643"));
System.out.println(maskCC("1234-5678-1234-4654"));
System.out.println(maskCC("4567-5678-1234-5435"));
}
}
可以插入"第1行和第34行的可能选项:
A)
return x + creditCard.substring(15, 19);
B)
StringBuilder sb = new StringBuilder(x);
sb.append(creditCard, 15, 19);
return sb.toString();
我认为这里最好的选择,因为A和B为我们提供了相同的输出,是B,因为它使用的是StringBuilder,这意味着它的方法是可变的,因此它将使用比选项A更少的内存。
我错了吗?可能是针对这种特殊情况的选项A是最好的选择吗?
答案 0 :(得分:5)
选项a和b是相同的,因为Java编译器会将选项a 转换为选项b。您可以在方法之外移动x
的声明(并使其成为final
)。像,
static final String x = "XXXX-XXXX-XXXX-";
public static String maskCC(final String creditCard) {
return x + creditCard.substring(15, 19);
}
使用javap
检查第一个,第二个。 Java代码,
String x = "XXXX-XXXX-XXXX-";
String creditCard = "1234-5678-1234-23324";
String x2 = x + creditCard.substring(15, 19);
StringBuilder sb = new StringBuilder(x);
sb.append(creditCard, 15, 19);
String x3 = sb.toString();
生成类似的字节代码(注意行 6-31 和 32-58 )< / p>
0: ldc #16 // String XXXX-XXXX-XXXX-
2: astore_1
3: ldc #18 // String 1234-5678-1234-23324
5: astore_2
6: new #20 // class java/lang/StringBuilder
9: dup
10: aload_1
11: invokestatic #22 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
14: invokespecial #28 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
17: aload_2
18: bipush 15
20: bipush 19
22: invokevirtual #31 // Method java/lang/String.substring:(II)Ljava/lang/String;
25: invokevirtual #35 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #39 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_3
32: new #20 // class java/lang/StringBuilder
35: dup
36: aload_1
37: invokespecial #28 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
40: astore 4
42: aload 4
44: aload_2
45: bipush 15
47: bipush 19
49: invokevirtual #43 // Method java/lang/StringBuilder.append:(Ljava/lang/CharSequence;II)Ljava/lang/StringBuilder;
52: pop
53: aload 4
55: invokevirtual #39 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
58: astore 5
60: return
答案 1 :(得分:2)
变体A return x + creditCard.substring(15, 19);
的一大优势在于它简单而干净,适用于从1
到8
的所有Java版本。在其编译的表单使用StringBuffer
的情况下,对Java 5或更新版本的简单重新编译将使其使用StringBuilder
。当您手动使用StringBuffer
或StringBuilder
时,这种灵活性会丢失。
确切的编译表格不是固定的。由于方法String.substring
的语义不是由Java语言规范修复的,因此编译器通常不会触及它并将其编译为普通的方法调用。规范鼓励编译器供应商使用StringBuilder
进行字符串连接(+
运算符),只要有好处,大多数编译器都会这样做,即使没有任何好处。在此,x
和substring
的结果均为String
,因此简单String.concat
会更简单,但大多数编译器始终使用StringBuilder
,编译变体A相当于
return new StringBuilder().append(x).append(creditCard.substring(15, 19)).toString();
。
将这种典型形式与您的变体B进行比较,我们可以得出结论,变体B在性能方面具有两个优势:
new StringBuilder(x)
将StringBuilder
初始化为x.length()+16
的容量,该容量足以进行整个操作,而默认容量new StringBuilder()
通常用于变体A,固定为16
个字符,因为我们有19
字符的结果而错过了这里的标记,因此将重新分配和复制基础字符数组
sb.append(creditCard, 15, 19);
将复制四个字符,而无需为这些字符创建中间String
表示。 substring
操作的费用因实施而异,例如在Oracle的实现中,版本1.7.0_06
发生了重大变化;从这个版本开始,子字符串需要一个新的char[]
数组,其中包含受影响的字符数据的副本,因为它没有单独的offset
和length
字段
但请注意,变体A和B的所有这些差异仅影响要执行的操作的形式描述。实际会发生什么,取决于JVM / JRE,通常Hotspot优化器知道许多与字符串相关的操作,并且可能融合操作或省略中间字符串表示。因此,有关绩效的结果相当不可预测,可能会受到subtle changes to the implementation的影响。
这就是为什么开发人员可能会坚持使用变体A,正如所说的那样,更简单,更易读,并且只有在分析器告诉他们存在可以通过处理Stringbuilder
来解决性能问题时才关心性能。手动