Java: บล็อกการเริ่มต้นแบบคงที่มีประโยชน์เมื่อใด


96

อะไรคือความแตกต่างระหว่างการเริ่มต้นภายในstaticบล็อก:

public class staticTest {

    static String s;
    static int n;
    static double d;

    static {
        s = "I'm static";
        n = 500;
        d = 4000.0001;
    }
    ...

และการเริ่มต้นแบบคงที่ของแต่ละบุคคล:

public class staticTest {

    static String s = "I'm static";
    static int n    = 500;
    static double d = 4000.0001;

    ....

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

เป็นสถานที่ที่ดีในการโหลดคลาสหรือโหลดไลบรารีดั้งเดิม
qrtt1

1
โปรดทราบว่าควรหลีกเลี่ยงตัวแปรคงที่ดังนั้นบล็อกการเริ่มต้นแบบคงที่โดยทั่วไปจึงไม่ใช่ความคิดที่ดี หากคุณพบว่าตัวเองใช้งานบ่อยครั้งอาจมีปัญหาได้
Bill K

คำตอบ:


113

บล็อกการเริ่มต้นแบบคงที่ช่วยให้การเตรียมใช้งานที่ซับซ้อนมากขึ้นตัวอย่างเช่นการใช้เงื่อนไข:

static double a;
static {
    if (SomeCondition) {
      a = 0;
    } else {
      a = 1;
    }
}

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

บล็อกการเริ่มต้นแบบคงที่ยังทำงานหลังจากตัวเริ่มต้นแบบคงที่แบบอินไลน์ดังนั้นสิ่งต่อไปนี้จึงถูกต้อง:

static double a;
static double b = 1;

static {
    a = b * 4; // Evaluates to 4
}

3
กำลังทำ "b = a * 4;" อินไลน์จะเป็นปัญหาก็ต่อเมื่อมีการประกาศ b ก่อน a ซึ่งไม่ใช่กรณีในตัวอย่างของคุณ
George Hawkins

1
@GeorgeHawkins ฉันแค่พยายามแสดงให้เห็นว่าตัวเริ่มต้นแบบคงที่ทำงานหลังจากตัวเริ่มต้นแบบอินไลน์ไม่ใช่ว่าการเทียบเท่าจะไม่สามารถทำได้แบบอินไลน์ อย่างไรก็ตามฉันเข้าใจประเด็นของคุณและได้อัปเดตตัวอย่างเพื่อ (หวังว่า) จะชัดเจนขึ้น
Rich O'Kelly

1
เพื่อความสนุกฉันอาจชี้ให้เห็นว่าตัวอย่างแรกของคุณอาจเป็น "static double a = someCondition? 0: 1;" ไม่ใช่ว่าตัวอย่างของคุณไม่ดีฉันแค่บอกว่า ... :)
Bill K

18

การใช้งานทั่วไป:

private final static Set<String> SET = new HashSet<String>();

static {
    SET.add("value1");
    SET.add("value2");
    SET.add("value3");
}

คุณจะทำอย่างไรหากไม่มีตัวเริ่มต้นคงที่


2
ตอบฝรั่ง :) +1
Paul Bellora

2
คำตอบอื่นที่ไม่มีไลบรารีเพิ่มเติม: สร้างวิธีการแบบคงที่ที่ห่อหุ้มการเริ่มต้นSETและใช้ตัวแปร initializer ( private final static Set<String> SET = createValueSet()) จะเป็นอย่างไรถ้าคุณมี 5 ชุดและ 2 แผนที่คุณจะทิ้งทั้งหมดลงในstaticบล็อกเดียวหรือไม่?
TWiStErRob

16

คุณสามารถใช้ try / catch block ภายในstatic{}ดังต่อไปนี้:

MyCode{

    static Scanner input = new Scanner(System.in);
    static boolean flag = true;
    static int B = input.nextInt();
    static int H = input.nextInt();

    static{
        try{
            if(B <= 0 || H <= 0){
                flag = false;
                throw new Exception("Breadth and height must be positive");
            }
        }catch(Exception e){
            System.out.println(e);
        }

    }
}

PS: อ้างอิงจากสิ่งนี้ !


12

การจัดการข้อยกเว้นระหว่างการเริ่มต้นเป็นอีกสาเหตุหนึ่ง ตัวอย่างเช่น:

static URL url;
static {
    try {
        url = new URL("https://blahblah.com");
    }
    catch (MalformedURLException mue) {
        //log exception or handle otherwise
    }
}

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


5

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


4

ในตัวอย่างของคุณไม่มีความแตกต่าง แต่บ่อยครั้งค่าเริ่มต้นมีความซับซ้อนมากกว่าที่จะแสดงออกอย่างสะดวกสบายในนิพจน์เดียว (เช่นซึ่งเป็นList<String>เนื้อหาที่แสดงได้ดีที่สุดโดยfor-loop หรือMethodอาจไม่มีอยู่ดังนั้นจึงจำเป็นต้องมีตัวจัดการข้อยกเว้น) และ / หรือ ต้องตั้งค่าฟิลด์คงที่ตามลำดับที่เฉพาะเจาะจง



3

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

แน่นอนฉันเกือบจะสร้างสถิตยศาสตร์finalและชี้ไปที่วัตถุที่ไม่สามารถปรับเปลี่ยนได้


3

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

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

ตัวอย่าง:

    class Shoe {
    int size;
    String colour;
    static String brand = "Nike";

    public Shoe(int size, String colour) {
        super();
        this.size = size;
        this.colour = colour;
    }

    void displayShoe() {
        System.out.printf("%-2d %-8s %s %n",size,colour, brand);
    }

    public static void main(String args[]) {
        Shoe s1 = new Shoe(7, "Blue");
        Shoe s2 = new Shoe(8, "White");

        System.out.println("=================");
        s1.displayShoe();
        s2.displayShoe();
        System.out.println("=================");
    }
}

1

บล็อกโค้ดแบบคงที่ช่วยให้สามารถเริ่มต้นฟิลด์ที่มีมากกว่า instuction เริ่มต้นฟิลด์ตามลำดับที่แตกต่างกันของการประกาศและยังสามารถใช้สำหรับ intialization แบบมีเงื่อนไข

โดยเฉพาะอย่างยิ่ง,

static final String ab = a+b;
static final String a = "Hello,";
static final String b = ", world";

จะไม่ทำงานเนื่องจาก a และ b ถูกประกาศหลังจาก ab

อย่างไรก็ตามฉันสามารถใช้การเริ่มต้นแบบคงที่ บล็อกเพื่อเอาชนะสิ่งนี้

static final String ab;
static final String a;
static final String b;

static {
  b = ", world";
  a = "Hello";
  ab = a + b;
}

static final String ab;
static final String a;
static final String b;

static {
  b = (...) ? ", world" : ", universe";
  a = "Hello";
  ab = a + b;
}

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

1

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

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

static Scanner input = new Scanner(System.in);
static int widht;
static int height;

static
{
    widht = input.nextInt();
    input.nextLine();
    height = input.nextInt();
    input.close();

    if ((widht < 0) || (height < 0))
    {
        System.out.println("java.lang.Exception: Width and height must be positive");
    }
    else
    {
        System.out.println("widht * height = " + widht * height);
    }
}

การอ่าน stdin ในตัวเริ่มต้นแบบคงที่เป็นความคิดที่แย่มาก และSystem.out.println("B * H");ค่อนข้างไร้ประโยชน์ และคำตอบนั้นค่อนข้างคลุมเครือ OP ไม่ได้กล่าวถึงตัวสร้างหรือตัวแปรอินสแตนซ์
shmosel

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

0

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


0

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

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

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