สนามแบบคงที่
เมื่อเราแสดงตัวแปรระดับชั้นเรียน เราระบุว่าค่านั้นเป็นของชั้นเรียน ถ้าคุณไม่ทำเช่นนี้ ค่าของตัวแปรจะถูกผูกไว้กับวัตถุที่สร้างขึ้นโดยใช้คลาสนี้ มันหมายความว่าอะไร? และความจริงก็คือถ้าตัวแปรไม่คงที่ แต่ละอ็อบเจ็กต์ใหม่ของคลาสนี้ก็จะมีค่าของตัวแปรนี้เอง โดยการเปลี่ยนแปลงที่เราเปลี่ยนมันในออบเจ็กต์เดียวเท่านั้น ตัวอย่างเช่น เรามีคลาส Car ที่ไม่มี -ตัวแปรคงที่:public class Car {
int km;
}
จากนั้นในหลัก:
Car orangeCar = new Car();
orangeCar.km = 100;
Car blueCar = new Car();
blueCar.km = 85;
System.out.println("Orange car - " + orangeCar.km);
System.out.println("Blue car - " + blueCar.km);
ผลลัพธ์ที่เราได้รับคือ:
Orange car - 100
Blue car - 85
อย่างที่คุณเห็น แต่ละวัตถุมีตัวแปรของตัวเอง ซึ่งการเปลี่ยนแปลงจะเกิดขึ้นกับวัตถุนี้เท่านั้น ถ้าเรามีตัวแปรคงที่ ค่าส่วนกลางนี้จะเหมือนกันสำหรับทุกคน ตอนนี้เรามีรถยนต์ที่มีตัวแปรคงที่:
public class Car {
static int km;
}
จากนั้นโค้ดเดียวกันใน main จะส่งออกไปยังคอนโซล:
Orange car - 85
Blue car - 85
ท้ายที่สุดแล้ว เรามีตัวแปรหนึ่งตัวสำหรับเราทุกคน และทุกครั้งที่เราเปลี่ยนแปลงมัน โดยปกติแล้วตัวแปรคงที่จะเข้าถึงไม่ได้โดยการอ้างอิงอ็อบเจ็กต์ - orangeCar.km แต่เข้าถึงโดยชื่อคลาส - Car.km
บล็อกแบบคงที่
มีบล็อกการเริ่มต้นสองบล็อก - แบบปกติและแบบคงที่ บล็อกมีวัตถุประสงค์เพื่อเริ่มต้นตัวแปรภายใน หากบล็อกเป็นเรื่องปกติ ตัวแปรภายในของออบเจ็กต์จะถูกเตรียมใช้งานด้วยบล็อกนั้น แต่ถ้าเป็นแบบคงที่ ตัวแปรคงที่ (นั่นคือ ตัวแปรคลาส) จะถูกกำหนดให้กับบล็อกเหล่านั้น ตัวอย่างของคลาสที่มีบล็อกการเริ่มต้นแบบคงที่:public class Car {
static int km;
static {
km = 150;
}
}
วิธีแบบคงที่
วิธีการแบบสแตติกแตกต่างจากวิธีการปกติตรงที่พวกมันยังถูกผูกไว้กับคลาสแทนที่จะเป็นอ็อบเจ็กต์ คุณสมบัติที่สำคัญของวิธีการแบบคงที่คือสามารถเข้าถึงตัวแปร/วิธีการแบบคงที่เท่านั้น ตัวอย่างเช่น ลองดูที่คลาสที่จะเป็นตัวนับที่ติดตามการเรียกใช้เมธอด:public class Counter {
static int count;
public static void invokeCounter() {
count++;
System.out.println("Current counter value - " + count);
}
}
เรียกมันว่าหลัก:
Counter.invokeCounter();
Counter.invokeCounter();
Counter.invokeCounter();
และเราได้รับผลลัพธ์ไปยังคอนโซล:
Текущее meaning счётчика - 1
Текущее meaning счётчика - 2
Текущее meaning счётчика - 3
คลาสคงที่ใน Java
เฉพาะคลาสภายในเท่านั้นที่สามารถเป็นคลาสคงที่ได้ อีกครั้งที่คลาสนี้เชื่อมโยงกับคลาสภายนอก และหากคลาสภายนอกได้รับการสืบทอดโดยคลาสอื่น คลาสนี้จะไม่ได้รับการสืบทอด ยิ่งไปกว่านั้น คลาสนี้สามารถสืบทอดได้ เช่นเดียวกับที่สามารถสืบทอดจากคลาสอื่น ๆ และใช้อินเทอร์เฟซได้ โดยพื้นฐานแล้ว คลาสที่ซ้อนกันแบบคงที่ไม่แตกต่างจากคลาสภายในอื่น ๆ ยกเว้นว่าออบเจ็กต์นั้นไม่มีการอ้างอิงถึงออบเจ็กต์คลาสภายนอกที่สร้างขึ้น อย่างไรก็ตาม สิ่งนี้ทำให้คลาสแบบสแตติกคล้ายกับคลาสที่ไม่ซ้อนกันทั่วไปมากที่สุด เนื่องจากความแตกต่างเพียงอย่างเดียวคือคลาสนั้นถูกรวมไว้ในคลาสอื่น ในบางกรณี นี่เป็นข้อได้เปรียบสำหรับเรา เนื่องจากจากนั้นเราจึงสามารถเข้าถึงตัวแปรสแตติกส่วนตัวของคลาสภายนอกได้ ตัวอย่างของคลาสสแตติกที่ซ้อนกัน:public class Vehicle {
public static class Car {
public int km;
}
}
การสร้างอินสแตนซ์ของคลาสนี้และการตั้งค่าของตัวแปรภายใน:
Vehicle.Car car = new Vehicle.Car();
car.km = 90;
หากต้องการใช้วิธีการ/ตัวแปร/คลาสแบบสแตติก เราไม่จำเป็นต้องสร้างอ็อบเจ็กต์ของคลาสนั้น แน่นอนว่าควรคำนึงถึงตัวแก้ไขการเข้าถึงด้วย ตัวอย่างเช่น ฟิลด์ต่างๆprivate
จะสามารถเข้าถึงได้ภายในคลาสที่มีการประกาศเท่านั้น ช่องต่างๆprotected
พร้อมใช้งานสำหรับทุกคลาสภายในแพ็คเกจ ( package ) รวมถึงคลาสที่สืบทอดทั้งหมดที่อยู่นอกแพ็คเกจ สำหรับรายละเอียดเพิ่มเติม โปรดดูบทความ “ ส่วนตัว vs ได้รับการป้องกัน vs สาธารณะ ” สมมติว่ามีวิธีการคงที่increment()
ในชั้นเรียนCounter
ที่มีหน้าที่เพิ่มตัวcount
นับ หากต้องการเรียกเมธอดนี้ คุณสามารถใช้แบบฟอร์มCounter.increment()
ได้ ไม่จำเป็นต้องสร้างอินสแตนซ์ของคลาสCounter
เพื่อเข้าถึงฟิลด์หรือเมธอดแบบสแตติก นี่คือความแตกต่างพื้นฐานระหว่างวัตถุแบบคงที่และแบบไม่คงที่ (สมาชิกของคลาส) ฉันขอเตือนคุณอีกครั้งว่าสมาชิกคลาสแบบคงที่เป็นของคลาสโดยตรง ไม่ใช่ของอินสแตนซ์ นั่นคือค่าของตัวแปรคงcount
ที่จะเหมือนกันสำหรับออบเจ็กต์ทุกCounter
ประเภท ต่อไปในบทความนี้ เราจะดูแง่มุมพื้นฐานของการใช้ตัวแก้ไขแบบคงที่ใน Java รวมถึงคุณสมบัติบางอย่างที่จะช่วยให้คุณเข้าใจแนวคิดการเขียนโปรแกรมหลัก
สิ่งที่โปรแกรมเมอร์ทุกคนควรรู้เกี่ยวกับ Static modifier ใน Java
ในส่วนนี้ เราจะดูพื้นฐานของการใช้เมธอด ฟิลด์ และคลาสแบบคงที่ เริ่มจากตัวแปรกันก่อน-
คุณไม่สามารถเข้าถึงสมาชิกของคลาสที่ไม่คงที่ภายในบริบทคงที่ เช่น วิธีการหรือบล็อก การคอมไพล์โค้ดด้านล่างจะส่งผลให้เกิดข้อผิดพลาด:
public class Counter{ private int count; public static void main(String args[]){ System.out.println(count); //compile time error }}
นี่เป็นหนึ่งในข้อผิดพลาดที่พบบ่อยที่สุดที่โปรแกรมเมอร์ Java ทำ โดยเฉพาะมือใหม่ เนื่องจากวิธีการนี้
main
เป็นแบบคงที่ แต่ตัวแปรcount
ไม่ใช่ ในกรณีนี้ วิธีการprintln
ภายในวิธีการmain
จะทำให้เกิด "ข้อผิดพลาดเวลาในการคอมไพล์" -
В отличие от локальных переменных, статические поля и методы НЕ потокобезопасны (Thread-safe) в Java. На практике это одна из наиболее частых причин возникновения проблем связанных с безопасностью мультипоточного программирования. Учитывая что каждый экземпляр класса имеет одну и ту же копию статической переменной, то такая переменная нуждается в защите — «залочивании» классом. Поэтому при использовании статических переменных, убедитесь, что они должным образом синхронизированы (synchronized), во избежание проблем, например таких How «состояние гонки» (race condition).
-
Статические методы имеют преимущество в применении, т.к. отсутствует необходимость каждый раз создавать новый an object для доступа к таким методам. Статический метод можно вызвать, используя тип класса, в котором эти методы описаны. Именно поэтому, подобные методы How нельзя лучше подходят в качестве методов-фабрик (
factory
), и методов-утorт (utility
). Классjava.lang.Math
— замечательный пример, в котором почти все методы статичны, по этой же причине классы-утorты в Java финализированы (final
). -
Другим важным моментом является то, что вы НЕ можете переопределять (
Override
) статические методы. Если вы объявите такой же метод в классе-наследнике (subclass
), т.е. метод с таким же именем и сигнатурой, вы лишь «спрячете» метод суперкласса (superclass
) instead of переопределения. Это явление известно How сокрытие методов (hiding methods
). Это означает, что при обращении к статическому методу, который объявлен How в родительском, так и в дочернем классе, во время компиляции всегда будет вызван метод исходя из типа переменной. В отличие от переопределения, такие методы не будут выполнены во время работы программы. Рассмотрим пример:class Vehicle{ public static void kmToMiles(int km){ System.out.println("Inside parent class/static method"); } } class Car extends Vehicle{ public static void kmToMiles(int km){ System.out.println("Inside child class/static method "); } } public class Demo{ public static void main(String args[]){ Vehicle v = new Car(); v.kmToMiles(10); }}
Вывод в консоль:
Внутри родительского класса/статического метода
Код наглядно демонстрирует: несмотря на то, что an object имеет тип
Car
, вызван статический метод из классаVehicle
, т.к. произошло обращение к методу во время компиляции. И заметьте, ошибки во время компиляции не возникло! -
Объявить статическим также можно и класс, за исключением классов верхнего уровня. Такие классы известны How «вложенные статические классы» (
nested static class
). Они бывают полезными для представления улучшенных связей. Яркий пример вложенного статического класса —HashMap.Entry
, который предоставляет структуру данных внутриHashMap
. Стоит заметить, также How и любой другой внутренний класс, вложенные классы находятся в отдельном файле .class. Таким образом, если вы объявor пять вложенных классов в вашем главном классе, у вас будет 6 файлов с расширением .class. Ещё одним примером использования является объявление собственного компаратора (Comparator
), например компаратор по возрасту (AgeComparator
) в классе сотрудники (Employee
). -
Модификатор static также может быть объявлен в статичном блоке, более известным How «Статический блок инициализации» (
Static initializer block
), который будет выполнен во время загрузки класса. Если вы не объявите такой блок, то Java соберёт все статические поля в один список и выполнит его во время загрузки класса. Однако, статичный блок НЕ может пробросить перехваченные исключения, но может выбросить не перехваченные. В таком случае возникнет «Exception Initializer Error». На практике, любое исключение возникшее во время выполнения и инициализации статических полей, будет завёрнуто Java в эту ошибку. Это также самая частая причина ошибки «No Class Def Found Error», т.к. класс не находился в памяти во время обращения к нему. -
Полезно знать, что статические методы связываются во время компиляции, в отличие от связывания виртуальных or не статических методов, которые связываются во время исполнения на реальном an objectе. Следовательно, статические методы не могут быть переопределены в Java, т.к. полиморфизм во время выполнения не распространяется на них. Это важное ограничение, которое необходимо учитывать, объявляя метод статическим. В этом есть смысл, только тогда, когда нет возможности or необходимости переопределения такого метода классами-наследниками. Методы-фабрики и методы-утorты хорошие образцы применения модификатора static. Джошуа Блох выделил несколько преимуществ использования статичного метода-фабрики перед конструктором, в книге «Effective Java», которая является обязательной для прочтения каждым программистом данного языка.
-
Важным свойством статического блока является инициализация. Статические поля or переменные инициализируются после загрузки класса в память. Порядок инициализации сверху вниз, в том же порядке, в Howом они описаны в исходном файле Java класса. Поскольку статические поля инициализируются на потокобезопасный манер, это свойство также используется для реализации паттерна
Singleton
. Если вы не используется списокEnum
HowSingleton
, по тем or иным причинам, то для вас есть хорошая альтернатива. Но в таком случае необходимо учесть, что это не «ленивая» инициализация. Это означает, что статическое поле будет проинициализировано ещё ДО того How кто-нибудь об этом «попросит». Если an object ресурсоёмкий or редко используется, то инициализация его в статическом блоке сыграет не в вашу пользу. -
ในระหว่างการทำให้เป็นอนุกรม เช่นเดียวกับ
transient
ตัวแปร ฟิลด์คงที่จะไม่ถูกทำให้เป็นอนุกรม แท้จริงแล้ว หากคุณบันทึกข้อมูลใดๆ ในช่องคงที่ หลังจากดีซีเรียลไลซ์แล้ว อ็อบเจ็กต์ใหม่จะมีค่าหลัก (ค่าเริ่มต้น) ของมัน ตัวอย่างเช่น หากฟิลด์คงที่เป็นตัวแปรประเภทint
ดังนั้น ค่าของมันหลังจากการดีซีเรียลไลซ์จะเป็นศูนย์ ถ้า ชนิดfloat
คือ 0.0 ถ้าชนิดObject
–null
. จริงๆ แล้ว นี่เป็นหนึ่งในคำถามที่พบบ่อยที่สุดเกี่ยวกับการทำให้เป็นอนุกรมในการสัมภาษณ์ Java อย่าเก็บข้อมูลที่สำคัญที่สุดเกี่ยวกับวัตถุไว้ในเขตข้อมูลคงที่! -
และสุดท้ายเรามาพูด
static import
ถึง ตัวแก้ไขนี้มีความเหมือนกันมากกับตัวดำเนินการมาตรฐานimport
แต่ต่างจากตัวแก้ไขตรงที่ช่วยให้คุณสามารถนำเข้าสมาชิกแบบคงที่หนึ่งตัวหรือทั้งหมดของคลาสได้ เมื่อนำเข้าเมธอดแบบสแตติก สามารถเข้าถึงได้เสมือนว่าถูกกำหนดไว้ในคลาสเดียวกัน ในทำนองเดียวกันเมื่อนำเข้าฟิลด์ เราสามารถเข้าถึงได้โดยไม่ต้องระบุชื่อคลาส คุณลักษณะนี้เปิดตัวใน Java เวอร์ชัน 1.5 และเมื่อใช้อย่างถูกต้อง จะช่วยเพิ่มความสามารถในการอ่านโค้ด โครงสร้างนี้มักพบใน การทดสอบJUnitเนื่องจาก นักพัฒนาทดสอบเกือบทั้งหมดใช้static import
วิธีการยืนยันassertEquals()
และทำซ้ำมากเกินไป หากไม่มีสิ่งใดชัดเจนยินดีรับข้อมูลเพิ่มเติม
GO TO FULL VERSION