หากเราต้องการดำเนินการตามกำหนดเวลาง่าย ๆ ระหว่างกระบวนการเขียนโปรแกรมเราไม่จำเป็นต้องควบคุมที่ซับซ้อน เราสามารถพิจารณาใช้งานการจับเวลาตัวจับเวลาใน JDK เพื่อให้ได้มัน LZ ต่อไปนี้วิเคราะห์ตัวจับเวลา Java ตามหลักการตัวอย่างและข้อบกพร่องของตัวจับเวลา
1. บทนำ
ใน Java งานเวลาที่สมบูรณ์จะต้องเสร็จสิ้นโดยคลาส Timer และ Timertask นี่คือวิธีที่พวกเขาถูกกำหนดไว้ใน API ตัวจับเวลา: เครื่องมือที่ใช้เพื่อจัดเรียงงานที่เธรดดำเนินการในเธรดพื้นหลังในภายหลัง งานสามารถดำเนินการได้หนึ่งครั้งหรือสามารถดำเนินการซ้ำ ๆ ได้ งานที่กำหนดโดย Timertask: จับเวลาเป็นงานที่ดำเนินการหรือทำซ้ำ เราสามารถเข้าใจได้ว่าตัวจับเวลาเป็นเครื่องมือจับเวลาที่ใช้ในการวางแผนที่จะดำเนินการงานที่ระบุในเธรดพื้นหลังและ Timertask คลาสนามธรรมที่มีคลาสย่อยแสดงถึงงานที่สามารถวางแผนได้โดยตัวจับเวลา
คลาสจับเวลามีวิธีการสร้างสี่วิธีในตัวจับเวลาคลาสเครื่องมือ แต่ละคอนสตรัคเตอร์เริ่มเธรดจับเวลา ในเวลาเดียวกันคลาสจับเวลาสามารถตรวจสอบให้แน่ใจว่าหลายเธรดสามารถแชร์วัตถุตัวจับเวลาเดียวโดยไม่ต้องซิงโครไนซ์ภายนอกดังนั้นคลาสจับเวลาจึงปลอดภัย อย่างไรก็ตามเนื่องจากวัตถุตัวจับเวลาแต่ละรายการสอดคล้องกับเธรดพื้นหลังเดียวซึ่งใช้ในการเรียกใช้งานตัวจับเวลาทั้งหมดตามลำดับโดยทั่วไปเวลาที่ใช้ในการดำเนินงานเธรดของเราควรสั้นมาก อย่างไรก็ตามเนื่องจากสถานการณ์พิเศษเวลาดำเนินการของงานตัวจับเวลาบางอย่างนั้นยาวเกินไปดังนั้นจึง "เฉพาะ" เธรดการดำเนินการจับเวลาของตัวจับเวลาและเธรดที่ตามมาทั้งหมดจะต้องรอให้ดำเนินการซึ่งจะชะลอการดำเนินงานที่ตามมา เราจะวิเคราะห์สถานการณ์เฉพาะในภายหลัง
เมื่อโปรแกรมเริ่มต้นตัวจับเวลางานเวลาจะถูกดำเนินการตามเวลาที่เราตั้งไว้ ตัวจับเวลาจัดเตรียมวิธีการกำหนดเวลาซึ่งมีการโอเวอร์โหลดหลายครั้งเพื่อปรับให้เข้ากับสถานการณ์ที่แตกต่างกันดังนี้:
กำหนดเวลา (งาน TimerTask เวลาวันที่): กำหนดเวลาการดำเนินการของงานที่ระบุในเวลาที่กำหนด
กำหนดเวลา (งาน TimerTask, วันที่ครั้งแรก, ระยะเวลานาน): กำหนดเวลางานที่ระบุเพื่อเริ่มต้นการดำเนินการล่าช้าแก้ไขซ้ำซ้ำในเวลาที่กำหนด
กำหนดเวลา (งาน TimerTask, การหน่วงเวลานาน): กำหนดเวลางานที่ระบุที่จะดำเนินการหลังจากการหน่วงเวลาที่ระบุ
กำหนดเวลา (งาน TimerTask, การหน่วงเวลานาน, เป็นเวลานาน): กำหนดเวลางานที่ระบุที่จะดำเนินการแก้ไขล่าช้าซ้ำ ๆ หลังจากการหน่วงเวลาที่ระบุ
ในเวลาเดียวกันวิธีการกำหนดตารางเวลา FIXEDRATE ก็เกินพิกัดเช่นกัน วิธี ScheduleatFixedrate นั้นเหมือนกับกำหนดการ แต่โฟกัสของพวกเขาแตกต่างกันและความแตกต่างจะถูกวิเคราะห์ในภายหลัง
ScheduleatFixedrate (งาน TimerTask, วันที่ครั้งแรก, ระยะเวลานาน): กำหนดเวลางานที่ระบุที่จะดำเนินการซ้ำ ๆ ในอัตราคงที่ในเวลาที่กำหนด
ScheduleatFixedrate (งาน TimerTask, การหน่วงเวลานาน, เป็นเวลานาน): กำหนดเวลางานที่ระบุเพื่อเริ่มต้นการดำเนินการอัตราคงที่ซ้ำ ๆ หลังจากการหน่วงเวลาที่ระบุ
timertask
คลาส Timertask เป็นคลาสนามธรรมที่จัดโดยตัวจับเวลาเป็นงานที่ดำเนินการหรือทำซ้ำ มันมีวิธีนามธรรม Run () ซึ่งใช้ในการดำเนินการที่จะดำเนินการโดยงานจับเวลาที่สอดคล้องกัน ดังนั้นแต่ละคลาสงานเฉพาะจะต้องสืบทอด timertask จากนั้นแทนที่วิธีการเรียกใช้ ()
นอกจากนี้ยังมีสองวิธีที่ไม่ได้รับการรับรอง:
บูลีนยกเลิก (): ยกเลิกงานจับเวลานี้
Long ScheduleDexecutionTime (): ส่งคืนเวลาดำเนินการตามกำหนดเวลาของการดำเนินการจริงล่าสุดของงานนี้
2. ตัวอย่าง
2.1. ระบุเวลาหน่วงในการดำเนินการตามกำหนดเวลา
ระดับสาธารณะ TimerTest01 {ตัวจับเวลาตัวจับเวลา; timertest01 สาธารณะ (เวลา int) {timer = new timer (); timer.schedule (TimertaskTest01 () ใหม่เวลา * 1000); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println ("ตัวจับเวลาเริ่ม ... "); ใหม่ TimerTest01 (3); }} คลาสสาธารณะ TimerTaskTest01 ขยาย TimerTask {โมฆะสาธารณะ Run () {System.out.println ("Time's Up !!!"); -ผลการทำงาน:
พิมพ์ครั้งแรก:
ตัวจับเวลาเริ่ม ....
พิมพ์ใน 3 วินาที:
เวลาขึ้น !!!
2.2. ดำเนินงานเวลาตามเวลาที่กำหนด
ระดับสาธารณะ TimerTest02 {ตัวจับเวลาตัวจับเวลา; timertest02 สาธารณะ () {เวลาวันที่ = getTime (); System.out.println ("ระบุเวลา =" + เวลา); ตัวจับเวลา = ตัวจับเวลาใหม่ (); timer.schedule (TimertaskTest02 (), เวลา); } วันที่สาธารณะ getTime () {ปฏิทินปฏิทิน = calendar.getInstance (); Calendar.set (Calendar.hour_of_day, 11); Calendar.set (Calendar.minute, 39); Calendar.set (Calendar.second, 00); เวลาวันที่ = calendar.getTime (); เวลากลับ; } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ใหม่ timertest02 (); }} คลาสสาธารณะ TimerTaskTest02 ขยาย TimerTask {@Override โมฆะสาธารณะเรียกใช้ () {System.out.println ("ดำเนินการงานเธรดตามเวลาที่กำหนด ... "); -เมื่อถึงเวลา 11:39:00 น. งานเธรดจะถูกดำเนินการแน่นอนมันจะถูกดำเนินการแม้ว่ามันจะยิ่งใหญ่กว่าเวลานั้น! - ผลการดำเนินการคือ:
เวลาที่กำหนด = อังคาร 10 มิ.ย. 11:39:00 CST 2014 เวลาที่ระบุในการดำเนินงานเธรด ...
2.3. หลังจากล่าช้าเวลาที่กำหนดงานเวลาจะถูกดำเนินการตามเวลาที่กำหนด
ระดับสาธารณะ TimerTest03 {ตัวจับเวลาตัวจับเวลา; timertest03 () {timer = new timer (); Timer.Schedule (ใหม่ TimerTaskTest03 (), 1,000, 2000); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ใหม่ timertest03 (); }} คลาสสาธารณะ TimerTaskTest03 ขยาย TimerTask {@Override โมฆะสาธารณะเรียกใช้ () {วันที่ = วันที่ใหม่ (this.ScheduleDexecutiontime ()); System.out.println ("เวลาสำหรับการดำเนินการเธรดนี้คือ:" + วันที่); -ผลการทำงาน:
เวลาสำหรับการดำเนินการเธรดนี้คือ: อังคาร 10 มิ.ย. 21:19:47 CST 2014 เวลาสำหรับการดำเนินการหัวข้อนี้คือ: อังคาร 10 มิ.ย. 10 21:19:49 CST 2014 เวลาสำหรับการดำเนินการหัวข้อนี้คือ: อังคาร 10 มิ.ย. 21:19:51 CST 2014 21:19:55 CST 2014 เวลาสำหรับการดำเนินการหัวข้อนี้คือ: อังคาร 10 มิ.ย. 21:19:57 CST 2014 ...............
สำหรับงานเธรดนี้หากเราไม่หยุดงานมันจะทำงานต่อไป
สำหรับสามตัวอย่างข้างต้น LZ เพิ่งแสดงให้เห็นสั้น ๆ และไม่ได้อธิบายตัวอย่างของวิธี ScheduleatFixedrate ในความเป็นจริงวิธีนี้เหมือนกับวิธีกำหนดเวลา!
2.4. วิเคราะห์กำหนดการและกำหนดการ FIXEDRATE
(1) กำหนดเวลา (งาน TimerTask เวลาวันที่) กำหนดเวลา (งาน TimerTask ล่าช้านาน)
สำหรับทั้งสองวิธีหากกำหนดการกำหนดเวลาที่ระบุ <= SystemCurrentTime งานจะถูกดำเนินการทันที ScheduleDexecutiontime จะไม่เปลี่ยนแปลงเนื่องจากการดำเนินงานมากเกินไป
(2) กำหนดเวลา (งาน TimerTask วันที่ครั้งแรกระยะเวลานาน) กำหนดเวลา (งาน TimerTask ล่าช้าระยะยาวระยะยาว)
สองวิธีนี้แตกต่างจากสองวิธีข้างต้น ดังที่ได้กล่าวไว้ก่อนหน้านี้งานจับเวลาตัวจับเวลาจะล่าช้าเนื่องจากงานก่อนหน้านี้ถูกดำเนินการเป็นเวลานาน ในสองวิธีนี้เวลาที่กำหนดเวลาของงานแต่ละงานที่ดำเนินการจะเปลี่ยนไปตามเวลาจริงของงานก่อนหน้านั่นคือ scheduleDexecutiontime (n+1) = realexecutiontime (n)+ช่วงเวลา กล่าวคือหากงาน nth ทำให้กระบวนการเวลาดำเนินการนี้เกิดขึ้นเนื่องจากสถานการณ์บางอย่างและในที่สุด SystemCurrentTime> = ScheduleDexecutiontime (N+1) นี่คืองาน N+1 และจะไม่ถูกดำเนินการเนื่องจากเวลา มันจะรอให้งาน nth ดำเนินการก่อนการดำเนินการจากนั้นสิ่งนี้จะนำไปสู่การใช้งาน N+2 การดำเนินการตามกำหนดเวลาและการเปลี่ยนแปลงของการดำเนินการตามกำหนดเวลาและการเปลี่ยนแปลงนั่นคือ scheduleDexecutiontime (n+2) = realexecutiontime (N+1)+ช่วงเวลา ดังนั้นวิธีการทั้งสองนี้ให้ความสำคัญกับความเสถียรของช่วงเวลาการจัดเก็บมากขึ้น
(3) ScheduleatFixedrate (งาน TimerTask, วันที่ครั้งแรก, ระยะเวลานาน), scheduleatfixedrate (งาน TimerTask, การหน่วงเวลานาน, ระยะเวลานาน)
ดังที่ได้กล่าวไว้ก่อนหน้านี้การมุ่งเน้นของ ScheduleatFixedrate และวิธีการกำหนดเวลานั้นแตกต่างกัน วิธีการกำหนดเวลามุ่งเน้นไปที่ความเสถียรของช่วงเวลาการออมในขณะที่วิธีการกำหนดตารางเวลา Fixedrate มุ่งเน้นไปที่การรักษาเสถียรภาพของความถี่ในการดำเนินการ ทำไมคุณถึงพูดอย่างนั้นเหตุผลมีดังนี้ ในวิธีการกำหนดเวลาความล่าช้าของงานก่อนหน้านี้จะทำให้เกิดความล่าช้าของงานเวลาหลังจากนั้นในขณะที่วิธีการกำหนดตารางเวลา fixedrate จะไม่ หากเวลาการดำเนินการของงาน nth ยาวเกินไป SystemCurrentTime> = scheduleDexecutiontime (n+1) จะไม่มีการรอให้มันดำเนินการงาน n+1 ทันที ดังนั้นวิธีการคำนวณของเวลาดำเนินการของวิธี ScheduleatFixedrate นั้นแตกต่างจากกำหนดเวลา แต่กำหนดเวลา Executiontime (n) = FirstExecuteTime +N*ระยะเวลาและวิธีการคำนวณจะยังคงไม่เปลี่ยนแปลงตลอดไป ดังนั้น ScheduleatFixedrate จึงมุ่งเน้นไปที่การรักษาความถี่การดำเนินการให้เสถียร
3. ข้อบกพร่องของตัวจับเวลา
3.1. ข้อบกพร่องของตัวจับเวลา
ตัวจับเวลาตัวจับเวลาสามารถเวลา (ดำเนินการงานตามเวลาที่กำหนด), ล่าช้า (งานล่าช้าที่ 5 วินาที) และดำเนินการเป็นระยะ (ดำเนินการงานที่ 1 วินาที) แต่ตัวจับเวลามีข้อบกพร่องบางอย่าง ก่อนอื่นการสนับสนุนของ Timer สำหรับการจัดตารางเวลานั้นขึ้นอยู่กับเวลาที่แน่นอนไม่ใช่เวลาที่สัมพันธ์กันดังนั้นจึงมีความอ่อนไหวต่อการเปลี่ยนแปลงเวลาของระบบ ประการที่สองเธรดตัวจับเวลาจะไม่จับข้อยกเว้น หากข้อยกเว้นที่ไม่ได้ตรวจสอบถูกโยนโดย Timertask มันจะทำให้เธรดจับเวลาสิ้นสุดลง ในเวลาเดียวกันตัวจับเวลาจะไม่ดำเนินการต่อการดำเนินการของเธรดและจะเชื่อผิดพลาดว่าเธรดตัวจับเวลาทั้งหมดจะถูกยกเลิก ในเวลาเดียวกัน Timertask ซึ่งได้รับการกำหนดให้ยังไม่ได้ดำเนินการจะไม่ถูกดำเนินการอีกต่อไปและไม่สามารถกำหนดงานใหม่ได้ ดังนั้นหาก Timertask โยนข้อยกเว้นที่ไม่ได้ตรวจสอบตัวจับเวลาจะสร้างพฤติกรรมที่คาดเดาไม่ได้
(1) ข้อบกพร่องของการจัดการเวลาการจัดการตัวจับเวลา ก่อนที่ตัวจับเวลาจะสร้างงานเธรดเพียงครั้งเดียวเมื่อดำเนินงานที่กำหนดเวลา หากมีหลายเธรดถ้าหนึ่งในเธรดทำให้เวลาการดำเนินการของงานเธรดยาวเกินไปด้วยเหตุผลบางอย่างและช่วงเวลาระหว่างงานทั้งสองจะเกิดขึ้นข้อบกพร่องบางอย่างจะเกิดขึ้น:
TimerTests04 ชั้นเรียนสาธารณะ {ตัวจับเวลาตัวจับเวลาส่วนตัว; การเริ่มต้นที่ยาวนานของสาธารณะ TimerTest04 () {this.timer = new timer (); start = system.currentTimeMillis (); } โมฆะสาธารณะ timerone () {timer.schedule (timertask ใหม่ () {public void run () {system.out.println ("timerone เรียกใช้เวลา:" + (system.currentTimeMillis () - เริ่มต้น); ลอง {thread.sleep (4000); E.PrintStackTrace ();}}}}, 1000); } โมฆะสาธารณะ timertwo () {timer.schedule (ใหม่ timertask () {public void run () {system.out.println ("timerone เรียกใช้เวลา:" + (system.currenttimeMillis () - เริ่มต้น));}}, 3000); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่นข้อยกเว้น {timerTest04 ทดสอบ = ใหม่ timerTest04 (); test.timerone (); test.timertwo (); -ตามความคิดปกติของเรา Timertwo ควรดำเนินการหลังจาก 3s และผลลัพธ์ควรเป็น:
Timerone เรียกใช้เวลา: 1001 Timerone เรียกใช้เวลา: 3001
แต่สิ่งต่าง ๆ ขัดกับความคาดหวังของฉัน Timerone นอนหลับ (4000) นอน 4S และตัวจับเวลาอยู่ในตัวจับเวลาซึ่งเป็นสาเหตุของเวลาที่ต้องใช้เวลาในการกำหนดเวลาเกินช่วงเวลา ผลลัพธ์:
Timerone เรียกใช้เวลา: 1,000 timerone เรียกใช้เวลา: 5000
(2) ตัวจับเวลาทำให้เกิดข้อบกพร่องข้อยกเว้น หาก Timertask โยน runtimeException ตัวจับเวลาจะยุติการทำงานของงานทั้งหมด ดังนี้:
TimerTests04 ชั้นเรียนสาธารณะ {ตัวจับเวลาตัวจับเวลาส่วนตัว; TimerTest04 () {this.timer = new timer (); } โมฆะสาธารณะ timerone () {timer.schedule (ใหม่ timertask () {public void run () {โยน runtimeException ใหม่ ();}}, 1000); } โมฆะสาธารณะ timertwo () {timer.schedule (ใหม่ timertask () {public void run () {system.out.println ("ฉันจะดำเนินการหรือไม่?");}}, 1,000); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {timerTest04 ทดสอบ = ใหม่ timerTest04 (); test.timerone (); test.timertwo (); -ผลการทำงาน: Timerone โยนข้อยกเว้นทำให้งาน Timertwo สิ้นสุดลง
ข้อยกเว้นในเธรด "Timer-0" java.lang.runtimeException ที่ com.chenssy.timer.timertest04 $ 1.Run (TimerTest04.Java:25) ที่ java.util.timerthread.mainloop (timer.java:555)
สำหรับข้อบกพร่องของตัวจับเวลาเราสามารถพิจารณา ScheduledThreadPoolexecutor แทน ตัวจับเวลาขึ้นอยู่กับเวลาที่แน่นอนและมีความอ่อนไหวต่อเวลาของระบบมากขึ้นในขณะที่ scheduledThreadPoolexecutor ขึ้นอยู่กับเวลาที่สัมพันธ์กัน ตัวจับเวลาเป็นเธรดเดียวภายในในขณะที่ ScheduleDThreadPoolexecutor เป็นพูลเธรดภายในดังนั้นจึงสามารถรองรับการดำเนินการพร้อมกันของงานหลายงาน
3.2. แทนที่ตัวจับเวลาด้วย ScheduleDexecutorService
(1) แก้ปัญหาหนึ่ง:
ชั้นเรียนสาธารณะ scheduleDexecutortest {ส่วนตัว ScheduleDexecutorService SchedueXec; การเริ่มต้นที่ยาวนานของสาธารณะ ScheduleDexecutortest () {this.schedueXec = executors.newscheduledThreadPool (2); this.start = System.currentTimeMillis (); } โมฆะสาธารณะ timerone () {schedueXec.schedule (ใหม่ runnable () {public void run () {system.out.println ("timerone, เวลา:" + (system.currenttimeMillis (เริ่มต้น)); ลอง {thread.sleep (4000); }, 1,000, timeunit.milliseconds); } โมฆะสาธารณะ timertwo () {scheduleexec.schedule (ใหม่ runnable () {public void run () {system.out.println ("timertwo, เวลา:" + (system.currentTimeMillis () - เริ่มต้น); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {scheduleDexecutortest test = ใหม่ scheduleDexecutortest (); test.timerone (); test.timertwo (); -ผลการทำงาน:
Timerone, The Time: 1003 Timertwo, The Time: 2005
(2) แก้ปัญหาที่สอง
ชั้นเรียนสาธารณะ scheduleDexecutortest {ส่วนตัว ScheduleDexecutorService SchedueXec; การเริ่มต้นที่ยาวนานของสาธารณะ ScheduleDexecutortest () {this.schedueXec = executors.newscheduledThreadPool (2); this.start = System.currentTimeMillis (); } โมฆะสาธารณะ timerone () {schedueXec.schedule (ใหม่ runnable () {public void run () {โยน runtimeException ใหม่ ();}}, 1,000, timeUnit.milliseconds); } โมฆะสาธารณะ timertwo () {scheduleexec.scheduleatfixedrate (ใหม่ runnable () {public void run () {system.out.println ("Timertwo เรียก ... ..... ");}}, 2000,500, TimeUnit.milliseconds); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {scheduleDexecutortest test = ใหม่ scheduleDexecutortest (); test.timerone (); test.timertwo (); -ผลการทำงาน:
Timertwo เรียกใช้ ... Timertwo เรียกใช้ ... Timertwo เรียกใช้ ... Timertwo เรียกใช้ ... Timertwo เรียก ... Timertwo เรียกใช้ ... Timertwo เรียกใช้ ... Timertwo เรียก ... Timertwo เรียก ... Timertwo
4. ใช้ตัวจับเวลาเพื่อให้ได้ลูกบอล
ตัวอย่างในสมุดจำลองได้ทำพินบอลซึ่งจะวาดวงกลมหลายวงที่ตำแหน่งที่กำหนดบนผืนผ้าใบและหลังจากช่วงเวลาล่าช้ามันก็ถูกวาดอีกครั้งในตำแหน่งใกล้เคียง ทำให้ลูกบอลดูเหมือนจะเคลื่อนไหวและปรับความล่าช้าผ่านส่วนประกอบ JSpinner เพื่อควบคุมความเร็วในการเคลื่อนไหวของลูกบอล
Ballscanvas.java
Ballscanvas ระดับสาธารณะขยาย Canvas ใช้ ActionListener, FocusListener {ลูกบอลส่วนตัว []; // ตัวจับเวลาส่วนตัวหลายลูก บอลคลาสคงที่ส่วนตัว {int x, y; // ประสานสีสี; // สีบูลีนขึ้น, ซ้าย; // ทิศทางของลูกบอลการเคลื่อนไหว (int x, int y, สีสี) {this.x = x; this.y = y; this.color = color; up = ซ้าย = false; }} public ballscanvas (สีสี [], int delay) {// เริ่มต้นสีและล่าช้า this.balls = ลูกใหม่ [colors.length]; สำหรับ (int i = 0, x = 40; i <colours.length; i ++, x+= 40) {balls [i] = ลูกบอลใหม่ (x, x, colours [i]); } this.addfocuslistener (นี่); ตัวจับเวลา = ตัวจับเวลาใหม่ (ล่าช้าสิ่งนี้); // สร้างวัตถุตัวจับเวลาล่าช้าระบุการหน่วงเวลา timer.start (); } // ตั้งค่าการหน่วงเวลาโมฆะสาธารณะ setDelay (int delay) {timer.setDelay (ล่าช้า); } // วาดภาพโมฆะสาธารณะ (กราฟิก g) {สำหรับ (int i = 0; i <balls.length; i ++) {g.setColor (ลูก [i] .color); // ตั้งค่าลูกบอลสี [i] .x = balls [i] .left? ลูกบอล [i] .x - 10: ลูกบอล [i] .x + 10; if (balls [i] .x <0 || balls [i] .x> = this.getWidth ()) {// เปลี่ยนทิศทางเป็นลูกบอลแนวนอน [i] .left =! balls [i] .left; } balls [i] .y = balls [i] .up? ลูกบอล [i] .y - 10: ลูกบอล [i] .y + 10; if (balls [i] .y <0 || balls [i] .y> = this.getheight ()) {// เปลี่ยนทิศทางเป็น Balls Direction Balls [i] .Up =! Balls [i] .Up; } g.filloval (ลูก [i] .x, บอล [i] .y, 20, 20); // วาดวงกลมของเส้นผ่านศูนย์กลางที่ระบุ}} // ตัวจับเวลาเวลาดำเนินการเหตุการณ์ @Override โมฆะสาธารณะ ActionPerformed (ActionEvent E) {repaint (); // repaint} // รับโฟกัส @Override โมฆะสาธารณะโฟกัสที่โฟกัส (โฟกัส e) {timer.stop (); // ตัวจับเวลาหยุด} // lost focus @Override โมฆะสาธารณะ focuslost (FocusEvent E) {timer.restart (); // ตัวจับเวลารีสตาร์ท}}ballsjframe.java
Class BallsjFrame ขยาย JFrame ใช้ changelistener {Ballscanvas Ball ส่วนตัว; สปินเนอร์ Jspinner ส่วนตัว; Public BallsjFrame () {super ("pinball"); this.setbounds (300, 200, 480, 360); this.setDefaultCloseOperation (exit_on_close); สีสี [] = {color.red, color.green, color.blue, color.magenta, color.cyan}; Ball = ใหม่ ballscanvas (สี, 100); this.getContentPane (). เพิ่ม (บอล); JPanel Panel = new JPanel (); this.getContentPane (). เพิ่ม (แผง, "ใต้"); Panel.Add (ใหม่ JLabel ("ล่าช้า")); Spinner = new jspinner (); Spinner.setValue (100); Panel.Add (Spinner); Spinner.addchangelistener (นี่); this.setVisible (จริง); } @Override โมฆะสาธารณะ stateChanged (ChangeEvent E) {// เมื่อแก้ไขค่า jspinner คลิกปุ่มขึ้นหรือลงของ jspinner หรือกด Enter ใน jspinner ball.setDelay (integer.parseint ("" spinner.getValue ()); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ใหม่ ballsjframe (); - ผลกระทบมีดังนี้: