Pull to refresh

Comments 55

Да, причем «эмуляция» goto приведённые — ничто. Это как раз причина по которой goto как таковой «запрещен» в высокоуровневых языках.
«Настоящий» goto позволяет перейти в середину цикла или в середину ветки исполнения.
Насколько я помню мануал по GW-BASIC, который я штудировал в 8 лет, даже там нельзя было войти в середину цикла по GOTO.
Какой-то это слишком модный и новый Basic, что в нем нет порядочного goto.
Проверяемые исключения также можно выбросить используя: Unsafe.getUnsafe().throwException(new SQLException()) ну или с помощью deprecated-метода: Thread.currentThread().stop(new SQLException());
только дальше Unsafe.getUnsafe() вы не уйдете
Thread.currentThread().stop(new SQLException()); больше не работает в Java8:

/**
 * Throws {@code UnsupportedOperationException}.
 *
 * @param obj ignored
 *
 * @deprecated This method was originally designed to force a thread to stop
 *        and throw a given {@code Throwable} as an exception. It was
 *        inherently unsafe (see {@link #stop()} for details), and furthermore
 *        could be used to generate exceptions that the target thread was
 *        not prepared to handle.
 *        For more information, see
 *        <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
 *        are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
 */
@Deprecated
public final synchronized void stop(Throwable obj) {
    throw new UnsupportedOperationException();
}
Есть ли в мире хоть один язык программирования, про который бы не появилось статей вроде этой? :)
Машина Тьюринга, подстановки Маркова.
Любой ассемблер можно приводить в качестве примера «языка программирования без подвохов» (хотя в этом случае подвохи надо ожидать от производителей процессоров, выпускающих не полностью одинаковые процессоры)
Чем больше в языке становится возможностей — тем больше извращений и недопониманий.

С ассемблером и процессорами можно таких хитростей на кучу статей набрать :-) В духе «в этом коде для таких-то процессоров стоит вставить пару раз NOP, чтобы предсказатель веток успел обновить свою таблицу, иначе у вас будет постоянный misprediction, и цикл будет выполняться гораздо медленнее».
Я же говорю, ассемблер в этом примере надо рассматривать в связке с процессором.
Вся сущность подобных проблем с «непонятностью» и «непредсказуемым поведением» — это вывод из самих понятий «алгоритм» и «исполнитель».
Алгоритм — это конечная запись некоторых команд, которые понимает и может интерпретировать исполнитель.
Если исполнители разные — они один и тот же алгоритм трактуют по-разному.
Добиться «абсолютно понятного и недвусмысленного поведения» можно как можно сильнее упростив систему команд и определив в тонкостях что та или иная команда значит.
Но это перфекционизм, вряд ли кому-либо нужный.
Я же говорю, ассемблер в этом примере надо рассматривать в связке с процессором.

Ну, в статье же рассматривается Java в связке с JVM :) Так почему бы не рассматривать и ассемблер в связке с процессором ;) Почти что одного поля ягоды ;)
Не почти что, а одного поля.
Хотя, справедливости ради следовало бы даже разделить и рассматривать отдельно
1) язык ассемблера и соответствуюший ему ассемблер-исполнитель который переводит текст программы в машинный код. Потом машкод как язык (вполне себе язычок кстати — я даже «исправлял» некоторые «программы» заменяя E6 61 на 90 90) и процессор как исполнитель
2) Java как язык и javac-исполнитель этого языка. Следующий этап — байткод как язык и JVM как исполнителя
UFO just landed and posted this here
Я знал примерно половину, но всё равно счёл статью достойной для перевода :-)
UFO just landed and posted this here
Конечно, в этой книжке 2005-го года рассказано о новой фиче Java 8 — кастовании к пересечению типов?

Книжка хорошая, не спорю. Но местами устарела.
UFO just landed and posted this here
Когда-то тоже любил подобные тонкости языка, даже статью об этом написал. Может быть, вам будет интересно почитать.

Хитрые задачи по Java
Читал когда-то тот ваш пост, любопытный. Хотя всевозможными сертификатами и тестами я не интересуюсь, а вот Java мне интересна.
На счёт generic в блоке throws странно. Почему вообще это разрашили делать? Ведь generic в Exception нельзя применять, а в throws почему то можно?!
P.S. Нельзя, означает, что class MyException extends Exception{} не скомпилируется, а, например, class MyException extends Date{} скомпилируется.
Честно говоря, недопонял ваш вопрос. Или там где-то парсер съел угловые скобки?
Ой. Точно! :) Не стал использовать source теги, а оказалось, что зря. Там ещё были Generic:

 class MyException<E> extends Exception{}


 class MyException<E> extends Date{}
Сегодня уже любой согласится, что проверяемые исключения были плохой идеей.

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

К примеру:
public FileInputStream(File file) throws FileNotFoundException {...} // FileNotFoundException - checked

явно заставляет программиста при использовании этого конструктора, включать мозг и что-то делать с возможностью того что данный файл будет не найден. Если это будет пустой catch блок — это тоже решение, но это решение программиста который пишет код, которое он (я надеюсь) обдумал.

Если бы в данном случае кидался unchecked то возможно было бы 2 печальных сценария:
  • не очень опытный программист — забыл бы обработать это сценарий и узнал бы о нем потом, когда было бы уже поздно
  • опытный программист — полез бы в документацию смотреть какое исключение там кидается в случае если что-то пошло не так

Если вы не любите checked exception никто не мешает заворачивать их вызовы в try/catch и пробрасывать дальше как unchecked. Тем более что если у Вас решено их не использовать то их вызовы будут использоваться только при работе с внешним API.

На StackOverflow есть хороший пост с обсуждением
Часто бывает надо написать код, в котором 100% никогда не будет исключения (а если будет, то это уже сигнал к тому, что программа в этом окружении вообще неработоспособна. Например, UnsupportedCharacterEncoding), но который требует обрамления в try-catch.

Ещё бывает ситуация, в которой хочется на некоторый блок действий глобально повесить try-catch(Throwable) и если произойдет любое исключение, считать, что операция в целом зафейлилась. А не разбираться с каждым конкретным исключением, которое может случиться внутри этого блока.

> Никто не мешает заворачивать их вызовы в try/catch и пробрасывать дальше как unchecked
Только это и остаётся. Но это замусоривает код. Возвращаясь к примеру с UnsupportedCharacterEncoding, можно отметить, что если нужно написать метод, который занимает 5 строчек, а теперь приходится к нему добавить еще 5 строк try-catch-rethrow RuntimeException, то это же чистый мусор.

Ну и конечно же, наличие checked исключений и легкость избавления от них пустым catch-блоком побуждает начинающих делать это в больших количествах.

Про пункт с не очень опытным программистом. Эта проблема решаема статическим анализатором (и в IDE?), который бы говорил «чувак, возможно ты пропустил исключение здесь!».
Только это и остаётся. Но это замусоривает код. Возвращаясь к примеру с UnsupportedCharacterEncoding, можно отметить, что если нужно написать метод, который занимает 5 строчек, а теперь приходится к нему добавить еще 5 строк try-catch-rethrow RuntimeException, то это же чистый мусор.

Согласен, но с восьмой версией и возможностью ловить несколько исключений проблема, на мой взгляд стала менее острой. Для меня плюсы от их использования, перевешивают необходимость написания 3-х строк кода. А вообще соглашусь с тем что UnsupportedChacacterEncoding — хороший пример плохого использования checked exceptions.

Ну и конечно же, наличие checked исключений и легкость избавления от них пустым catch-блоком побуждает начинающих делать это в больших количествах.

Бейте по рукам! :)
Мне кажется, что по пустому catch блоку ошибку понять легче, нежели по не обернутому методу.

Про пункт с не очень опытным программистом. Эта проблема решаема статическим анализатором (и в IDE?), который бы говорил «чувак, возможно ты пропустил исключение здесь!».

Статический анализатор будет работать либо только со стандартной библиотекой, что не решает всех проблем, либо опираться на документацию конкретного функционала, что тоже не всегда надежно работает :(
Заметьте, что в Java 8 появилось UncheckedIOException и новые API-методы используют его. Ну и да, весь Stream API крайне плохо совместим с checked exceptions. Ясно, что сами разработчики Java сменили парадигму.

В checked-исключениях есть плюсы, но красивая теория разбивается о суровую реальность, когда кто-нибудь неоправданно отнёс исключение не к той группе. Я сам бы не стал формулировать настолько самоуверенно как «Сегодня уже любой согласится» (Today, everyone agrees that checked exceptions were a mistake), но перевод есть перевод, надо мысль автора передавать, а не свою.
UFO just landed and posted this here
Да, насколько я понял, это получилось так, потому что там вручную изменил кеш Integer.
Я бы еще добавил что в bytecode не множественных catch, а блок finally отсутствует вообще и дублируется между телом try и catch-блоками. В итоге правильно составленный исходник будет расти экспоненциально при компиляции в байткод.
И все таки какое применение 3 пункта на практике? Думал instanceof по разному срабатывает, но нет. В чем подвох?
Если вы про аннотации типов, то вероятно это может принести пользу. Скажем,
@Nonnull int[][] x;

будет означать, что в x не может быть null. А, например,
int @Nonnull [][] x;

будет означать, что в x может быть null, но в x[0] не может быть. Если IDE поддерживает аннотации типов, вы увидите предупреждение при попытке присвоить null.

А запись int[] x[], на мой взгляд, абсолютно бесполезна. Просто тяжёлое наследие, дурное влияние Си.
Меньшим злом оказалось доработать JVM, чтобы она поддерживала такую возможность


Разве? На сколько я понимаю, эта доработка исключительно на уровне компилятора. Как раз если бы дорабатывали jvm, то научили бы ее нормально обрабатывать ковариантные возвращаемые типы, и бридж методы вообще бы не появились.
Если я правильно понял, до JVM 1.5 виртуальная машина не пропустила бы класс, где есть два метода, отличающиеся только возвращаемым типом. В этом смысле доработали JVM. К сожалению, не проверял, так ли это. Сейчас попробуй найди спецификацию JVM 1.4.
6 пункт и «разгадка» заставили меня негодовать. С помощью рефлексии, класслоадинга и проч. можно подменить вообще все что угодно, заставить любой код действовать как угодно. Я «угадал» что там подменили System.setOut().
И то верно. Я даже не подумал о таком варианте :-)
Все примеры хороши, но вот пункт 5-й — все интуитивно понятно. Не то, чтобы он тут лишний, но…
У автора много статей в стиле «Top 10». Не исключено, что он придумал сколько-то крутых примеров, а потом пришлось набивать до круглого числа :-) А вообще конкретно для меня этот пункт был неожиданный. Если б меня спросили, что будет, если char умножить на полтора, я бы предположил, что случится ошибка компиляции.
Прикол в том, что там тип не char а byte, поэтому я даже сразу предположил верный ответ ;)
Вот ещё интересный пример:
System.out.println(Arrays.asList(new Integer[] {1, 2, 3}));
System.out.println(Arrays.asList(new int[] {1, 2, 3}));

Что выведется в первом случае, и что во втором?
Это легко :-)
Скрытый текст
В первом [1, 2, 3], а во втором что-то типа [[I@12345678]. Со Stream.of, кстати, та же ситуёвина.
Легко-то легко, а вот если человек с таким не сталкивался, то и по граблям пробежаться легко :-) И не только Stream.of, а вообще любое место, где есть сочетание variable arguments с автобоксингом.
Никто не спорит, что FindBugs, PMD и CheckStyle (особенно в Sonar-е) — это нужно, полезно и удобно, но, к сожалению, не всегда возможно. Если вам дают legacy-проект с десятками-сотнями мегабайт кода, тысячами-десятками тысяч классов, и при этом он написан, скажем политкорректно, не очень качественно, то навесить на него любой статический анализатор — деяние, сравнимое с подвигом.
Глаза боятся, руки делают. Если вы можете скомпилировать проект, то и запустить FindBugs несложно. Даже если вы не укажете ему все зависимости, он работать будет (возможно, чуть хуже, но всё-таки). В мире Java прикручивать статический анализатор к мутному проекту на порядок проще, чем в мире C++, например. Я не так давно к очень мутному проекту прикручивал, где сборка идёт через стокилобайтные ant-скрипты, много раз запаковывается и снова распаковывается war, сорцы и зависимости раскиданы по сотне мест. Не больше пары часов заняло, теперь в Хадсоне каждую ночь выполняется. Именно для legacy-кода статический анализ нужен как воздух. FindBugs ловит много багов, которые люди делали 10 лет назад, а сейчас делают редко.

Конечно, одно дело — прикрутить, а другое — проанализировать результаты, понять, что серьёзное, а что — не очень, и пофиксать все баги =)

Если реально стоит такой вопрос — пиши в личку, помогу интегрировать FindBugs забесплатно по старой дружбе ;-)
Кстати, у меня на этом коде FindBugs не нашёл ошибку. В консоль выводилось, что Вы указали (т.е.фактически ошибка была), но FindBugs не показал её.
P.S. Проверял в IDEA с плагином FindBugs. При проверке другого кода ошибки находятся, т.е. FindBugs работает.
Sign up to leave a comment.

Articles