คุณสมบัติที่ซ่อนของ Java


295

หลังจากอ่านคุณสมบัติที่ซ่อนของ C #ฉันสงสัยว่าอะไรคือคุณสมบัติที่ซ่อนอยู่ของ Java?


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

1
คุณ (/ คน) ควรสรุปคำตอบอย่างเรียบร้อยในเนื้อหาคำถามเช่นคำถาม C #
ripper234

คำตอบ:


432

การเริ่มต้น Double Braceทำให้ฉันประหลาดใจเมื่อไม่กี่เดือนที่ผ่านมาเมื่อฉันค้นพบครั้งแรกไม่เคยได้ยินมาก่อน

ThreadLocalsโดยทั่วไปจะไม่รู้จักกันอย่างกว้างขวางว่าเป็นวิธีการจัดเก็บสถานะต่อเธรด

ตั้งแต่ JDK 1.5 Java ได้ได้ดำเนินการเป็นอย่างดีและเครื่องมือที่เห็นพ้องที่แข็งแกร่งกว่าเพียงแค่ล็อคพวกเขาอาศัยอยู่ในjava.util.concurrentและตัวอย่างที่น่าสนใจโดยเฉพาะเป็นjava.util.concurrent.atomicแพ็กเกจย่อยที่มีพื้นฐานด้ายปลอดภัยที่ใช้เปรียบเทียบ - and -swapและสามารถแมปกับเวอร์ชันดั้งเดิมที่สนับสนุนฮาร์ดแวร์ดั้งเดิมของการดำเนินการเหล่านี้


40
การกำหนดค่าเริ่มต้นแบบรั้งสองครั้ง ... แปลก ... ฉันต้องระวังที่จะใช้สำนวนนั้นอย่างกว้างขวางเกินไปเนื่องจากมันจะสร้างคลาสย่อยที่ไม่ระบุชื่อของวัตถุซึ่งอาจทำให้เกิดปัญหาสับสนเท่ากับ / แฮชโค้ด java.util.concurrent เป็นแพ็คเกจที่ยอดเยี่ยมอย่างแท้จริง
MB

6
ฉันได้สอน Java และนี่เป็นครั้งแรกที่ฉันได้พบกับไวยากรณ์นี้ ... ซึ่งจะแสดงให้คุณเห็นว่าไม่เคยหยุดเรียนรู้ = 8-)
Yuval

49
โปรดทราบว่าถ้าคุณยังคงมีการอ้างอิงไปยังคอลเลกชันที่เริ่มต้นด้วยสำนวน "double brace" (หรือถ้าเราเรียกมันด้วยชื่อจริง - คลาสที่มีชื่อพ้องกับ initializer block) คุณจะต้องทำการอ้างอิงไปยังวัตถุด้านนอกซึ่งอาจทำให้เกิด การรั่วไหล ฉันขอแนะนำให้หลีกเลี่ยงมันโดยสิ้นเชิง
ddimitrov

51
"การกำหนดค่าเริ่มต้นสองวงเล็บ" เป็นชื่อที่ไพเราะมากสำหรับการสร้างคลาสภายในแบบไม่ระบุชื่อการทำสิ่งที่เกิดขึ้นจริงและทำให้ดูเหมือนว่าคลาสภายในตั้งใจใช้วิธีนี้ นี่คือรูปแบบที่ฉันต้องการซ่อนอยู่
erickson

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

279

สหภาพร่วมในความแปรปรวนพารามิเตอร์ประเภท:

public class Baz<T extends Foo & Bar> {}

ตัวอย่างเช่นหากคุณต้องการใช้พารามิเตอร์ที่มีทั้งแบบเทียบเคียงและแบบสะสม:

public static <A, B extends Collection<A> & Comparable<B>>
boolean foo(B b1, B b2, A a) {
   return (b1.compareTo(b2) == 0) || b1.contains(a) || b2.contains(a);
}

เมธอดที่ถูกประดิษฐ์นี้จะคืนค่าเป็นจริงหากทั้งสองคอลเลกชันที่กำหนดมีค่าเท่ากันหรือหากหนึ่งในนั้นมีองค์ประกอบที่กำหนดมิฉะนั้นจะเป็นเท็จ จุดสังเกตคือคุณสามารถเรียกใช้เมธอดทั้ง Comparable และ Collection บนอาร์กิวเมนต์ b1 และ b2


การใช้ที่ชื่นชอบสำหรับสิ่งนี้คือเมื่อคุณต้องการวิธีการที่ยอมรับการแสดงที่น่าสนใจ
Neil Coffey

5
เป็นไปได้หรือไม่ที่จะมี "หรือ" แทนการ &?
mainstringargs

9
@rasper: ไม่มีหรือ (disjoint union) ไม่ได้ระบุไว้ในบริบทนั้น แต่คุณสามารถใช้ประเภทข้อมูลแบบแยกส่วนอย่างเช่น <A, B> แทนได้ ประเภทนี้เป็นคู่ของประเภท <A, B> ดูไลบรารี Functional Java หรือไลบรารีแกน Scala เพื่อดูตัวอย่างประเภทข้อมูลดังกล่าว
Apocalisp

5
คุณควรจะกล่าวว่าคุณต้องขยายเพียงคลาสเดียวและหลายอินเตอร์เฟส -> คลาสสาธารณะ Baz <T ขยาย Clazz & Interface1 & InterfaceI ... และไม่ใช่คลาส Baz <T ขยาย Clazz1 & ClazzI>
JohnJohnGa

220

ฉันรู้สึกประหลาดใจโดย initializers อินสแตนซ์วันอื่น ๆ ฉันกำลังลบวิธีการพับโค้ดบางส่วนและสร้างการเริ่มต้นอินสแตนซ์หลายรายการ:

public class App {
    public App(String name) { System.out.println(name + "'s constructor called"); }

    static { System.out.println("static initializer called"); }

    { System.out.println("instance initializer called"); }

    static { System.out.println("static initializer2 called"); }

    { System.out.println("instance initializer2 called"); }

    public static void main( String[] args ) {
        new App("one");
        new App("two");
  }
}

ดำเนินการmainวิธีการจะแสดง:

static initializer called
static initializer2 called
instance initializer called
instance initializer2 called
one's constructor called
instance initializer called
instance initializer2 called
two's constructor called

ฉันเดาว่าสิ่งเหล่านี้จะมีประโยชน์หากคุณมีตัวสร้างหลายตัวและต้องการรหัสทั่วไป

พวกเขายังให้น้ำตาลประโยคสำหรับการเริ่มต้นเรียนของคุณ:

List<Integer> numbers = new ArrayList<Integer>(){{ add(1); add(2); }};

Map<String,String> codes = new HashMap<String,String>(){{ 
  put("1","one"); 
  put("2","two");
}};

38
ข้อดีของการใช้วิธีนี้อย่างชัดเจนที่ต้องเรียกใช้คือถ้ามีคนเพิ่มตัวสร้างในภายหลังพวกเขาไม่จำเป็นต้องจำชื่อ call (); ที่จะทำโดยอัตโนมัติ สิ่งนี้สามารถป้องกันข้อผิดพลาดโดยโปรแกรมเมอร์ในอนาคต
Mr. Shiny และ New 安宇

40
นอกจากนี้ยังแตกต่างจากวิธี init () ซึ่งสามารถเริ่มต้นฟิลด์สุดท้าย
Darron

2
ถ้าคุณขยายชั้นเรียนและยกตัวอย่างเด็ก ๆ มันจะยังคงทำงานในลักษณะเดียวกันหรือไม่?
Kezzer

12
ผู้คนมักจะออกใบรับรอง (เช่นstackoverflow.com/questions/281100/281127#281127 ) และโดยเฉพาะอย่างยิ่งคำถามเกี่ยวกับข้อดีทางเทคนิคของพวกเขา แต่ถ้าโปรแกรมเมอร์ที่นี่ศึกษา SCJP มากกว่านี้คงไม่ถือว่าเป็น "คุณสมบัติซ่อนเร้น" มากมาย ;-)
Jonik

12
ฉันเดิมพันแม้กระทั่งหนังสือ "java ใน 24 ชั่วโมง" มีคุณลักษณะ "ชัดเจน" นี้ อ่านคนอื่น ๆ
:)

201

JDK 1.6_07 + มีแอปที่เรียกว่า VisualVM (bin / jvisualvm.exe) ซึ่งเป็น GUI ที่ดีอยู่ด้านบนของเครื่องมือมากมาย ดูเหมือนว่าครอบคลุมมากกว่า JConsole


และมีปลั๊กอิน (เป็น netbeans thingie) ที่อนุญาตให้ใช้ปลั๊กอิน Jconsole ค่อนข้างดี
Thorbjørn Ravn Andersen

VisualVM เป็นสิ่งที่ดีที่สุดตั้งแต่หั่นขนมปังจริงๆ! น่าเสียดายที่มันไม่มีคุณสมบัติทั้งหมดเมื่อใช้งานกับ JDK 1.5
sandos

ฉันพบว่า VisualVM ทำงานช้าหรือใช้ไม่ได้ในบางสถานการณ์ YourKit เชิงพาณิชย์ไม่มีปัญหานี้ แต่น่าเสียดายที่ไม่ฟรี
Roalt

Visual VM รุ่นที่ใหม่กว่าจาก JDK 1.6.0_22 และใหม่กว่านั้นได้รับการปรับปรุงให้ดีขึ้นมาก ฉันจะเดิมพัน JDK1.7 มีรุ่นที่ดียิ่งขึ้น
djangofan


156

สำหรับคนส่วนใหญ่ฉันสัมภาษณ์ตำแหน่งนักพัฒนาซอฟต์แวร์ Java ที่มีข้อความระบุว่าเป็นบล็อกที่น่าแปลกใจมาก นี่คือตัวอย่าง:

// code goes here

getmeout:{
    for (int i = 0; i < N; ++i) {
        for (int j = i; j < N; ++j) {
            for (int k = j; k < N; ++k) {
                //do something here
                break getmeout;
            }
        }
    }
}

ใครบอกว่าgotoใน java เป็นเพียงคำหลัก? :)


2
ฉันอยากจะมีตัวเลือกให้ทำหลายวิธี ตัวอย่างเช่นฉันเห็นคนใช้ System.exit (1); ในรหัส Servlet และ EJB แต่นั่นไม่ได้หมายความว่าเราควรลบ System.exit (); จาก JDK ใช่ไหม
Georgy Bolyuba

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

75
สิ่งที่ผู้เขียนโปรแกรมหลายคนไม่รู้จักโดยเฉพาะ (และอาจจะเป็นเช่นนั้น) ก็คือคุณสามารถติดป้ายกำกับและเลิกบล็อกเก่า ๆ ได้ ไม่จำเป็นต้องเป็นลูป - คุณสามารถกำหนดบล็อกที่กำหนดเองสร้างป้ายกำกับและใช้ break ด้วยก็ได้
Neil Coffey

27
มันไม่ใช่ goto เลยมันสามารถย้อนกลับไปยังการทำซ้ำก่อนหน้า (เช่น: คุณไม่สามารถข้ามไปข้างหน้าได้) นี่เป็นกลไกเดียวกับที่เกิดขึ้นเมื่อการวนซ้ำส่งคืนค่าเท็จ การบอกว่าจาวามี goto ก็เหมือนกับการพูดภาษาใด ๆ ที่คอมไพเลอร์สร้างคำสั่ง JUMP มีคำสั่ง goto
Zombies

4
ดังนั้นคุณสัมภาษณ์บนพื้นฐานของ Java เล็กน้อยมากกว่าความถนัดในการแก้ปัญหาและคิดผ่านการออกแบบ ฉันจะทำให้แน่ใจว่าฉันไม่เคยสัมภาษณ์ บริษัท ของคุณ :)
Javid Jamae

144

แล้วประเภทการคืนค่า covariantที่มีมาตั้งแต่ JDK 1.5 ล่ะ? มันเป็นการประชาสัมพันธ์ที่ไม่ดีเท่าที่ควรเพราะมันเป็นการเพิ่มที่ไม่ปลอดภัย แต่อย่างที่ฉันเข้าใจมันเป็นสิ่งที่จำเป็นอย่างยิ่งสำหรับยาสามัญประจำการ

โดยพื้นฐานแล้วคอมไพเลอร์อนุญาตให้คลาสย่อย จำกัด ประเภทการส่งคืนของเมธอด overridden ให้แคบลงเป็นประเภทย่อยของประเภทการส่งคืนของวิธีดั้งเดิม ดังนั้นจึงได้รับอนุญาต:

class Souper {
    Collection<String> values() {
        ...
    }
}

class ThreadSafeSortedSub extends Souper {
    @Override
    ConcurrentSkipListSet<String> values() {
        ...
    }
}

คุณสามารถเรียก subclass ของvaluesวิธีการและได้รับความปลอดภัยด้ายเรียงลำดับSetของStrings โดยไม่ต้องโยนลงConcurrentSkipListSetไป


คุณสามารถให้ตัวอย่างการใช้งานได้หรือไม่?
Allain Lalonde

24
ฉันใช้มันมาก โคลน () เป็นตัวอย่างที่ดี มันควรจะคืนค่า Object ซึ่งหมายความว่าคุณต้องพูดเช่น (List) list.clone () อย่างไรก็ตามหากคุณประกาศเป็น List clone () {... } แสดงว่าไม่จำเป็น
Jason Cohen

142

การถ่ายโอนการควบคุมในบล็อกในที่สุดก็ทิ้งข้อยกเว้นใด ๆ รหัสต่อไปนี้ไม่ทิ้ง RuntimeException - มันหายไป

public static void doSomething() {
    try {
      //Normally you would have code that doesn't explicitly appear 
      //to throw exceptions so it would be harder to see the problem.
      throw new RuntimeException();
    } finally {
      return;
    }
  }

จากhttp://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html


7
มันน่ารังเกียจ แต่ก็เป็นเพียงแค่ผลสืบเนื่องจากเหตุผลของวิธีการทำงานในที่สุด การควบคุมแบบลอง / จับ / ในที่สุดก็ทำในสิ่งที่ตั้งใจทำ แต่จะอยู่ในขอบเขตที่ จำกัด เท่านั้น ในทำนองเดียวกันคุณต้องระวังไม่ให้เกิดข้อยกเว้นภายใน catch / สุดท้าย block หรือคุณจะทิ้งข้อยกเว้นเดิม และถ้าคุณทำ System.exit () ภายในบล็อกลองบล็อกสุดท้ายจะไม่ถูกเรียกใช้ ถ้าคุณทำลายการไหลปกติคุณทำลายการไหลปกติ ...
นีลเบนจามิน

อย่าใช้ในที่สุด {return; } แต่สุดท้ายก็ {code โดยไม่ส่งคืน} นี่เป็นเหตุผลในที่สุดก็ตั้งใจที่จะดำเนินการเมื่อมีข้อยกเว้นเกิดขึ้นและความหมายเดียวที่เป็นไปได้ของการส่งคืนเป็นตัวจัดการข้อยกเว้นจะต้องละเว้นข้อยกเว้นและ - แน่นอน - ผลตอบแทน
extraneon

21
ดูเหมือนว่า "gotcha" มากกว่าฟีเจอร์ที่ซ่อนอยู่แม้ว่าจะมีหลายวิธีที่สามารถใช้เป็นหนึ่งได้การใช้วิธีนี้จะไม่ใช่ความคิดที่ดี
davenpcj

Eclipse ส่งเสียงเตือนหากคุณทำสิ่งนี้ ฉันอยู่กับ Eclipse หากคุณต้องการตรวจสอบข้อยกเว้น ... จากนั้นตรวจสอบข้อยกเว้น
helios

นั่นคือสิ่งที่คุณจ่ายสำหรับรหัสสกปรกที่กลับมาจากวิธีการกลาง แต่ก็ยังเป็นตัวอย่างที่ดี
Rostislav Matl

141

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

แทน:

if( null != aObject && aObject instanceof String )
{
    ...
}

เพียงใช้:

if( aObject instanceof String )
{
    ...
}

9
มันเป็นความอัปยศที่เป็นคุณลักษณะที่ไม่ค่อยมีใครรู้จัก ฉันเห็นรหัสจำนวนมากคล้ายกับรหัสแรก
Peter Dolberg

4
นั่นเป็นเพราะ Java มีชนิด null พิเศษ (ซ่อนอยู่) ที่คุณไม่เห็นและไม่สามารถอ้างอิงได้
Nick Hristov

สิ่งที่แย่กว่านั้นคือการตรวจสอบโมฆะก่อนที่จะเข้าfreeหรือออกdeleteใน C / C ++ แนวคิดพื้นฐานดังกล่าว
Thomas Eding

134

การอนุญาตให้วิธีการและผู้สร้างใน enums ทำให้ฉันประหลาดใจ ตัวอย่างเช่น:

enum Cats {
  FELIX(2), SHEEBA(3), RUFUS(7);

  private int mAge;
  Cats(int age) {
    mAge = age;
  }
  public int getAge() {
    return mAge;
   }
}

คุณยังสามารถมี "คลาสเฉพาะค่าคงที่" ซึ่งอนุญาตให้ค่า enum เฉพาะเพื่อแทนที่เมธอด

เอกสารเพิ่มเติมที่นี่


20
คุณลักษณะที่ยอดเยี่ยมจริง ๆ ทำให้ enums OO และแก้ปัญหาการเริ่มต้นจำนวนมากในวิธีที่สะอาด
Bill K

5
นับเป็นซิงเกิลเหรอ? Enum มีค่าเดียวเท่านั้น?
Georgy Bolyuba

6
Georgy: Singleton เป็นตัวอย่างหนึ่งของวัตถุไม่ใช่วัตถุที่มีค่าเดียว
dshaw

20
@Georgy: ดูรายการ 3 ในจาวาที่มีประสิทธิภาพของ Joshua Bloch (2nd ed) "ในขณะที่วิธีการนี้ยังไม่ได้นำมาใช้อย่างกว้างขวางประเภท enum องค์ประกอบเดียวเป็นวิธีที่ดีที่สุดในการใช้งานซิงเกิลตัน"
Jonik

3
nitpick เล็กน้อย: mAgeควรเป็นที่สิ้นสุด ไม่ค่อยมีเหตุผลสำหรับ felds ที่ไม่ใช่คนสุดท้ายใน enums
Joachim Sauer

121

พารามิเตอร์ประเภทสำหรับวิธีการทั่วไปสามารถระบุได้อย่างชัดเจนเช่น:

Collections.<String,Integer>emptyMap()

10
และโดยพระเจ้ามันน่าเกลียดและสับสน และไม่เกี่ยวข้องกับประเภทความปลอดภัย
Chris Broadfoot

8
ฉันรักสิ่งนี้. มันทำให้โค้ดของคุณชัดเจนยิ่งขึ้นและชัดเจนยิ่งขึ้นสำหรับคนยากจนที่จะต้องดูแลมันในปีหรือสองปี
extraneon

6
public static <T> T foo(T t)นี้เป็นจริงที่มีประโยชน์มากในสถานการณ์ที่คุณได้ประกาศวิธีการทั่วไปคงที่เช่น จากนั้นคุณสามารถโทรไปที่Class.<Type>foo(t);
Finbarr

ด้วยเหตุผลบางอย่างนี้ดูเหมือนจะไม่ทำงานกับวิธีการนำเข้าแบบคงที่ .. ฉันสงสัยว่าทำไม
oksayt

สิ่งนี้มีประโยชน์โดยเฉพาะอย่างยิ่งเมื่อใช้ ifs ที่ประกอบไปด้วยผลตอบแทน return set1.equals(set2) ? new ArrayList<String>(set1) : Collections.<String>emptyList()เช่น นอกจากนี้ยังมีประโยชน์สำหรับการเรียกใช้เมธอดบางอย่างที่ Collections.emptyMap แบบง่าย () จะให้ข้อผิดพลาดในการคอมไพล์
Andreas Holstenson

112

คุณสามารถใช้ enums เพื่อนำอินเตอร์เฟสมาใช้

public interface Room {
   public Room north();
   public Room south();
   public Room east();
   public Room west();
}

public enum Rooms implements Room {
   FIRST {
      public Room north() {
         return SECOND;
      }
   },
   SECOND {
      public Room south() {
         return FIRST;
      }
   }

   public Room north() { return null; }
   public Room south() { return null; }
   public Room east() { return null; }
   public Room west() { return null; }
}

แก้ไข: ปีต่อมา ....

ฉันใช้คุณสมบัตินี้ที่นี่

public enum AffinityStrategies implements AffinityStrategy {

https://github.com/peter-lawrey/Java-Thread-Affinity/blob/master/src/main/java/vanilla/java/affinity/AffinityStrategies.java

โดยใช้อินเทอร์เฟซผู้พัฒนาสามารถกำหนดกลยุทธ์ของตนเอง การใช้enumวิธีการที่ฉันสามารถกำหนดคอลเลกชัน (ห้า) ในตัว


27
นี่คือความบ้า. (+1)
Cephalopod

12
@Arian นี่ไม่ใช่ความบ้าคลั่ง นี้. คือ. JAVAAAAAAHHHH!
slezica

1
WHAAAAAT ไม่ฉันอยู่กับเอเดรีย นี่ไม่ใช่จาวา นี่คือความบ้า. ฉันกำลังจะตายเพื่อใช้สิ่งนี้
slezica

104

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

public void foo(String... bars) {
   for (String bar: bars)
      System.out.println(bar);
}

บาร์จะถูกแปลงเป็นประเภทที่ระบุโดยอัตโนมัติ ไม่ใช่การชนะครั้งใหญ่ แต่เป็นการชนะอย่างไรก็ตาม


23
สิ่งที่สำคัญเกี่ยวกับเรื่องนี้เมื่อโทรวิธีการที่คุณสามารถเขียน: foo ( "ครั้งแรก", "สอง", "สาม")
สตีฟอาร์มสตรอง

9
เพื่อสวัสดีโลกเก่าสามารถเขียนใหม่; โมฆะคงที่สาธารณะหลัก (String ... args) {System.out.println ("Hello World!"); }
Karussell

@ Karussell บางที แต่ดูเหมือนว่าข้อโต้แย้งจะไม่เกี่ยวข้อง: p
Kelly Elton

93

สิ่งที่ฉันชอบ: ทิ้งร่องรอยสแต็กสแต็กทั้งหมดให้เป็นมาตรฐาน

windows: CTRL- Breakในหน้าต่างjava cmd / console ของคุณ

ยูนิกซ์: kill -3 PID


15
ctrl- \ ใน Unix ด้วยเช่นกัน หรือใช้ jstack จาก JDK
Tom Hawtin - tackline

9
ขอบคุณคุณเพิ่งสอนฉันแป้นพิมพ์ของฉันมีBreakกุญแจ
Amy B

2
บน windows CTRL-BREAK จะทำงานเฉพาะเมื่อคุณมีกระบวนการทำงานในหน้าต่างคอนโซลปัจจุบัน คุณสามารถใช้ JAVA_HOME / bin / jstack.exe แทน เพียงระบุรหัสกระบวนการของ Windows
Javid Jamae

ฉันคิดว่ามันฆ่า -SIGQUIT
Nick Hristov

@ ไม่แน่ใจใช่ SIGQUIT มักเป็นสัญญาณ # 3
Chris Mazzola

89

มีคนสองคนโพสต์เกี่ยวกับอินสแตนซ์ initializers ต่อไปนี้เป็นประโยชน์อย่างมากสำหรับมัน:

Map map = new HashMap() {{
    put("a key", "a value");
    put("another key", "another value");
}};

เป็นวิธีที่รวดเร็วในการเริ่มต้นแผนที่หากคุณเพิ่งทำสิ่งที่รวดเร็วและง่ายดาย

หรือใช้มันเพื่อสร้างต้นแบบเฟรมการแกว่งอย่างรวดเร็ว:

JFrame frame = new JFrame();

JPanel panel = new JPanel(); 

panel.add( new JLabel("Hey there"){{ 
    setBackground(Color.black);
    setForeground( Color.white);
}});

panel.add( new JButton("Ok"){{
    addActionListener( new ActionListener(){
        public void actionPerformed( ActionEvent ae ){
            System.out.println("Button pushed");
        }
     });
 }});


 frame.add( panel );

แน่นอนมันสามารถถูกทารุณกรรม:

    JFrame frame = new JFrame(){{
         add( new JPanel(){{
               add( new JLabel("Hey there"){{ 
                    setBackground(Color.black);
                    setForeground( Color.white);
                }});

                add( new JButton("Ok"){{
                    addActionListener( new ActionListener(){
                        public void actionPerformed( ActionEvent ae ){
                            System.out.println("Button pushed");
                        }
                     });
                 }});
        }});
    }};

อย่างใดมันเป็นบิตราวกับว่ามีการเพิ่มคำหลัก "กับ" (ฟังก์ชั่นจริง) ใน Java มีประโยชน์มากเมื่อเร็ว ๆ นี้ที่ฉันทำเสียงฮึดฮัดเพราะไม่สามารถเริ่มต้นอาร์เรย์ได้ ขอบคุณ!
PhiLho

15
มีหนึ่งผลข้างเคียงของการใช้สิ่งนี้แม้ว่า วัตถุที่ไม่ระบุชื่อถูกสร้างขึ้นซึ่งอาจไม่ได้ดีเสมอ
amit

17
แม้ว่าหลังจากดูเพิ่มเติมแล้วฉันต้องบอกว่าฉันสนใจสิ่งที่ทำรังตามธรรมชาติอย่างแปลกประหลาดแม้กระทั่งเวอร์ชัน "ทำร้าย"
Bill K

4
อ๋อ - ฉันค่อนข้างชอบเวอร์ชัน 'ถูกทารุณกรรม' ฉันคิดว่ามันชัดเจนและอ่านง่าย แต่อาจเป็นแค่ฉัน
barryred

3
เห็นด้วยอย่างแน่นอนลำดับชั้นของโค้ดที่แสดงถึงลำดับชั้นของ gui นั้นเป็นการปรับปรุงที่ใหญ่กว่าลำดับของการเรียกเมธอด
opsb

88

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


10
พร็อกซีแบบไดนามิกเป็นเหตุผลที่ดีในการเลือกเขียนอินเทอร์เฟซ Foo และใช้งานได้ทุกที่ด้วยคลาส "FooImpl" ที่เป็นค่าเริ่มต้น มันอาจดูน่าเกลียดในตอนแรก ("ทำไมไม่เพียงแค่มีคลาสที่เรียกว่า Foo") แต่ประโยชน์ในแง่ความยืดหยุ่นในอนาคตและความสามารถในการจำลองในการทดสอบหน่วยนั้นมีประโยชน์ ในขณะที่มีวิธีที่จะทำเช่นนั้นสำหรับอินเทอร์เฟซที่ไม่ใช่พวกเขามักจะต้องการสิ่งพิเศษเช่น cblib
Darien

82

สามารถเลื่อนการเริ่มต้นขั้นสุดท้ายได้

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

public Object getElementAt(int index) {
    final Object element;
    if (index == 0) {
         element = "Result 1";
    } else if (index == 1) {
         element = "Result 2";
    } else {
         element = "Result 3";
    }
    return element;
}

น่าประหลาดใจมาก หนึ่งสามารถพูดได้อย่างเป็นธรรมว่า "ค่าของตัวแปรสุดท้ายสามารถตั้งค่าได้ครั้งเดียว" ไม่ว่าจะเกิดขึ้นที่ใด?
David Koelle

29
ใช่ แต่ยิ่งกว่านี้: "ค่าของตัวแปรสุดท้ายจะต้องตั้งค่าหนึ่งครั้ง"
Allain Lalonde

6
+1 เห็นด้วยนี่เป็นเครื่องมือที่มีค่าอีกตัวหนึ่งสำหรับการระบุข้อผิดพลาดในเวลารวบรวมและสิ่งหนึ่งที่โปรแกรมเมอร์ดูเหมือนจะอายที่จะใช้ด้วยเหตุผลบางอย่าง โปรดทราบว่าเนื่องจากจาก Java 5 เป็นต้นไป 'final' ยังมีความหมายของความปลอดภัยของเธรดด้วยเช่นกันความสามารถในการตั้งค่าตัวแปรขั้นสุดท้ายในระหว่างตัวสร้างมีค่า
Neil Coffey

4
แม้ว่าวิธีการเฉพาะนี้ฉันจะใช้ผลตอบแทนหลายรายการ ในความเป็นจริงในกรณีส่วนใหญ่ที่มีการใช้ฉันอาจ refactor มันเป็นวิธีที่แยกต่างหากและใช้ผลตอบแทนหลาย ๆ
ripper234

ฉันรักสิ่งนี้! ฉันลบคำหลักสุดท้ายเสมอเพราะฉันคิดว่ามันจะล้มเหลว ขอบคุณ!
KARASZI István

62

ฉันคิดว่าฟีเจอร์อื่นที่“ มองข้าม” ของจาวาคือ JVM มันน่าจะเป็น VM ที่ดีที่สุด และรองรับภาษาที่น่าสนใจและมีประโยชน์มากมาย (Jython, JRuby, Scala, Groovy) ทุกภาษาเหล่านั้นสามารถร่วมมือกันได้อย่างง่ายดายและราบรื่น

หากคุณออกแบบภาษาใหม่ (เช่นใน scala-case) คุณจะต้องมีไลบรารีที่มีอยู่ทั้งหมดทันทีดังนั้นภาษาของคุณจึงมี "ประโยชน์" ตั้งแต่เริ่มต้น

ทุกภาษาเหล่านั้นใช้ประโยชน์จากการปรับให้เหมาะสมที่สุดของ HotSpot VM นั้นตรวจสอบและ debuggable ได้เป็นอย่างดี


18
ไม่จริงมันไม่ใช่ VM ที่ดีมาก มันถูกออกแบบมาเพื่อใช้งาน JAVA แต่เพียงผู้เดียว ภาษาไดนามิกและการทำงานที่ปกติจะทำงานได้ไม่ดีนัก สิ่งที่คุณต้องการใช้ VM คุณควรใช้. NET / Mono ที่ถูกออกแบบมาเพื่อทำงานร่วมกับทุกภาษา ...
Hades32

14
จริงๆแล้ว JVM ได้รับการออกแบบมาเพื่อรัน Java Bytecodes เท่านั้น คุณสามารถรวบรวมภาษาที่ทันสมัยที่สุดไปยัง Java Bytecode เกี่ยวกับสิ่งเดียวที่ Java Bytecode ขาดคือการสนับสนุนภาษาแบบไดนามิกตัวชี้และการสนับสนุนการเรียกซ้ำแบบหาง
mcjabberz

12
@ Hades32: จริง ๆ แล้ว. NET VM นั้นค่อนข้างคล้ายกับ JVM มันเพิ่งรองรับภาษาไดนามิกที่ค่อนข้างเร็ว ๆ นี้ (ด้วย DLR) และ Java 7 กำลังจะได้รับการสนับสนุนเช่นกัน และคลาสสิก "ทุกภาษา" ของ. NET (C #, Visual Basic.NET, ... ) ล้วนมีชุดคุณสมบัติที่เหมือนกัน
Joachim Sauer

13
JVM ไม่สนับสนุน generics ในขณะที่. NET VM ทำ JVM อยู่ใกล้ที่ดีที่สุด
Blindy

3
เพื่อลดข้อร้องเรียนเกี่ยวกับคุณสมบัติภาษาเฉพาะที่ไม่ได้รับการสนับสนุนโดยตรงจาก JVM ... แต่ฉันมักจะคิดว่ามีเสถียรภาพความสอดคล้องข้ามแพลตฟอร์มและประสิทธิภาพที่ดีนั้นอยู่ไกลและเหนือเหตุผลที่ JVM ได้รับคะแนนพิเศษ ฉันทำงานกับฝั่งเซิร์ฟเวอร์ Java เป็นเวลาหลายปีแล้วในหลาย ๆ แพลตฟอร์ม (รวมถึง AS / 400) และฉันก็สามารถลืมมันได้อย่างสิ้นเชิง - ข้อผิดพลาดนั้นเป็นรหัสที่ฉันสามารถแก้ไขได้เสมอ เพียงแค่ไม่ผิดพลาด
Rob Whelan

58

คุณสามารถกำหนดคลาสย่อยที่ไม่ระบุชื่อและเรียกเมธอดได้โดยตรงแม้ว่าจะไม่มีส่วนต่อประสาน

new Object() {
  void foo(String s) {
    System.out.println(s);
  }
}.foo("Hello");

@Vuntic - อนุญาตให้คุณกำหนดคลาสแบบง่าย ๆ ภายในบริบทที่จำเป็น
ChaosPandion

1
@Chaos แต่ทำไม มีตัวอย่างจริงที่มีประโยชน์หรือไม่
Thorbjørn Ravn Andersen

10
@Wouter - ในกรณีนั้นวิธีการที่เรียกว่าบนวัตถุที่ไม่ระบุชื่อ ( start()) ไม่ได้กำหนดไว้ในคลาสย่อยจริง ๆ ...
Axel

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

56

asListวิธีการในการjava.util.Arraysช่วยให้การรวมกันที่ดีของ varargs วิธีการทั่วไปและ autoboxing:

List<Integer> ints = Arrays.asList(1,2,3);

15
คุณต้องการที่จะตัดรายชื่อกลับมาพร้อมกับรายการนวกรรมิกขนาดมิฉะนั้น ints จะได้รับการแก้ไข (เพราะมันมีการสนับสนุนจากอาร์เรย์)
KitsuneYMG

2
Arrays.asListมีลักษณะที่ผิดปกติที่คุณสามารถset()องค์ประกอบ แต่ไม่หรือadd() remove()ดังนั้นฉันมักจะห่อในnew ArrayList(...)หรือCollections.unmodifiableList(...)ขึ้นอยู่กับว่าฉันต้องการแก้ไขรายการหรือไม่
Christian Semrau

53

การใช้คำสำคัญนี้เพื่อเข้าถึงฟิลด์ / วิธีการบรรจุคลาสจากคลาสภายใน ในตัวอย่างด้านล่างนี้เราต้องการใช้เขตข้อมูล sortAscending ของคลาสคอนเทนเนอร์จากคลาสภายในที่ไม่ระบุชื่อ ใช้ ContainerClass.this.sortAscending แทน this.sortAscending ไม่หลอกลวง

import java.util.Comparator;

public class ContainerClass {
boolean sortAscending;
public Comparator createComparator(final boolean sortAscending){
    Comparator comparator = new Comparator<Integer>() {

        public int compare(Integer o1, Integer o2) {
            if (sortAscending || ContainerClass.this.sortAscending) {
                return o1 - o2;
            } else {
                return o2 - o1;
            }
        }

    };
    return comparator;
}
}

4
จำเป็นเฉพาะเมื่อคุณแชโดว์ชื่อ (ในกรณีของคุณด้วยชื่อพารามิเตอร์เมธอด) หากคุณเรียกอาร์กิวเมนต์เป็นอย่างอื่นคุณสามารถเข้าถึงตัวแปรสมาชิก sortAscending ของคลาส Container โดยตรงโดยไม่ต้องใช้ 'this'
sk.

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

มักจะใช้ในการพัฒนารูปแบบของ Android MyActivity.thisในการสั่งซื้อที่จะได้รับบริบทจากการพูดฟังปุ่มเพียงปุ่มเดียวด้วย
espinchi

52

ไม่ใช่คุณสมบัติจริงๆ แต่เป็นเคล็ดลับที่สนุกที่ฉันค้นพบเมื่อเร็ว ๆ นี้ในบางเว็บเพจ:

class Example
{
  public static void main(String[] args)
  {
    System.out.println("Hello World!");
    http://Phi.Lho.free.fr

    System.exit(0);
  }
}

เป็นโปรแกรม Java ที่ถูกต้อง (แม้ว่าจะสร้างคำเตือน) หากคุณไม่เห็นสาเหตุให้ดูคำตอบของ Gregory! ;-) การเน้นไวยากรณ์ที่นี่ก็ให้คำแนะนำด้วย!


15
เรียบร้อยฉลากที่มีความคิดเห็น :)
Thorbjørn Ravn Andersen

46

นี่ไม่ใช่ "คุณสมบัติที่ซ่อนอยู่" และไม่มีประโยชน์มาก แต่อาจเป็นที่น่าสนใจอย่างมากในบางกรณี:
คลาส sun.misc.Unsafe - จะช่วยให้คุณสามารถใช้การจัดการหน่วยความจำโดยตรงใน Java (คุณสามารถเขียนโค้ด Java ที่ปรับเปลี่ยนได้เองด้วย ถ้าคุณลองมาก):

public class UnsafeUtil {

    public static Unsafe unsafe;
    private static long fieldOffset;
    private static UnsafeUtil instance = new UnsafeUtil();

    private Object obj;

    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);

            unsafe = (Unsafe)f.get(null);
            fieldOffset = unsafe.objectFieldOffset(UnsafeUtil.class.getDeclaredField("obj"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}

20
นั่นคืออาทิตย์ * API ซึ่งไม่ได้เป็นส่วนหนึ่งของภาษา Java ต่อ se
DW

มีวิธีแปลก ๆ อีกมากมายที่ไม่ปลอดภัยเช่นการสร้างวัตถุโดยไม่เรียกตัวสร้างวิธีการสไตล์ malloc / realloc / free
Peter Lawrey

ฉันรักเอสเซ้นต์โดยเฉพาะอย่างยิ่ง getters ดั้งเดิมและ setters ในตำแหน่งหน่วยความจำเช่น putDouble (ที่อยู่ยาวสองเท่า)
Daniel

42

เมื่อทำงานในสวิงผมชอบที่ซ่อนอยู่Ctrl- Shift- F1คุณลักษณะ

มันทิ้งต้นไม้องค์ประกอบของหน้าต่างปัจจุบัน
(สมมติว่าคุณไม่ได้กดแป้นพิมพ์นั้นกับสิ่งอื่น)


1
เป็นไปได้มากว่าตัวจัดการหน้าต่างของคุณมีบางสิ่งที่ผูกติดกับคีย์ Gnome ไม่ได้ผูกมันดังนั้นฉันสมมติว่าคุณกำลังใช้ KDE ซึ่งผูกไว้กับ 'เปลี่ยนเป็นเดสก์ท็อป 13' คุณสามารถเปลี่ยนได้โดยไปที่แผงควบคุมภูมิภาคแป้นพิมพ์ลัดและลบการแมปสำหรับ Shift-Ctrl-F1
Devon_C_Miller


38

การลงคะแนนของฉันไปที่java.util.concurrentพร้อมด้วยคอลเลกชันที่เกิดขึ้นพร้อมกันและตัวเรียกใช้งานที่ยืดหยุ่นช่วยให้กลุ่มเธรดพูลงานกำหนดเวลาและงานประสานงานอื่น ๆ The DelayQueue เป็นรายการโปรดส่วนตัวของฉันซึ่งองค์ประกอบต่างๆจะมีให้หลังจากการล่าช้าที่ระบุ

java.util.Timer และ TimerTask อาจถูกพักอย่างปลอดภัย

นอกจากนี้ยังไม่ถูกซ่อนอยู่อย่างแน่นอน แต่อยู่ในแพ็คเกจอื่นจากคลาสอื่น ๆ ที่เกี่ยวข้องกับวันที่และเวลา java.util.concurrent.TimeUnit มีประโยชน์เมื่อแปลงระหว่างนาโนวินาที, ไมโครวินาที, มิลลิวินาทีและวินาที

มันอ่านดีกว่าค่าปกติบางอย่าง * 1000 หรือ someValue / 1000


ค้นพบเมื่อเร็ว ๆ นี้CountDownLatchและCyclicBarrier- เพื่อประโยชน์!
Raphael

37

คำหลักยืนยันระดับภาษา


19
ปัญหาเกี่ยวกับการยืนยันคือต้องเปิดเครื่องระหว่างรันไทม์
extraneon

10
แต่ถ้ามันถูกปิดการใช้งานมันไม่เหมือนอยู่ที่นั่น คุณสามารถเพิ่มการยืนยันได้มากเท่าที่คุณต้องการในรหัสของคุณและคุณจะไม่ได้รับโทษใด ๆ หากปิดการใช้งาน
Ravi Wallau

5
ฉันเชื่อว่าเป็นสิ่งที่ดีเกี่ยวกับการยืนยัน: สามารถปิดได้โดยไม่มีการลงโทษ
andref

ปัญหาคือถ้าคุณใช้ผลข้างเคียงในการยืนยันโดยไม่ได้ตั้งใจคุณจะต้องเปิดใช้งานการยืนยันเพื่อให้โปรแกรมของคุณทำงาน ...
Chii

ในการตรวจสอบว่าเปิดใช้งาน asserts อยู่หรือไม่คุณสามารถใช้ {boolean assertsOn = false; ยืนยัน assertsOn = จริง; if (assertsOn) {/ * การตรวจสอบที่ซับซ้อน * /}} นอกจากนี้ยังอนุญาตให้เข้าสู่ระบบหรือโยนข้อยกเว้นหากสถานะการยืนยันไม่เป็นไปตามที่คาดไว้
fernacolo

37

ไม่ใช่ส่วนหนึ่งของภาษา Java แต่ javap disassembler ซึ่งมาพร้อมกับ JDK ของ Sun นั้นไม่เป็นที่รู้จักหรือใช้กันอย่างแพร่หลาย


36

การเพิ่มโครงสร้างวนรอบสำหรับแต่ละรายการใน 1.5 ฉัน <3 มัน

// For each Object, instantiated as foo, in myCollection
for(Object foo: myCollection) {
  System.out.println(foo.toString());
}

และสามารถใช้ในอินสแตนซ์ที่ซ้อนกัน:

for (Suit suit : suits)
  for (Rank rank : ranks)
    sortedDeck.add(new Card(suit, rank));

การสร้างสำหรับแต่ละยังใช้กับอาร์เรย์ซึ่งซ่อนตัวแปรดัชนีมากกว่าตัววนซ้ำ วิธีการต่อไปนี้จะคืนค่าผลรวมของค่าในอาร์เรย์ int:

// Returns the sum of the elements of a
int sum(int[] a) {
  int result = 0;
  for (int i : a)
    result += i;
  return result;
}

เชื่อมโยงไปยังเอกสารของซัน


30
ฉันคิดว่าการใช้iที่นี่มีความสับสนอย่างมากเนื่องจากคนส่วนใหญ่คาดหวังว่าฉันจะเป็นดัชนีและไม่ใช่องค์ประกอบอาร์เรย์
cdmckay

ดังนั้น ... int sum (int [] array) {int result = 0; สำหรับ (องค์ประกอบ int: อาร์เรย์) {ผลลัพธ์ + = องค์ประกอบ; } ผลตอบแทน; }
Drew

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

6
มันเป็นความอัปยศที่มันใช้ไม่ได้กับ Enumerations เช่นเดียวกับที่ใช้ใน JNDI มันกลับไปที่การวนซ้ำที่นั่น
extraneon

3
@extraneon: ดูที่ Collections.list (การแจกแจง <T> e) ที่ควรช่วยระบุการวนซ้ำในลูป foreach
เวอร์เนอร์เลห์มันน์

34

ฉันค้นพบโดยส่วนตัวjava.lang.Voidช้ามาก - ปรับปรุงการอ่านโค้ดร่วมกับยาชื่อสามัญเช่นCallable<Void>


2
ไม่มาก ในบางจุดคุณจะต้องเจาะจง: Executors.newSingleThreadExecutor (). ส่ง (Callable <Void> () {.. }) ใหม่ - คุณไม่สามารถยกตัวอย่าง Callable <?> () ใหม่ได้คุณต้องระบุ ประเภทที่ชัดเจน - จึงเป็นทางเลือกแทน Callable <Object>
Rahel Lüthy

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