JavaRush /จาวาบล็อก /Random-TH /วิธีการ พารามิเตอร์ การโต้ตอบ และการโอเวอร์โหลด

วิธีการ พารามิเตอร์ การโต้ตอบ และการโอเวอร์โหลด

เผยแพร่ในกลุ่ม
สวัสดีอีกครั้ง! ในการบรรยายครั้งล่าสุด เราได้ทำความคุ้นเคยกับคลาสและคอนสตรัคเตอร์ และเรียนรู้วิธีสร้างคลาสของเราเอง วิธีการ, พารามิเตอร์, การโต้ตอบและการโอเวอร์โหลด - 1วันนี้เราจะมาดูส่วนสำคัญของคลาสดังกล่าวอย่างใกล้ชิดยิ่งขึ้นในฐานะวิธีการ วิธีการคือชุดคำสั่งที่ช่วยให้คุณสามารถดำเนินการบางอย่างในโปรแกรมได้ กล่าวอีกนัยหนึ่ง วิธีการก็คือฟังก์ชัน สิ่งที่ชั้นเรียนของคุณสามารถทำได้ ในภาษาโปรแกรมอื่น ๆ method มักเรียกว่า "functions" แต่ในภาษา Java คำว่า "method" ได้รับความนิยมมากขึ้น :) ในการบรรยายครั้งล่าสุด ถ้าคุณจำได้ เราได้สร้างวิธีการง่ายๆ สำหรับคลาส Cat เพื่อให้แมวของเราร้องเหมียวได้ และกระโดด:
public class Cat {

    String name;
    int age;

    public void sayMeow() {
        System.out.println("Meow!");
    }

    public void jump() {
        System.out.println("Jumping gallop!");
    }

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Barsik";

        barsik.sayMeow();
        barsik.jump();
    }
}
sayMeow()และjump()เป็นวิธีการของชั้นเรียนของเรา ผลงานของพวกเขาคือผลลัพธ์ไปยังคอนโซล:
Мяу!
Прыг-скок!
วิธีการของเราค่อนข้างง่าย: เพียงพิมพ์ข้อความไปยังคอนโซล แต่ใน Java วิธีการมีหน้าที่หลัก - ต้องดำเนินการกับข้อมูลของวัตถุ เปลี่ยนค่าของข้อมูลของออบเจ็กต์ แปลงข้อมูล ส่งออกไปยังคอนโซล หรือดำเนินการอย่างอื่นกับข้อมูลนั้น Catวิธีการปัจจุบันของเราไม่ได้ทำอะไรกับ ข้อมูลของวัตถุ ลองดูตัวอย่างที่ชัดเจนกว่านี้:
public class Truck {

    int length;
    int width;
    int height;
    int weight;

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
ตัวอย่างเช่น เรามีคลาสที่แสดงถึงรถบรรทุกTruck- รถพ่วงบรรทุกมีความยาว กว้าง สูง และมีน้ำหนัก (จะต้องระบุในภายหลัง) ในวิธีการนี้getVolume()เราทำการคำนวณ - เราแปลงข้อมูลของวัตถุของเราเป็นตัวเลขที่ระบุปริมาตร (เราคูณความยาวความกว้างและความสูง) นี่คือตัวเลขที่จะเป็นผลของวิธีการ โปรดทราบ - ในคำอธิบายของวิธีการเขียนpublic int getVolumeไว้ ซึ่งหมายความว่าผลลัพธ์ของวิธีนี้จะต้องเป็นตัวเลขในรูปintแบบ เราได้คำนวณผลลัพธ์ของเมธอดแล้ว และตอนนี้เราต้องส่งคืนมันไปยังโปรแกรมของเราที่เรียกว่าเมธอด หากต้องการส่งคืนผลลัพธ์ของวิธีการใน Java จะใช้returnคีย์เวิร์ด
return volume;

พารามิเตอร์วิธีการ

วิธีการสามารถรับค่าเป็นอินพุตได้ซึ่งเรียกว่า "พารามิเตอร์วิธีการ" วิธีการปัจจุบันของเราgetVolume()ในคลาสTruckไม่ยอมรับพารามิเตอร์ใดๆ ดังนั้นเรามาลองขยายตัวอย่างด้วยรถบรรทุกกันดีกว่า มาสร้างคลาสใหม่กันเถอะ - BridgeOfficer. เจ้าหน้าที่ตำรวจปฏิบัติหน้าที่บนสะพานและตรวจรถบรรทุกที่ผ่านไปมาทั้งหมดเพื่อให้แน่ใจว่าน้ำหนักบรรทุกไม่เกินขีดจำกัดน้ำหนักที่อนุญาต
public class BridgeOfficer {

    int maxWeight;

    public BridgeOfficer(int normalWeight) {
        this.maxWeight = normalWeight;
    }

    public boolean checkTruck(Truck truck) {
        if (truck.weight > maxWeight) {
            return false;
        } else {
            return true;
        }
    }
}
วิธีการนี้checkTruckใช้พารามิเตอร์หนึ่งตัวเป็นอินพุต - วัตถุรถบรรทุกTruckและกำหนดว่าเจ้าหน้าที่จะอนุญาตให้รถบรรทุกขึ้นไปบนสะพานหรือไม่ ตรรกะภายในวิธีการนั้นค่อนข้างง่าย: หากน้ำหนักของรถบรรทุกเกินค่าสูงสุดที่อนุญาต วิธีการจะส่งfalseคืน คุณจะต้องมองหาถนนสายอื่น :( หากน้ำหนักน้อยกว่าหรือเท่ากับสูงสุดคุณสามารถผ่านได้และวิธีการคืนค่าtrueหากคุณยังไม่เข้าใจวลี "return" อย่างถ่องแท้ "method return a value" ” - พักจากการเขียนโปรแกรมแล้วดูสิ่งนี้โดยใช้ตัวอย่างง่ายๆจากชีวิตในโลกแห่งความเป็นจริง :) สมมติว่าคุณป่วยและไม่ได้ทำงานเป็นเวลาหลายวัน คุณมาที่แผนกบัญชีพร้อมกับลาป่วยซึ่งคุณต้องจ่าย หากเราทำการเปรียบเทียบกับวิธีการ นักบัญชีก็มีวิธีการpaySickLeave()(“การลาป่วย”) คุณส่งใบรับรองการลาป่วยให้กับวิธีนี้เป็นพารามิเตอร์ (หากไม่มีวิธีการนี้จะไม่ทำงานและคุณจะไม่ได้รับค่าตอบแทนอะไรเลย!) ภายในวิธีการเวิร์กชีทจะมีการคำนวณที่จำเป็น (นักบัญชีใช้เพื่อคำนวณจำนวนเงินที่ บริษัท ควรจ่ายให้คุณ) และผลงานจะถูกส่งกลับคืนให้คุณ - จำนวนเงิน โปรแกรมก็ทำงานในลักษณะเดียวกัน มันจะเรียกเมธอด ส่งข้อมูลไปที่นั่น และสุดท้ายก็ได้รับผลลัพธ์ นี่คือวิธีการmain()สำหรับโปรแกรมของเราBridgeOfficer:
public static void main(String[] args) {
    Truck first = new Truck();
    first.weight = 10000;
    Truck second = new Truck();
    second.weight = 20000;

    BridgeOfficer officer = new BridgeOfficer(15000);
    System.out.println("Truck number 1! May I pass, officer?");
    boolean canFirstTruckGo = officer.checkTruck(first);
    System.out.println(canFirstTruckGo);

    System.out.println();

    System.out.println("Truck number 2! May I?");
    boolean canSecondTruckGo = officer.checkTruck(second);
    System.out.println(canSecondTruckGo);
}
เรากำลังสร้างรถบรรทุกสองคันที่บรรทุกได้ 10,000 และ 20,000 ในเวลาเดียวกันน้ำหนักสูงสุดสำหรับสะพานที่เจ้าหน้าที่ปฏิบัติหน้าที่คือ 15,000 โปรแกรมเรียกว่าเมธอด เมธอดจะofficer.checkTruck(first)คำนวณทุกอย่างแล้วส่งคืนผลลัพธ์ให้กับโปรแกรม - trueและโปรแกรมก็บันทึกไว้ในboolean canFirstTruckGoตัวแปร ตอนนี้เขาสามารถทำทุกอย่างที่ต้องการได้ (เช่นเดียวกับคุณกับเงินที่คุณได้รับจากนักบัญชี) ในที่สุดรหัส
boolean canFirstTruckGo = officer.checkTruck(first);
ลงมาที่
boolean canFirstTruckGo = true;
จุดสำคัญมาก: ตัวดำเนินการreturnไม่เพียงแต่ส่งคืนผลลัพธ์ของเมธอดเท่านั้น แต่ยังยุติการทำงานด้วย ! รหัสทั้งหมดที่เขียนหลังจากการส่งคืนจะไม่ถูกดำเนินการ!
public boolean checkTruck(Truck truck) {

    if (truck.weight > maxWeight) {
        return false;
        System.out.println("Turn around, overweight!");
    } else {
        return true;
        System.out.println("Alright, move on!");
    }
}
วลีที่เจ้าหน้าที่พูดจะไม่ถูกส่งออกไปยังคอนโซลเนื่องจากวิธีการนี้ได้ส่งคืนผลลัพธ์และทำงานเสร็จแล้ว! โปรแกรมจะกลับไปยังจุดที่มีการเรียกเมธอด คุณไม่ต้องกังวลเกี่ยวกับเรื่องนี้ด้วยตัวเอง - คอมไพเลอร์ Java นั้นฉลาดพอที่จะส่งข้อผิดพลาดหากคุณพยายามเขียนโค้ดหลังจากreturn.

เวนเจอร์ส: สงครามตัวเลือก

มีสถานการณ์ที่โปรแกรมของเราต้องการตัวเลือกมากมายเกี่ยวกับวิธีการทำงาน ทำไมเราไม่สร้างปัญญาประดิษฐ์ของเราเองล่ะ? Amazon มี Alexa, Yandex มี Alice แล้วทำไมเราถึงแย่กว่านี้ :) ในภาพยนตร์เรื่อง Iron Man โทนี่สตาร์กได้สร้างปัญญาประดิษฐ์ที่โดดเด่นของเขาเอง - JARVIS มาแสดงความเคารพต่อตัวละครที่ยอดเยี่ยมและตั้งชื่อ AI ของเราเพื่อเป็นเกียรติแก่เขา :) สิ่งแรกที่เราต้องสอนจาร์วิส - ทักทายผู้คนที่เข้ามาในห้อง (คงจะแปลกถ้าสติปัญญาที่ยอดเยี่ยมเช่นนี้กลับกลายเป็นว่าไม่สุภาพ)
public class Jarvis {

    public void sayHi(String name) {
        System.out.println("Good evening, " + name + ", How are you doing?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
    }
}
เอาต์พุตคอนโซล:
Добрый вечер, Тони Старк, How ваши дела?
ยอดเยี่ยม! จาร์วิสรู้วิธีทักทายคนที่เข้ามา แน่นอนว่ามักจะเป็นเจ้าของของเขา - โทนี่สตาร์ก แต่เขาอาจจะไม่มาคนเดียว! และวิธีการของเราsayHi()รับเพียงอาร์กิวเมนต์เดียวเป็นอินพุต ดังนั้นเขาจะสามารถทักทายได้เพียงคนเดียวจากผู้ที่มา และจะไม่สนใจอีกคนหนึ่ง ไม่ค่อยสุภาพเท่าไหร่ เห็นด้วยไหม? :/ ในกรณีนี้ เพื่อแก้ปัญหา เราสามารถเขียน 2 วิธีในคลาสที่มีชื่อเดียวกัน แต่มีพารามิเตอร์ต่างกัน:
public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ", How are you doing?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + ", " + secondGuest + ", How are you doing?");
    }
}
สิ่งนี้เรียกว่าวิธีการโอเวอร์โหลด การโอเวอร์โหลดทำให้โปรแกรมของเรามีความยืดหยุ่นมากขึ้นและรองรับตัวเลือกการทำงานที่แตกต่างกัน ตรวจสอบวิธีการทำงาน:
public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ", How are you doing?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + ", " + secondGuest + ", How are you doing?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
        jarvis.sayHi("Tony Stark", "Captain America");
    }
}
เอาต์พุตคอนโซล:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Тони Старк, Капитан Америка, How ваши дела?
เยี่ยมมาก ทั้งสองตัวเลือกใช้งานได้ :) อย่างไรก็ตาม เราไม่สามารถแก้ปัญหาได้! เกิดอะไรขึ้นถ้ามีแขกสามคน? แน่นอนว่าเราสามารถโอเวอร์โหลดวิธีการอีกครั้งsayHi()เพื่อรับชื่อแขกสามคนได้ แต่อาจมี 4 หรือ 5 อันก็ได้ และอื่นๆ ไม่มีที่สิ้นสุด มีวิธีอื่นในการสอนจาร์วิสให้ทำงานกับชื่อจำนวนเท่าใดก็ได้โดยไม่ต้องมีวิธีการเกินล้านวิธีsayHi()หรือไม่ :/ มีแน่นอน! ไม่เช่นนั้น Java จะเป็นภาษาโปรแกรมที่ได้รับความนิยมมากที่สุดในโลกหรือไม่? ;)
public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ", How are you doing?");
    }
}
บันทึก ( String...names) ที่ส่งผ่านเป็นพารามิเตอร์ช่วยให้เราสามารถระบุได้ว่ามีการส่งสตริงจำนวนหนึ่งไปยังเมธอด เราไม่ได้ระบุล่วงหน้าว่าควรมีจำนวนเท่าใด ดังนั้นการดำเนินการตามวิธีการของเราจึงมีความยืดหยุ่นมากขึ้น:
public class Jarvis {

    public void sayHi(String...names) {
        for (String name: names) {
            System.out.println("Good evening, " + name + ", How are you doing?");
        }
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
    }
}
เอาต์พุตคอนโซล:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Капитан Америка, How ваши дела?
Добрый вечер, Черная Вдова, How ваши дела?
Добрый вечер, Халк, How ваши дела?
รหัสบางส่วนที่นี่ไม่คุ้นเคยสำหรับคุณ แต่ไม่ต้องกังวล สาระสำคัญของมันนั้นง่าย - วิธีการจะผ่านชื่อทั้งหมดตามลำดับและทักทายแขกแต่ละคน! ยิ่งไปกว่านั้น มันจะใช้ได้กับสายที่โอนกี่สายก็ได้! สองสิบหรือพัน - วิธีการนี้จะใช้ได้กับแขกจำนวนเท่าใดก็ได้ สะดวกกว่าการโอเวอร์โหลดสำหรับตัวเลือกที่เป็นไปได้ทั้งหมดมาก คุณเห็นด้วยไหม :) อีกประเด็นสำคัญ: ลำดับของข้อโต้แย้งมีความสำคัญ! สมมติว่าวิธีการของเรารับสตริงและตัวเลขเป็นอินพุต:
public class Man {

    public static void sayYourAge(String greeting, int age) {
        System.out.println(greeting + " " + age);
    }

    public static void main(String[] args) {
        sayYourAge("My age - ", 33);
        sayYourAge(33, "My age - "); //error!
    }
}
หาก เมธอด sayYourAgeคลาสManรับสตริงและตัวเลขเป็นอินพุต แสดงว่านี่คือลำดับที่ต้องส่งผ่านในโปรแกรม! หากเราส่งผ่านตามลำดับอื่น คอมไพเลอร์จะส่งข้อผิดพลาดและบุคคลนั้นจะไม่สามารถบอกอายุของเขาได้ อย่างไรก็ตาม ตัวสร้างที่เรากล่าวถึงในการบรรยายครั้งล่าสุดก็เป็นวิธีการเช่นกัน! นอกจากนี้ยังสามารถโอเวอร์โหลดได้ (สร้างตัวสร้างหลายตัวด้วยชุดอาร์กิวเมนต์ที่แตกต่างกัน) และสำหรับพวกเขา ลำดับการส่งผ่านอาร์กิวเมนต์ก็มีความสำคัญขั้นพื้นฐานเช่นกัน วิธีการจริง! :)

และอีกครั้งเกี่ยวกับพารามิเตอร์

ใช่แล้ว เรายังไม่จบกับพวกเขา :) หัวข้อที่เราจะพิจารณาตอนนี้มีความสำคัญมาก มีโอกาส 90% ที่พวกเขาจะถามเกี่ยวกับเรื่องนี้ในการสัมภาษณ์ครั้งต่อไปของคุณ! เราจะพูดถึงการส่งพารามิเตอร์ไปยังเมธอด ลองดูตัวอย่างง่ายๆ:
public class TimeMachine {

    public void goToFuture(int currentYear) {
        currentYear = currentYear+10;
    }

    public void goToPast(int currentYear) {
        currentYear = currentYear-10;
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2020;

        System.out.println("What is the year now?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("And now?");
        System.out.println(currentYear);
    }
}
ไทม์แมชชีนมีสองวิธี ทั้งสองใช้ตัวเลขที่แสดงถึงปีปัจจุบันเป็นอินพุตและเพิ่มหรือลดค่า (ขึ้นอยู่กับว่าเราต้องการย้อนเวลากลับหรือไปสู่อนาคต) แต่ดังที่เห็นได้จากเอาต์พุตคอนโซล วิธีการนี้ใช้ไม่ได้! เอาต์พุตคอนโซล:
Какой сейчас год?
2020
А сейчас?
2020
เราส่งตัวแปรcurrentYearไปยัง method goToPast()แต่ค่าของมันกลับไม่เปลี่ยนแปลง เช่นเดียวกับในปี 2020 มันยังคงเป็นเช่นนี้ แต่ทำไม? :/ เนื่องจากพื้นฐานใน Java ถูกส่งผ่านไปยังวิธีการตามค่า มันหมายความว่าอะไร? เมื่อเราเรียกเมธอดgoToPast()และส่งตัวแปรไปตรงนั้นint currentYear = 2020ไม่ใช่ตัวตัวแปรเองที่จะเข้าไปในเมธอดcurrentYearแต่เป็นสำเนา ของตัวแปร นั้น แน่นอนว่ามูลค่าของสำเนานี้ก็เท่ากับปี 2020 เช่นกัน แต่การเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นกับสำเนาจะไม่ส่งผลกระทบต่อตัวแปรดั้งเดิมของเรา แต่อย่างใดcurrentYear ! มาทำให้โค้ดของเราละเอียดยิ่งขึ้นและดูว่าเกิดอะไรขึ้นกับcurrentYear:
public class TimeMachine {

    public void goToFuture(int currentYear) {
        currentYear = currentYear+10;
    }

    public void goToPast(int currentYear) {
        System.out.println("The goToPast method has started!");
        System.out.println("The currentYear value inside the goToPast method (at the beginning) = " + currentYear);
        currentYear = currentYear-10;
        System.out.println("The currentYear value inside the goToPast method (at the end) = " + currentYear);
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2020;

        System.out.println("What is the year at the very beginning of the program?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("What year is it now?");
        System.out.println(currentYear);
    }
}
เอาต์พุตคอนโซล:
Какой год в самом начале работы программы?
2020
Метод goToPast начал работу!
Значение currentYear внутри метода goToPast (в начале) = 2020
Значение currentYear внутри метода goToPast (в конце) = 2010
А сейчас Howой год?
2020
นี่แสดงให้เห็นชัดเจนว่าตัวแปรที่ถูกส่งผ่านไปยังเมธอดนั้นgoToPast()เป็นเพียงสำเนาcurrentYearเท่านั้น และการเปลี่ยนสำเนาก็ไม่มีผลต่อความหมายของ "ต้นฉบับ" " passing by Reference " มีความหมายตรงกันข้ามทุกประการ มาฝึกแมวกันเถอะ! คือเรามาดูกันว่าการผ่านลิงค์จะเป็นอย่างไรโดยใช้แมวเป็นตัวอย่าง :)
public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
ตอนนี้ ด้วยความช่วยเหลือของไทม์แมชชีน เราจะนำ Barsik นักเดินทางข้ามเวลาแบบแมวคนแรกของโลกไปสู่อดีตและอนาคต! มาเปลี่ยนคลาสTimeMachineเพื่อให้เครื่องสามารถทำงานกับวัตถุCatได้
public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }
}
ตอนนี้เมธอดเปลี่ยนไปไม่ใช่แค่ตัวเลขที่ส่งผ่าน แต่ฟิลด์ageของอ็อบเจ็กต์เฉพาะเจาะจงCatด้วย ในกรณีของยุคดึกดำบรรพ์ อย่างที่คุณจำได้ เราไม่ประสบความสำเร็จ: หมายเลขเดิมไม่เปลี่ยนแปลง มาดูกันว่าเกิดอะไรขึ้นที่นี่!
public static void main(String[] args) {

    TimeMachine timeMachine = new TimeMachine();
    Cat barsik = new Cat(5);

    System.out.println("How old is Barsik at the very beginning of the program?");
    System.out.println(barsik.age);

    timeMachine.goToFuture(barsik);
    System.out.println("And now?");
    System.out.println(barsik.age);

    System.out.println("Firs-sticks! Barsik has aged 10 years! Drive back quickly!");
    timeMachine.goToPast(barsik);
    System.out.println("Did it work? Have we returned the cat to its original age?");
    System.out.println(barsik.age);
}
เอาต์พุตคอนโซล:
Сколько лет Барсику в самом начале работы программы?
5
А теперь?
15
Елки-палки! Барсик постарел на 10 лет! Живо гони назад!
Получилось? Мы вернули коту его изначальный возраст?
5
ว้าว! ตอนนี้วิธีการทำงานแตกต่างออกไป: แมวของเราแก่ลงกะทันหัน และดูเด็กลงอีกครั้ง! :) ลองหาสาเหตุว่าทำไม ไม่เหมือนกับตัวอย่างที่มีพื้นฐาน ในกรณีของอ็อบเจ็กต์ การอ้างอิงไปยังอ็อบเจ็กต์จะถูกส่งผ่านไปยังเมธอด การอ้างอิงถึงออบเจ็กต์ดั้งเดิมของเราถูกส่งผ่านไปgoToFuture(barsik)ยังเมธอด ดังนั้นเมื่อเราเปลี่ยนวิธีการภายในเราจะเข้าถึงพื้นที่หน่วยความจำเดียวกับที่เก็บวัตถุของเรา นี่คือลิงก์ไปยัง Barsik เดียวกับที่เราสร้างขึ้นตั้งแต่เริ่มต้น นี้เรียกว่า "ผ่านการอ้างอิง"! อย่างไรก็ตาม ทุกอย่างไม่ง่ายเลยด้วยลิงก์เหล่านี้ :) ลองเปลี่ยนตัวอย่างของเรา: goToPast(barsik)barsikbarsik.age
public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat = new Cat(cat.age);
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat = new Cat(cat.age);
        cat.age -= 10;
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        Cat barsik = new Cat(5);

        System.out.println("How old is Barsik at the very beginning of the program?");
        System.out.println(barsik.age);

        timeMachine.goToFuture(barsik);
        System.out.println("Barsik went to the future! Has his age changed?");
        System.out.println(barsik.age);

        System.out.println("And if you try in the past?");
        timeMachine.goToPast(barsik);
        System.out.println(barsik.age);
    }
}
เอาต์พุตคอนโซล:
Сколько лет Барсику в самом начале работы программы?
5
Барсик отправился в будущее! Его возраст изменился?
5
А если попробовать в прошлое?
5
ไม่ทำงานอีกครั้ง! O_O เรามาดูกันว่าเกิดอะไรขึ้น :) ทุกอย่างเกี่ยวกับวิธีการทำงานgoToPast/ goToFutureและกลไกการทำงานของลิงก์ ตอนนี้ให้ความสนใจ!ประเด็นนี้สำคัญที่สุดในการทำความเข้าใจว่าลิงก์และวิธีการทำงานอย่างไร ในความเป็นจริงเมื่อเราเรียกเมธอดgoToFuture(Cat cat)มันไม่ใช่การอ้างอิงอ็อบเจ็กต์ที่ถูกส่งผ่านไปcatแต่เป็นสำเนาของการอ้างอิงนี้ นั่นคือเมื่อเราส่งวัตถุไปยังวิธีการ จะมีการอ้างอิงถึงวัตถุนี้สองครั้ง นี่เป็นสิ่งสำคัญมากสำหรับการทำความเข้าใจกับสิ่งที่เกิดขึ้น ท้ายที่สุด นี่คือเหตุผลว่าทำไมตัวอย่างสุดท้ายของเราไม่ได้เปลี่ยนอายุของแมว ในตัวอย่างก่อนหน้านี้ที่มีการเปลี่ยนอายุ เราเพียงแค่ใช้การอ้างอิงที่ส่งผ่านภายในเมธอดgoToFuture()พบวัตถุในหน่วยความจำที่ใช้มัน และเปลี่ยนอายุของมัน ( cat.age += 10) ตอนนี้อยู่ในวิธีการที่goToFuture()เราสร้างวัตถุใหม่
(cat = new Cat(cat.age)),
และลิงก์คัดลอกเดียวกันกับที่ส่งไปยังเมธอดนั้นถูกกำหนดให้กับอ็อบเจ็กต์นี้ ผลที่ตามมา:
  • ลิงค์แรก ( Cat barsik = new Cat(5)) ชี้ไปที่แมวตัวเดิม (อายุ 5 ปี)
  • หลังจากที่เราส่งตัวแปรcatไปยังเมธอดgoToPast(Cat cat)และกำหนดให้กับออบเจ็กต์ใหม่ การอ้างอิงก็ถูกคัดลอก
หลังจากนี้ เรามีสถานการณ์สุดท้าย: ลิงก์สองลิงก์ชี้ไปที่วัตถุสองชิ้นที่แตกต่างกัน แต่เราเปลี่ยนอายุเพียงอันเดียว - อันที่เราสร้างขึ้นภายในวิธีการ
cat.age += 10;
และโดยธรรมชาติแล้ว เมื่อเราส่งออกmain()ไปยังคอนโซล ด้วยเมธอด barsik.ageเราจะเห็นว่าอายุของมันไม่มีการเปลี่ยนแปลง ท้ายที่สุดbarsikนี่คือตัวแปรอ้างอิงที่ยังคงชี้ไปที่วัตถุดั้งเดิมอันเก่าที่มีอายุ 5 ปี ซึ่งไม่มีอะไรเกิดขึ้น การปรับเปลี่ยนตามอายุทั้งหมดของเรานั้นดำเนินการกับวัตถุใหม่ ดังนั้นปรากฎว่าวัตถุถูกส่งผ่านไปยังวิธีการโดยการอ้างอิง สำเนาของออบเจ็กต์จะไม่ถูกสร้างขึ้นโดยอัตโนมัติ หากคุณส่งวัตถุ cat ไปยังวิธีการและเปลี่ยนอายุ มันจะเปลี่ยนได้สำเร็จ แต่ค่าของตัวแปรอ้างอิงจะถูกคัดลอกเมื่อกำหนดและ/หรือเรียกใช้วิธีการ! เรามาทำซ้ำย่อหน้าเกี่ยวกับการส่งผ่านดั้งเดิมที่นี่: “ เมื่อเราเรียกใช้เมธอดchangeInt()และส่งตัวแปรของเราไปที่นั่นint x = 15มันไม่ใช่ตัวแปรเองที่เข้าไปในเมธอดxแต่เป็นการคัดลอกท้ายที่สุดแล้วการเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นกับการคัดลอกจะไม่เกิดขึ้น ส่งผลต่อตัวแปรเดิมของเราแต่อย่างใดx ด้วยการคัดลอกลิงก์ ทุกอย่างจะทำงานเหมือนกันทุกประการ! คุณส่งผ่านวัตถุcatไปยังวิธีการ หากคุณทำอะไรบางอย่างกับแมว (นั่นคือโดยมีวัตถุอยู่ในหน่วยความจำ) การเปลี่ยนแปลงทั้งหมดจะผ่านไปได้สำเร็จ - เรามีวัตถุเพียงชิ้นเดียวและยังคงมีอยู่ แต่ถ้าภายในวิธีการที่คุณสร้างวัตถุใหม่และบันทึกไว้ในตัวแปรอ้างอิงซึ่งเป็นพารามิเตอร์ของวิธีการ จากนี้ไปเราจะมีวัตถุสองตัวและตัวแปรอ้างอิงสองตัว นั่นคือทั้งหมด! มันไม่ง่ายขนาดนั้น คุณอาจต้องบรรยายหลายครั้งด้วยซ้ำ แต่สิ่งสำคัญคือคุณได้เรียนรู้หัวข้อที่สำคัญอย่างยิ่งนี้แล้ว คุณมักจะพบกับข้อโต้แย้ง (แม้แต่ในหมู่นักพัฒนาที่มีประสบการณ์) เกี่ยวกับวิธีการส่งผ่านข้อโต้แย้งใน Java ตอนนี้คุณรู้แล้วว่ามันทำงานอย่างไร ติดตามมัน! :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION