DateFormat.getDateInstance();

Константа DateFormat.MEDIUM указывает на то, что будут представлены только дата и время без указания часового пояса. Для указания часового пояса используются константы класса DateFormat со значением LONG и FULL. Константа SHORT применяется для сокращенной записи даты, где месяц представлен в виде своего порядкового номера.

Для получения даты в виде строки для заданного региона используется метод String format(Date date) в виде:

String dat = df.format(new Date());

С помощью метода Date parse(String source) можно преобразовать переданную в виде строки дату в объектное представление конкретного регионального формата, например:

String str = "April 3, 2006";

Date d = df.parse(str);

Класс содержит большое количество методов, позволяющих выполнять разнообразные манипуляции с датой и временем.

В качестве примера рассмотрено преобразование заданной даты в различные региональные форматы.

// пример # 18 : региональные представления дат: DemoDateFormat.java

package chapt07;

import java.text.DateFormat;

import java.text.ParseException;

import java.util.*;

 

public class DemoDateFormat {

public static void main(String[] args) {

DateFormat df =

DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US);

Date d = null;

String str = "April 3, 2006";

try {

d = df.parse(str);

System.out.println(d);

} catch (ParseException e) {

e.printStackTrace();

}

df =

DateFormat.getDateInstance(DateFormat.FULL,

new Locale("ru","RU"));

System.out.println(df.format(d));

 

df =

DateFormat.getDateInstance(DateFormat.FULL,Locale.GERMAN);

System.out.println(df.format(d));

 

d = new Date();

//загрузка в объект df текущего времени

df = DateFormat.getTimeInstance();

//представление и вывод времени в текущем формате дат

System.out.println(df.format(d));

}

}

Результат работы программы:

Mon Apr 03 00:00:00 EEST 2006

Апрель 2006 г.

Montag, 3. April 2006

05:45:16

Чтобы получить представление текущей даты во всех возможных региональных стандартах, можно воспользоваться следующим фрагментом кода:

Date d = new Date();

Locale[] locales =

DateFormat.getAvailableLocales();

for (Locale loc : locales) {

DateFormat df =

DateFormat.getDateInstance(DateFormat.FULL, loc);

System.out.println(loc.toString() + "---> "

+ df.format(d));

}

В результате будут выведены две сотни строк, каждая из которых представляет текущую дату в соответствии с региональным стандартом, выводимым перед датой с помощью инструкции loc.toString().

Задания к главе 7

Вариант A

1. В каждом слове текста k-ю букву заменить заданным символом. Если
k больше длины слова, корректировку не выполнять.

2. В русском тексте каждую букву заменить ее порядковым номером
в алфавите. При выводе в одной строке печатать текст с двумя пробелами между буквами, в следующей строке внизу под каждой буквой печатать ее номер.

3. В тексте после буквы Р, если она не последняя в слове, ошибочно напечатана буква А вместо О. Внести исправления в текст.

4. В тексте слова заданной длины заменить указанной подстрокой, длина которой может не совпадать с длиной слова.

5. В тексте после k-го символа вставить заданную подстроку.

6. После каждого слова текста, заканчивающегося заданной под­строкой, вставить указанное слово.

7. В зависимости от признака (0 или 1) в каждой строке текста удалить указанный символ везде, где он встречается, или вставить его после
k-гo символа.

8. Из небольшого текста удалить все символы, кроме пробелов, не являющиеся буквами. Между последовательностями подряд идущих букв оставить хотя бы один пробел.

9. Из текста удалить все слова заданной длины, начинающиеся на согласную букву.

10. Удалить из текста его часть, заключенную между двумя сим­волами, которые вводятся (например, между скобками ‘(’ и ‘)’ или между звездочками ‘*’ и т.п.).

11. В тексте найти все пары слов, из которых одно является об­ращением другого.

12. Найти и напечатать, сколько раз повторяется в тексте каждое слово, которое встречается в нем.

13. В тексте найти и напечатать n символов (и их количество), встречающихся наиболее часто.

14. Найти, каких букв, гласных или согласных, больше в каждом предложении текста.

15. В стихотворении найти количество слов, начинающихся и заканчиваю­щихся гласной буквой.

16. Напечатать без повторения слова текста, у которых первая и последняя буквы совпадают.

17. В тексте найти и напечатать все слова максимальной и все слова минимальной длины.

18. Напечатать квитанцию об оплате телеграммы, если стоимость одного слова задана.

19. В стихотворении найти одинаковые буквы, которые встречаются во всех словах.

20. В тексте найти первую подстроку максимальной длины, не со­держащую букв.

21. В тексте определить все согласные буквы, встречающиеся не более чем в двух словах.

22. Преобразовать текст так, чтобы каждое слово начиналось с заглавной буквы.

23. Подсчитать количество содержащихся в данном тексте знаков препинания.

24. В заданном тексте найти сумму всех встречающихся цифр.

25. Из кода Java удалить все комментарии (//, /*, /**).

26. Дан текст на английском языке. Пусть все слова встречаются четное количество раз, за исключением одного. Определить это слово. При сравнении слов регистр не учитывать.

27. Определить сумму всех целых чисел, встречающихся в заданном тексте.

28. Из английского текста удалить все пробелы, если он разделяет два различных знака препинания и если рядом с ним находится еще один пробел.

29. Строка состоит из упорядоченных чисел от 0 до 100000, записанных подряд без пробелов. Определить, что будет подстрокой от позиции
n до m.

30. Определить количество вхождений заданного слова в текст, игнорируя регистр символов и считая буквы «е», «ё», и «и», «й» одинаковыми.

31. Преобразовать текст так, чтобы только первые буквы каждого предложения были заглавными.

32. Заменить в тексте все шаблоны типа %user%Бендер%/user% на
<a href=”http://www.my.by/search.htm?param=Бендер”>Бендер</a>.

33. В Java код добавить корректные getter и setter-методы для всех полей данного класса, при их отсутствии.

34. Вывести все предложения заданного текста в порядке возрастания
количества слов в каждом из них.

Вариант B

1. В тексте нет слов, начинающихся одинаковыми буквами. Напе­чатать слова текста в таком порядке, чтобы последняя буква каждого слова совпадала с первой буквой последующего слова. Если все слова нельзя напечатать в таком порядке, найти такую цепочку, состоящую из наибольшего количества слов.

2. Найти наибольшее количество предложений текста, в которых есть одинаковые слова.

3. Найти такое слово в первом предложении, которого нет ни в одном из остальных предложений.

4. Во всех вопросительных предложениях текста найти и напеча­тать без повторений слова заданной длины.

5. В каждом предложении текста поменять местами первое слово с последним, не изменяя длины предложения.

6. В предложении из n слов первое слово поставить на место второго, второе – на место третьего, и т.д., (n-1)-е слово – на место n-го, n-е слово поставить на место первого. В исходном и преобразованном предложениях между словами должны быть или один пробел, или знак препинания и один пробел.

7. Текст шифруется по следующему правилу: из исходного текста выби­рается 1, 4, 7, 10-й и т.д. (до конца текста) символы, затем 2, 5, 8, 11-й
и т.д. (до конца текста) символы, затем 3, 6, 9, 12-й и т.д. Зашифровать заданный текст.

8. На основании правила кодирования, описанного в предыдущем примере, расшифровать заданный набор символов.

9. Напечатать слова русского текста в алфавитном порядке по первой
букве. Слова, начинающиеся с новой буквы, печатать с красной строки.

10. Рассортировать слова русского текста по возрастанию доли гласных букв (отношение количества гласных к общему количеству букв в слове).

11. Слова английского текста, начинающиеся с гласных букв, рассортировать в алфавитном порядке по первой согласной букве слова.

12. Все слова английского текста рассортировать по возрастанию количества заданной буквы в слове. Слова с одинаковым коли­чеством расположить в алфавитном порядке.

13. Ввести текст и список слов. Для каждого слова из заданного списка найти, сколько раз оно встречается в тексте, и рассортировать слова по убыванию количества вхождений.

14. Все слова текста рассортировать в порядке убывания их длин, при этом все слова одинаковой длины рассортировать в порядке возрастания
в них количества гласных букв.

15. В тексте исключить подстроку максимальной длины, начинающуюся
и заканчивающуюся заданными символами.

16. Заменить все одинаковые рядом стоящие символы в тексте одним символом.

17. Вывести в заданном тексте все слова, расположив их в алфавитном
порядке.

18. Подсчитать, сколько слов в заданном тексте начинается с прописной
буквы.

19. Подсчитать, сколько раз заданное слово входит в текст.

20. Преобразовать каждое слово в тексте, удалив из него все последующие (предыдущие) вхождения первой (последней) буквы этого слова.

21. Вычеркнуть из текста минимальное количество предложений, так чтобы у любых двух оставшихся предложений было хотя бы одно общее слово.

22. Текст из n2 символов шифруется по следующему правилу:

– все символы текста записываются в квадратную таблицу размерности n в порядке слева направо, сверху вниз;

– таблица поворачивается на 900 по часовой стрелке;

– 1-я строка таблицы меняется местами с последней, 2-я – с предпоследней и т.д.

– 1-й столбец таблицы меняется местами со 2-м, 3-й – с 4-м и т.д.

– зашифрованный текст получается в результате обхода результирующей таблицы по спирали по часовой стрелке, начиная с левого верхнего угла.

Зашифровать текст по указанному правилу.

23. На основании правила кодирования, описанного в предыдущем примере, расшифровать заданный набор символов.

24. Исключить из текста подстроку максимальной длины, начинающуюся и заканчивающуюся одним и тем же символом.

25. Осуществить сжатие английского текста, заменив каждую группу из двух или более рядом стоящих символов, на один символ, за которым следует количество его вхождений в группу. К примеру, строка hellowoooorld должна сжиматься в hel2owo4rld.

26. Распаковать текст, сжатый по правилу из предыдущего задания.

27. Определить, удовлетворяет ли имя файла маске. Маска может содержать символы ‘?’ (произвольный символ) и ‘*’ (произвольное количество произвольных символов).

28. Отсортировать слова в тексте по убыванию количества вхождений заданного символа, а в случае равенства – по алфавиту. Словом считать максимальную группу подряд стоящих не пробельных символов.

29. Буквенная запись телефонных номеров основана на том, что каждой цифре соответствует несколько английских букв: 2 – ABC, 3 – DEF, 4 – GHI, 5 – JKL, 6 – MNO, 7 – PQRS, 8 – TUV, 9 – WXYZ. Написать программу, которая находит в заданном телефонном номере подстроку максимальной длины, соответствующую слову из словаря.

30. В заданном тексте найти подстроку максимальной длины, являющуюся палиндромом, т.е. читающуюся слева направо и справа налево одинаково.

31. Осуществить форматирование заданного текста с выравниванием по левому краю. Программа должна разбивать текст на строки с длиной, не превосходящей заданного количества символов. Если очередное слово не помещается в текущей строке, его необходимо переносить на следующую.

32. Изменить программу из предыдущего примера так, чтобы она осуществляла форматирование с выравниванием по обоим краям. Для этого добавить дополнительные пробелы между словами.

33. Добавить к программе из предыдущего примера возможность переноса слов по слогам. Предполагается, что есть доступ к словарю, в котором для каждого слова указано, как оно разбивается на слоги.

34. Пусть массив содержит миллион символов и необходимо сформировать из них строку путем конкатенации. Определить время работы кода. Как можно ускорить процесс, используя класс StringBuffer?

35. Алгоритм Барроуза – Уиллера для сжатия текстов основывается на пре­образовании Барроуза – Уиллера. Оно производится следующим обра­зом: для слова рассматриваются все его циклические сдвиги, которые затем сортируются в алфавитном порядке, после чего формируется сло­во из последних символов отсортированных циклических сдвигов. К примеру, для слова JAVA циклические сдвиги – это JAVA, AVAJ, VAJA, AJAV. После сортировки по алфавиту получим AJAV, AVAJ, JAVA, VAJA. Значит, результат преобразования – слово VJAA. Реализо­вать программно преобразование Барроуза – Уиллера для данного слова.

36. Восстановить слово по его преобразованию Барроуза – Уиллера. К примеру, получив на вход VJAA, в результате работы программа должна выдать слово JAVA.

Тестовые задания к главе 7

Вопрос 7.1.

Дан код:

public class Quest1 {

public static void main(String[] args) {

String str = new String("java");

int i=1;

char j=3;

System.out.println(str.substring(i,j));

}}

В результате при компиляции и запуске будет выведено:

1) ja;

2) av;

3) ava;

4) jav;

5) ошибка компиляции: заданы некорректные параметры для метода substring().

Вопрос 7.2.

Какую инструкцию следует использовать, чтобы обнаружить позицию буквы v в строке str = "Java"?

1) charAt(2,str);

2) str.charAt(2);

3) str.indexOf('v');

4) indexOf(str,'v');

Вопрос 7.3.

Какие из следующих операций корректны при объявлении?

String s = new String("Java");
String t = new String();
String r = null;

1) r = s + t + r;

2) r = s + t + ’r’;

3) r = s & t & r;

4) r = s && t && r;

Вопрос 7.4.

Дан код:

public class Quest4 {

public static void main(String[] args) {

String str="ava";

char ch=0x74; // 74 - это код символа 'J'

str=ch+str;

System.out.print(str);

}}

В результате при компиляции и запуске будет выведено:

1) 74ava;

2) Java;

3) 0H74ava;

4) ошибка компиляции: недопустимая операция ch+str;

5) ошибка компиляции: недопустимое объявление char ch=0x74;

6) нет правильного ответа.

Вопрос 7.5.

Что будет результатом компиляции и выполнения следующего кода?

public class Quest5 {

public static void main(String[] args) {

StringBuffer s = new StringBuffer("you java");

s.insert(2, "like ");

System.out.print(s);

}}

1) yolike u java;

2) you like java;

3) ylike ou java;

4) you java like;

5) ошибка компиляции;

6) нет правильного ответа.

 

 

 
Глава 8

ИСКЛЮЧЕНИЯ И ОШИБКИ

Иерархия и способы обработки

Исключительные ситуации (исключения) возникают во время выполнения программы, когда возникшая проблема не может быть решена в текущем контексте и невозможно продолжение работы программы. Примерами являются особо «популярные»: попытка индексации вне границ массива, вызов метода на нулевой ссылке или деление на нуль. При возникновении исключения создается объект, описывающий это исключение. Затем текущий ход выполнения приложения останавливается, и включается механизм обработки исключений. При этом ссылка на объект-исключение передается обработчику исключений, который пытается решить возникшую проблему и продолжить выполнение программы. Если в классе используется метод, в котором может возникнуть проверяемая исключительная ситуация, но не предусмотрена ее обработка, то ошибка возникает еще на этапе компиляции. При создании такого метода программист должен включить в код обработку исключений, которые могут генерировать этот метод, или передать обработку исключения на более высокий уровень методу, вызвавшему данный метод.

Throwable
Error
Exception
RuntimeException

Каждой исключительной ситуации поставлен в соответствие некоторый класс. Если подходящего класса не существует, то он может быть создан разработчиком. Все исключения являются наследниками суперкласса Throwableи его подклассов Error и Exception из пакета java.lang.

Рис. 8.1. Иерархия основных классов исключений

Исключительные ситуации типа Error возникают только во время выполнения программы. Такие исключения связаны с серьезными ошибками,
к примеру – переполнение стека, и не подлежат исправлению и не могут обрабатываться приложением. Иерархия классов, наследуемых от класса Error, приведена на рисунке 8.2.

Рис. 8.2. Иерархия классов исключений, наследуемых от класса Error.

На рисунке 8.3 приведена иерархия классов исключений, наследуемых от класса Exception.

Проверяемые исключения должны быть обработаны в методе, который может их генерировать, или включены в throws-список метода для дальнейшей обработки в вызывающих методах. Возможность возникновения проверяемого исключения может быть отслежена на этапе компиляции кода.

Во время выполнения могут генерироваться также исключения, которые могут быть обработаны без ущерба для выполнения программы. Иерархия этих исключений приведена на рисунке 8.4. В отличие от проверяемых исключений, класс RuntimeException и порожденные от него классы относятся к непрове­ряемым исключениям. Компилятор не проверяет, генерирует ли и обрабатывает ли метод эти исключения. Исключения типа RuntimeException автоматически генерируются при возникновении ошибок во время выполнения приложения. Таким образом, нет необходимости в проверке генерации исключения вида:

if(a==null) throw new NullPionterException();

объект класса NullPionterException при возникновении ошибки будет сгенерирован автоматически. Кроме этого, в любом случае нет необходимости в обработке этого исключения непосредственно в методе или в передаче на обработку вызывающему методу с помощью оператора throw. В конце концов исключение будет передано в метод main(), где обрабатывается вызовом метода
printStackTrace(), выдающего данные трассировки.

Рис. 8.3. Иерархия классов проверяемых (checked) исключительных ситуаций

Обычно используется один из трех способов обработки исключений:

· перехват и обработка исключения в блоке try-catchметода;

· объявление исключения в секции throws метода и передача вызывающему методу (для проверяемых исключений);

· использование собственных исключений.

Первый подход можно рассмотреть на следующем примере. При клонировании объекта в определенных ситуациях может возникать исключительная ситуация типа CloneNotSupportedException. Например:

public void changeObject(Student ob) {

try {

Object temp = ob.clone();

//реализация

} catch (CloneNotSupportedException e) {

System.err.print(e);

}

}

Рис. 8.4. Иерархия непроверяемых (unchecked) исключений

При клонировании может возникнуть исключительная ситуация в случае, если переданный объект не поддерживает клонирование (не включен интерфейс Cloneable). В этом случае генерируется соответствующий объект, и управление передается блоку catch, в котором обрабатывается данный тип исключения, иначе блок catch пропускается. Блок try похож на обычный логический блок. Блок catch(){} похож на метод, принимающий в качестве единственного параметра ссылку на объект-исключение и обрабатывающий этот объект.

Второй подход демонстрируется на этом же примере. Метод может генери­ровать исключения, которые сам не обрабатывает, а передает для обработки другим методам, вызывающим данный метод. В этом случае метод должен объявить о таком поведении с помощью ключевого слова throws, чтобы вызывающие методы могли защитить себя от этих исключений. В вызывающих методах должна быть предусмотрена обработка этих исключений. Форма объявления такого метода:

Тип имяМетода(список_аргументов)

throws список_исключений { }

При этом сам таким образом объявляемый метод может содержать блоки try-catch, а может и не содержать их. Например, метод changeObject() можно объявить:

public void changeObject(Student ob)

throws CloneNotSupportedException {

Object temp = ob.clone();

//реализация

}

Ключевое слово throws позволяет разобраться с исключениями методов «чужих» классов, код которых отсутствует. Обрабатывать исключение при этом будет метод, вызывающий changeObject():

public void load(Student stud) {

try {

changeObject(stud);

} catch (CloneNotSupportedException e) {

String error = e.toString();

System.err.println(error);

}

}

Создание собственных исключений будет рассмотрено позже в этой главе.

Если в блоке try может быть сгенеририровано в разных участках кода несколько типов исключений, то необходимо наличие нескольких блоков catch, если только блок catch не обрабатывает все типы исключений.

/* пример # 1 : обработка двух типов исключений: TwoException.java */

packagechapt08;

public class TwoException {

public static void main(String[] args) {

try {

int a = (int)(Math.random() * 2);

System.out.println("a = " + a);

int c[] = { 1/a };

c[a] = 71;

} catch(ArithmeticException e) {

System.err.println("деление на 0" + e);

} catch(ArrayIndexOutOfBoundsException e) {

System.err.println(

"превышение границ массива: " + e);

}//последний catch

System.out.println("после блока try-catch");

}

}

Исключение "деление на 0" возникнет при инициализации элемента массива а=0. В противном случае (при а=1) генерируется исключение "превышение границ массива" при попытке присвоить значение второму элементу массива с[], который содержит только один элемент. Однако пример, приведенный выше, носит чисто демонстративный характер и не является образцом хорошего кода, так как в этой ситуации можно было обойтись простой проверкой аргументов на допустиые значения перед выполнением операций. К тому же генерация и обработка исключения – операция значительно более ресурсоемкая, чем вызов оператора if для проверки аргумента. Исключения должны применяться только для обработки исключительных ситуаций, и если существует возможность обойтись без них, то следует так и поступить.

Подклассы исключений в блоках catch должны следовать перед любым из их суперклассов, иначе суперкласс будет перехватывать эти исключения. Например:

try {/*код, который может вызвать исключение*/

} catch(RuntimeException e) { /* суперкласс RuntimeException

перехватит объекты всех своих подклассов */

} catch(ArithmeticException e) {} /* не может быть вызван,

поэтому возникает ошибка компиляции */

Операторы try можно вкладывать друг в друга. Если у оператораtry низкого уровня нет раздела catch, соответствующего возникшему исключению, поиск будет развернут на одну ступень выше, и будут проверены разделыcatch внешнего оператора try.

/* пример # 2 : вложенные блоки try-catch: MultiTryCatch.java */

packagechapt08;

public class MultiTryCatch {

public static void main(String[] args) {

try { // внешний блок

int a = (int) (Math.random() * 2) - 1;

System.out.println("a = " + a);

try { // внутренний блок

int b = 1/a;

StringBuffer sb = new StringBuffer(a);

} catch (NegativeArraySizeException e) {

System.err.println(

"недопустимый размер буфера: " + e);

}

} catch (ArithmeticException e) {

System.err.println("деление на 0" + e);

}

}

}

В результате запуска приложения при a=0 будет сгенерировано исключение ArithmeticException,а подходящий для его обработки блок try–catchявляется внешним по отношению к месту генерации исключения. Этот блок и будет задействован для обработки возникшей исключительной ситуации.

Третий подход к обработке исключений будет рассмотрен ниже на примере создания пользовательских исключений.

Оператор throw

В программировании часто возникают ситуации, когда программисту необходимо самому инициировать генерацию исключения для указания, напри­мер, на заведомо ошибочный результат выполнения операции, на некорректные значения параметра метода и др. Исключительную ситуацию можно создать с помощью оператора throw, если объект-исключение уже существует, или инициализировать его прямо после этого оператора. Оператор throw используется для генерации исключения. Для этого может быть использован объект класса Throwableили объект его подкласса, а также ссылки на них. Общая форма записи инструкции throw, генерирующей исключение:

throwобъектThrowable;

Объект-исключение может уже существовать или создаваться с помощью оператора new:

throw new IOException();

При достижении оператора throw выполнение кода прекращается. Ближайший блок try проверяется на наличие соответствующего обработчика catch. Если он существует, управление передается ему, иначе проверяется следующий из вложенных операторов try. Инициализация объекта-исключения без оператора throw никакой исключительной ситуации не вызовет.

Ниже приведен пример, в котором сначала создается объект-исключение, затем оператор throwгенерирует исключение, обрабатываемое в разделе catch,
в котором генерируется другое исключение.

/* пример # 3 : генерация исключений : ThrowGeneration.java */

package chapt08;

import java.io.File;

public class ThrowGeneration {

public static void connectFile(File file) {

if (file == null|| !file.exists())

throw new IllegalArgumentException();/*генерация

исключения */

//…

}

public static void main(String[] args) {

File f = new File("demo.txt");

// File f = null;

try {

connectFile(f);

} catch(IllegalArgumentException e) {

System.err.print("обработка unchecked-"

+ " исключения вне метода: " + e);

}

}

Вызываемый метод connectFile()может (при отсутствии файла на диске или при аргументе null) генерировать исключение, перехватываемое обработчиком. В результате этого объект непроверяемого исключения IllegalArgumentException, как подкласса классаRuntimeException, передается обработчику исключений в методе main().

В случае генерации проверяемого исключения компилятор требует обра­ботки исключения в методе или отказа от нее с помощью инструкции throws.

Если метод генерирует исключение с помощью оператора throw и при этом блок catch в методе отсутствует, то для передачи обработки исключения вызывающему методу тип проверяемого (checked) класса исключений должен быть указан в операторе throws при объявлении метода. Для исключений, являющихся подклассами класса RuntimeException(unchecked) и используемых для отображения программных ошибок, при выполнении приложения throws в объявлении должен отсутствовать.

Ключевое слово finally

Возможна ситуация, при которой нужно выполнить некоторые действия по завершению программы (закрыть поток, освободить соединение с базой данных) вне зависимости от того, произошло исключение или нет. В этом случае используется блок finally, который выполняется после инструкций try или catch. Например:

try{/*код, который может вызвать исключение*/}

catch(Exception1 e1) {/*обработка исключения е1*/}//необязателен

catch(Exception2 e2) {/*обработка исключения е2*/}//необязателен

finally{/*выполняется или после try, или после catch */}

Каждому разделу try должен соответствовать по крайней мере один раздел catch или блок finally. Блок finallyчасто используется для закрытия файлов и освобождения других ресурсов, захваченных для временного использования в начале выполнения метода. Код блока выполняется перед выходом из метода даже в том случае, если перед ним были выполнены инструкции вида return, break, continue. Приведем пример:

/* пример # 4 : выполнение блоков finally: StudentFinally.java */

packagechapt08;

 

public class StudentFinally {

private static int age;

public static void setAge(int age) {

try {

//реализация

if(age <= 0)

throw new RuntimeException("не бывает");

} finally {

System.out.print("освобождение ресурсов");

//реализация

}

System.out.print("конец метода");

}

public static int getAgeWoman() {

try {

returnage - 3;

} finally {

returnage;

}

}

public static void main(String[] args) {

try {

setAge(23);

setAge(-5);

} catch (RuntimeException e) {

e.printStackTrace();

}

System.out.print(getAgeWoman());

}

}

В методе setAge() из-за генерации исключения происходит преждевременный выход из блока try, но до выхода из функции выполняется раздел finally. Метод getAgeWoman() завершает работу выполнением стоящего
в блокеtry оператора return, но и при этом перед выходом из метода выполняется код блока finally.

Собственные исключения

Разработчик может создать собственное исключение как подкласс класса Exception и затем использовать его при обработке ситуаций, не являющихся исключениями с точки зрения языка, но нарушающих логику вещей. Например, появление объектов типа Human (Человек) с отрицательным значением поля age (возраст).

/* пример # 5 : метод, вызывающий исключение, созданное программистом:

RunnerLogic.java */

packagechapt08;

public class RunnerLogic {

public static double salary(int coeff)

throws SalaryException {

double d;

try {

if((d = 10 - 100/coeff) < 0)

throw new SalaryException("negative salary");

else return d;

} catch (ArithmeticException e) {

throw new SalaryException("div by zero", e);

}

}

public static void main(String[] args) {

try {

double res = salary(3); //или 0, или 71;

} catch (SalaryException e) {

System.err.println(e.toString());

System.err.println(e.getHiddenException());

}

}

}

При невозможности вычислить значение генерируется объект ArithmeticException, обработчик которого, в свою очередь, генерирует исключение SalaryException, используемое в качестве собственного исключения. Он принимает два аргумента. Один из них - сообщение, которое может быть выведено в поток ошибок; другой - реальное исключение, которое привело к вызову нашего исключения. Этот код показывает, как можно сохранить другую информацию внутри пользовательского исключения. Преимущество этого сохранения состоит в том, что если вызываемый метод захочет узнать реальную причину вызова SalaryException, он всего лишь должен вызвать метод getHiddenException(). Это позволяет вызываемому методу решить, нужно ли работать со специфичным исключением или достаточно обработки SalaryException.

/* пример # 6 : собственное исключение: SalaryException.java */

packagechapt08;

public class SalaryException extends Exception {

private Exception _hidden;

 

public SalaryException(String er) {

super(er);

}

public SalaryException(String er, Exception e) {

super(er);

_hidden = e;

}

public Exception getHiddenException() {

return_hidden;

}

}

Разработчики программного обеспечения стремятся к высокому уровню повторного использования кода, поэтому они постарались предусмотреть и закодировать все возможные исключительные ситуации. Поэтому при реальном программировании можно вполне обойтись без создания собственных классов исключений.

Наследование и исключения

Создание сложных распределенных систем редко обходится без наследования и обработки исключений. Следует знать два правила для проверяемых исключений при наследовании:

· переопределяемый метод в подклассе не может содержать в инструк­ции throws исключений, не обрабатываемых в соответствующем методе суперкласса;

· конструктор подкласса должен включить в свой блок throws все классы исключений или их суперклассы из блокаthrows конструк­тора суперкласса, к которому он обращается при создании объекта.

Первое правило имеет непосредственное отношение к расширяемости приложения. Пусть при добавлении в цепочку наследования нового класса его полиморфный метод включил в блок throws «новое» проверяемое исключение. Тогда методы логики пріложения, принимающие объект нового класса в качестве параметра и вызывающие данный полиморфный метод, не готовы обрабатывать «новое» исключение, так как ранее в этом не было необходимости. Поэтому при попытке добавления «нового» checked-исключения в полиморфный метод компилятор выдает сообщение об ошибке.

/* пример # 7 : полиморфизм и исключения: Stone.java: WhiteStone.java: BlackStone.java: StoneLogic.java */

packagechapt08;

class Stone { //старый класс

public void build() throws FileNotFoundException {

/* реализация*/ }

}

class WhiteStone extends Stone {//старый класс

public void build() {

System.out.println("белый каменный шар");

}

}

public class StoneLоgic {//старый класс

public static void infoStone(Stone stone) {

try{

stone.build();//обработка IOException не предусмотрена

} catch(FileNotFoundException e) {

System.err.print("файл не найден");

}

}

}

class BlackStone extends Stone {//новый класс

public void build() throws IOException{//ошибка компиляции

System.out.println("черный каменный шар");

/* реализация*/

}

}

Если же при объявлении метода суперкласса инструкция throws присутствует, то в подклассе эта инструкция может вообще отсутствовать или в ней могут быть объявлены любые исключения, являющееся подклассами исключения из блока throws метода суперкласса

Второе правило позволяет защитить программиста от возникновения неизвестных ему исключений при создании объекта.

/* пример # 8 : конструкторы и исключения: FileInput.java: SocketInput.java */

packagechapt08;

import java.io.FileNotFoundException;

import java.io.IOException;

 

class FileInput {//старый класс

public FileInput(String filename)

throws FileNotFoundException {

//реализация

}

}

class SocketInput extends FileInput {

//старый конструктор

public SocketInput(String name)

throws FileNotFoundException {

super(name);

//реализация

}

/старый конструктор

public SocketInput() throws IOException {

super("file.txt");

//реализация

}

/новый конструктор

public SocketInput(String name, int mode) {/*ошибка

компиляции*/

super(name);

//реализация

}

}

В приведенном выше случае компилятор не разрешит создать конструктор подкласса, обращающийся к конструктору суперкласса без корректной инструк­ции throws. Если бы это было возможно, то при создании объекта подкласса класса FileInput не было бы никаких сообщений о возможности генерации исключения, и при возникновении исключительной ситуации ее источник было бы трудно идентифицировать.

Отладочный механизм assertion

Борьба за качество программ ведется всеми возможными способами. На этапе отладки найти неявные ошибки в функционировании приложения бывает доволь­но сложно. Например, в методе, устанавливающем возраст пользователя, информация о возрасте извлекается из внешних источников (файл, БД), и в результате получается отрицательное значение. Далее неверные данные влияют на результат вычисления среднего возраста пользователей и т.д. Определять и исправ­лять такие ситуации позволяет механизм проверочных утверждений (assertion). При помощи этого механизма можно сформулировать требования к входным, выходным и промежуточным данным методов классов в виде некоторых логических условий.

Попытка обработать ситуацию появления отрицательного возраста может выглядеть следующим образом:

int age = ob.getAge();

if (age >= 0) {

// реализация

} else{

// сообщение о неправильных данных

}

Теперь механизм assertion позволяет создать код, который будет генерировать исключение на этапе отладки проверки постусловия или промежуточных данных в виде:

int age = ob.getAge();

assert (age >= 0): "NEGATIVE AGE!!!";

// реализация

Правописание инструкции assert:

assert (boolexp):expression;

assert (boolexp);

Выражение boolexp может принимать только значение типов booleanили Boolean, а expression – любое значение, которое может быть преобра­зовано к строке. Если логическое выражение получает значение false, то гене­рируется исключение AssertionError, и выполнение программы прекращается с выводом на консоль значения выражения expression (если оно задано).

Механизм assertion хорошо подходит для проверки инвариантов, например, перечислений:

enum Mono { WHITE, BLACK }

String str = "WHITE";//"GRAY"

Mono mono = Mono.valueOf(str);

// …

switch (mono) {

case WHITE : // …

break;

case BLACK : // …

break;

default :

assert false : "Colored!";

}

Создателями языка не рекомендуется использовать assertion при проверке параметров public-методов. В таких ситуациях лучше генерировать исключения одного из типов: IllegalArgumentException, NullPointerExceptionили собственное исключение. Нет также особого смысла в механизме assertion при проверке пограничных значений переменных, поскольку исключительные ситуации генерируются в этом случае без посторонней помощи.

Assertion можно включать для отдельных классов и пакетов при запуске виртуальной машины в виде:

java –enableassertions MyClass

или

java –ea MyClass

Для выключения применяется –da или -disableassertions.

Задания к главе 8

Вариант A

Выполнить задания на основе варианта А главы 4, контролируя состояние потоков ввода/вывода. При возникновении ошибок, связанных с корректностью выполнения математических операций, генерировать и обрабатывать исключитель­ные ситуации. Предусмотреть обработку исключений, возникающих при нехватке памяти, отсутствии требуемой записи (объекта) в файле, недопустимом значении поля и т.д.

Вариант B

Выполнить задания из варианта В главы 4, реализуя собственные обработчики исключений и исключения ввода/вывода.

Тестовые задания к главе 8

Вопрос 8.1.

Дан код:

class Quest1{

int counter;

java.io.OutputStream out;

Quest1(){/* инициализация out */}

float inc() {

try { counter++;
out.write(counter); }

//комментарий

}}

Какой код достаточно добавить в метод inc() вместо комментария, чтобы компиляция прошла без ошибок? (выберите два).

1) catch (java.io.OutputStreamException e){};

2) catch (java.io.IOException e){};

3) catch (java.io.OutputException e){};

4) finally{};

5) return counter;

6) return;.

Вопрос 8.2.

Какое значение будет возвращено при вызове meth(5)?

public int meth(int x) {

int y = 010; //1
try { y += x; //2

if(x<=5) throw new Exception(); //3

y++; } //4
catch(Exception e) { y--; } //5

return y; } //6

1) 12;

2) 13;

3) 14;

4) 15;

5) ошибка компиляции: невыполнимый код в строке 4.

Вопрос 8.3.

Какое значение будет возвращено при вызове meth(12), если при вызове mexcept(int x) возникает исключительная ситуация ArithmeticException?

int meth(int x) {

int count=0;

try { count += x;

count += mexcept(count);

count++;

}catch(Exception e) {

--count;

return count;

}

finally {

count += 3;

return count;

}

}

1) 11;

2) 12;

3) 13;

4) 14;

5) ошибка компиляции из-за отсутствия return после блока finally.

Вопрос 8.4.

Какое из следующих определений метода show()может законно использоваться вместо комментария //КОД в классе Quest4?

class Base{

public void show(int i) {/*реализация*/}
}
public class Quest4 extends Base{
//КОД
}

1) void show (int i) throws Exception {/*реализация*/}

2) voidshow (long i) throws IOException {/*реализация*/}

3) voidshow (short i){ /*реализация*/}

4) public void show (int i) throws IOException {/*реализация*/}

Вопрос 8.5.

Дан код:

import java.io.*;

public class Quest5 {

//ОБЪЯВЛЕНИЕ ioRead()

public static void main(String[] args) {

try{

ioRead();

}catch(IOException e){}

}}

Какое объявление метода ioRead() должно быть использовано вместо комментария, чтобы компиляция и выполнение кода прошли успешно?

1) private static void ioRead()

throws IOException{};

2) public static void ioRead()

throw IOException{};

3) public static void ioRead(){};

4) public static void ioRead()

throws Exception{}.

 
Глава 9

ФАЙЛЫ. ПОТОКИ ВВОДА/ВЫВОДА

Потоки ввода/вывода используются для передачи данных в файловые потоки, на консоль или на сетевые соединения. Потоки представляют собой объекты соответствующих классов. Библиотека ввода/вывода предоставляет пользователю большое число классов и методов и постоянно обновляется.

Класс File

Для работы с физическим файлами и каталогами (директориями), расположенными на внешних носителях, в приложениях Java используются классы из пакета java.io.

Класс File служит для хранения и обработки в качестве объектов каталогов и имен файлов. Этот класс не содержит методы для работы с содержимым файла, но позволяет манипулировать такими свойствами файла, как права доступа, дата и время создания, путь в иерархии каталогов, создание, удаление файла, изменение его имени и каталога и т.д.

Объект класса File создается одним из нижеприведенных способов:

File myFile = new File(”\\com\\myfile.txt”);

File myDir = new File(”c:\\jdk1.6.0\\src\\java\\io”);

File myFile = new File(myDir, ”File.java”);

File myFile = new File(”c:\\com”, ”myfile.txt”);

File myFile = new File(new URI(”Интернет-адрес”));

В первом случае создается объект, соответствующий файлу, во втором – подкаталогу. Третий и четвертый случаи идентичны. Для создания объекта указы­вается каталог и имя файла. В пятом – создается объект, соответствующий адресу
в Интернете.

При создании объекта класса File любым из конструкторов компилятор не выполняет проверку на существование физического файла с заданным путем.

Существует разница между разделителями, употребляющимися при записи пути к файлу: для системы Unix – “/”, а для Windows – “\\”. Для случаев, когда неизвестно, в какой системе будет выполняться код, предусмотрены специальные поля в классе File:

public static final String separator;