ทำไมเราจึงประกาศ Loggers static final?


132

ใน Java ทำไมมันเป็นวิธีที่ดีที่สุดที่จะประกาศคนตัดไม้static final?

private static final Logger S_LOGGER

คำตอบ:


209
  • private- เพื่อไม่ให้ชั้นเรียนอื่นสามารถจี้คนตัดไม้ของคุณได้
  • static - ดังนั้นจึงมีอินสแตนซ์คนตัดไม้เพียงหนึ่งรายการต่อคลาสและหลีกเลี่ยงความพยายามที่จะทำให้คนตัดไม้เป็นอนุกรม
  • final - ไม่จำเป็นต้องเปลี่ยนคนตัดไม้ตลอดอายุการใช้งานของคลาส

นอกจากนี้ฉันชอบตั้งชื่อlogให้เรียบง่ายที่สุด แต่สื่อความหมายได้ดี

แก้ไข: อย่างไรก็ตามมีข้อยกเว้นที่น่าสนใจสำหรับกฎเหล่านี้:

protected final Logger log = LoggerFactory.getLogger(getClass());

ตรงข้ามกับ:

private static final Logger log = LoggerFactory.getLogger(Foo.class);

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


36
ถ้าเป็นแบบคงที่และสุดท้ายให้ใช้ LOG (ตัวพิมพ์ใหญ่)
zacheusz

39
@zacheuszฉันรู้ว่านั่นคือประเด็น บางคนติดตาม Java การตั้งชื่อการประชุมเคร่งครัด (ผิดอะไรกับที่) แต่ฉันชอบง่ายต่อการเขียนและสะดวกสบายมากขึ้นในการอ่านชื่อมากกว่ากระเจิงรหัสด้วยlog LOGแค่เรื่องของ dev. ข้อตกลงของทีม
Tomasz Nurkiewicz

27
โปรดทราบว่าไม่แนะนำให้ประกาศคนตัดไม้เป็นแบบคงที่และขั้นสุดท้ายอีกต่อไปโปรดดูที่slf4j.org/faq.html#declared_staticและ wiki.apache.org/commons/Logging/FrequentlyAskedQuestionsส่วนฉันควรประกาศการอ้างอิงบันทึกแบบคงที่หรือไม่?
Matthew Farwell

6
@zacheusz ชื่อฟิลด์ตัวพิมพ์ใหญ่ใช้สำหรับค่าคงที่ คนตัดไม้ไม่คงที่ http://stackoverflow.com/questions/1417190/should-a-static-final-logger-be-declared-in-upper-case
michal.kreuzman

2
@zacheusz ไม่ใช่แอตทริบิวต์สุดท้ายแบบคงที่ทั้งหมดควรเป็น
UPPERCASE

15

ตรวจสอบการโพสต์บล็อกนี้: กำจัดของ Java คงตัดไม้ นี่คือวิธีที่คุณใช้ slf4j กับjcabi-log :

import com.jcabi.log.Logger;
class Foo {
  void save(File f) {
    Logger.info(this, "file %s saved successfully", f);
  }
}

และอย่าใช้เสียงคงที่อีกต่อไป


ทางเลือกที่น่าสนใจและสะอาดกว่าแน่นอน ฉันสงสัยว่าเครื่องชั่งนี้เทียบกับคนตัดไม้แต่ละชั้นอย่างไร
รอส

12
เขียน Logger ให้ยาวขึ้น .. (นี่ ... ) ทุกครั้ง Nah
Mikhail Boyarsky

ความคิดเห็นแรกในบล็อกโพสต์ที่เกี่ยวข้องบ่งบอกถึงด้านที่ชั่วร้ายของวิธีการคงที่ :) ดังนั้นการใช้ตัวบันทึกขั้นสุดท้ายส่วนตัวจึงเป็นแนวทางปฏิบัติที่ดีที่สุด
Bahadir Tasdemir

5

staticหมายความว่าคุณจะสร้าง Logger ได้เพียงหนึ่งคนต่อคลาสเท่านั้นไม่ใช่หนึ่งคนตัดไม้ต่ออินสแตนซ์ของคลาสของคุณ โดยทั่วไปนี่คือสิ่งที่คุณต้องการเนื่องจากคนตัดไม้มักจะแตกต่างกันไปตามชั้นเรียนเท่านั้น

finalหมายความว่าคุณจะไม่เปลี่ยนค่าของloggerตัวแปร ซึ่งเป็นความจริงเนื่องจากคุณมักจะโยนข้อความบันทึกทั้งหมด (จากคลาสเดียว) ไปยังคนตัดไม้คนเดียวกัน แม้ในบางครั้งที่เกิดขึ้นไม่บ่อยนักที่ชั้นเรียนอาจต้องการส่งข้อความบางอย่างไปยังคนตัดไม้คนอื่นมันจะชัดเจนกว่ามากที่จะสร้างตัวแปรคนตัดไม้อื่น (เช่นwidgetDetailLogger) แทนที่จะเปลี่ยนค่าของตัวแปรคงที่ทันที


4

คุณต้องการเปลี่ยนค่าของฟิลด์เมื่อใด

หากคุณจะไม่เปลี่ยนค่าการทำให้ฟิลด์สุดท้ายทำให้เห็นได้ชัดว่าคุณจะไม่มีวันเปลี่ยนค่า


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

2
@Dima: ฉันยังคงรู้สึกขอบคุณที่คอมไพเลอร์จะยังคงแสดงข้อผิดพลาดหากฉันพยายามเปลี่ยนค่าโดยไม่ได้ตั้งใจในกรณีเหล่านี้ ...
Jon Skeet

3

โดยปกติคุณเริ่มต้นเครื่องบันทึกเพื่อบันทึกโดยใช้ชื่อคลาส - ซึ่งหมายความว่าหากพวกเขาไม่คงที่คุณจะจบลงด้วยการที่แต่ละอินสแตนซ์ของคลาสมีอินสแตนซ์ของมัน (หน่วยความจำสูง) แต่คนตัดไม้เหล่านี้ทั้งหมดจะ แบ่งปันการกำหนดค่าเดียวกันและทำงานเหมือนกันทุกประการ นั่นคือเหตุผลเบื้องหลังstaticบิต เนื่องจากแต่ละชื่อLoggerเริ่มต้นด้วยชื่อคลาสเพื่อป้องกันความขัดแย้งกับคลาสย่อยคุณจึงประกาศprivateดังนั้นจึงไม่สามารถสืบทอดได้ finalมาจากจุดที่คุณตามปกติไม่ได้เปลี่ยนLoggerในระหว่างการดำเนินการ - ดังนั้นเมื่อเริ่มต้นที่คุณไม่เคย "กำหนดค่าใหม่" มัน - ในกรณีที่มันทำให้ความรู้สึกที่จะทำให้มันเป็นครั้งสุดท้ายเพื่อให้แน่ใจว่าไม่มีใครสามารถเปลี่ยนได้ (โดย ผิดพลาดหรืออย่างอื่น) แน่นอนถ้าคุณจะใช้ไฟล์Loggerด้วยวิธีอื่นที่คุณอาจไม่จำเป็นต้องใช้static final- แต่ฉันอยากจะเดาว่า 80% ของแอปจะใช้การบันทึกตามที่อธิบายไว้ข้างต้น


3

ในการตอบคำถามนั้นคุณควรถามตัวเองว่า "คงที่" และ "สุดท้าย" มีไว้เพื่ออะไร

สำหรับ Logger (ฉันสมมติว่าคุณพูดถึงคลาส Log4J Logger) คุณต้องการประเภทต่อคลาส ซึ่งควรนำไปสู่การที่คุณกำหนดเพียงครั้งเดียวและไม่จำเป็นต้องมีมากกว่าหนึ่งอินสแตนซ์ต่อคลาส และสันนิษฐานว่าไม่มีเหตุผลที่จะเปิดเผยอ็อบเจ็กต์ Logger ของคลาสหนึ่งไปยังอีกคลาสหนึ่งดังนั้นทำไมไม่ทำให้เป็นแบบส่วนตัวและปฏิบัติตามหลักการ OO บางประการ

นอกจากนี้คุณควรทราบด้วยว่าคอมไพเลอร์สามารถใช้ประโยชน์จากสิ่งนั้นได้ ดังนั้นโค้ดของคุณจึงทำงานได้ดีขึ้นเล็กน้อย :)


2

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

อย่างไรก็ตามคุณยังสามารถเห็นบางครั้งคลาสของคนตัดไม้ที่ประกาศเป็นเสื้อกล้ามหรือแม้แต่เสนอฟังก์ชันคงที่เพื่อบันทึกข้อมูลของคุณ


2

โค้ดนี้มีช่องโหว่, แต่หลังจาก Java7 เราสามารถใช้Logger lgr = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); แทนสแตติกล็อกเกอร์ได้


This is code is vulnerableคุณช่วยชี้แจงคุณตอบหน่อยได้ไหม
Dmitry Zagorulkin

1

ในกรณีส่วนใหญ่คุณจะไม่เปลี่ยนข้อมูลอ้างอิงและfinalตัวปรับแต่งจะทำเครื่องหมาย คุณไม่จำเป็นต้องแยกกรณีเช่นแต่ละชั้น - staticดังนั้น และประการแรกคือเพื่อประสิทธิภาพ - สามารถปรับให้เหมาะสม (ขั้นสุดท้าย) และประหยัดหน่วยความจำ (คงที่)


1

ตามหลักการแล้ว Logger ควรเป็นดังต่อไปนี้จนถึง Java 7 สำหรับการไม่ให้ Sonar และการให้ Compliant Code: private: ไม่สามารถเข้าถึงได้จากภายนอกคลาสหลัก หากชั้นเรียนอื่นต้องการบันทึกบางสิ่งก็ควรสร้างอินสแตนซ์คนตัดไม้ของตัวเอง คงที่: ไม่ต้องขึ้นอยู่กับอินสแตนซ์ของคลาส (อ็อบเจ็กต์) เมื่อทำการบันทึกบางสิ่งแน่นอนสามารถให้ข้อมูลเชิงบริบทในข้อความได้ แต่ควรสร้างตัวบันทึกที่ระดับชั้นเรียนเพื่อป้องกันการสร้างคนตัดไม้พร้อมกับวัตถุแต่ละชิ้นและด้วยเหตุนี้จึงป้องกันรอยเท้าหน่วยความจำสูง ขั้นสุดท้าย: สร้างครั้งเดียวและครั้งเดียวต่อชั้นเรียน


0

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

...
public Logger logger = LoggerFactory.getLogger(DataSummary.class);

public String toJson() {
  GsonBuilder gsonBuilder = new GsonBuilder();   
  return gsonBuilder.create().toJsonTree(this).toString();
}
...

ในบางกรณี (เมื่อฉันใช้ไลบรารี Gson) ฉันจะได้รับข้อยกเว้น stackoverflow สถานการณ์เฉพาะของฉันคือการสร้างอินสแตนซ์ของคลาสที่มีตัวบันทึกขั้นสุดท้ายแบบไม่คงที่ จากนั้นเรียกใช้เมธอด toJson ซึ่งเรียกใช้ GsonBuilder:

...
DataSummary ds = new DataSummary(data);    
System.out.println(ds.toJson());
...

0

จริงๆแล้วเครื่องตัดไม้แบบคงที่อาจเป็น "อันตราย" ได้เนื่องจากควรจะทำงานในบริบทคงที่ เมื่อมีสภาพแวดล้อมแบบไดนามิกเช่น OSGi อาจช่วยในการใช้เครื่องตัดไม้แบบไม่คงที่ เนื่องจากการใช้งานการบันทึกบางอย่างทำการแคชของคนตัดไม้ภายใน (AFAIK อย่างน้อยก็ log4j) ผลกระทบด้านประสิทธิภาพอาจเล็กน้อย

ข้อเสียเปรียบประการหนึ่งของเครื่องตัดไม้แบบคงที่คือ การรวบรวมขยะ (เมื่อใช้คลาสเพียงครั้งเดียวเช่นในระหว่างการเริ่มต้นคนตัดไม้จะยังคงเก็บไว้รอบ ๆ )

ตรวจสอบรายละเอียดเพิ่มเติม:

ดูสิ่งนี้ด้วย:


0

ตามข้อมูลที่ฉันอ่านจากอินเทอร์เน็ตเกี่ยวกับการทำให้คนตัดไม้เป็นแบบคงที่หรือไม่แนวทางปฏิบัติที่ดีที่สุดคือใช้ตามกรณีการใช้งาน

มีสองข้อโต้แย้งหลัก:

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

2) เมื่อคุณไม่ทำให้มันคงที่มันจะถูกสร้างขึ้นสำหรับแต่ละอินสแตนซ์คลาส (การใช้หน่วยความจำ)

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

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


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