บังคับให้ผู้พัฒนารายอื่นเรียกใช้วิธีการหลังจากทำงานเสร็จ


34

ในห้องสมุดใน Java 7 ฉันมีคลาสที่ให้บริการกับคลาสอื่น หลังจากสร้างอินสแตนซ์ของคลาสบริการนี้วิธีการหนึ่งในนั้นอาจถูกเรียกหลายครั้ง (ลองเรียกมันว่าdoWork()เมธอด) ดังนั้นฉันไม่ทราบว่าเมื่องานของคลาสบริการเสร็จสมบูรณ์

ปัญหาคือระดับบริการใช้วัตถุหนักและควรปล่อยพวกเขา ฉันตั้งค่าส่วนนี้ในวิธีการ (เรียกว่าrelease()) แต่ไม่รับประกันว่านักพัฒนารายอื่นจะใช้วิธีนี้

มีวิธีบังคับให้ผู้พัฒนารายอื่นเรียกใช้วิธีนี้หลังจากทำงานของคลาสบริการหรือไม่ แน่นอนฉันสามารถบันทึกได้ แต่ฉันต้องการบังคับพวกเขา

หมายเหตุ: ฉันไม่สามารถเรียกrelease()เมธอดในdoWork()เมธอดได้เนื่องจากdoWork() ต้องการอ็อบเจ็กต์เหล่านั้นเมื่อมันถูกเรียกในครั้งถัดไป


46
สิ่งที่คุณกำลังทำมีรูปแบบของการมีเพศสัมพันธ์ชั่วคราวและมักจะถือว่าเป็นกลิ่นรหัส คุณอาจต้องการบังคับตัวเองให้คิดการออกแบบที่ดีกว่าแทน
Frank

1
@ Frank ฉันตกลงถ้าเปลี่ยนการออกแบบของเลเยอร์ต้นแบบเป็นตัวเลือกแล้วนั่นจะเป็นทางออกที่ดีที่สุด แต่ฉันสงสัยว่านี่เป็นเสื้อคลุมรอบ ๆ บริการของคนอื่น
Dar Brett

บริการของคุณต้องการของหนักชิ้นใด หากระดับบริการไม่สามารถจัดการทรัพยากรเหล่านั้นได้โดยอัตโนมัติ (โดยไม่ต้องมีการเชื่อมต่อทางโลก) ดังนั้นบางทีทรัพยากรเหล่านั้นควรได้รับการจัดการที่อื่นและส่งไปยังบริการ คุณเขียนการทดสอบหน่วยสำหรับชั้นบริการแล้วหรือยัง?
จาก

18
มันเป็น "un-Java" ที่จะต้องใช้มัน Java เป็นภาษาที่มีการรวบรวมขยะและตอนนี้คุณเกิดข้อผิดพลาดบางอย่างที่คุณต้องการให้นักพัฒนาซอฟต์แวร์ "ล้างข้อมูล"
Pieter B

22
@PieterB ทำไม AutoCloseableถูกสร้างขึ้นเพื่อวัตถุประสงค์ที่แน่นอนนี้
BgrWorker

คำตอบ:


48

วิธีแก้ปัญหาในทางปฏิบัติคือการทำให้ชั้นเรียนAutoCloseableและจัดให้มีfinalize()วิธีการหนุนหลัง (ถ้าเหมาะสม ... ดูด้านล่าง!) จากนั้นคุณต้องพึ่งพาผู้ใช้ในชั้นเรียนของคุณเพื่อใช้ลองกับทรัพยากรหรือโทรclose()อย่างชัดเจน

แน่นอนฉันสามารถบันทึกได้ แต่ฉันต้องการบังคับพวกเขา

แต่น่าเสียดายที่ไม่มีวิธีที่1ใน Java เพื่อบังคับให้โปรแกรมเมอร์ทำสิ่งที่ถูกต้อง สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือรับการใช้งานที่ไม่ถูกต้องในเครื่องวิเคราะห์รหัสแบบคงที่


ในหัวข้อของ finalizers คุณลักษณะ Java นี้มีกรณีการใช้งานที่ดีน้อยมาก หากคุณพึ่งพา finalizers เป็นระเบียบคุณพบปัญหาที่อาจใช้เวลานานมากสำหรับการเป็นระเบียบเรียบร้อยเกิดขึ้น Finalizer จะทำงานหลังจาก GC ตัดสินใจว่าวัตถุนั้นไม่สามารถเข้าถึงได้อีกต่อไป สิ่งนี้อาจไม่เกิดขึ้นจนกว่า JVM จะทำการรวบรวมเต็มรูปแบบ

ดังนั้นหากปัญหาที่คุณคาดว่าจะแก้คือการเรียกคืนทรัพยากรที่จำเป็นต้องได้รับการปล่อยตัวก่อนกำหนดจากนั้นคุณจะพลาดเรือลำนั้นโดยใช้การสรุป

และในกรณีที่คุณไม่ได้รับสิ่งที่ฉันพูดข้างต้น ... มันเกือบจะไม่เหมาะสมที่จะใช้ finalizers ในรหัสการผลิตและคุณไม่ควรพึ่งพาพวกเขา!


1 - มีหลายวิธี หากคุณกำลังเตรียมที่จะ "ซ่อน" บริการวัตถุจากรหัสผู้ใช้หรือแน่นควบคุมวงจรชีวิตของพวกเขา (เช่นhttps://softwareengineering.stackexchange.com/a/345100/172 ) แล้วรหัสผู้ใช้ไม่จำเป็นต้องมีrelease()การเรียกร้อง อย่างไรก็ตาม API มีความซับซ้อน จำกัด ... และ IMO "น่าเกลียด" มากขึ้น นอกจากนี้ไม่ได้ถูกบังคับให้โปรแกรมเมอร์ที่จะทำสิ่งที่ถูกต้อง เป็นการลบความสามารถของโปรแกรมเมอร์ในการทำสิ่งที่ผิด!


1
Finalizers สอนบทเรียนที่แย่มาก - ถ้าคุณทำพลาดฉันจะซ่อมให้คุณ ฉันชอบแนวทางของ netty -> พารามิเตอร์การกำหนดค่าที่สั่งให้โรงงาน (ByteBuffer) สุ่มสร้างด้วยความน่าจะเป็นของ X% bytebuffers ด้วย finalizer ที่พิมพ์ตำแหน่งที่ได้รับบัฟเฟอร์นี้ (ถ้ายังไม่ได้เปิดตัว) ดังนั้นในการผลิตค่านี้สามารถตั้งค่าเป็น 0% - คือไม่มีค่าใช้จ่ายและในระหว่างการทดสอบ & dev เป็นค่าที่สูงขึ้นเช่น 50% หรือ 100% เพื่อตรวจหารอยรั่วก่อนปล่อยผลิตภัณฑ์
Svetlin Zarev

11
และอย่าลืมว่าผู้เข้ารอบสุดท้ายจะไม่รับประกันว่าจะมีการเรียกใช้แม้ว่าจะมีการรวบรวมตัวอย่างวัตถุ!
jwenting

3
เป็นที่น่าสังเกตว่า Eclipse ส่งเสียงเตือนคอมไพเลอร์หากไม่ได้ปิด AutoCloseable ฉันคาดว่า Java IDE อื่นจะทำเช่นนั้นเช่นกัน
meriton - เมื่อ

1
@ jwenting ในกรณีใดพวกเขาจะไม่ทำงานเมื่อวัตถุ GC'd? ฉันไม่พบทรัพยากรใด ๆ ที่ระบุหรืออธิบายสิ่งนั้น
Cedric Reichenbach

3
@Voo คุณเข้าใจผิดความคิดเห็นของฉัน คุณมีสองการใช้งาน -> DefaultResourceและFinalizableResourceซึ่งทอดตัวเป็นคนแรกและเพิ่ม finalizer ในการผลิตคุณใช้DefaultResourceซึ่งไม่มี finalizer-> ไม่มีค่าใช้จ่าย ในระหว่างการพัฒนาและทดสอบคุณกำหนดค่าแอปที่จะใช้FinalizableResourceซึ่งมีตัวปรับขั้นสุดท้าย ในระหว่างการพัฒนาและทดสอบนั่นไม่ใช่ปัญหา แต่มันสามารถช่วยคุณระบุรอยรั่วและแก้ไขก่อนถึงการผลิต แต่ถ้าการรั่วไหลมาถึง PRD คุณสามารถกำหนดค่าโรงงานเพื่อสร้าง X% FinalizableResourceเพื่อ id การรั่วไหล
Svetlin Zarev

55

ดูเหมือนว่าจะเป็นกรณีใช้งานสำหรับAutoCloseableอินเทอร์เฟซและtry-with-resourcesคำสั่งที่นำมาใช้ใน Java 7 หากคุณมีสิ่งที่ชอบ

public class MyService implements AutoCloseable {
    public void doWork() {
        // ...
    }

    @Override
    public void close() {
        // release resources
    }
}

จากนั้นผู้บริโภคของคุณสามารถใช้มันเป็น

public class MyConsumer {
    public void foo() {
        try (MyService myService = new MyService()) {
            //...
            myService.doWork()
            //...
            myService.doWork()
            //...
        }
    }
}

และไม่ต้องกังวลเกี่ยวกับการโทรอย่างชัดเจนreleaseไม่ว่ารหัสนั้นจะมีข้อผิดพลาดหรือไม่


คำตอบเพิ่มเติม

หากสิ่งที่คุณกำลังมองหาคือการทำให้แน่ใจว่าไม่มีใครสามารถใช้ฟังก์ชั่นของคุณได้คุณต้องทำสองสิ่ง

  1. ตรวจสอบให้แน่ใจว่าผู้สร้างทั้งหมดMyServiceเป็นแบบส่วนตัว
  2. กำหนดจุดเข้าใช้งานเพียงจุดเดียวเพื่อMyServiceให้แน่ใจว่าได้ทำความสะอาดแล้ว

คุณจะทำอะไรเช่น

public interface MyServiceConsumer {
    void accept(MyService value);
}

public class MyService implements AutoCloseable {
    private MyService() {
    }


    public static void useMyService(MyServiceConsumer consumer) {
        try (MyService myService = new MyService()) {
            consumer.accept(myService)
        }
    }
}

จากนั้นคุณจะใช้มันเป็น

public class MyConsumer {
    public void foo() {
        MyService.useMyService(new MyServiceConsumer() {
            public void accept(MyService myService) {
                myService.doWork()
            }
        });
    }
}

ฉันไม่แนะนำเส้นทางนี้ตั้งแต่แรกเพราะมันน่าเกลียดหากไม่มี lambdas Java-8 และส่วนต่อประสานการใช้งาน ( MyServiceConsumerกลายเป็นที่ไหนConsumer<MyService>) และมันค่อนข้างน่าประทับใจสำหรับผู้บริโภคของคุณ แต่ถ้าสิ่งที่คุณต้องการคือrelease()ต้องได้รับการเรียกมันจะได้ผล


1
คำตอบนี้น่าจะดีกว่าของฉัน วิธีการของฉันมีความล่าช้าก่อนที่ Garbage Collector จะเตะเข้ามา
Dar Brett

1
คุณจะมั่นใจได้อย่างไรว่าลูกค้าจะใช้งานtry-with-resourcesและไม่เพียง แต่ละเว้นtryการcloseโทรที่หายไป
แฟรงค์

@walpen วิธีนี้มีปัญหาเดียวกัน ฉันจะบังคับให้ผู้ใช้ใช้งานtry-with-resourcesอย่างไร?
hasanghaforian

1
@Frank / @hasanghaforian ใช่วิธีนี้ไม่บังคับให้เรียกใกล้ มันจะมีประโยชน์ในการคุ้นเคย: การพัฒนา Java รู้ว่าคุณควรให้แน่ใจว่าจะถูกเรียกเมื่อการดำเนินการในชั้นเรียน.close() AutoCloseable
walpen

1
คุณไม่สามารถจับคู่ finalizer กับusingบล็อกสำหรับการสรุปที่แน่นอนได้หรือไม่? อย่างน้อยใน C # นี่จะกลายเป็นรูปแบบที่ชัดเจนสำหรับนักพัฒนาที่จะใช้นิสัยในการใช้ประโยชน์จากทรัพยากรที่ต้องการการสรุปที่แน่นอน บางสิ่งที่ง่ายและการสร้างนิสัยเป็นสิ่งที่ดีที่สุดถัดไปเมื่อคุณไม่สามารถบังคับใครให้ทำอะไร stackoverflow.com/a/2016311/84206
AaronLS

52

ใช่คุณสามารถ

คุณสามารถบังคับให้พวกเขาปล่อยและทำให้เป็นไปไม่ได้ 100% ที่จะลืมโดยทำให้เป็นไปไม่ได้ที่พวกเขาจะสร้างServiceอินสแตนซ์ใหม่

หากพวกเขาทำอะไรแบบนี้มาก่อน:

Service s = s.new();
try {
  s.doWork("something");
  if (...) s.doWork("something else");
  ...
}
finally {
  s.release(); // oops: easy to forget
}

จากนั้นเปลี่ยนมันเพื่อให้พวกเขาต้องเข้าถึงอย่างนี้

// Their code

Service.runWithRelease(new ServiceConsumer() {
  void run(Service s) {
    s.doWork("something");
    if (...) s.doWork("something else");
    ...
  }  // yay! no s.release() required.
}

// Your code

interface ServiceConsumer {
  void run(Service s);
}

class Service {

   private Service() { ... }      // now: private
   private void release() { ... } // now: private
   public void doWork() { ... }   // unchanged

   public static void runWithRelease(ServiceConsumer consumer) {
      Service s = new Service();
      try {
        consumer.run(s);
      }
      finally {
        s.release();
      } 
    } 
  }

คำเตือน:

  • พิจารณารหัสเทียมนี้มานานแล้วตั้งแต่ฉันเขียน Java
  • อาจมีตัวแปรที่หรูหรากว่าในสมัยนี้บางทีอาจรวมถึงAutoCloseableอินเทอร์เฟซที่มีคนพูดถึง แต่ตัวอย่างที่ให้มาควรจะออกนอกกรอบและยกเว้นเพื่อความสวยงาม (ซึ่งเป็นเป้าหมายที่ดีในตัวเอง) ไม่ควรมีเหตุผลที่สำคัญในการเปลี่ยนแปลง โปรดทราบว่านี่เป็นการตั้งใจที่จะบอกว่าคุณสามารถใช้AutoCloseableในการนำไปใช้ส่วนตัวของคุณ ประโยชน์ของโซลูชันของฉันที่มีต่อผู้ใช้ของคุณAutoCloseableคือสามารถทำได้อีกครั้งและไม่ลืม
  • คุณจะต้องสรุปออกมาตามที่ต้องการเช่นการฉีดอาร์กิวเมนต์ลงในการnew Serviceโทร
  • ดังที่ได้กล่าวไว้ในข้อคิดเห็นคำถามของการสร้างเช่นนี้ (เช่นการนำกระบวนการสร้างและทำลาย a Service) มาอยู่ในมือของผู้โทรหรือไม่อยู่นอกขอบเขตของคำตอบนี้ แต่ถ้าคุณตัดสินใจว่าคุณต้องการสิ่งนี้จริงๆนี่คือวิธีที่คุณทำได้
  • ผู้วิจารณ์คนหนึ่งให้ข้อมูลนี้เกี่ยวกับการจัดการข้อยกเว้น: Google หนังสือ

2
ฉันมีความสุขกับความคิดเห็นสำหรับ downvote ดังนั้นฉันจึงสามารถปรับปรุงคำตอบได้
AnoE

2
ฉันไม่ได้ลงคะแนน แต่การออกแบบนี้น่าจะมีปัญหามากกว่าที่ควรจะเป็น มันเพิ่มความซับซ้อนอย่างมากและกำหนดภาระให้กับผู้โทร นักพัฒนาควรคุ้นเคยกับคลาสสไตล์ลองกับทรัพยากรที่ใช้เป็นลักษณะที่สองเมื่อใดก็ตามที่คลาสใช้อินเทอร์เฟซที่เหมาะสมดังนั้นโดยทั่วไปจะดีพอ
jpmc26

30
@ jpmc26 สนุกฉันรู้สึกว่าวิธีการนี้ช่วยลดความซับซ้อนและภาระให้กับผู้โทรโดยช่วยพวกเขาไม่ให้คิดถึงวงจรชีวิตของทรัพยากรเลย นอกเหนือจากนั้น OP ไม่ได้ถามว่า "ฉันควรบังคับผู้ใช้ ... " แต่ "ฉันจะบังคับผู้ใช้อย่างไร" และถ้ามันมีความสำคัญพอนี่เป็นทางออกหนึ่งที่ทำงานได้ดีมากเป็นโครงสร้างที่เรียบง่าย (แค่ห่อมันในรูปแบบการปิด / เรียกใช้) หรือแม้แต่ถ่ายโอนไปยังภาษาอื่นที่มี lambdas / procs / closures เช่นกัน ฉันจะปล่อยให้เป็นประชาธิปไตยใน SE เพื่อตัดสิน; OP ได้ตัดสินใจแล้วอย่างไรก็ตาม ;)
AnoE

14
อีกครั้ง @ jpmc26 ฉันไม่สนใจที่จะพูดถึงว่าวิธีการนี้ถูกต้องสำหรับกรณีการใช้งานครั้งเดียว OP ถามถึงวิธีบังคับใช้สิ่งนั้นและคำตอบนี้บอกวิธีบังคับใช้สิ่งนั้น ไม่ใช่สำหรับเราที่จะตัดสินใจว่ามันเหมาะสมที่จะทำในตอนแรก เทคนิคที่แสดงเป็นเครื่องมือไม่มีอะไรมากน้อยไปกว่านี้และมีประโยชน์ที่จะมีอยู่ในเครื่องมือแบบ bag-of-tools
AnoE

11
นี่คือคำตอบที่ถูกต้องมันเป็นรูปแบบของรูที่อยู่ตรงกลาง
jk

11

คุณสามารถลองใช้ประโยชน์จากรูปแบบคำสั่ง

class MyServiceManager {

    public void execute(MyServiceTask tasks...) {
        // set up everyting
        MyService service = this.acquireService();
        // execute the submitted tasks
        foreach (Task task : tasks)
            task.executeWith(service);
        // cleanup yourself
        service.releaseResources();
    }

}

สิ่งนี้ทำให้คุณสามารถควบคุมการได้มาและปล่อยทรัพยากรได้อย่างสมบูรณ์ ผู้โทรส่งงานไปยังบริการของคุณเท่านั้นและคุณเองต้องรับผิดชอบในการจัดหาและล้างทรัพยากร

มีการจับเป็นอย่างไร ผู้เรียกยังคงสามารถทำได้:

MyServiceTask t1 = // some task
manager.execute(t1);
MyServiceTask t2 = // some task
manager.execute(t2);

แต่คุณสามารถแก้ไขปัญหานี้ได้เมื่อมันเกิดขึ้น เมื่อมีปัญหาเกี่ยวกับประสิทธิภาพและคุณพบว่าผู้โทรบางคนทำสิ่งนี้เพียงแสดงวิธีที่เหมาะสมและแก้ไขปัญหา:

MyServiceTask t1 = // some task
MyServiceTask t2 = // some task
manager.execute(t1, t2);

คุณสามารถสร้างความซับซ้อนได้ตามใจชอบโดยการปฏิบัติตามคำมั่นสัญญาสำหรับงานที่ต้องพึ่งพางานอื่น แต่การปล่อยสิ่งต่าง ๆ ก็มีความซับซ้อนมากขึ้น นี่เป็นเพียงจุดเริ่มต้น

async

ดังที่มีการระบุไว้ในความคิดเห็นข้างต้นไม่ได้ทำงานกับคำขอแบบอะซิงโครนัส ถูกต้อง. แต่สิ่งนี้สามารถแก้ไขได้อย่างง่ายดายใน Java 8 ด้วยการใช้CompleteableFutureโดยเฉพาะอย่างยิ่งCompleteableFuture#supplyAsyncเพื่อสร้างฟิวเจอร์สรายบุคคลและCompleteableFuture#allOfเพื่อปล่อยทรัพยากรให้สมบูรณ์เมื่องานทั้งหมดเสร็จสิ้น อีกวิธีหนึ่งสามารถใช้เธรดหรือ ExecutorServices เพื่อหมุนการใช้งานฟิวเจอร์ส / สัญญาของตัวเอง


1
ฉันจะขอบคุณถ้าผู้ลงคะแนนแสดงความคิดเห็นว่าทำไมพวกเขาถึงคิดว่ามันไม่ดีเพื่อที่ฉันจะสามารถจัดการกับข้อกังวลเหล่านั้นได้โดยตรงหรืออย่างน้อยก็รับมุมมองใหม่สำหรับอนาคต ขอบคุณ!
Polygnome

+1 โหวต นี่อาจเป็นวิธีการ แต่การบริการนั้นเป็นแบบอะซิงก์และผู้บริโภคจำเป็นต้องได้รับผลลัพธ์แรกก่อนที่จะขอบริการครั้งต่อไป
hasanghaforian

1
นี่เป็นเพียงเทมเพลตเปล่า ๆ JS แก้ปัญหานี้ด้วยคำมั่นสัญญาคุณสามารถทำสิ่งที่คล้ายกันใน Java ด้วยฟิวเจอร์สและตัวสะสมที่เรียกว่าเมื่องานทั้งหมดเสร็จสิ้นแล้วล้างให้สะอาด เป็นไปได้อย่างแน่นอนที่จะได้รับ async และแม้กระทั่งการพึ่งพาในนั้น แต่ก็มีความซับซ้อนมาก
Polygnome

3

หากคุณสามารถทำได้อย่าอนุญาตให้ผู้ใช้สร้างทรัพยากร ให้ผู้ใช้ส่งต่อวัตถุผู้เยี่ยมชมไปยังวิธีการของคุณซึ่งจะสร้างทรัพยากรส่งต่อไปยังวิธีการของวัตถุผู้เข้าชมแล้วปล่อยหลังจากนั้น


ขอบคุณสำหรับการตอบกลับของคุณ แต่ขอโทษนะคุณหมายความว่าเป็นการดีกว่าที่จะส่งต่อทรัพยากรให้กับผู้บริโภคและจากนั้นรับมันอีกครั้งเมื่อเขาขอบริการ จากนั้นปัญหาจะยังคงอยู่: ผู้บริโภคอาจลืมที่จะปล่อยทรัพยากรเหล่านั้น
hasanghaforian

หากคุณต้องการควบคุมทรัพยากรอย่าปล่อยมันไปอย่าส่งไปยังการดำเนินการของคนอื่นอย่างสมบูรณ์ วิธีหนึ่งที่จะทำได้คือการเรียกใช้วิธีการที่กำหนดไว้ล่วงหน้าของวัตถุผู้เข้าชมของผู้ใช้ AutoCloseableทำสิ่งที่คล้ายกันมากกับเบื้องหลัง ภายใต้มุมมองอื่นก็คล้ายกับการฉีดพึ่งพา
9000

1

ความคิดของฉันคล้ายกับของ Polygnome

คุณสามารถมีวิธีการ "do_something ()" ในชั้นเรียนของคุณเพียงแค่เพิ่มคำสั่งลงในรายการคำสั่งส่วนตัว จากนั้นมีเมธอด "commit ()" ที่ใช้งานได้จริงและเรียกว่า "release ()"

ดังนั้นหากผู้ใช้ไม่เคยเรียกคอมมิท () ผลงานก็จะไม่ถูกดำเนินการ หากพวกเขาทำทรัพยากรที่เป็นอิสระ

public class Foo
{
  private ArrayList<ICommand> _commands;

  public Foo doSomething(int arg) { _commands.Add(new DoSomethingCommand(arg)); return this; }
  public Foo doSomethingElse() { _commands.Add(new DoSomethingElseCommand()); return this; }

  public void commit() { 
     for(ICommand c : _commands) c.doWork();
     release();
     _commands.clear();
  }

}

จากนั้นคุณสามารถใช้มันเหมือน foo.doSomething.doSomethineElse (). commit ();


นี่เป็นโซลูชันเดียวกัน แต่แทนที่จะขอให้ผู้โทรรักษารายการงานที่คุณดูแลไว้ภายใน แนวคิดหลักเหมือนกันอย่างแท้จริง แต่สิ่งนี้ไม่ได้เป็นไปตามอนุสัญญาการเข้ารหัสใด ๆ สำหรับ Java ที่ฉันเคยเห็นและจริง ๆ แล้วค่อนข้างยากที่จะอ่าน (ลำตัววนซ้ำในบรรทัดเดียวกับส่วนหัววน _ ในตอนแรก)
Polygnome

ใช่มันเป็นรูปแบบคำสั่ง ฉันเพิ่งวางโค้ดเพื่อให้ส่วนสำคัญของสิ่งที่ฉันคิดในทางที่รัดกุมที่สุด ฉันส่วนใหญ่เขียน C # วันนี้ที่ตัวแปรส่วนตัวมักจะนำหน้าด้วย _
Sava B.

-1

อีกทางเลือกหนึ่งของที่กล่าวถึงคือการใช้ "การอ้างอิงอัจฉริยะ" เพื่อจัดการทรัพยากรที่ห่อหุ้มของคุณในวิธีที่ช่วยให้ตัวรวบรวมขยะสามารถล้างข้อมูลโดยอัตโนมัติโดยไม่ต้องใช้ทรัพยากรด้วยตนเอง ประโยชน์ของมันขึ้นอยู่กับการใช้งานของคุณ อ่านเพิ่มเติมในหัวข้อนี้ในโพสต์บล็อกที่ยอดเยี่ยมนี้: https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references


นี่เป็นแนวคิดที่น่าสนใจ แต่ฉันไม่สามารถเห็นวิธีการใช้การอ้างอิงที่อ่อนแอเพื่อแก้ไขปัญหาของ OP คลาสบริการไม่ทราบว่ากำลังจะได้รับคำขอ doWork () อื่น ถ้ามันปล่อยการอ้างอิงไปยังวัตถุที่มีขนาดใหญ่และจากนั้นจะได้รับการเรียก doWork () อีกครั้งมันจะล้มเหลว
Jay Elston

-4

สำหรับภาษา Class Oriented อื่น ๆ ฉันจะบอกว่าวางไว้ใน destructor แต่เนื่องจากนั่นไม่ใช่ตัวเลือกฉันคิดว่าคุณสามารถแทนที่วิธีสุดท้ายได้ ด้วยวิธีนี้เมื่ออินสแตนซ์ออกจากขอบเขตและเก็บรวบรวมขยะจะถูกปล่อยออกมา

@Override
public void finalize() {
    this.release();
}

ฉันไม่คุ้นเคยกับ Java มากนัก แต่ฉันคิดว่านี่เป็นจุดประสงค์ที่จะทำการสรุป ()

http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#finalize ()


7
นี่เป็นวิธีแก้ปัญหาที่ไม่ดีสำหรับเหตุผลที่สตีเฟ่นพูดในคำตอบของเขาIf you rely on finalizers to tidy up, you run into the problem that it can take a very long time for the tidy-up to happen. The finalizer will only be run after the GC decides that the object is no longer reachable. That may not happen until the JVM does a full collection.
Daenyth

4
และแม้กระทั่งในที่สุดผู้
เข้ารอบ

3
Finalizers ไม่น่าเชื่อถือฉาวโฉ่: พวกเขาอาจทำงานได้พวกเขาอาจไม่วิ่งถ้าพวกเขาวิ่งไม่มีการรับประกันเมื่อพวกเขาจะวิ่ง แม้แต่สถาปนิกของจาวาเช่นJosh Bloch ก็กล่าวว่าผู้เข้ารอบสุดท้ายนั้นเป็นความคิดที่แย่มาก (การอ้างอิง: Effective Java (2nd Edition) )

ปัญหาแบบนี้ทำให้ฉันคิดถึง C ++ ด้วยการทำลายล้างที่กำหนดไว้และ RAII ...
Mark K Cowan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.