Малоизвестные особенности Java. Вторая часть

    Как и обещал, предлагаю вашему вниманию следующие пять пунктов.

    Малоизвестные особенности Java. Первая часть

    6. Конфликт имён.

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

    Неужели ничего нельзя с этим поделать? Оказывается можно. Следующий код скомпилируется без проблем, несмотря на то, что класс List присутствует и в пакете java.awt, и в пакете java.util:

    import java.awt.*;
    import java.util.*;
    import java.util.List;
    
    public class Класс {
    	public static void main(String... аргументы) {
    		List простоСписок = Collections.emptyList();
    		System.out.println(простоСписок);
    	}
    }


    Достаточно дополнительно импортировать необходимый класс, java.util.List в данном примере.

    Тут, как вы заметили, используются кириллические идентификаторы. Да! Для кого-то это станет откровением, но Java… такая Java. Идентификатор может состоять из совершенно любых букв, помимо цифр, знаков подчёркивания и валюты США (однако последний знак ($) использовать не рекомендуется, он предназначен для системных нужд). Но оно нам надо? Разве только в целях обфускации. Только представьте себе, сколько разных идентификаторов можно сгенерировать всего-то из символов «А» английского, русского и греческого алфавитов…



    7. Инициализация коллекций.

    К каким только хитростям не приходится прибегать, чтобы упростить инициализацию коллекций и облегчить восприятие кода. Благодаря переменному числу аргументов в методе (varargs), которое появилось в пятой версии SDK, а также заботливому обновлению разработчиками стандартного API, ситуация стала немного лучше:

    List<Integer> theNumbers = new LinkedList<Integer>();
    Collections.addAll(theNumbers, 4, 8, 15, 16, 23, 42);


    Но этот код занимает две строки вместо одной и не кажется логически связанным. Можно использовать сторонние библиотеки, такие как Google Collections, или изобрести свой велосипед, но есть и более опрятный вариант:

    List<Integer> theNumbers = new LinkedList<Integer>(Arrays.asList(4, 8, 15, 16, 23, 42));


    А с появлением статического импорта во всё той же версии Java можно укоротить эту конструкцию ещё на одно слово:

    import static java.util.Arrays.*;
    
    // ...
    
    List<Integer> theNumbers = new LinkedList<Integer>(asList(4, 8, 15, 16, 23, 42));


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

    import static java.util.Arrays.*;
    
    // ...
    
    List<Integer> theNumbers = asList(4, 8, 15, 16, 23, 42);


    Эти приёмы позволяют инициализировать коллекции-наследники интерфейса Collection, простите за каламбур, но с картами так не получится, к сожалению. Хотя решение есть, смотрите предыдущую часть статьи.

    8. Подсписки.

    Интерфейс java.util.List, от которого наследуются в частности ArrayList и LinkedList, обладает замечательным методом: List.subList(). Он возвращает не новый список, как может показаться, а вид (view) списка, для которого этот метод был вызван, таким образом, что оба списка станут разделять хранимые элементы. Из этого вытекают прекрасные свойства:

    someList.subList(3, 7).clear();


    В данном примере из списка someList будут удалены четыре элемента, с третьего по седьмой (не включительно).

    Подсписки можно использовать в качестве диапазонов (ranges), которые являются довольно мощной идиомой программирования. Как часто вам требовалось обойти коллекцию, исключая первый или последний элемент, например? Теперь foreach становится ещё мощнее:

    import static java.util.Arrays.*;
    
    // ...
    
    List<Integer> theNumbers = asList(4, 8, 15, 16, 23, 42);
    int size = theNumbers.size();
    for (Integer number : theNumbers.subList(0, size - 1)) {
    	System.out.print(number + ", ");
    }
    System.out.println(theNumbers.get(size - 1));


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

    9. Cafe babe.

    Все скомпилированные классы и интерфейсы хранятся в специальных файлах с расширением .class. В них содержится байт-код, интерпретируемый виртуальной машиной Java. Чтобы быстро распознавать эти файлы, в них, в первых четырёх байтах, содержится метка, которая в шестнадцатеричном виде выглядит так: 0xCAFEBABE.

    Ну с первым словом всё ясно — Java, как известно, названа была не в честь тропического острова, а одноимённого сорта кофе, и среди знаков, используемых в шестнадцатеричной системе cчисления, литер «J» и «V» не нашлось. А вот чем руководствовались разработчики, выдумывая второе слово, остаётся только догадываться:)

    10. Исключительные ситуации.

    И напоследок небольшой кусочек кода:

    try {
    	throw null;
    } finally {
    	return;
    }


    Здесь выбрасывается NullPointerException и… теряется, исчезает без следа! Будьте бдительны.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 42

      +6
      «квалифицированное имя, то есть полное имя»
      Просто «полное имя». Ни в русском языке, ни в программистском жаргоне такого понятия нет. Появилось в результате подстрочного перевода qualified name.
        0
        Интерфейс java.util.List, от которого наследуются в частности ArrayList и LinkedList
        Также слух режет. Интерфейс они реализуют все таки, а не наследуют.
        +6
        А вот чем руководствовались разработчики, выдумывая второе слово, остаётся только догадываться:)

        Magic Number
          0
          А может по Фрейду? :)
            0
            Гослинг говорил, что спонтанно придумал babe и ему просто понравилось. Но с тем же успехом и более подходяще по смыслу можно было придумать cafe feed. Так что, по-моему, вопрос открыт.
              0
              At that time, it didn't seem terribly important or destined to go anywhere but the trash-can of history.

              Мне кажется это достаточно явное указание но то, что никакого тайного смысла нет. Зачем искать смысл там где его нет? :)
                +1
                На самом деле я пошутил по поводу тайного смысла. Улыбнитесь.
                  0
                  :) Ну вдруг вы серьёзно озаботились данной проблемой.
            +6
            Вы уж простите, но пункты 6 и 7 не могу назвать малоизвестными даже я со своими знаниями недоджуниора. Ну и говорить «А» и не говорить «Б» не хорошо:
            Подсписки следует использовать с осторожностью из-за особенностей, вытекающих из их сути (для подробностей смотрите документацию).
              +2
              Да и короткое гугление по 10 пункту выдало куда больше интересного, чем сухой намек в топике. http://stackoverflow.com/questions/48088/returning-from-a-finally-block-in-java
                +2
                С 9ым еще проще оказалось. Все написано в вики.
                  +1
                  С 8ым пунктом в спеку поленился лазить, а в документации написано, что проблемы могут быть при обращении к подсписку в случае изменения структуры и размера исходного листа. Полагаю, что это произойдет только в случае, когда будут затронуты элементы, входящие в диапазон подсписка. Других предупреждений нет.
                    0
                    Повторюсь, заголовок несколько неудачный… для Java-гуру, коим я не являюсь, а для остальных, я считаю, ничего себе так.

                    И, вот к примеру, я знаю что в C++ что a[b], что b[a] эквиваленты для массивов, поскольку арифметика указателей. Однако я довольно плохо разбираюсь в ООП-тонкостях этого языка.
                    +2
                    девятый пункт особенно порадовал
                      0
                      По поводу 6-ого пункта и русских имен: все зависит от JDK и операционной системы. Был реальный случай, когда в имени переменной был русский символ. Код собирался и компилировался под ubuntu, windows, а вот на solaris выдавало ошибку. В своих проектах придерживаемся латиницы из-за распределенной комманды.
                        +1
                        Стало быть виртуальная машина не соответствует спецификации.
                          +1
                          Я бы глянул в сторону кодировки файлов исходников. Возможно там символ в 1251. С UTF8 все должно работать.
                          +16
                          цикл статей немного странный — представленные примеры выглядят просто бессистемно выхваченными приемами программирования из тех, «о которых я до этого не знал». Большинство тривиальны, а часть не разобраны, как следовало-бы.

                          Возмём последний —

                          try {
                          throw null;
                          } finally {
                          return;
                          }


                          «throw null» выглядит так, как будто «null» эквивалентно «NullPointerException». Нв самом деле, «throw» тупо падает (сорри — бросает исключение) при попытке использовать нулевой объект. Потом управление переходит в «finally», как оно и должно, и «finally» переопределяет поведение.

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

                          Правильно было-бы объяснить отдельно два базовых правила языка:

                          1. если вызывается «throw» для создания исключительной ситуации, и в процессе её создания происходит другая исключительная ситуация, то первая отбрасывается. Простой пример может быть если конструктор исключительной ситуации слишком сложный и пытается, к примеру, писать в файл, или добраться до данных по нулевому указателю — будет сгенерирована другая исключительная ситуация.
                          Это именно то, что здесь и происходит.

                          2. В блоке «finally» или «catch» можно «заглушить исключительную ситуация». Пример с «return;» менее интересен, чем, например, более жизненный пример с вызовом «close» потока в «finally», который потенциально может сбоить. Проблема в том, что при сбое обработчика «finally» теряется оригинальная исключительная ситуация, и становится невозможно диагностировать проблему по логам.

                          Или возьмем пример с переменным количеством аргументов — эта супер-пупер фича, к сожалению, хромает на обе ноги с параметеризированными классами.

                          Или пример с Arrays — если про него рассказывать, то стоит упомянуть про ArrayUtils, CollectionUtils, etc.

                          — В-обсчем Ж-), автору пожелание — пришите больше, только старайтесь глубже прорабатывать материал. Этот цикл слишком поверхностный для своего названия.
                            0
                            пока я все так длинно писал, вверху уже накидали ссылок и пояснений :-)
                              +1
                              Finally, насколько я понял, может «вылететь» только если умер поток с этим блоком или свалилась jvm. Неожиданности вызваны именно использованием return, который находясь в finally замещает остальные точки возврата из метода (exception и return в try и catch). Везде как решение предлагают вариант только не использовать return в finally.
                                0
                                Да не, почему. Если я в «finally» напишу «throw MyException», то будет выброшено «MyException» — не важно, был-ли «finally» вызван штатно или в процессе обработки исключительной ситуации (которая будет потеряна). На самом деле Java простая как валенок в этом плане.
                              +1
                              Очень хорошо, что вообще написал и инициировал обсуждение темы.

                              Статья вместе с содержательными комментариями очень даже интересна.

                              Да и вообще есть правило:
                              Если не можешь решить для себя какой-то вопрос, напиши о нём в вызывающей форме в интернетах, будет много мнений для подумать.
                                0
                                Пункт 10 был для «домашнего задания». Но вы его решили почти за всех.

                                Я специально написал про throw null, это была «дополнительная магия». Так тоже можно бросать исключения. Это скорее из разряда багов.
                                0
                                try {
                                throw new ArithmeticException();
                                } finally {
                                throw new NullPointerException();
                                }

                                Ну по сути в данном случае все тоже самое. Только все точки возврата из метода в блоке try заменяет не return, а исключение из finally.
                                  0
                                  Промазал. Цель здесь
                                    0
                                    Я был уверен, что хабровчане на пункт №10 дадут развёрнутый ответ. Ещё можно в catch-блоке бросить с тем же эффектом.
                                    0
                                    Прошлый топик был интереснее и информативнее… Опять сиквел провалился

                                    Вопрос к гуру: что произойдет, если один и тот же объект хранится в разных списках? Вернее можно ли добавить его в 2 разных списка? На это ругнется компилятор?
                                      +2
                                      А Вы попробуйте сами ответить на свой вопрос. Думаю ответ очевиден.
                                      Я не гуру, если что.
                                        +1
                                        Для этого не надо быть гуру.
                                        Сорцы стандартных классов идут с JDK. Любая IDE вам в помощь (Ctrl+Click по классу).
                                        –2
                                        > 10. Исключительные ситуации.
                                        > Здесь выбрасывается NullPointerException и… теряется, исчезает без следа! Будьте бдительны.


                                        Правильнее сказать не «Будьте бдительны» а "Ни в коем случае не используйте return или throw в finally блоке — это считается плохой практикой".
                                          0
                                          Вот ещё малоизвестная и беполезная возможность:

                                          String doit() {
                                          for(;;);
                                          }


                                          — возвращаемый тип (String) «теряется, исчезает без следа»…
                                            0
                                            То что new LinkedList(Arrays.asList(4, 8, 15, 16, 23, 42)) по пути создаст еще один массив никого видимо не волнует :)
                                              –2
                                              Который тает под воздействием сборщика муссора.
                                                0
                                                Я и говорю — кого волнует такая ерунда, как GC :)
                                              +2
                                              Автору зачет. В яве есть много про что писать. Пишите еще. Особенно много можно написать про оптимизации компилятора, которые непредсказуемо меняют поведения кода :)
                                                0
                                                Ну так как же помимо цифр. Integer x1; вполне себе валидное имя переменной.
                                                  –1
                                                  Правильно, помимо буквы «x», в вашем идентификаторе используется цифра «1». В чём, собственно, проблема?
                                                  –2
                                                  Правильно, помимо буквы «x», в вашем идентификаторе используется цифра «1». В чём, собственно, проблема?
                                                    –2
                                                    Ой, не туда.
                                                    –1
                                                    если бы вы только знали какая автор сука
                                                      –1
                                                      моральный урод просто
                                                        0
                                                        Кто-то воспользовался моим аккаунтом. Впредь буду внимательнее с куками. Вот ведь же незадача.

                                                      Only users with full accounts can post comments. Log in, please.