1. Tóm Tắt về String Class
Một Java
String
chứa một chuỗi các ký tự Unicode bất biến. Không giống như C / C ++, trong đó chuỗi chỉ đơn giản là một mảng char
, Java String
là một đối tượng của lớp java.lang.String
.
Java
String
là, tuy nhiên, đặc biệt. Không giống như một lớp học bình thường:String
được liên kết với chuỗi ký tự dưới dạng văn bản trích dẫn kép như "hello, world
". Bạn có thể gán một chuỗi ký tự trực tiếp vào mộtString
biến, thay vì gọi hàm tạo để tạo mộtString
thể hiện.- Các
'+'
nhà điều hành được nạp chồng để nối haiString
toán hạng.'+'
không hoạt động trên bất kỳ đối tượng khác nhưPoint
vàCircle
. String
là bất biến . Đó là, nội dung của nó không thể được sửa đổi một khi nó được tạo ra. Ví dụ, phương thứctoUpperCase()
xây dựng và trả về một cái mớiString
thay vì sửa đổi nội dung hiện có.
1.1 Method Summary
Phương thức thường được sử dụng trong
String
lớp được tóm tắt dưới đây. Tham khảo API JDK để biết java.lang.String
danh sách đầy đủ.// Length int length() // returns the length of the String boolean isEmpty() // same as str.length() == 0 boolean isBlank() // contains only white spaces (Unicode aware) (JDK 11) // Comparison boolean equals(String another) // CANNOT use '==' or '!=' to compare two Strings in Java boolean equalsIgnoreCase(String another) int compareTo(String another) // return 0 if this string is the same as another; // <0 if lexicographically less than another; or >0 int compareToIgnoreCase(String another) boolean startsWith(String another) boolean startsWith(String another, int fromIdx) // search begins at fromIdx boolean endsWith(String another) // Searching: index from 0 to str.length()-1 int indexOf(String key) int indexOf(String key, int fromIdx) int indexOf(int char) int indexOf(int char, int fromIdx) // search forward starting at fromIdx int lastIndexOf(String key) int lastIndexOf(String key, int fromIdx) // search backward starting at fromIdx int lastIndexOf(int char) int lastIndexOf(int char, int fromIdx) // Extracting a char or substring, include fromIdx but exclude toIdx char charAt(int idx) String substring(int fromIdx) String substring(int fromIdx, int toIdx) // Creating a new String or char[] from the original - Strings are immutable String toLowerCase() String toUpperCase() String concat(String another) // same as str+another String trim() // creates a new String removing white spaces from front and back String strip() // strips the leading and trailing white spaces (Unicode aware) (JDK 11) String stripLeading() // (JDK 11) String stripTrailing() // (JDK 11) String repeat(int count) // (JDK 11) String indent(int n) // adjusts the indentation by n (JDK 12) char[] toCharArray() // create a char[] from this string void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) // copy into dst char[] // Working with CharSequence (super-interface of String, StringBuffer, StringBuilder) boolean contains(CharSequence cs) // (JDK 5) boolean contentEquals(CharSequence cs) // (JDK 5) boolean contentEquals(StringBuffer sb) // (JDK 4) static String join(CharSequence delimiter, CharSequence... elements) // (JDK 8) static String join(CharSequence delimiter, Iterable<CharSequence> elements) // (JDK 8) // Text Processing and Regular Expression (JDK 4) boolean matches(String regex) String replace(char old, char new) String replace(CharSequence target, CharSequence replacement) // (JDK 4) String replaceAll(String regex, String replacement) String replaceFirst(String regex, String replacement) String[] split(String regex) // Split the String using regex as delimiter, return a String array String[] split(String regex, int count) // for count times only /*** static methods ***/ // Converting primitives to String static String valueOf(type arg) // type can be primitives or char[] // Formatting using format specifiers static String format(String formattingString, Object... args) // same as printf() /*** Stream and Functional Programming ***/ Stream<String> lines() // returns a stream of lines (JDK 11) IntStream chars() // returns a IntStream of characters (JDK 9) IntStream codePoints() R transform(Function<String, R> f) // transforms from String to type R (JDK 12)
1.2 Examples
static method String.format() (JDK 5)
Các
static
phương pháp String.format()
(giới thiệu trong JDK 5) có thể được sử dụng để sản xuất ra một định dạng String
sử dụng C như- printf()
s' định dạng specifiers. Các format()
phương pháp có dạng giống như printf()
. Ví dụ,String.format("%.1f", 1.234); // returns String "1.2"
String.format()
là hữu ích nếu bạn cần tạo một định dạng đơn giản String
cho một số mục đích (ví dụ: được sử dụng trong phương thức toString()
). Đối với chuỗi phức tạp, sử dụng StringBuffer
/ StringBuilder
với a Formatter
. Nếu bạn chỉ cần gửi một chuỗi được định dạng đơn giản đến bàn điều khiển, hãy sử dụng System.out.printf()
, ví dụ:System.out.printf("%.1f", 1.234);
[TODO] More examples
1.3 New Methods
JDK 9 new methods
.chars()|.codePoints() -> IntStream
JDK 9 new methods
- NIL
JDK 11 new methods
.repeat(int count) -> String
.strip()|.stripLeading()|.stripTrailing() -> String
and.isBlank() -> boolean
which are unicode white-space aware..lines()
to produces aStream<String>
. (JDK 9 added.chars()
and.codePoints()
to produce anIntStream
.)
JDK 12 new methods
.indent(int n) -> String
.transform(Function<String,R> f) -> R
.describeConstable() -> Optional<String>
.resolveConstantDesc() -> String
JDK 13 new methods
- NIL
2. String is Really Special!
Các chuỗi được xử lý đặc biệt trong Java, vì chúng được sử dụng thường xuyên trong một chương trình. Do đó, hiệu quả (về mặt tính toán và lưu trữ) là rất quan trọng.
Các nhà thiết kế của Java đã quyết định giữ lại các kiểu nguyên thủy trong một ngôn ngữ hướng đối tượng, thay vì biến mọi thứ thành một đối tượng, để cải thiện hiệu suất của ngôn ngữ. Nguyên thủy được lưu trữ trong ngăn xếp phương thức, đòi hỏi ít không gian lưu trữ hơn và rẻ hơn để thao tác. Mặt khác, các đối tượng được lưu trữ trong heap chương trình, đòi hỏi quản lý bộ nhớ phức tạp và nhiều không gian lưu trữ hơn.
Vì lý do hiệu năng, Java
String
được thiết kế để ở giữa một đối tượng nguyên thủy và một đối tượng. Các tính năng đặc biệt String
bao gồm:- Các
'+'
nhà điều hành, thực hiện bổ sung vào nguyên thủy (nhưint
vàdouble
), bị quá tải hoạt động trênString
các đối tượng.'+'
thực hiện nối cho haiString
toán hạng.
Java không hỗ trợ quá tải toán tử cho việc xem xét kỹ thuật phần mềm. Trong ngôn ngữ hỗ trợ quá tải toán tử như C ++, bạn có thể biến'+'
toán tử thực hiện phép trừ, dẫn đến mã kém. Các'+'
nhà điều hành là chỉ khai thác được trong nội bộ quá tải để hỗ trợ nối chuỗi trong Java. Lưu ý rằng'+'
không hoạt động trên bất kỳ hai đối tượng tùy ý, chẳng hạn nhưPoint
s hoặcCircle
s. - A
String
có thể được xây dựng bởi một trong hai:- trực tiếp gán một chuỗi ký tự cho một
String
tham chiếu - giống như một nguyên thủy , hoặc - thông qua
new
toán tử "constructor và constructor, tương tự như bất kỳ lớp nào khác. Tuy nhiên, điều này không được sử dụng phổ biến và không được khuyến khích.
Ví dụ, - trực tiếp gán một chuỗi ký tự cho một
String str1 = "Java is Hot"; // Implicit construction via string literal String str2 = new String("I'm cool"); // Explicit construction via new
- Trong câu lệnh đầu tiên,
str1
được khai báo làString
tham chiếu và khởi tạo bằng một chuỗi ký tự"Java is Hot"
. Trong câu lệnh thứ hai,str2
được khai báo làString
tham chiếu và khởi tạo thông quanew
toán tử và hàm tạo để chứa"I'm cool"
. String
chữ được lưu trữ trong một hồ bơi chung . Điều này tạo điều kiện chia sẻ lưu trữ cho các chuỗi có cùng nội dung để bảo tồn lưu trữ.String
các đối tượng được phân bổ thông quanew
toán tử được lưu trữ trong heap và không có chia sẻ lưu trữ cho cùng một nội dung.
2.1 String Literal vs. String Object
Như đã đề cập, có hai cách để xây dựng một chuỗi: xây dựng ngầm định bằng cách gán một chuỗi bằng chữ hoặc rõ ràng tạo một đối tượng rõ ràng
String
thông qua new
toán tử và hàm tạo. Ví dụ,String s1 = "Hello"; // String literal String s2 = "Hello"; // String literal String s3 = s1; // same reference String s4 = new String("Hello"); // String object String s5 = new String("Hello"); // String object
Java đã cung cấp một cơ chế đặc biệt để giữ các
String
chữ - trong một nhóm chung được gọi là chuỗi chung . Nếu hai chuỗi ký tự có cùng nội dung, chúng sẽ chia sẻ cùng một bộ lưu trữ bên trong nhóm chung. Cách tiếp cận này được áp dụng để bảo tồn lưu trữ cho các chuỗi được sử dụng thường xuyên. Mặt khác, String
các đối tượng được tạo ra thông qua , trên các mặt khác, được tạo ra thông quanew
nhà điều hành và nhà xây dựng được lưu giữ trong các đống. Mỗi String
đối tượng trong heap có lưu trữ riêng giống như bất kỳ đối tượng nào khác. Không có chia sẻ lưu trữ trong heap ngay cả khi hai String
đối tượng có cùng nội dung.
Bạn có thể sử dụng phương thức
equals()
của String
lớp để so sánh nội dung của hai String
s. Bạn có thể sử dụng toán tử đẳng thức quan hệ '=='
để so sánh các tham chiếu (hoặc con trỏ) của hai đối tượng. Nghiên cứu các mã sau:s1 == s1; // true, same pointer s1 == s2; // true, s1 and s1 share storage in common pool s1 == s3; // true, s3 is assigned same pointer as s1 s1.equals(s3); // true, same contents s1 == s4; // false, different pointers s1.equals(s4); // true, same contents s4 == s5; // false, different pointers in heap s4.equals(s5); // true, same contents
Ghi chú quan trọng:
- Trong ví dụ trên, tôi đã sử dụng toán tử đẳng thức quan hệ
'=='
để so sánh các tham chiếu của haiString
đối tượng. Điều này được thực hiện để chứng minh sự khác biệt giữa lưu trữ chia sẻ chuỗi bằng chữ trong nhóm chung vàString
các đối tượng được tạo trong heap. Đó là một lỗi logic để sử dụng(str1 == str2)
trong chương trình của bạn để so sánh nội dung của haiString
s. String
có thể được tạo bằng cách gán trực tiếp một từ được chia sẻ trực tiếpString
theo nghĩa đen được chia sẻ trong một nhóm chung. Nó là không phổ biến và không nên sử dụng trực tiếp trong một nhóm công cộng. Nó không phổ biến và không nên sử dụngnew
toán tử để xây dựng mộtString
đối tượng trong heap.
2.2 String is Immutable
Do các chuỗi ký tự có cùng nội dung chia sẻ lưu trữ trong nhóm chung, Java
String
được thiết kế để không thay đổi . Đó là, một khi a String
được xây dựng, nội dung của nó không thể được sửa đổi. Mặt khác, các String
tham chiếu khác chia sẻ cùng một vị trí lưu trữ sẽ bị ảnh hưởng bởi thay đổi, điều này có thể không dự đoán được và do đó là không mong muốn. Các phương thức như toUpperCase()
có thể xuất hiện để sửa đổi nội dung của một String
đối tượng. Trong thực tế, một đối tượng hoàn toàn mới hoàn toàn mớiString
được tạo ra và trả lại cho người gọi. String
Đối tượng ban đầu sẽ được giải quyết, một khi không có thêm tài liệu tham khảo và sau đó được thu gom rác.Bởi vì String
không thay đổi, sẽ không hiệu quả khi sử dụng String
nếu bạn cần sửa đổi chuỗi của mình thường xuyên (điều đó sẽ tạo ra nhiều vùng mới String
chiếm các vùng lưu trữ mới). Ví dụ,// inefficient codes
String str = "Hello";
for (int i = 1; i < 1000; ++i) {
str = str + i;
}
Nếu nội dung của một
String
phải được sửa đổi thường xuyên, sử dụng StringBuffer
hoặc StringBuilder
lớp thay thế.2.3 String.intern()
[TODO]
3. StringBuffer & StringBuilder
Như đã giải thích trước đó,
String
s là bất biến vì các String
chữ có cùng nội dung chia sẻ cùng một bộ lưu trữ trong nhóm chung chuỗi. Sửa đổi nội dung của một String
trực tiếp có thể gây ra tác dụng phụ bất lợi cho những người khác String
chia sẻ cùng một bộ lưu trữ.
JDK cung cấp hai lớp để hỗ trợ có thể thay đổi sự ủng hộ có thể thay đổi trình tự chuỗi xâu chuỗi:
StringBuffer
và StringBuilder
(trong gói cốt lõi java.lang
). Một StringBuffer
hoặc StringBuilder
đối tượng cũng giống như bất kỳ đối tượng bình thường nào, được lưu trữ trong heap và không được chia sẻ, và do đó, có thể được sửa đổi mà không gây ra tác dụng phụ bất lợi cho các đối tượng khác.StringBuilder
lớp đã được giới thiệu trong JDK 5. Nó cũng giống như StringBuffer
lớp, ngoại trừ việc StringBuilder
được không đồng bộ cho các hoạt động đa luồng. Tuy nhiên, đối với chương trình đơn luồng StringBuilder
, không có chi phí đồng bộ hóa, sẽ hiệu quả hơn.3.1 java.lang.StringBuffer
Đọc đặc tả API JDK cho
java.lang.StringBuffer
.// Constructors StringBuffer() // an initially-empty StringBuffer StringBuffer(int size) // with the specified initial size StringBuffer(String s) // with the specified initial content // Length int length() // Methods for building up the content StringBuffer append(type arg) // type could be primitives, char[], String, StringBuffer, etc StringBuffer insert(int offset, arg) // Methods for manipulating the content StringBuffer delete(int fromIdx, int toIdx) StringBuffer deleteCharAt(int idx) void setLength(int newSize) void setCharAt(int idx, char newChar) StringBuffer replace(int fromIdx, int toIdx, String s) StringBuffer reverse() // Methods for extracting whole/part of the content char charAt(int idx) String substring(int fromIdx) String substring(int fromIdx, int toIdx) String toString() // Indexing int indexOf(String key) int indexOf(String key, int fromIdx) int lastIndexOf(String key) int lastIndexOf(String key, int fromIdx)
Hãy lưu ý rằng đó
StringBuffer
là một đối tượng bình thường. Bạn cần sử dụng một hàm tạo để tạo một StringBuffer
(thay vì gán cho một String
chữ). Hơn nữa, '+'
toán tử không áp dụng cho các đối tượng, bao gồm cả StringBuffer
. Bạn cần sử dụng một phương pháp thích hợp như append()
hoặc insert()
để thao tác a StringBuffer
.
Để tạo một chuỗi từ các bộ phận, sẽ hiệu quả hơn khi sử dụng
StringBuffer
(đa luồng) hoặc StringBuilder
(đơn luồng) thay vì thông qua String
nối. Ví dụ,// Create a string of YYYY-MM-DD HH:MM:SS int year = 2010, month = 10, day = 10; int hour = 10, minute = 10, second = 10; String dateStr = new StringBuilder() .append(year).append("-").append(month).append("-").append(day).append(" ") .append(hour).append(":").append(minute).append(":").append(second).toString(); System.out.println(dateStr); // StringBuilder is more efficient than String concatenation String anotherDataStr = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second; System.out.println(anotherDataStr);
Trình biên dịch JDK, trên thực tế, sử dụng cả hai
String
và StringBuffer
để xử lý nối chuỗi thông qua '+'
toán tử. Ví dụ như,String msg = "a" + "b" + "c";
sẽ được tổng hợp thành các mã sau để có hiệu quả tốt hơn:
String msg = new StringBuffer().append("a").append("b").append("c").toString();
Hai đối tượng được tạo ra trong quá trình, một đối tượng trung gian
StringBuffer
và String
đối tượng trả về .
Rule of Thumb:
String
s hiệu quả hơn nếu chúng không được sửa đổi (vì chúng được chia sẻ trong nhóm chung chuỗi). Tuy nhiên, nếu bạn phải sửa đổi nội dung của chuỗi thường xuyên (chẳng hạn như thông báo trạng thái), bạn nên sử dụng StringBuffer
lớp (hoặc StringBuilder
mô tả bên dưới) để thay thế.3.2 java.lang.StringBuilder (JDK 5)
JDK 5 đã giới thiệu một lớp mới
StringBuilder
(trong gói java.lang
), gần giống với StringBuffer
lớp này, ngoại trừ việc nó không được đồng bộ hóa . Nói cách khác, nếu nhiều luồng đang truy cập một StringBuilder
thể hiện cùng một lúc, tính toàn vẹn của nó không thể được đảm bảo. Tuy nhiên, đối với chương trình một luồng (phổ biến nhất), việc loại bỏ chi phí đồng bộ hóa sẽ giúp việc này StringBuilder
nhanh hơn.StringBuilder
tương thích API với StringBuffer
lớp, nghĩa là có cùng một bộ hàm tạo và phương thức, nhưng không đảm bảo đồng bộ hóa. Nó có thể là một sự thay thế thả trong cho StringBuffer
trong một môi trường môi trường đơn luồng .3.3 Benchmarking String/StringBuffer/StringBuilder
Chương trình sau đây so sánh thời gian thực hiện để đảo ngược một chuỗi dài thông qua một
String
đối tượng và a StringBuffer
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | // Reversing a long String via a String vs. a StringBuffer public class StringsBenchMark { public static void main(String[] args) { long beginTime, elapsedTime; // Build a long string String str = ""; int size = 16536; char ch = 'a'; beginTime = System.nanoTime(); // Reference time in nanoseconds for (int count = 0; count < size; ++count) { str += ch; ++ch; if (ch > 'z') { ch = 'a'; } } elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " usec (Build String)"); // Reverse a String by building another String character-by-character in the reverse order String strReverse = ""; beginTime = System.nanoTime(); for (int pos = str.length() - 1; pos >= 0 ; pos--) { strReverse += str.charAt(pos); // Concatenate } elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " usec (Using String to reverse)"); // Reverse a String via an empty StringBuffer by appending characters in the reverse order beginTime = System.nanoTime(); StringBuffer sBufferReverse = new StringBuffer(size); for (int pos = str.length() - 1; pos >= 0 ; pos--) { sBufferReverse.append(str.charAt(pos)); // append } elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " usec (Using StringBuffer to reverse)"); // Reverse a String by creating a StringBuffer with the given String and invoke its reverse() beginTime = System.nanoTime(); StringBuffer sBufferReverseMethod = new StringBuffer(str); sBufferReverseMethod.reverse(); // use reverse() method elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " usec (Using StringBuffer's reverse() method)");
// Reverse a String via an empty StringBuilder by appending characters in the reverse order
beginTime = System.nanoTime();
StringBuilder sBuilderReverse = new StringBuilder(size);
for (int pos = str.length() - 1; pos >= 0 ; pos--) {
sBuilderReverse.append(str.charAt(pos));
}
elapsedTime = System.nanoTime() - beginTime;
System.out.println("Elapsed Time is " + elapsedTime/1000
+ " usec (Using StringBuilder to reverse)");
// Reverse a String by creating a StringBuilder with the given String and invoke its reverse()
beginTime = System.nanoTime();
StringBuffer sBuilderReverseMethod = new StringBuffer(str);
sBuilderReverseMethod.reverse();
elapsedTime = System.nanoTime() - beginTime;
System.out.println("Elapsed Time is " + elapsedTime/1000
+ " usec (Using StringBuidler's reverse() method)"); } } |
Elapsed Time is 332100 usec (Build String) Elapsed Time is 346639 usec (Using String to reverse) Elapsed Time is 2028 usec (Using StringBuffer to reverse) Elapsed Time is 847 usec (Using StringBuffer's reverse() method) Elapsed Time is 1092 usec (Using StringBuilder to reverse) Elapsed Time is 836 usec (Using StringBuidler's reverse() method)
Quan sát
StringBuilder
là nhanh hơn gấp 2 lần StringBuffer
và nhanh hơn 300 lần so với String
. Các reverse()
phương pháp là nhanh nhất, mà mất khoảng cùng thời gian cho StringBuilder
và StringBuffer
.4. java.util.StringTokenizer (Regex Đã Lỗi Th)
Rất thường xuyên, bạn cần chia một dòng văn bản thành các thẻ được phân định bởi các khoảng trắng. Các
java.util.StringTokenizer
lớp hỗ trợ này.
Ví dụ, chương trình sau đảo ngược các từ trong Chuỗi.
import java.util.StringTokenizer;
/**
* Reverse the words in a String using StringTokenizer
*/
public class StringTokenizerTest {
public static void main(String[] args) {
String str = "Monday Tuesday Wednesday Thursday Friday Saturday Sunday";
String strReverse;
StringBuilder sb = new StringBuilder();
StringTokenizer st = new StringTokenizer(str);
while (st.hasMoreTokens()) {
sb.insert(0, st.nextToken());
if (st.hasMoreTokens()) {
sb.insert(0, " ");
}
}
strReverse = sb.toString();
System.out.println(strReverse);
}
}
// Constructors StringTokenizer(String s) // Constructs a StringTokenizer for the given string, // using the default delimiter set of " \t\n\r\f" // (i.e., blank, tab, newline, carriage-return, and form-feed). // Delimiter characters themselves will not be treated as tokens. StrintTokenizer(String s, String delimiterChars) // Use characters in delimiterSet as delimiters. // Methods boolean hasNextToken() // Returns true if next token available String nextToken() // Returns the next token
For example,
// Code Sample
StringTokenizer tokenizer = new StringTokenizer(aString);
while (tokenizer.hasNextToken()) {
String token = tokenizer.nextToken();
.....
}
Tài liệu JDK nói rằng "
StringTokenizer
là một lớp kế thừa được giữ lại vì lý do tương thích mặc dù việc sử dụng nó không được khuyến khích trong mã mới. Chúng tôi khuyên mọi người tìm kiếm chức năng này nên sử dụng split()
phương thức String
hoặc java.util.regex
gói thay thế."
Ví dụ, chương trình sau sử dụng phương thức lớp
split()
của String
lớp để đảo ngược các từ của a String
./** * Reverse the words in a String using split() method of the String class */ public class StringSplitTest { public static void main(String[] args) { String str = "Monday Tuesday Wednesday Thursday Friday Saturday Sunday"; String[] tokens = str.split("\\s"); // white space '\s' as delimiter StringBuilder sb = new StringBuilder(); for (int i = 0; i < tokens.length; ++i) { sb.insert(0, tokens[i]); if (i < tokens.length - 1) { sb.insert(0, " "); } } String strReverse = sb.toString(); System.out.println(strReverse); } }
5. Regular Expression (Regex), Patterns & Matches (JDK 4)
Read "Regular Expression in Java".
6. Super-Interface CharSequence for String, StringBuffer and StringBuilder (since JDK 4)
Giao diện
java.lang.CharSequence
được thực hiện bởi các lớp học String
, StringBuffer
, StringBuilder
, CharBuffer
và Segment
.
Nó định nghĩa hành vi phổ biến thông qua các
abstract
phương thức sau:// java.lang.CharSequence abstract charAt(int index) -> char abstract length() -> int abstract subSequence(int fromIdx, int toIdx) -> CharSequence abstract toString() -> String
JDK 8 đã thêm hai
default
phương thức vào giao diện:// java.lang.CharSequence (JDK 8) default chars() -> IntStream default codePoints() -> IntStream
JDK 11 đã thêm một
static
phương thức vào giao diện:// java.lang.CharSequence (JDK 11) static compare(CharSequence cs1, CharSequence cs2) -> int
Đăng nhận xét