Hướng dẫn và ví dụ Java String, StringBuffer và StringBuilder
1. Phân cấp thừa kế
Khi làm việc với các dữ liệu văn bản, Java cung cấp cho bạn 3 class String, StringBuffer và StringBuilder. Nếu làm việc với các dữ liệu lớn bạn nên sử dụng StringBuffer hoặc StringBuilder để đạt hiệu năng nhanh nhất. Về cơ bản 3 class này có nhiều điểm giống nhau.
StringBuilder và StringBuffer là giống nhau, nó chỉ khác biệt tình huống sử dụng có liên quan tới đa luồng (Multi Thread).
- String là không thể thay đổi (immutable), khái niệm này sẽ được nói chi tiết ở trong tài liệu, và không cho phép có class con.
- StringBuffer, StringBuilder có thể thay đổi (mutable)
StringBuilder và StringBuffer là giống nhau, nó chỉ khác biệt tình huống sử dụng có liên quan tới đa luồng (Multi Thread).
- Nếu xử lý văn bản sử dụng nhiều luồng (Thread) bạn nên sử dụng StringBuffer để tránh tranh chấp giữa các luồng.
- Nếu xử lý văn bản sử dụng 1 luồng (Thread) nên sử dụng StringBuilder.
2. Khái niệm mutable & immutable
Hãy xem một ví dụ minh họa:
// Đây là một class với trường value, name.
// Khi bạn khởi tạo đối tượng class này
// bạn không thể sét đặt lại value từ bên ngoài, và tất cả các trường khác của nó cũng thế.
// Class này không hề có các hàm để sét đặt lại các trường (field) từ bên ngoài.
// Nếu muốn bạn chỉ có thể tạo mới một đối tượng khác.
// Điều đó có nghĩa là class này là không thể thay đổi (immutable)
public class ImmutableClassExample {
private int value;
private String name;
public ImmutableClassExample(String name, int value) {
this.value = value;
this.name= name;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}
// Đây là một class có 1 trường value.
// Sau khi khởi tạo đối tượng bạn có thể sét đặt lại giá trị của trường value
// thông qua việc gọi method setNewValue(int).
// Như vậy đây là class có thể thay đổi (mutable).
public class MutableClassExample {
private int value;
public MutableClassExample(int value) {
this.value= value;
}
public void setNewValue(int newValue) {
this.value = newValue;
}
}
String là một class không thể thay đổi, String có nhiều thuộc tính (trường), ví dụ length,... nhưng các giá trị đó là không thể thay đổi.
3. String
String là một class rất quan trọng trong Java, và bất kỳ ai bắt đầu với Java đều đã sử dụng câu lệnh System.out.println() để in ra một String lên màn hình Console. Nhiều người thường không hề có ý niệm rằng String là không thể thay đổi (immutable) và là final (Không cho phép class nào thừa kế nó), tất cả các thay đổi trên String đều tạo ra một đối tượng String khác.
String thực sự là một lớp rất đặc biệt
Trong java, String là một class đặc biệt, nguyên nhân là nó được sử dụng một cách thường xuyên trong một chương trình, vì vậy đòi hỏi nó phải có hiệu suất và sự mềm dẻo. Đó là lý do tại sao String có tính đối tượng và vừa có tính nguyên thủy (primitive).
Tính nguyên thủy:
Bạn có thể tạo một string literal (chuỗi chữ), string literal được lưu trữ trong ngăn sếp (stack), đòi hỏi không gian lưu trữ ít, và rẻ hơn khi thao tác.
Các string literal được chứa trong một bể chứa (common pool). Như vậy hai string literal có nội dung giống nhau sử dụng chung một vùng bộ nhớ trên stack, điều này giúp tiết kiệm bộ nhớ.
- String literal = "Hello World";
Các string literal được chứa trong một bể chứa (common pool). Như vậy hai string literal có nội dung giống nhau sử dụng chung một vùng bộ nhớ trên stack, điều này giúp tiết kiệm bộ nhớ.
Tính đối tượng
Vì String là một class, vì vậy nó có thể được tạo ra thông qua toán tử new.
- String object = new String("Hello World");
Ví dụ:
// Tạo ngầm một String, thông qua "string literal".
// Đây là một "string literal".
// Cách này thể hiện tính nguyên thủy của String.
String str1 = "Java is Hot";
// Tạo một cách rõ ràng thông qua toán tử new.
// Đây là một "String object".
// Cách này thể hiện tính đối tượng của String,
// giống như các đối tượng khác trong Java.
String str2 = new String("I'm cool");
String Literal vs. String Object
Như đã đề cập, có hai cách để xây dựng một chuỗi (String): ngầm xây dựng bằng cách chỉ định một chuỗi chữ (String literal) hay một cách rõ ràng tạo ra một đối tượng String thông qua toán tử new và cấu tử của String. Ví dụ,
String s1 = "Hello"; // String literal
String s2 = "Hello"; // String literal
String s3 = s1; // Cùng tham chiếu (trỏ tới cùng một vị trí)
String s4 = new String("Hello"); // Tạo mới một đối tượng String
String s5 = new String("Hello"); // Tạo mới một đối tượng String
Chúng ta sẽ giải thích bằng hình minh họa dưới đây:
Các string literal có cùng một nội dung, chúng sẽ chia sẻ cùng một vị trí lưu trữ trong bể chứa (common pool). Trong khi đó các đối tượng String lưu trữ trong Heap, và không chia sẻ vị trí lưu trữ kể cả 2 đối tượng string này có nội dung giống nhau.
equals() vs ==
Phương thức equals() sử dụng để so sánh 2 đối tượng, với String nó có ý nghĩa là so sánh nội dung của 2 string. Đối với các kiểu tham chiếu (reference) toán tử == có ý nghĩa là so sánh địa chỉ vùng bộ nhớ lưu trữ của đối tượng. Hãy xem ví dụ:
String s1 = "Hello"; // String literal
String s2 = "Hello"; // String literal
String s3 = s1; // Cùng tham chiếu (trỏ tới cùng một vị trí)
String s4 = new String("Hello"); // Tạo mới một đối tượng String
String s5 = new String("Hello"); // Tạo mới một đối tượng String
s1 == s1; // true, cùng trỏ vào một vị trí
s1 == s2; // true, s1 và s2 cùng trỏ tới 1 ví trí trong "bể chứa" (common pool)
s1 == s3; // true, s3 được gán bởi s1, nó sẽ trỏ tới vị trí s1 trỏ tới.
s1 == s4; // false, trỏ tới khác vị trí.
s4 == s5; // false, trỏ tới khác vị trí trên heap
s1.equals(s3); // true, cùng nội dung
s1.equals(s4); // true, cùng nội dung
s4.equals(s5); // true, cùng nội dung
Trong thực tế bạn nên sử dụng String literal, thay vì sử dụng toán tử new. Điều này làm tăng tốc chương trình của bạn.
Các phương thức của String
Dưới đây là danh sách các method của String.
SN | Methods | Description |
1 | char charAt(int index) | Trả về một ký tự tại vị trí có chỉ số được chỉ định. |
2 | int compareTo(Object o) | So sánh một String với một Object khác. |
3 | int compareTo(String anotherString) | So sánh hai chuỗi theo từ điển. (Phân biệt chữ hoa chữ thường) |
4 | int compareToIgnoreCase(String str) | So sánh hai chuỗi theo từ điển. (Không phân biệt chữ hoa chữ thường) |
5 | String concat(String str) | Nối chuỗi được chỉ định đến cuối của chuỗi này. |
6 | boolean contentEquals(StringBuffer sb) | Trả về true nếu và chỉ nếu chuỗi này đại diện cho cùng một chuỗi ký tự như là StringBuffer quy định. |
7 | static String copyValueOf(char[] data) | Trả về một chuỗi đại diện cho chuỗi ký tự trong mảng quy định. |
8 | static String copyValueOf(char[] data, int offset, int count) | Trả về một chuỗi đại diện cho chuỗi ký tự trong mảng quy định. |
9 | boolean endsWith(String suffix) | Kiểm tra nếu chuỗi này kết thúc với hậu tố quy định. |
10 | boolean equals(Object anObject) | So sánh với một đối tượng |
11 | boolean equalsIgnoreCase(String anotherString) | So sánh với một String khác, không phân biệt chữ hoa chữ thường. |
12 | byte[] getBytes() | Mã hóa chuỗi này thành một chuỗi các byte bằng cách sử dụng bảng mã mặc định của flatform (nền tảng), lưu trữ kết quả vào một mảng byte mới. |
13 | byte[] getBytes(String charsetName) | Mã hóa chuỗi này thành một chuỗi các byte bằng cách sử dụng bảng mã cho trước, lưu trữ kết quả vào một mảng byte mới. |
14 | void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) | Copy các ký tự từ chuỗi này vào mảng ký tự đích. |
15 | int hashCode() | Trả về một mã "hash code" cho chuỗi này. |
16 | int indexOf(int ch) | Trả về chỉ số trong chuỗi này xuất hiện đầu tiên của ký tự cụ thể. |
17 | int indexOf(int ch, int fromIndex) | Trả về chỉ số trong chuỗi này xuất hiện đầu tiên của ký tự được chỉ định, bắt đầu tìm kiếm từ chỉ số cụ thể đến cuối. |
18 | int indexOf(String str) | Trả về chỉ số trong chuỗi này xuất hiện đầu tiên của chuỗi quy định. |
19 | int indexOf(String str, int fromIndex) |
Trả về chỉ số trong chuỗi này xuất hiện đầu tiên của chuỗi quy định, bắt đầu từ chỉ số xác định.
|
20 | String intern() | Returns a canonical representation for the string object. |
21 | int lastIndexOf(int ch) | Trả về chỉ số trong chuỗi này về sự xuất hiện cuối cùng của ký tự cụ thể. |
22 | int lastIndexOf(int ch, int fromIndex) | Trả về chỉ số trong chuỗi này về sự xuất hiện cuối cùng của ký tự được chỉ định, tìm kiếm lùi lại bắt đầu từ chỉ số xác định. |
23 | int lastIndexOf(String str) | Trả về chỉ số trong chuỗi này xảy ra cuối cùng bên phải của chuỗi quy định. |
24 | int lastIndexOf(String str, int fromIndex) |
Trả về chỉ số trong chuỗi này về sự xuất hiện cuối cùng của chuỗi xác định, tìm kiếm lùi lại bắt đầu từ chỉ số xác định.
|
25 | int length() | Trả về độ dài chuỗi. |
26 | boolean matches(String regex) | Kiểm tra chuỗi này khớp với biểu thức chính quy chỉ định hay không. |
27 | boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) | Kiểm tra chuỗi có một phần giống nhau. |
28 | boolean regionMatches(int toffset, String other, int ooffset, int len) | Kiểm tra chuỗi có một phần giống nhau. |
29 | String replace(char oldChar, char newChar) | Trả về một chuỗi mới từ thay thế tất cả các lần xuất hiện của ký tự oldChar trong chuỗi này với ký tự newChar. |
30 | String replaceAll(String regex, String replacement) |
Thay thế tất cả các chuỗi con của chuỗi này khớp với biểu thức chính quy bởi String mới replacement
|
31 | String replaceFirst(String regex, String replacement) | Thay thế chuỗi con đầu tiên của chuỗi này khớp với biểu thức chính quy bởi một String mới replacement |
32 | String[] split(String regex) | Tách chuỗi này thành các chuỗi con, tại các chỗ khớp với biểu thức chính quy cho trước. |
33 | String[] split(String regex, int limit) | Tách chuỗi này thành các chuỗi con, tại các chỗ khớp với biểu thức chính quy cho trước. Tối đa limit chuỗi con. |
34 | boolean startsWith(String prefix) | Kiểm tra nếu chuỗi này bắt đầu với tiền tố quy định. |
35 | boolean startsWith(String prefix, int toffset) |
Kiểm tra nếu chuỗi này bắt đầu với tiền tố quy định bắt đầu một chỉ số xác định.
|
36 | CharSequence subSequence(int beginIndex, int endIndex) | Trả về một chuỗi ký tự mới là một dãy con của dãy này. |
37 | String substring(int beginIndex) | Trả về một chuỗi ký tự mới là một dãy con của dãy này. Từ chỉ số cho trước tới cuối |
38 | String substring(int beginIndex, int endIndex) | Trả về một chuỗi ký tự mới là một dãy con của dãy này. Từ chỉ số bắt đầu cho tới chỉ số cuối. |
39 | char[] toCharArray() | Chuyển chuỗi này thành mảng ký tự. |
40 | String toLowerCase() | Chuyển tất cả các ký tự của chuỗi này sang chữ thường, sử dụng miền địa phương mặc định (default locale) |
41 | String toLowerCase(Locale locale) | Chuyển tất cả các ký tự của chuỗi này sang chữ thường, sử dụng miền địa phương (locale) cho trước. |
42 | String toString() | Trả về String này. |
43 | String toUpperCase() | Chuyển tất cả các ký tự của chuỗi này sang chữ hoa, sử dụng miền địa phương mặc định (default locale) |
44 | String toUpperCase(Locale locale) | Chuyển tất cả các ký tự của chuỗi này sang chữ hoa, sử dụng miền địa phương (locale) cho trước. |
45 | String trim() | Trả về một String mới, sau khi loại bỏ các ký tự trắng (whitespace) bên trái và bên phải. |
46 | static String valueOf(primitive data type x) | Returns the string representation of the passed data type argument. |
4. StringBuffer vs StringBuilder
StringBuilder và StringBuffer là rất giống nhau, điều khác biệt là tất cả các phương thức của StringBuffer đã được đồng bộ, nó thích hợp khi bạn làm việc với ứng dụng đa luồng, nhiều luồng có thể truy cập vào một đối tượng StringBuffer cùng lúc. Trong khi đó StringBuilder có các phương thức tương tự nhưng không được đồng bộ, nhưng vì vậy mà hiệu suất của nó cao hơn, bạn nên sử dụng StringBuilder trong ứng dụng đơn luồng, hoặc sử dụng như một biến địa phương trong một phương thức.
Các method của StringBuffer (StringBuilder cũng tương tự)
// Cấu tử.
StringBuffer() // an initially-empty StringBuffer
StringBuffer(int size) // with the specified initial size
StringBuffer(String s) // with the specified initial content
// Độ dài
int length()
// Các method xây dựng nội dung
// type ở đây có thể là kiểu nguyên thủy (primitive), char[], String, StringBuffer, .v.v..
StringBuffer append(type arg) // ==> chú ý (ở trên)
StringBuffer insert(int offset, type arg) // ==> chú ý (ở trên)
// Các method thao tác trên nội dung.
StringBuffer delete(int start, int end)
StringBuffer deleteCharAt(int index)
void setLength(int newSize)
void setCharAt(int index, char newChar)
StringBuffer replace(int start, int end, String s)
StringBuffer reverse()
// Các method trích ra toàn bộ hoặc một phần dữ liệu.
char charAt(int index)
String substring(int start)
String substring(int start, int end)
String toString()
// Các method tìm kiếm vị trí.
int indexOf(String searchKey)
int indexOf(String searchKey, int fromIndex)
int lastIndexOf(String searchKey)
int lastIndexOf(String searchKey, int fromIndex)
StringBuilderDemo.java
package org.o7planning.tutorial.strbb;
public class StringBuilderDemo {
public static void main(String[] args) {
// Tạo đối tượng StringBuilder
// Hiện tại chưa có dữ liệu trên StringBuilder.
StringBuilder sb = new StringBuilder(10);
// Nối thêm chuỗi Hello vào sb.
sb.append("Hello...");
System.out.println("- sb after appends a string: " + sb);
// append a character
char c = '!';
sb.append(c);
System.out.println("- sb after appending a char: " + sb);
// Trèn một String vào vị trí thứ 5
sb.insert(8, " Java");
System.out.println("- sb after insert string: " + sb);
// Xóa đoạn String con trên StringBuilder.
// Tại vị trí có chỉ số 5 tới 8
sb.delete(5,8);
System.out.println("- sb after delete: " + sb);
}
}
Kết quả chạy ví dụ:
- sb after appends a string: Hello...
- sb after appending a char: Hello...!
- sb after insert string: Hello... Java!
- sb after delete: Hello Java!
Java cơ bản
- Tùy biến trình biên dịch java xử lý Annotation của bạn (Annotation Processing Tool)
- Lập trình Java theo nhóm sử dụng Eclipse và SVN
- Hướng dẫn và ví dụ Java WeakReference
- Hướng dẫn và ví dụ Java PhantomReference
- Hướng dẫn nén và giải nén trong Java
- Cấu hình Eclipse để sử dụng JDK thay vì JRE
- Phương thức String.format() và printf() trong Java
- Cú pháp và các tính năng mới trong Java 5
- Cú pháp và các tính năng mới trong Java 8
- Hướng dẫn sử dụng biểu thức chính quy trong Java
- Hướng dẫn lập trình đa luồng trong Java - Java Multithreading
- Thư viện điều khiển các loại cơ sở dữ liệu khác nhau trong Java
- Hướng dẫn sử dụng Java JDBC kết nối cơ sở dữ liệu
- Lấy các giá trị của các cột tự động tăng khi Insert một bản ghi sử dụng JDBC
- Hướng dẫn và ví dụ Java Stream
- Functional Interface trong Java
- Giới thiệu về Raspberry Pi
- Hướng dẫn và ví dụ Java Predicate
- Abstract class và Interface trong Java
- Access modifier trong Java
- Hướng dẫn và ví dụ Java Enum
- Hướng dẫn và ví dụ Java Annotation
- So sánh và sắp xếp trong Java
- Hướng dẫn và ví dụ Java String, StringBuffer và StringBuilder
- Hướng dẫn xử lý ngoại lệ trong Java - Java Exception Handling
- Hướng dẫn và ví dụ Java Generics
- Thao tác với tập tin và thư mục trong Java
- Hướng dẫn và ví dụ Java BiPredicate
- Hướng dẫn và ví dụ Java Consumer
- Hướng dẫn và ví dụ Java BiConsumer
- Bắt đầu với Java cần những gì?
- Lịch sử của Java và sự khác biệt giữa Oracle JDK và OpenJDK
- Cài đặt Java trên Windows
- Cài đặt Java trên Ubuntu
- Cài đặt OpenJDK trên Ubuntu
- Cài đặt Eclipse
- Cài đặt Eclipse trên Ubuntu
- Học nhanh Java cho người mới bắt đầu
- Lịch sử của bit và byte trong khoa học máy tính
- Các kiểu dữ liệu trong Java
- Các toán tử Bitwise
- Câu lệnh rẽ nhánh (if else) trong Java
- Câu lệnh rẽ nhánh switch trong Java
- Vòng lặp trong Java
- Mảng (Array) trong Java
- JDK Javadoc định dạng CHM
- Thừa kế và đa hình trong Java
- Hướng dẫn và ví dụ Java Function
- Hướng dẫn và ví dụ Java BiFunction
- Ví dụ về Java encoding và decoding sử dụng Apache Base64
- Hướng dẫn và ví dụ Java Reflection
- Hướng dẫn gọi phương thức từ xa với Java RMI
- Hướng dẫn lập trình Java Socket
- Các nền tảng nào bạn nên chọn để lập trình ứng dụng Java Desktop?
- Hướng dẫn và ví dụ Java Commons IO
- Hướng dẫn và ví dụ Java Commons Email
- Hướng dẫn và ví dụ Java Commons Logging
- Tìm hiểu về Java System.identityHashCode, Object.hashCode và Object.equals
- Hướng dẫn và ví dụ Java SoftReference
- Hướng dẫn và ví dụ Java Supplier
- Lập trình Java hướng khía cạnh với AspectJ (AOP)
Show More
- Hướng dẫn lập trình Java Servlet/JSP
- Các hướng dẫn Java Collections Framework
- Java API cho HTML & XML
- Các hướng dẫn Java IO
- Các hướng dẫn Java Date Time
- Các hướng dẫn Spring Boot
- Các hướng dẫn Maven
- Các hướng dẫn Gradle
- Các hướng dẫn Java Web Services
- Các hướng dẫn lập trình Java SWT
- Các hướng dẫn lập trình JavaFX
- Các hướng dẫn Java Oracle ADF
- Các hướng dẫn Struts2 Framework
- Các hướng dẫn Spring Cloud