Cách "cũ" tạo ra một loạt các StringBuilder
hoạt động được định hướng. Hãy xem xét chương trình này:
public class Example {
public static void main(String[] args)
{
String result = args[0] + "-" + args[1] + "-" + args[2];
System.out.println(result);
}
}
Nếu chúng tôi biên dịch nó với JDK 8 hoặc cũ hơn và sau đó sử dụng javap -c Example
để xem mã bytecode, chúng tôi sẽ thấy một cái gì đó như thế này:
Ví dụ về lớp công khai {
public Ví dụ ();
Mã:
0: aload_0
1: invokespecial # 1 // Phương thức java / lang / Object. "<init>" :() V
4: trở lại
public static void main (java.lang.String []);
Mã:
0: new # 2 // class java / lang / StringBuilder
3: trùng lặp
4: invokespecial # 3 // Phương thức java / lang / StringBuilder. "<init>" :() V
7: aload_0
8: biểu tượngt_0
9: aaload
10: invokevirtual # 4 // Phương thức java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
13: ldc # 5 // Chuỗi -
15: invokevirtual # 4 // Phương thức java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
18: aload_0
19: biểu tượngt_1
20: aaload
21: invokevirtual # 4 // Phương thức java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
24: ldc # 5 // Chuỗi -
26: invokevirtual # 4 // Phương thức java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
29: aload_0
30: biểu tượngt_2
31: aaload
32: invokevirtual # 4 // Phương thức java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
35: invokevirtual # 6 // Phương thức java / lang / StringBuilder.toString :() Ljava / lang / String;
38: astore_1
39: getstatic # 7 // Trường java / lang / System.out: Ljava / io / PrintStream;
42: aload_1
43: invokevirtual # 8 // Phương thức java / io / PrintStream.println: (Ljava / lang / String;) V
46: trở lại
}
Như bạn có thể thấy, nó tạo ra một StringBuilder
và sử dụng append
. Điều này nổi tiếng là khá kém hiệu quả vì dung lượng mặc định của bộ đệm tích hợp StringBuilder
chỉ là 16 ký tự và không có cách nào để trình biên dịch biết trước để phân bổ thêm, vì vậy nó sẽ phải phân bổ lại. Nó cũng là một loạt các cuộc gọi phương thức. (Tuy nhiên, lưu ý rằng JVM đôi khi có thể phát hiện và viết lại các mẫu lệnh gọi này để làm cho chúng hiệu quả hơn.)
Hãy xem những gì Java 9 tạo ra:
Ví dụ về lớp công khai {
public Ví dụ ();
Mã:
0: aload_0
1: invokespecial # 1 // Phương thức java / lang / Object. "<init>" :() V
4: trở lại
public static void main (java.lang.String []);
Mã:
0: aload_0
1: biểu tượngt_0
2: aaload
3: aload_0
4: biểu tượngt_1
5: aaload
6: aload_0
7: biểu tượngt_2
8: aaload
9: invokedynamic # 2, 0 // InvokeDynamic # 0: makeConcatWithConstants: (Ljava / lang / String; Ljava / lang / String; Ljava / lang / String;) Ljava / lang / String;
14: astore_1
15: getstatic # 3 // Trường java / lang / System.out: Ljava / io / PrintStream;
18: aload_1
19: invokevirtual # 4 // Phương thức java / io / PrintStream.println: (Ljava / lang / String;) V
22: trở lại
}
Ôi trời nhưng ngắn hơn. :-) Nó thực hiện một cuộc gọi duy nhất đến makeConcatWithConstants
từ StringConcatFactory
, nói điều này trong Javadoc của nó:
Các phương pháp để tạo điều kiện thuận lợi cho việc tạo ra các phương pháp nối chuỗi, có thể được sử dụng để nối một cách hiệu quả một số lượng đối số đã biết của các kiểu đã biết, có thể sau khi điều chỉnh kiểu và đánh giá một phần các đối số. Các phương thức này thường được sử dụng làm phương thức khởi động cho invokedynamic
các trang web cuộc gọi, để hỗ trợ tính năng nối chuỗi của Ngôn ngữ lập trình Java.