ควรลอง ... จับไปข้างในหรือนอกวง?


185

ฉันมีห่วงที่มีลักษณะเช่นนี้:

for (int i = 0; i < max; i++) {
    String myString = ...;
    float myNum = Float.parseFloat(myString);
    myFloats[i] = myNum;
}

นี่คือเนื้อหาหลักของวิธีการที่มีจุดประสงค์เพียงอย่างเดียวคือการส่งคืนแถวลอย ฉันต้องการให้วิธีนี้คืนnullหากมีข้อผิดพลาดดังนั้นฉันจึงใส่วนเข้าไปในtry...catchบล็อกเช่นนี้

try {
    for (int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    }
} catch (NumberFormatException ex) {
    return null;
}

แต่จากนั้นฉันก็นึกถึงการใส่try...catchบล็อกเข้าไปในลูปเช่นนี้

for (int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
    } catch (NumberFormatException ex) {
        return null;
    }
    myFloats[i] = myNum;
}

มีเหตุผลประสิทธิภาพหรืออย่างอื่นที่จะชอบอย่างใดอย่างหนึ่งมากกว่าอื่น ๆ ?


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


2
ฉันไม่รู้เกี่ยวกับประสิทธิภาพการทำงาน แต่โค้ดดูสะอาดตายิ่งขึ้นโดยลองจับนอกลูป
Jack B Nimble

คำตอบ:


132

ประสิทธิภาพ:

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

นี่คือข้อมูลอ้างอิง: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html

ตารางอธิบายไว้ครึ่งทาง


ตกลงดังนั้นคุณกำลังบอกว่าไม่มีความแตกต่างยกเว้นความสวยงาม คุณสามารถลิงค์ไปยังแหล่งที่มาได้หรือไม่?
Michael Myers

2
ขอบคุณ ฉันคิดว่าสิ่งต่าง ๆ อาจมีการเปลี่ยนแปลงตั้งแต่ปี 1997 แต่พวกเขาแทบจะไม่ทำให้มีประสิทธิภาพน้อยลง
Michael Myers

1
เป็นคำที่ยากแน่นอน ในบางกรณีสามารถยับยั้งการปรับให้เหมาะสมของคอมไพเลอร์ ไม่ใช่ค่าใช้จ่ายทางตรง แต่อาจเป็นโทษทางอ้อม
Tom Hawtin - tackline

2
จริงฉันคิดว่าฉันควรจะมีความกระตือรือร้นเล็กน้อย แต่การบิดเบือนข้อมูลนั้นเกิดขึ้นกับประสาทของฉัน! ในกรณีของการปรับให้เหมาะสมเนื่องจากเราไม่สามารถทราบได้ว่าจะส่งผลกระทบต่อประสิทธิภาพการทำงานอย่างไรฉันคิดว่าจะกลับมาลองใช้และทดสอบอีกครั้ง (เช่นเคย)
Jeffrey L Whitledge

1
ไม่มีความแตกต่างด้านประสิทธิภาพเฉพาะในกรณีที่รหัสทำงานโดยไม่มีข้อยกเว้น
Onur Bıyık

72

การแสดง : อย่างที่เจฟฟรีย์พูดไว้ในคำตอบของเขาใน Java มันไม่ได้สร้างความแตกต่างมากนัก

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

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

วิธีที่สาม : คุณสามารถเขียนวิธี ParseFloat ของคุณเองได้ตลอดเวลาและมีข้อยกเว้นในการจัดการกับวิธีนั้นแทนที่จะวนซ้ำ ทำให้การจัดการข้อยกเว้นแยกต่างหากเพื่อห่วงตัวเอง!

class Parsing
{
    public static Float MyParseFloat(string inputValue)
    {
        try
        {
            return Float.parseFloat(inputValue);
        }
        catch ( NumberFormatException e )
        {
            return null;
        }
    }

    // ....  your code
    for(int i = 0; i < max; i++) 
    {
        String myString = ...;
        Float myNum = Parsing.MyParseFloat(myString);
        if ( myNum == null ) return;
        myFloats[i] = (float) myNum;
    }
}

1
มองใกล้ ๆ เขาออกจากข้อยกเว้นแรกในทั้งสองอย่าง ตอนแรกฉันก็คิดแบบเดียวกัน
kervin

2
ฉันรู้ว่านั่นเป็นเหตุผลที่ฉันพูดในกรณีของเขาเนื่องจากเขากลับมาเมื่อเขาพบปัญหาจากนั้นฉันจะใช้การจับกุมภายนอก เพื่อความชัดเจนว่าเป็นกฎฉันต้องการใช้ ...
เรย์เฮย์ส

โค้ดดี แต่น่าเสียดายที่ Java ไม่มีพารามิเตอร์หรูหรา
Michael Myers

ByRef? นานมากแล้วตั้งแต่ฉันได้สัมผัส Java!
เรย์เฮย์ส

2
มีคนแนะนำ TryParse ซึ่งใน C # /. net ช่วยให้คุณแยกวิเคราะห์ได้อย่างปลอดภัยโดยไม่ต้องจัดการกับข้อยกเว้นตอนนี้ทำซ้ำและวิธีนี้สามารถนำมาใช้ซ้ำได้ สำหรับคำถามเดิมที่ฉันชอบวงภายในจับก็แค่ดูสะอาดแม้ว่าจะไม่มีปัญหาประสิทธิภาพการทำงาน ..
เรย์เฮย์ส

46

เอาล่ะหลังจากเจฟฟรีย์แอลไวท์เลเดอร์บอกว่าไม่มีความแตกต่างด้านประสิทธิภาพ (ตั้งแต่ปี 1997) ฉันไปและทดสอบมัน ฉันใช้เกณฑ์มาตรฐานขนาดเล็กนี้:

public class Main {

    private static final int NUM_TESTS = 100;
    private static int ITERATIONS = 1000000;
    // time counters
    private static long inTime = 0L;
    private static long aroundTime = 0L;

    public static void main(String[] args) {
        for (int i = 0; i < NUM_TESTS; i++) {
            test();
            ITERATIONS += 1; // so the tests don't always return the same number
        }
        System.out.println("Inside loop: " + (inTime/1000000.0) + " ms.");
        System.out.println("Around loop: " + (aroundTime/1000000.0) + " ms.");
    }
    public static void test() {
        aroundTime += testAround();
        inTime += testIn();
    }
    public static long testIn() {
        long start = System.nanoTime();
        Integer i = tryInLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static long testAround() {
        long start = System.nanoTime();
        Integer i = tryAroundLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static Integer tryInLoop() {
        int count = 0;
        for (int i = 0; i < ITERATIONS; i++) {
            try {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            } catch (NumberFormatException ex) {
                return null;
            }
        }
        return count;
    }
    public static Integer tryAroundLoop() {
        int count = 0;
        try {
            for (int i = 0; i < ITERATIONS; i++) {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            }
            return count;
        } catch (NumberFormatException ex) {
            return null;
        }
    }
}

ฉันตรวจสอบไบต์ที่เกิดขึ้นโดยใช้ javap เพื่อให้แน่ใจว่าไม่มีสิ่งใดเข้ามา

ผลการศึกษาพบว่าสมมติว่าการเพิ่มประสิทธิภาพ JIT ไม่มีนัยสำคัญเจฟฟรีย์ถูกต้อง ; มีอย่างไม่แตกต่างกันประสิทธิภาพการทำงานบน Java 6 ลูกค้าอาทิตย์ VM (ฉันไม่ได้มีการเข้าถึงรุ่นอื่น ๆ ) ความแตกต่างของเวลาทั้งหมดอยู่ในคำสั่งของไม่กี่มิลลิวินาทีในการทดสอบทั้งหมด

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


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

16

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

Integer j = 0;
    try {
        while (true) {
            ++j;

            if (j == 20) { throw new Exception(); }
            if (j%4 == 0) { System.out.println(j); }
            if (j == 40) { break; }
        }
    } catch (Exception e) {
        System.out.println("in catch block");
    }

ขณะที่ลูปอยู่ใน try catch block ตัวแปร 'j' จะเพิ่มขึ้นจนกว่าจะถึง 40 พิมพ์ออกมาเมื่อ j mod 4 เป็นศูนย์และมีการโยนข้อยกเว้นเมื่อ j เข้าชม 20

ก่อนรายละเอียดใด ๆ นี่คือตัวอย่างอื่น ๆ :

Integer i = 0;
    while (true) {
        try {
            ++i;

            if (i == 20) { throw new Exception(); }
            if (i%4 == 0) { System.out.println(i); }
            if (i == 40) { break; }

        } catch (Exception e) { System.out.println("in catch block"); }
    }

ตรรกะเดียวกับข้างต้นข้อแตกต่างก็คือตอนนี้บล็อก try / catch อยู่ในขณะที่ลูป

เอาท์พุทมาที่นี่ (ในขณะที่ลอง / จับ):

4
8
12 
16
in catch block

และเอาท์พุทอื่น ๆ (ลอง / จับในขณะที่):

4
8
12
16
in catch block
24
28
32
36
40

ที่นั่นคุณมีความแตกต่างค่อนข้างมาก:

ในขณะที่พยายาม / จับแยกออกจากวง

ลอง / จับในขณะที่ยังใช้งานลูป


ดังนั้นให้try{} catch() {}วนรอบลูปถ้าลูปนั้นถือเป็น "หน่วย" เดียวและค้นหาปัญหาในหน่วยนั้นจะทำให้ "หน่วย" ทั้งหมด (หรือลูปในกรณีนี้) ไปทางทิศใต้ ใส่try{} catch() {}ในลูปถ้าคุณทำการวนซ้ำของลูปหรือองค์ประกอบเดียวในอาร์เรย์โดยรวมเป็น "หน่วย" ทั้งหมด หากมีข้อยกเว้นใน "หน่วย" เกิดขึ้นเราเพียงข้ามองค์ประกอบนั้นจากนั้นเราดำเนินการต่อในการวนรอบถัดไปของการวนซ้ำ
Galaxy

14

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

ลองพิจารณาตัวอย่างที่แก้ไขเล็กน้อยนี้:

public static void main(String[] args) {
    String[] myNumberStrings = new String[] {"1.2345", "asdf", "2.3456"};
    ArrayList asNumbers = parseAll(myNumberStrings);
}

public static ArrayList parseAll(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        myFloats.add(new Float(numberStrings[i]));
    }
    return myFloats;
}

หากคุณต้องการให้ parseAll () วิธีการคืนค่าเป็นโมฆะหากมีข้อผิดพลาด (เช่นตัวอย่างดั้งเดิม) คุณจะต้องลอง / catch ด้านนอกดังนี้:

public static ArrayList parseAll1(String[] numberStrings){
    ArrayList myFloats = new ArrayList();
    try{
        for(int i = 0; i < numberStrings.length; i++){
            myFloats.add(new Float(numberStrings[i]));
        }
    } catch (NumberFormatException nfe){
        //fail on any error
        return null;
    }
    return myFloats;
}

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

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

public static ArrayList parseAll2(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        try{
            myFloats.add(new Float(numberStrings[i]));
        } catch (NumberFormatException nfe){
            //don't add just this one
        }
    }

    return myFloats;
}

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

5

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


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

4

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


4

ตราบใดที่คุณตระหนักถึงสิ่งที่คุณต้องทำให้สำเร็จในลูปคุณสามารถลองจับนอกวง แต่สิ่งสำคัญคือต้องเข้าใจว่าการวนซ้ำนั้นจะจบลงทันทีที่มีข้อยกเว้นเกิดขึ้นและอาจไม่เป็นอย่างที่คุณต้องการเสมอไป นี่เป็นข้อผิดพลาดที่พบบ่อยมากในซอฟต์แวร์ที่ใช้ Java ผู้คนจำเป็นต้องประมวลผลรายการจำนวนมากเช่นการล้างคิวและพึ่งพาข้อความสั่ง try / catch ภายนอกซึ่งจัดการข้อยกเว้นที่เป็นไปได้ทั้งหมด พวกเขายังสามารถจัดการข้อยกเว้นเฉพาะภายในวงและไม่คาดว่าจะเกิดข้อยกเว้นอื่น ๆ จากนั้นหากมีข้อยกเว้นเกิดขึ้นที่ไม่ได้จัดการภายในลูปลูปจะ "preemted" มันจะจบลงก่อนกำหนดและคำสั่ง catch ภายนอกจะจัดการกับข้อยกเว้น

หากลูปมีบทบาทในชีวิตในการลบคิวดังนั้นลูปนั้นน่าจะจบได้ก่อนที่คิวนั้นจะว่างเปล่า ความผิดปกติที่พบบ่อยมาก


2

ในตัวอย่างของคุณไม่มีความแตกต่างการทำงาน ฉันพบตัวอย่างแรกของคุณอ่านได้มากขึ้น


2

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

ในบันทึกย่ออื่นคุณอาจดู float.TryParse หรือ Convert.ToFloat


2

หากคุณลอง / จับภายในวงคุณจะวนลูปหลังจากข้อยกเว้น หากคุณวางไว้นอกวงคุณจะหยุดทันทีที่มีการโยนข้อยกเว้น


ฉันกลับเป็นโมฆะโดยไม่มีข้อยกเว้นดังนั้นมันจะไม่ประมวลผลต่อในกรณีของฉัน
Michael Myers

2

มุมมองของฉันคือการลอง / จับบล็อกมีความจำเป็นเพื่อประกันการจัดการข้อยกเว้นที่เหมาะสม แต่การสร้างบล็อกดังกล่าวมีผลต่อประสิทธิภาพ เนื่องจากลูปมีการคำนวณซ้ำ ๆ อย่างเข้มข้นจึงไม่แนะนำให้ใส่บล็อกลอง / จับภายในลูป นอกจากนี้ดูเหมือนว่าเงื่อนไขนี้เกิดขึ้นมักจะเป็น "ข้อยกเว้น" หรือ "RuntimeException" ซึ่งติดอยู่ RuntimeException ที่ติดอยู่ในรหัสควรหลีกเลี่ยง อีกครั้งถ้าหากคุณทำงานใน บริษัท ใหญ่จำเป็นต้องบันทึกข้อยกเว้นนั้นอย่างเหมาะสมหรือหยุดข้อยกเว้นรันไทม์ให้เกิดขึ้น จุดรวมของคำอธิบายนี้คือPLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS


1

การตั้งค่ากรอบสแต็กพิเศษสำหรับลอง / จับเพิ่มค่าใช้จ่ายเพิ่มเติม แต่ JVM อาจสามารถตรวจสอบความจริงที่ว่าคุณกำลังกลับมาและเพิ่มประสิทธิภาพนี้ออกไป

ขึ้นอยู่กับจำนวนของการวนซ้ำความแตกต่างของประสิทธิภาพจะมีน้อยมาก

อย่างไรก็ตามฉันเห็นด้วยกับคนอื่น ๆ ที่มีมันอยู่นอกวงทำให้ร่างกายดูห่วงสะอาด

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


1

หากอยู่ข้างในคุณจะได้รับค่าโสหุ้ยของโครงสร้าง try / catch N ครั้งเมื่อเทียบกับภายนอกเพียงครั้งเดียว


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


คุณสามารถอธิบายเพิ่มเติมเล็กน้อยเกี่ยวกับค่าใช้จ่าย?
Michael Myers

1
โอเค แต่ Jeffrey L Whitledge บอกว่าไม่มีค่าใช้จ่ายเพิ่มเติม คุณสามารถให้แหล่งข้อมูลที่ระบุว่ามีหรือไม่
Michael Myers

ราคามีขนาดเล็กเกือบเล็กน้อย แต่มีทุกอย่างมีค่าใช้จ่าย นี่คือบทความบล็อกจาก Programmer's Heaven ( tiny.cc/ZBX1b ) ที่เกี่ยวข้องกับ. NET และนี่คือโพสต์กลุ่มข่าวสารจาก Reshat Sabiq เกี่ยวกับ JAVA ( tiny.cc/BV2hS )
Stephen Wrighton

ฉันเห็น ... ดังนั้นตอนนี้คำถามคือคือค่าใช้จ่ายที่เกิดขึ้นจากการป้อน try / catch (ซึ่งในกรณีนี้ควรอยู่นอกลูป) หรือเป็นค่าคงที่ตลอดระยะเวลาของการลอง / จับ (ในกรณีนี้ ควรจะอยู่ในวง)? คุณพูดว่ามัน # 1 และฉันก็ยังไม่มั่นใจว่าจะมีค่าใช้จ่าย
Michael Myers

1
ตาม Google Book นี้ ( tinyurl.com/3pp7qj ), "VMs ล่าสุดไม่มีโทษ"
Michael Myers

1

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


ไม่ถูกต้อง! จุดของข้อยกเว้นคือการกำหนดพฤติกรรมพิเศษที่จะนำมาใช้เมื่อเกิดข้อผิดพลาด ดังนั้นการเลือกบล็อก try / catch ภายในหรือภายนอกขึ้นอยู่กับว่าคุณต้องการจัดการกับลูปบำบัดอย่างไร
gizmo

ที่สามารถทำได้ดีพอ ๆ กันกับการรักษาข้อผิดพลาดที่มีข้อยกเว้นล่วงหน้าเช่นการผ่านธง "เกิดข้อผิดพลาด" รอบ ๆ
wnoise

1

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

พิจารณา:

try {
   // parse
} catch (NumberFormatException nfe){
   throw new RuntimeException("Could not parse as a Float: [" + myString + 
                              "] found at index: " + i, nfe);
} 

ในเวลาที่คุณต้องการคุณจะประทับใจกับข้อยกเว้นเช่นนี้ที่มีข้อมูลมากที่สุดเท่าที่จะเป็นไปได้


ใช่นี่เป็นจุดที่ถูกต้องเช่นกัน ในกรณีของฉันฉันกำลังอ่านจากไฟล์ดังนั้นสิ่งที่ฉันต้องการจริงๆคือสตริงที่ไม่สามารถแยกวิเคราะห์ได้ ดังนั้นฉันจึงใช้ System.out.println (ex) แทนการโยนข้อยกเว้นอื่น (ฉันต้องการแยกไฟล์ให้มากที่สุดเท่าที่จะทำได้ตามกฎหมาย)
Michael Myers

1

ฉันต้องการเพิ่ม0.02cข้อพิจารณาที่แข่งขันกันสองข้อของตัวเองเมื่อดูปัญหาทั่วไปว่าจะจัดการข้อยกเว้นได้ที่ใด:

  1. ความรับผิดชอบ "ที่กว้างขึ้น" ของtry-catchบล็อก (เช่นนอกวงในกรณีของคุณ) หมายความว่าเมื่อเปลี่ยนรหัสในภายหลังคุณอาจเพิ่มบรรทัดที่จัดการโดยcatchบล็อกที่มีอยู่ของคุณผิดพลาด อาจไม่ได้ตั้งใจ ในกรณีของคุณสิ่งนี้มีโอกาสน้อยกว่าเนื่องจากคุณกำลังจับNumberFormatException

  2. ความรับผิดชอบของtry-catchบล็อกที่"แคบลง" จะทำให้การเปลี่ยนโครงสร้างใหม่ยากขึ้น โดยเฉพาะอย่างยิ่งเมื่อ (ในกรณีของคุณ) คุณกำลังดำเนินการคำสั่ง "นอกท้องถิ่น" จากภายในcatchบล็อก ( return nullคำสั่ง)


1

ขึ้นอยู่กับความล้มเหลวในการจัดการ หากคุณเพียงต้องการข้ามองค์ประกอบข้อผิดพลาดลองภายใน:

for(int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    } catch (NumberFormatException ex) {
        --i;
    }
}

ในกรณีอื่น ๆ ฉันอยากลองด้านนอก รหัสอ่านได้ง่ายขึ้นสะอาดขึ้นกว่าเดิม อาจเป็นการดีกว่าที่จะโยน IllegalArgumentException ในกรณีที่เกิดข้อผิดพลาดแทนหากส่งคืน null


1

ฉันจะใส่เงิน $ 0.02 ในบางครั้งคุณต้องมีการเพิ่ม "ในที่สุด" ในภายหลังในรหัสของคุณ (เพราะใครเคยเขียนรหัสของพวกเขาอย่างสมบูรณ์ในครั้งแรก? ในกรณีเหล่านั้นทันใดนั้นก็เหมาะสมกว่าที่จะลอง / จับนอกวง ตัวอย่างเช่น:

try {
    for(int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        dbConnection.update("MY_FLOATS","INDEX",i,"VALUE",myNum);
    }
} catch (NumberFormatException ex) {
    return null;
} finally {
    dbConnection.release();  // Always release DB connection, even if transaction fails.
}

เพราะถ้าคุณได้รับข้อผิดพลาดหรือไม่คุณต้องการปล่อยการเชื่อมต่อฐานข้อมูลของคุณ (หรือเลือกประเภทอื่น ๆ ของทรัพยากรที่คุณชื่นชอบ ... ) หนึ่งครั้ง


1

อีกแง่มุมที่ไม่ได้กล่าวถึงข้างต้นก็คือความจริงที่ว่าทุกการทดลองมีบางส่วนผลกระทบในกองซึ่งจะมีผลกระทบต่อวิธีการเรียกซ้ำ

หากเมธอด "outer ()" เรียกใช้เมธอด "inner ()" (ซึ่งอาจเรียกตัวเองซ้ำ ๆ ) ให้ลองค้นหาวิธีลองจับในเมธอด "outer ()" ถ้าเป็นไปได้ ตัวอย่างง่ายๆ "stack crash" ที่เราใช้ในคลาสประสิทธิภาพล้มเหลวที่ประมาณ 6,400 เฟรมเมื่อ try-catch อยู่ใน method ด้านในและประมาณ 11,600 เมื่ออยู่ในเมธอด outer

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


0

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

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

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

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