สวัสดี! คำว่า "ตัวแก้ไข" เป็นคำที่คุณคุ้นเคยอยู่แล้ว อย่างน้อยที่สุด คุณจะได้พบกับตัวแก้ไขการเข้าถึง (สาธารณะ ส่วนตัว) และตัวแก้ไขแบบคงที่ วัน นี้เราจะมาพูดถึงตัวแก้ไขพิเศษขั้นสุดท้าย อาจกล่าวได้ว่าเป็นการ "ประสาน" ด้านต่างๆ ของโปรแกรมของเราซึ่งเราต้องการพฤติกรรมที่คงที่ ไม่คลุมเครือ และไม่เปลี่ยนแปลง สามารถใช้งานได้สามส่วนในโปรแกรมของเรา: คลาส วิธีการ และตัวแปร
มาดูกันทีละคน หากการประกาศคลาสมี ตัวแก้ไข ขั้นสุดท้ายหมายความว่าคุณไม่สามารถสืบทอดจากคลาสนี้ได้ ในการบรรยายครั้งก่อน เราเห็นตัวอย่างง่ายๆ ของการสืบทอด: เรามีคลาส parent
ไม่มีอะไรเปลี่ยนแปลง และมันก็ไม่ควรมี อย่างที่เราพูดไปแล้ว วัตถุนั้น
Animal
และคลาสย่อยสองคลาส - Cat
และDog
public class Animal {
}
public class Cat extends Animal {
//..поля и методы класса Cat
}
public class Dog extends Animal {
//..поля и методы класса Dog
}
อย่างไรก็ตาม หากเราระบุAnimal
ตัวแก้ไข สำหรับคลาส คลาส final
จะไม่สามารถสืบทอดจากคลาส นั้น Cat
ได้เช่นDog
กัน
public final class Animal {
}
public class Cat extends Animal {
//ошибка! Cannot inherit from final Animal
}
คอมไพเลอร์ทำให้เกิดข้อผิดพลาดทันที มีหลาย -classes ที่ใช้งานอยู่แล้วในfinal
Java สิ่งที่มีชื่อเสียงที่สุดที่คุณใช้เป็นประจำString
คือ นอกจากนี้ หากมีการประกาศคลาสเป็น เมธอดfinal
ทั้งหมดของคลาสก็จะกลายเป็นfinal
. มันหมายความว่าอะไร? หากมีการระบุตัวแก้ไขสำหรับ method final
วิธีนี้จะไม่สามารถแทนที่ได้ ตัวอย่างเช่น เรามีคลาสAnimal
ที่กำหนดวิธีvoice()
การ อย่างไรก็ตาม สุนัขและแมว “พูดคุย” แตกต่างกันอย่างชัดเจน ดังนั้นในแต่ละคลาส - Cat
และDog
- เราจะสร้างเมธอดvoice()
แต่เราจะใช้มันต่างกัน
public class Animal {
public void voice() {
System.out.println("Voice!");
}
}
public class Cat extends Animal {
@Override
public void voice() {
System.out.println("Meow!");
}
}
public class Dog extends Animal {
@Override
public void voice() {
System.out.println("Woof!");
}
}
ในชั้นเรียนCat
และDog
เราได้แทนที่วิธีการของคลาสผู้ปกครอง ตอนนี้สัตว์จะเปล่งเสียงขึ้นอยู่กับว่ามันคือวัตถุคลาสใด:
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.voice();
dog.voice();
}
}
สรุป: เหมียว! โฮ่ง! อย่างไรก็ตาม หากAnimal
เราประกาศวิธีการ ในคลาส voice()
เป็นfinal
จะไม่สามารถกำหนดวิธีการใหม่ในคลาสอื่นได้:
public class Animal {
public final void voice() {
System.out.println("Voice!");
}
}
public class Cat extends Animal {
@Override
public void voice() {//ошибка! final-метод не может быть переопределен!
System.out.println("Meow!");
}
}
จากนั้นอ็อบเจ็กต์ของเราจะถูกบังคับให้ใช้วิธีการvoice()
ตามที่กำหนดไว้ในคลาสพาเรนต์:
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.voice();
dog.voice();
}
สรุป: เสียง! เสียง! ตอนนี้เกี่ยวกับfinal
-variables มิฉะนั้นจะเรียกว่าค่าคงที่ อันดับแรก (และที่สำคัญที่สุด) ไม่สามารถเปลี่ยนแปลงค่าแรกที่กำหนดให้กับค่าคงที่ได้ มันถูกมอบหมายครั้งเดียวและตลอดไป
public class Main {
private static final int CONSTANT_EXAMPLE = 333;
public static void main(String[] args) {
CONSTANT_EXAMPLE = 999;//ошибка! Нельзя присвоить новое meaning final-переменной!
}
}
ค่าคงที่ไม่จำเป็นต้องเริ่มต้นทันที ซึ่งสามารถทำได้ในภายหลัง แต่คุณค่าที่ได้รับมอบหมายก่อนจะคงอยู่ตลอดไป
public static void main(String[] args) {
final int CONSTANT_EXAMPLE;
CONSTANT_EXAMPLE = 999;//так делать можно
}
ประการที่สอง ใส่ใจกับชื่อตัวแปรของเรา ค่าคงที่ Java มีรูปแบบการตั้งชื่อที่แตกต่างกัน นี่ไม่ใช่ CamelCase ที่เราคุ้นเคย ในกรณีของตัวแปรปกติ เราจะเรียกมันว่าค่าคงที่ตัวอย่าง แต่ชื่อของค่าคงที่จะถูกเขียนด้วยตัวพิมพ์ใหญ่ และระหว่างคำ (หากมีหลายคำ) จะมีขีดล่าง - “CONSTANT_EXAMPLE” เหตุใดจึงต้องมีค่าคงที่? ตัวอย่างเช่น มันจะมีประโยชน์หากคุณใช้ค่าคงที่ในโปรแกรมอยู่ตลอดเวลา สมมติว่าคุณตัดสินใจลงไปในประวัติศาสตร์และเขียนเกม "The Witcher 4" เพียงอย่างเดียว เห็นได้ชัดว่าเกมจะใช้ชื่อของตัวละครหลัก - "Geralt of Rivia" อย่างต่อเนื่อง เป็นการดีกว่าที่จะแยกบรรทัดนี้และชื่อของฮีโร่อื่น ๆ ออกเป็นค่าคงที่: ค่าที่คุณต้องการจะถูกเก็บไว้ในที่เดียวและคุณจะไม่ทำผิดพลาดอย่างแน่นอนเมื่อพิมพ์เป็นครั้งที่ล้าน
public class TheWitcher4 {
private static final String GERALT_NAME = "Геральт из Ривии";
private static final String YENNEFER_NAME = "Йеннифэр из Венгерберга";
private static final String TRISS_NAME = "Трисс Меригольд";
public static void main(String[] args) {
System.out.println("Ведьмак 4");
System.out.println("Это уже четвертая часть Ведьмака, а " + GERALT_NAME + " ниHow не определится кто ему" +
" нравится больше: " + YENNEFER_NAME + " or " + TRISS_NAME);
System.out.println("Но если вы никогда не играли в Ведьмака - начнем сначала.");
System.out.println("Главного героя зовут " + GERALT_NAME);
System.out.println(GERALT_NAME + " - ведьмак, охотник на чудовищ");
}
}
บทสรุป:
Ведьмак 4
Это уже четвертая часть Ведьмака, а Геральт из Ривии ниHow не определится, кто ему нравится больше: Йеннифэр из Венгерберга or Трисс Меригольд.
Но если вы никогда не играли в Ведьмака — начнем сначала.
Главного героя зовут Геральт из Ривии
Геральт из Ривии — ведьмак, охотник на чудовищ
เราได้แยกชื่อของตัวละครออกเป็นค่าคงที่แล้ว และตอนนี้เราจะไม่สะกดผิดอย่างแน่นอน และไม่จำเป็นต้องเขียนด้วยมือทุกครั้ง ข้อดีอีกประการ: หากในที่สุดเราจำเป็นต้องเปลี่ยนค่าของตัวแปรทั่วทั้งโปรแกรม ก็เพียงพอแล้วที่จะทำในที่เดียว แทนที่จะทำซ้ำด้วยตนเองตลอดทั้งโค้ด :)
ประเภทที่ไม่เปลี่ยนรูป
ในช่วงเวลาที่คุณทำงานใน Java คุณอาจคุ้นเคยกับความจริงที่ว่าโปรแกรมเมอร์ควบคุมสถานะของวัตถุทั้งหมดเกือบทั้งหมดแล้ว ต้องการ-สร้างวัตถุCat
. ถ้าฉันต้องการฉันก็เปลี่ยนชื่อมัน หากเขาต้องการเขาก็เปลี่ยนอายุหรืออย่างอื่น แต่ใน Java มีข้อมูลหลายประเภทที่มีสถานะพิเศษ พวกมันไม่เปลี่ยนรูปหรือไม่เปลี่ยนรูป ซึ่งหมายความว่าหากคลาสไม่เปลี่ยนรูป สถานะของอ็อบเจ็กต์จะไม่สามารถเปลี่ยนแปลงได้ ตัวอย่าง? คุณอาจจะแปลกใจ แต่ตัวอย่างที่มีชื่อเสียงที่สุดของคลาส ImmutableคือString
! ดูเหมือนว่าเราไม่สามารถเปลี่ยนค่าของสตริงได้ใช่ไหม มาลองกัน:
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1 = "I love Python";//но поведение str1 ниHow не влияет на str2
System.out.println(str2);//str2 продолжает указывать на строку "I love Java", хотя str1 уже указывает на другой an object
}
สรุป: ฉันรัก Java ฉันรัก Java หลังจากที่เราเขียน:
str1 = "I love Python";
วัตถุที่มีสตริง"ฉันรัก Java"ไม่มีการเปลี่ยนแปลงและไม่ได้หายไปไหน มันมีอยู่อย่างปลอดภัยและมีข้อความเหมือนกันทุกประการอยู่ข้างในเหมือนเมื่อก่อน รหัส:
str1 = "I love Python";
เพิ่งสร้างวัตถุอื่นและตอนนี้ตัวแปรชี้str1
ไปที่มัน แต่ เราไม่สามารถมีอิทธิพลต่อวัตถุ"ฉันรัก Java" แต่อย่างใด เอาล่ะ เรามาลองอย่างอื่นกันดีกว่า! ชั้นเรียนString
เต็มไปด้วยวิธีการ และบางวิธีดูเหมือนจะเปลี่ยนสถานะของแถว! เช่น มีวิธีreplace()
หนึ่ง มาเปลี่ยนคำว่า “Java” เป็นคำว่า “Python” ในบรรทัดของเรากันเถอะ!
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1.replace("Java", "Python");//попробуем изменить состояние str1, заменив слово "Java" на “Python”
System.out.println(str2);
}
สรุป: ฉันรัก Java ฉันรัก Java มันไม่ได้ผลอีกต่อไป! บางทีวิธีเส้นโค้งอาจไม่ทำงานใช่ไหม เรามาลองอันอื่นกันดีกว่า ตัวอย่างเช่น, substring()
. ตัดทอนสตริงตามจำนวนอักขระที่ส่ง เรามาเล็มของเราให้เหลือ 10 ตัวอักษรแรก:
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1.substring(10);//обрезаем исходную строку
System.out.println(str2);
}
สรุป: ฉันรัก Java ฉันรัก Java 
String
ไม่เปลี่ยนรูป แล้ววิธีการเรียนทั้งหมดนี้คืออะไรString
? พวกเขาสามารถตัดแต่งเส้น เปลี่ยนตัวละครในนั้น ฯลฯ ทำไมพวกเขาถึงต้องการถ้าไม่มีอะไรเกิดขึ้น? พวกเขาสามารถ! แต่จะส่งคืนวัตถุสตริงใหม่ทุกครั้ง ไม่มีประโยชน์ที่จะเขียน:
str1.replace("Java", "Python");
- คุณจะไม่เปลี่ยนวัตถุดั้งเดิม แต่ถ้าคุณเขียนผลลัพธ์ของวิธีการลงในตัวแปรอ้างอิงใหม่ คุณจะเห็นความแตกต่างทันที!
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
String str1AfterReplacement = str1.replace("Java", "Python");
System.out.println(str2);
System.out.println(str1AfterReplacement);
}
นี่เป็นวิธีเดียวที่วิธีการทั้งหมดนี้String
ใช้ได้ คุณไม่สามารถทำอะไรกับ วัตถุ "ฉันรัก Java" เพียงสร้างวัตถุใหม่และเขียน: “วัตถุใหม่ = ผลลัพธ์ของการยักย้ายบางอย่างด้วย วัตถุ “ ฉันรัก Java” ” ประเภทอื่น ๆ ที่ไม่เปลี่ยนรูปมีอะไรบ้าง? จากสิ่งที่คุณต้องจำไว้ตอนนี้ - คลาส wrapper ทั้งหมดเหนือประเภทดั้งเดิมนั้นไม่เปลี่ยนรูป Integer
, Byte
, Character
, Short
, Boolean
, Long
, Double
, Float
- คลาสทั้งหมดเหล่านี้สร้างวัตถุที่ไม่เปลี่ยนรูป รวมถึงคลาสที่ใช้สร้างตัวเลขจำนวนมาก - BigInteger
และBigDecimal
. เมื่อเร็วๆ นี้เราได้ผ่านข้อยกเว้นและได้ดำเนินการStackTrace
แล้ว ดังนั้น: อ็อบเจ็กต์ของ คลาส java.lang.StackTraceElementก็ไม่เปลี่ยนรูปเช่นกัน นี่เป็นตรรกะ: หากมีใครสามารถเปลี่ยนแปลงข้อมูลในสแต็กของเราได้ ก็อาจทำให้การทำงานในนั้นกลายเป็นโมฆะได้ ลองนึกภาพใคร บางคนเข้าไปในStackTraceและเปลี่ยนOutOfMemoryErrorเป็นFileNotFoundException และคุณควรทำงานกับสแต็กนี้และค้นหาสาเหตุของข้อผิดพลาด และโปรแกรมไม่ได้ใช้ไฟล์เลย :) ดังนั้นเพื่อความปลอดภัย วัตถุเหล่านี้จึงถูกทำให้ไม่เปลี่ยนรูป ด้วยStackTraceElementมันชัดเจนไม่มากก็น้อย ทำไมทุกคนถึงอยากให้สตริงไม่เปลี่ยนรูป? จะเกิดปัญหาอะไรขึ้นหากสามารถเปลี่ยนค่านิยมได้ คงจะสะดวกกว่านี้อีก :/ มีสาเหตุหลายประการ ประการแรก การบันทึกหน่วยความจำ สามารถวางสตริงที่ไม่เปลี่ยนรูปได้String Pool
และสามารถใช้สตริงเดียวกันในแต่ละครั้งแทนการสร้างสตริงใหม่ ประการที่สองความปลอดภัย ตัวอย่างเช่น ข้อมูลเข้าสู่ระบบและรหัสผ่านส่วนใหญ่ในโปรแกรมใดๆ จะเป็นสตริง ความเป็นไปได้ในการเปลี่ยนแปลงอาจนำไปสู่ปัญหาเกี่ยวกับการอนุญาตได้ มีเหตุผลอื่นๆ อีก แต่เรายังไม่เข้าใจเหตุผลเหล่านั้นในการเรียนรู้ Java เราจะกลับมาใหม่ในภายหลัง
GO TO FULL VERSION