Tôi đã không hiểu đầy đủ về các hàm tạo tĩnh trong Java. Nếu nó được cho phép, tại sao nó được cho phép? Trong những kịch bản bạn sẽ sử dụng nó? Mục đích gì nó sẽ phục vụ? Ai đó có thể cho tôi một ví dụ đơn giản xin vui lòng?
Tôi đã không hiểu đầy đủ về các hàm tạo tĩnh trong Java. Nếu nó được cho phép, tại sao nó được cho phép? Trong những kịch bản bạn sẽ sử dụng nó? Mục đích gì nó sẽ phục vụ? Ai đó có thể cho tôi một ví dụ đơn giản xin vui lòng?
Câu trả lời:
Nói một cách chính xác, Java không có các hàm tạo tĩnh bởi vì một hàm tạo, theo định nghĩa, không thể là tĩnh. Những gì bạn đang đề cập đến được gọi là "khối khởi tạo tĩnh." Một constructor ngụ ý rằng bạn đang xây dựng một đối tượng. Bạn không thể có constructor cho một lớp vì một lớp không phải là một thể hiện của chính nó. Nó chỉ đơn giản là một lớp học. Thứ "xây dựng" lớp được gọi là trình biên dịch (hoặc máy ảo, tùy thuộc vào ý nghĩa của "cấu trúc") và nếu bạn tham gia xây dựng mã trong mã khác, bạn sẽ bắt đầu tạo mã, đó là một con thú hoàn toàn khác
Tất cả việc chọn nit sang một bên, một khối khởi tạo tĩnh được sử dụng để khởi tạo các trường tĩnh (hoặc cấp độ) phức tạp cho một lớp. Thông thường chúng được sử dụng để khởi tạo những thứ không thể khởi tạo trong một dòng hoặc yêu cầu một số đối tượng khác (có thể có hoặc không có trong lớp trong đó khối tĩnh được triển khai) trước tiên được khởi tạo.
Về cơ bản, người ta có thể sử dụng chúng để nói với lớp "Này, đặt biến A thành giá trị này ĐẦU TIÊN, sau đó, khi đã xong, sử dụng giá trị của A để khởi tạo B." Do Java yêu cầu khởi tạo trường tiêu chuẩn phải được thực hiện trong một hàm tạo hoặc phương thức hoặc thông qua lệnh gọi của hàm tạo hoặc phương thức (trừ khi nó là một chữ), đây có thể là một phương thức thuận tiện để khởi tạo các đối tượng tĩnh, phức tạp.
Các khối khởi tạo tĩnh không cần thiết quá thường xuyên và thường nên tránh trừ khi chúng có công dụng thực sự . Đừng hiểu sai ý tôi, họ có vị trí của họ trong Java, nhưng giống như nhiều thứ khác (chẳng hạn như các câu lệnh break, return, switch và goto), họ có thể dễ dàng sử dụng quá mức, làm giảm khả năng đọc và khả năng duy trì của mã -base họ được sử dụng trong.
Một ví dụ ngắn gọn về khối khởi tạo tĩnh đang được sử dụng sẽ là như sau (theo giải thích tuyệt vời về khối khởi tạo tĩnh được tìm thấy ở đây ):
Mã số:
public class StaticExample{
static {
System.out.println("This is first static block");
}
public StaticExample(){
System.out.println("This is constructor");
}
public static String staticString = "Static Variable";
static {
System.out.println("This is second static block and "
+ staticString);
}
public static void main(String[] args){
StaticExample statEx = new StaticExample();
StaticExample.staticMethod2();
}
static {
staticMethod();
System.out.println("This is third static block");
}
public static void staticMethod() {
System.out.println("This is static method");
}
public static void staticMethod2() {
System.out.println("This is static method2");
}
}
Đầu ra:
This is first static block
This is second static block and Static Variable
This is static method
This is third static block
This is constructor
This is static method2
Một số trường hợp họ liệt kê khi các khối tĩnh có thể hữu ích:
Một số lý do KHÔNG sử dụng khối tĩnh (trong các tình huống khác):
this
từ khóa vì không có ví dụ.Tôi nên lưu ý: Mặc dù một số ngôn ngữ (như C #) có thể có cú pháp cho "hàm tạo" là tĩnh, nhưng các "hàm tạo" đó hoạt động giống như cách các khối khởi tạo tĩnh thực hiện trong Java và được nhiều người (bao gồm cả tôi) nhìn thấy như hiểu sai trong ngôn ngữ, đưa ra khái niệm cơ bản của một hàm tạo OOP .
nó được sử dụng để khởi tạo các trường khó hơn là chỉ định nó:
public class Example{
public final static Map<String, String> preFilledField;
static{
Map<String, String> tmp = new HashMap<>();
//fill map
preFilledField = Collections.unmodifiableMap(tmp);
}
}
không thể điền vào bản đồ khi khởi tạo (trừ khi bạn sử dụng hack lớp con ẩn danh) vì vậy đây là cách tốt nhất để đảm bảo nó được điền trước khi sử dụng lần đầu
Bạn cũng có thể làm điều này để bắt ngoại lệ được kiểm tra khi khởi tạo
Câu trả lời của Gurgadurgen có lẽ là những gì bạn đang tìm kiếm, nhưng tôi sẽ chỉ thêm một vài điểm khác đôi khi bị bỏ qua khi ai đó muốn có một "nhà xây dựng tĩnh".
Nếu bạn muốn một phương thức tĩnh tạo một thể hiện của lớp của bạn, bạn có thể tạo một phương thức tĩnh chỉ đơn giản gọi hàm tạo của lớp.
public class Example
{
/** Static method to create an instance. */
public static Example build()
{ return new Example() ; }
/** A field of each instance. */
private String stuff ;
/** The class's actual constructor. */
public Example()
{ stuff = new String() ; }
public String getStuff()
{ return this.stuff ; }
/**
* Mutator for "stuff" property. By convention this returns "void"
* but you might want to return the object itself, to support the
* sort of chained invocations that are becoming trendy now. You'll
* see the stylistic benefits of chaining later in this example.
*/
public Example setStuff( String newStuff )
{
this.stuff = newStuff ;
return this ;
}
}
public class ExampleTest
{
public static void main( String[] args )
{
// The usual instance model.
Example first = new Example() ;
System.out.println( first.setStuff("stuff").getStuff() ) ;
// Using your static method to construct an instance:
Example second = Example.build() ;
System.out.println( second.setStuff("more stuff").getStuff() ) ;
// Chaining all the invocations at once:
System.out.println( Example.build().setStuff("even more stuff").getStuff() ) ;
}
}
Điều này tạo ra đầu ra:
stuff
more stuff
even more stuff
Lý do khác để tạo một phương thức tĩnh để xây dựng một thể hiện là trường hợp khi bạn muốn đảm bảo rằng chính xác một thể hiện của lớp của bạn tồn tại tại bất kỳ thời điểm nào; này được gọi là một singleton . Theo quy ước, một lớp như vậy sẽ cung cấp một phương thức tĩnh được gọi getInstance()
để lấy một thể hiện duy nhất và duy nhất được coi là "singleton".
public class SingletonExample extends Example
{
// Note: This extends my previous example, which has a "stuff"
// property, and a trivial constructor.
/** The singleton instance, statically initialized as null. */
private static SingletonExample singleton = null ;
/**
* The static accessor for the singleton. If no instance exists,
* then it will be created; otherwise, the one that already exists
* will be returned.
*/
public static SingletonExample getInstance()
{
if( singleton == null )
singleton = new SingletonExample() ;
return singleton ;
}
}
public class SingletonExampleTest
{
public static void main( String[] args )
{
System.out.println( SingletonExample.getInstance().setStuff("stuff").getStuff() ) ;
// You could still create instances of this class normally if you want to.
SingletonExample otherstuff = new SingletonExample() ;
otherstuff.setStuff("other stuff") ;
// But watch what happens to this.
System.out.println( SingletonExample.getInstance().getStuff() ) ;
System.out.println( otherstuff.getStuff() ) ;
// Now we show what happens when you start modifying the singleton.
SingletonExample theoneandonly = SingletonExample.getInstance() ;
theoneandonly.setStuff("changed stuff") ;
System.out.println( SingletonExample.getInstance().getStuff() ) ;
}
}
Điều này tạo ra sau đây.
stuff
stuff
other stuff
changed stuff
Bằng cách lưu trữ một tham chiếu đến singleton, và sau đó sửa đổi nó, cuộc gọi tiếp theo để getInstance()
có được singleton đã sửa đổi đó.
Thật hấp dẫn khi sử dụng singletons như một cách để bắt đầu tạo các biến toàn cục cho ứng dụng của bạn. Trong một số bối cảnh, điều này có thể hữu ích, nhưng nó cũng có thể khiến bạn gặp rắc rối. Cụ thể, tôi đã gặp phải một lỗi thú vị khi phát triển ứng dụng Android, nơi các trường hợp đơn lẻ có thể bị mất. Nhảy từ hoạt động này sang hoạt động khác đôi khi có thể khiến JVM sử dụng một "trình nạp lớp" mới mà sẽ không biết về đơn lẻ được lưu trữ tĩnh bởi trình nạp lớp trước đó.
Mặc dù tôi hiểu rằng có nhiều người coi khái niệm "nhà xây dựng tĩnh" là một cách hiểu sai, tôi không tin đó là trường hợp. Vấn đề là trong quá trình xây dựng cả hai lớp và các đối tượng thể hiện của chúng . Nó đã được nêu trong các luồng khác rằng việc xây dựng lớp là công việc của trình biên dịch. Ngay cả trong Java, điều này chỉ đúng một nửa.
Trình biên dịch xây dựng một giàn giáo cho mỗi định nghĩa lớp. Giàn giáo chứa siêu dữ liệu về lớp và những trường hợp nào nên có trong chúng tại thời điểm xây dựng. Nếu một lớp định nghĩa một trường được gán một giá trị nguyên thủy không đổi, thì giá trị đó được bao gồm trong giàn giáo bởi trình biên dịch. Đối với bất kỳ loại giá trị nào khác được gán, trình biên dịch sẽ tạo một thói quen khởi tạo được chạy 1 lần, trước khi tạo phiên bản lớp đầu tiên, cập nhật giàn giáo với các giá trị phù hợp. Bản cập nhật này không thể được thực hiện bởi trình biên dịch.
Vì bản cập nhật một lần này cho giàn giáo thường là điều kiện tiên quyết quan trọng đối với hoạt động đúng của các hàm tạo cá thể, nên cũng hợp lý khi gọi nó là một loại hàm tạo. Đây là lý do tại sao trong các ngôn ngữ OO phổ biến hỗ trợ khái niệm này, nó được gọi là hàm tạo tĩnh. Khái niệm đằng sau các khối khởi tạo tĩnh trong Java ít hơn một thay đổi ngữ nghĩa để phù hợp với quan niệm rằng một lập trình viên Java phải là cả hai hệ thống và bất khả tri thực hiện.
Như các câu trả lời khác đã nói, bạn có thể viết các phương thức tĩnh xây dựng một đối tượng. Điều này làm tròn việc thiếu các hàm tạo có tên trong Java (và nhiều ngôn ngữ khác. Delphi không hỗ trợ các hàm tạo có tên). Bạn có thể chơi với các loại và thứ tự của các tham số nhưng điều này có thể dẫn đến mã không rõ ràng và dễ vỡ.
Chẳng hạn, chúng ta có thể phát minh ra một kịch bản trong đó đối tượng của bạn có thể được xây dựng từ chuỗi XML hoặc chuỗi JSON. Bạn có thể viết các phương thức như:
static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);
Thỉnh thoảng tôi đã sử dụng điều này như là một thay thế cho một hàm tạo không tham số với các phương thức khởi tạo được đặt tên:
MyObject myObject = new MyObject();
myObject.loadXml(xml).
Bạn có thể xem một phương thức tạo tĩnh khi triển khai mẫu trình xây dựng bên trong lớp của bạn.
Bạn có thể xem phần "tĩnh" giống như trình xây dựng mức lớp sử dụng để khởi tạo các thuộc tính lớp (tĩnh trong java). Giống như hàm tạo "bình thường" được sử dụng để khởi tạo các thuộc tính mức thể hiện.