היי! היום נסקור מקרוב את הטיפול בחריגים ב-Java. אני שונא להזכיר את זה, אבל חלק עצום מהעבודה של מתכנת הוא התמודדות עם שגיאות. לרוב, שלו או שלה. מסתבר שאין אנשים שלא עושים טעויות. וגם אין תוכניות כאלה. כמובן, כאשר מתמודדים עם שגיאה, העיקר להבין את הסיבה שלה. והרבה דברים יכולים לגרום לבאגים בתוכנית. בשלב מסוים, היוצרים של Java שאלו את עצמם מה צריך לעשות עם שגיאות התכנות הסבירות ביותר? הימנעות מוחלטת מהם אינה מציאותית, מתכנתים מסוגלים לכתוב דברים שאתה אפילו לא יכול לדמיין. :) אז, אנחנו צריכים לתת לשפה מנגנון לעבודה עם שגיאות. במילים אחרות, אם יש שגיאה בתוכנית שלך, אתה צריך איזה סקריפט מה לעשות הלאה. מה בדיוק צריכה תוכנית לעשות כאשר מתרחשת שגיאה? היום נכיר את המנגנון הזה. זה נקרא " חריגים בג'אווה
".
קוד שבו המתכנת מאמין שעלול להתרחש חריג, ממוקם בבלוק
מה זה חריג?
חריג הוא מצב חריג, לא מתוכנן, המתרחש בזמן שתוכנית פועלת. יש הרבה יוצאי דופן. לדוגמה, כתבת קוד שקורא טקסט מקובץ, ומציג את השורה הראשונה.public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
}
}
אבל מה אם אין קובץ כזה! התוכנית תיצור חריג: FileNotFoundException
. פלט: חריגה בשרשור "ראשי" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (המערכת לא יכולה למצוא את הנתיב שצוין) ב-Java, כל חריג מיוצג על ידי מחלקה נפרדת. כל מחלקות החריגות הללו נובעות מ"אב קדמון" משותף - Throwable
מחלקת האב. שם מחלקה חריגה בדרך כלל משקף באופן תמציתי מדוע התרחש החריג:
FileNotFoundException
(הקובץ לא נמצא)ArithmeticException
(חריג אירע בעת ביצוע פעולה מתמטית)ArrayIndexOutOfBoundsException
(האינדקס הוא מעבר לגבולות המערך). לדוגמה, חריג זה מתרחש אם אתה מנסה להציג מיקום 23 של מערך שיש בו רק 10 אלמנטים.
Exception in thread "main"
אהההה. :/ זה לא עוזר הרבה. לא ברור מה משמעות השגיאה ומאיפה היא הגיעה. אין כאן מידע מועיל. אבל המגוון הגדול של מחלקות חריגים בג'אווה נותן למתכנת את מה שהכי חשוב: סוג השגיאה והסיבה הסבירה שלה (מוטמעת בשם המחלקה). זה דבר אחר לגמרי לראות
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
ברור מיד מה יכולה להיות הבעיה ואיפה להתחיל לחפור כדי לפתור את הבעיה! חריגים, כמו מופעים של כל מחלקות, הם אובייקטים.
תפיסה וטיפול בחריגים ב-Java
ל-Java יש בלוקים מיוחדים של קוד לעבודה עם טיפול בחריגים:try
, catch
ו finally
. 
try
. זה לא אומר שיתרחש כאן חריג. זה אומר שזה עלול להתרחש כאן, והמתכנת מודע לאפשרות הזו. סוג השגיאה שאתה מצפה להתרחש ממוקם בבלוק catch
. זה מכיל גם את כל הקוד שצריך להפעיל אם מתרחש חריג. הנה דוגמה:
public static void main(String[] args) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
System.out.println("Error! File not found!");
}
}
פלט: שגיאה! הקובץ לא נמצא! שמנו את הקוד שלנו בשני בלוקים. בבלוק הראשון, אנו צופים כי עלולה להתרחש שגיאת "הקובץ לא נמצא". זה try
הבלוק. בשנייה, אנו אומרים לתוכנית מה לעשות אם מתרחשת שגיאה. וסוג השגיאה הספציפי: FileNotFoundException
. אם נשים מחלקה חריגה אחרת בסוגריים של הבלוק catch
, אז FileNotFoundException
לא ייתפס.
public static void main(String[] args) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (ArithmeticException e) {
System.out.println("Error! File not found!");
}
}
פלט: חריגה בשרשור "ראשי" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (המערכת לא מוצאת את הנתיב שצוין) הקוד בבלוק catch
לא פעל, כי "הגדרנו" בלוק הזה לתפוס ArithmeticException
, והקוד בבלוק try
זרק סוג אחר: FileNotFoundException
. לא כתבנו שום קוד לטיפול FileNotFoundException
, אז התוכנית מציגה את מידע ברירת המחדל עבור FileNotFoundException
. כאן אתה צריך לשים לב לשלושה דברים. מספר אחד. ברגע שמתרחש חריג בשורה כלשהי בבלוק try
, הקוד שאחריו לא יבוצע. ביצוע התוכנית "קופץ" מיד לבלוק catch
. לדוגמה:
public static void main(String[] args) {
try {
System.out.println("Divide by zero");
System.out.println(366/0);// This line of code will throw an exception
System.out.println("This");
System.out.println("code");
System.out.println("will not");
System.out.println("be");
System.out.println("executed!");
} catch (ArithmeticException e) {
System.out.println("The program jumped to the catch block!");
System.out.println("Error! You can't divide by zero!");
}
}
פלט: חלק באפס התוכנית קפצה לבלוק ה-catch! שְׁגִיאָה! אי אפשר לחלק באפס! בשורה השנייה של try
הבלוק, אנו מנסים לחלק ב-0, וכתוצאה מכך ArithmeticException
. כתוצאה מכך, שורות 3-10 של try
הבלוק לא יבוצעו. כפי שאמרנו, התוכנית מתחילה מיד לבצע את catch
הבלוק. מספר שתיים. יכולים להיות מספר catch
בלוקים. אם הקוד בבלוק try
עשוי לזרוק לא אחד, אלא כמה סוגים שונים של חריגים, אתה יכול לכתוב בלוק catch
עבור כל אחד מהם.
public static void main(String[] args) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
System.out.println(366/0);
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
System.out.println("Error! File not found!");
} catch (ArithmeticException e) {
System.out.println("Error! Division by 0!");
}
}
בדוגמה זו, כתבנו שני catch
בלוקים. אם FileNotFoundException
מתרחשת בבלוק try
, הבלוק הראשון catch
יבוצע. אם ArithmeticException
מתרחש, הבלוק השני יבוצע. אתה יכול לכתוב 50 catch
בלוקים אם אתה רוצה. כמובן, עדיף לא לכתוב קוד שיכול לגרום ל-50 סוגים שונים של חריגים. :) שלישית. איך אתה יודע אילו חריגים הקוד שלך עלול לזרוק? ובכן, אולי תוכל לנחש כמה מהם, אבל זה בלתי אפשרי עבורך לשמור הכל בראש. לכן, מהדר Java מכיר את החריגים הנפוצים ביותר ואת המצבים שבהם הם עלולים להתרחש. לדוגמה, אם אתה כותב קוד שהמהדר יודע שעלול לגרום לשני סוגים של חריגים, הקוד שלך לא יקמפל עד שתטפל בהם. נראה דוגמאות לכך להלן.
עכשיו כמה מילים על טיפול בחריגים
ישנן 2 דרכים לטיפול בחריגים ב-Java. כבר נתקלנו בראשון: השיטה יכולה להתמודד עם החריגה עצמה בבלוקcatch()
. ישנה אפשרות שנייה: השיטה יכולה לזרוק מחדש את החריג למחסנית השיחות. מה זה אומר? לדוגמה, יש לנו מחלקה עם אותה printFirstString()
שיטה, שקוראת קובץ ומציגה את השורה הראשונה שלו:
public static void printFirstString(String filePath) {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
נכון לעכשיו, הקוד שלנו אינו קומפילציה, כי יש לו חריגים לא מטופלים. בשורה 1, אתה מציין את הנתיב לקובץ. המהדר יודע שקוד כזה יכול בקלות לייצר FileNotFoundException
. בשורה 3 אתה קורא את הטקסט מהקובץ. תהליך זה יכול בקלות לגרום לשגיאה IOException
(שגיאת קלט/פלט). עכשיו המהדר אומר לך, "אחי, אני לא אאשר את הקוד הזה ואני לא ארכיב אותו עד שתגיד לי מה אני צריך לעשות אם אחד מהחריגים האלה מתרחש. והם בהחלט יכולים לקרות על סמך הקוד שכתבת !" אין דרך לעקוף את זה: אתה צריך לטפל בשניהם! אנחנו כבר יודעים על שיטת הטיפול החריגים הראשונה: אנחנו צריכים לשים את הקוד שלנו בבלוק try
ולהוסיף שני catch
בלוקים:
public static void printFirstString(String filePath) {
try {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
System.out.println("Error, file not found!");
e.printStackTrace();
} catch (IOException e) {
System.out.println("File input/output error!");
e.printStackTrace();
}
}
אבל זו לא האפשרות היחידה. נוכל פשוט לזרוק את החריג גבוה יותר במקום לכתוב קוד לטיפול בשגיאות בתוך השיטה.
טיפול בחריגים ב-Java מילות מפתח
זה נעשה באמצעות מילת המפתחthrows
בהצהרת השיטה:
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
אחרי מילת המפתח throws
, אנו מציינים רשימה מופרדת בפסיקים של כל סוגי החריגים שהשיטה עשויה לזרוק. למה? כעת, אם מישהו רוצה לקרוא לשיטה printFirstString()
בתוכנית, הוא או היא (לא אתה) יצטרכו ליישם טיפול בחריגים. לדוגמה, נניח שבמקום אחר בתוכנית אחד מהקולגות שלך כתב שיטה שקוראת printFirstString()
לשיטה שלך:
public static void yourColleagueMethod() {
// Your colleague's method does something
//...and then calls your printFirstString() method with the file it needs
printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
אנחנו מקבלים שגיאה! הקוד הזה לא יתבצע קומפילציה! לא כתבנו קוד טיפול בחריג בשיטה printFirstString()
. כתוצאה מכך, משימה זו מוטלת כעת על כתפי המשתמשים בשיטה. במילים אחרות, methodWrittenByYourColleague()
לשיטה יש כעת אותן 2 אפשרויות: עליה להשתמש בבלוק try-catch
כדי לטפל בשני החריגים, או לזרוק אותם מחדש.
public static void yourColleagueMethod() throws FileNotFoundException, IOException {
// The method does something
//...and then calls your printFirstString() method with the file it needs
printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
במקרה השני, השיטה הבאה בערימת השיחות - זו שקוראת methodWrittenByYourColleague()
- תצטרך לטפל בחריגים. זו הסיבה שאנו קוראים לזה "השלכת או העברת החריג". אם תזרוק חריגים כלפי מעלה באמצעות מילת המפתח throws
, הקוד שלך יקמפל. בשלב זה, נראה שהמהדר אומר, "בסדר, בסדר. הקוד שלך מכיל חבורה של חריגים פוטנציאליים, אבל אני ארכיב אותו. אבל נחזור לשיחה הזו!" וכשאתה קורא לכל שיטה שיש לה חריגים לא מטופלים, המהדר מקיים את הבטחתו ומזכיר לך אותם שוב. לבסוף, נדבר על finally
הבלוק (סליחה על משחק המילים). זהו החלק האחרון של try-catch-finally
טריומווירט הטיפול בחריג. זה שונה בכך שהוא מבוצע לא משנה מה קורה בתוכנית.
public static void main(String[] args) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
System.out.println("Error! File not found!");
e.printStackTrace();
} finally {
System.out.println ("And here's the finally block!");
}
}
בדוגמה זו, הקוד בתוך finally
הבלוק יבוצע בשני המקרים. אם הקוד בבלוק try
פועל במלואו מבלי לזרוק חריגים כלשהם, finally
הבלוק ירוץ בסופו של דבר. אם הקוד בתוך try
הבלוק נקטע על ידי חריגה והתוכנית קופצת לבלוק catch
, finally
הבלוק עדיין יפעל אחרי הקוד שבתוך catch
הבלוק.
למה זה נחוץ?
מטרתו העיקרית היא לבצע קוד חובה: קוד שיש לבצע ללא קשר לנסיבות. לדוגמה, לעתים קרובות זה משחרר כמה משאבים המשמשים את התוכנית. בקוד שלנו, אנחנו פותחים זרם כדי לקרוא מידע מהקובץ ולהעביר אותו לאובייקטBufferedReader
. עלינו לסגור את הקורא שלנו ולשחרר את המשאבים. זה חייב להיעשות לא משנה מה - מתי התוכנית פועלת כפי שהיא צריכה, ומתי היא זורקת חריג. הבלוק finally
הוא מקום נוח מאוד לעשות זאת:
public static void main(String[] args) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
System.out.println ("And here's the finally block!");
if (reader != null) {
reader.close();
}
}
}
כעת אנו בטוחים שנדאג למשאבים, ללא קשר למה שקורה כאשר התוכנית פועלת. :) זה לא כל מה שאתה צריך לדעת על חריגים. טיפול בשגיאות הוא נושא סופר חשוב בתכנות. הרבה מאמרים מוקדשים לזה. בשיעור הבא, נגלה אילו סוגי חריגים קיימים וכיצד ליצור חריגים משלך. :) נתראה!
קריאה נוספת: |
---|
GO TO FULL VERSION