Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Хитроумные задачки по Java

Anna | 3.06.2014 | нет комментариев

Совсем незадолго я сдал OCA Java SE 7 Programmer I. За время подготовки поспел решить большое число задач и извлечь из них много тонких моментов языка. Самые увлекательные и искусные — сберегал на грядущее. И вот у меня накопилась маленькая личная коллекция, лучшую часть которой я тут и опишу.

В восхитительной статье Знаешь ли ты JAVA, %username% и её 2-й части автор тоже поделился навыком позже подготовки. Впрочем я пришёл к итогу, что могу добавить что-то своё. Так и зародилась эта статья.

Задачи

Выходит, начнём. Я разбил все хитрости на маленькие задачки, которые составил намеренно для вас. Тонкости языка выдаются в чистом виде — без лишних наворотов и заблуждений, как это было в тестах. Также я рекомендую вам вначале ответить на вопрос с точностью до символа и записать куда-нибудь, а потом теснее глядеть верный результат. Увлекательно, сколько пользователей, решивших данный тест, ответит огромнее чем на половину? И не забывайте, что все эти примеры ориентированы на Java 7.

1)Скомпилируется ли данный код и если да — то каким будет итог?

long year = 201l;
System.out.print(year);  
Результат

201
Пояснение

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

В английском языке строчная буква l дюже схожа на цифру 1. И этим примером я хочу вас предостеречь —никогда не используйте l маленькую для обозначения long-литералов, правда Java это разрешает. И вообще не используете её там, где допустимо может быть единица. Легко возьмите за правило применять прописную L, как это и делает множество программистов.

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

Итог будет — 201, а не 2011, как может показаться на 1-й взор.

2)Скомпилируется ли данный код и если да — то каким будет итог?

int[][] array = {{1, 2, 3}, {0, 0, 0,},};       
System.out.println(Arrays.deepToString(array)); 
Результат

[[1, 2, 3], [0, 0, 0]]
Пояснение

Заблуждение традиционно вызывают две «лишних» запятых в конце.

Когда я 1-й раз увидел такой код в одном из тестов, я твёрдо решил, что код не скомпилируется — и оказался не прав. Некоторые из вас могут предположить, что не указанные элементы будут заполнены значением по-умолчанию — и это будет тоже неверно.

А итог будет примитивный — [[1, 2, 3], [0, 0, 0]]. Получается, что компилятор легко игнорирует одну лишнюю запятую в конце массива. Причём именно одну — две подряд теснее вызовут ошибку компиляции.

Изложение этой обстановки я без задач нашёл в спецификации языка — A trailing comma may appear after the last expression in an array initializer and is ignored.

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

3)Скомпилируется ли данный код и если да — то каким будет итог?

double $ = 0XD_EP2F;
System.out.print($);
Результат

888.0
Пояснение

Вы вероятно будете уверены в том, что данный код не скомпилируется. А вот и нет! он абсолютно рабочий.

Когда я постигал Java, я сразу взял на заметку, что из подчёркиваний и экспоненциальной формы для HEX дозволено сделать что-то дикое. Данный пример, безусловно же, не для применения в реальных планах — а для тренировки ваших познаний. Я думаю, это самый трудный пример и изюминка статьи. Первые два были лёгкой разминкой. Кто-нибудь ответил верно с первого раза?

В этом примере заключены два увлекательных момента. Первое — это наименование переменной. Называть переменную баксом крайне комично. Но никогда, никогда так не делайте в реальных планах. Правда компилятор не воспрещает такой подход — он не рекомендован, так как бакс применяется в технических целях. Скажем, для названия неизвестных и вложенных классов. MyClass$MyInnerClass, либо же MyClass$1. И дозволено без труда устроить кризис имён — один класс назвать легко MyClass$MyInnerClass, а иной — MyClass с вложенным MyInnerClass — и они будут иметь идентичное имя. Так что включать в свои переменные бакс — не рекомендуется.

Также абсолютно правилен такой код

double  _ = 8.0;

А сейчас давайте разберём 2-й момент — непринужденно сам литерал. Я наворотил его, как только мог. Для начала легко подметить, что это число записано в шестнадцатеричной форме. Но чай она допускает только A,B,C,D,E в качестве букв — кажете вы. Откуда тогда P и F? И причём здесь знак подчёркивания?

Обо всём по-порядку. F в конце обозначает, что данный литерал — типа float. И у нас он механически приводится к типу double. Дальше дюже увлекательный момент — P2. Если мы хотим записать шестнадцатеричное число в экспоненциальной форме — мы не сумеем применять E N, потому что E у нас может применяться в самом числе и может появиться неоднозначность. Но никто не мешает нам применятьBinaryExponentIndicator — указываем p и степень. Число будет умножено на 2 в указанной степени. В данном случае — на 4.

А вот символ подчёркивания — дюже комфортное новшество Java 7. Мы легко можем вставлять его в число и разделять им, скажем, разряды, либо группировать цифры. Компилятор его легко вырежет — он необходим для больше комфортного чтения, не больше.

Выходит, Дабы верно ответить на вопрос, нам необходимо припомнить, что System.out.print выводит переменную типа double в обыкновенной, десятичной форме. Следовательно, нам необходимо перевести из шестнадцатеричного вида в десятичный. Это теснее вовсе банальная задача. DE16 = 22210. Дальше, умножаем 222 на 22 и получаем 888. Наконец не забываем, что для типа double при отсутствии дробной части дописывается точка и нуль. Вот и результат — 888.0.

4)Скомпилируется ли данный код и если да — то каким будет итог?

public class Main{;
    public static void main(String[] args) {
        System.out.println(new Main().$_$()[2]);
    }

    ;short $_$()[] {{{
        return new short[007];
    }}};
};
Результат

0
Пояснение

Первое, что кидается в глаза — это применение точек с запятой там, где их дозволено не применять. Скажем позже объявления класса (как в С ). Либо же между членами класса. Для чего компилятор дал вероятность ставить их там — я так и не узнал. Но это абсолютно возможно.

Имя способа — абсолютно возможный идентификатор. А вот возвращаемый тип тут не short а массив из short. Компилятор разрешает 2 формы объявления массива — квадратные скобки до идентификатора и позже. Причём 1-й случай предуготовлен как-раз для способа. А что если испробовать 2-й случай для способа? Он тоже правилен, но выглядит страшно, следственно никогда не используйте его в реальных планах. Но такая вероятность есть.

Дальше — внутри способа легко два вложенных блока кода, которые дозволено безболезненно убрать. И в результате — способ легко возвращает массив из short длинной в 7 элементов, тот, что инициализируется нулями и элемент с индексом 2 равен 0. Ах да, 007 — это восьмеричный литерал. Но это тоже ни на что не влияет.

5)Скомпилируется ли данный код и если да — то каким будет итог?

public class Main {
    public static void main(String[] args) {
        ((Main) null).haбra();
    }

    static void haбra() {
        System.out.println("Hello habrahabr!");
    }
}
Результат

Hello habrahabr!
Пояснение

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

Значимый момент, тот, что я хотел донести до вас — дозволено нечаянно напечатать не тот символ из иного языка и потом длинно и невыносимо искать ошибку. Скажем, английская a и русская а визуально неотличимы (по крайней мере, в этом шрифте). Если единицу дозволено отличить от l хоть как-то, то здесь всё гораздо дрянней. Представьте себе обстановку — вы по каким-либо причинам нечаянно в конце в наименовании класса либо способа набрали русскую букву взамен английской. Либо что больше видимо — редактировали теснее существующее латинское наименование с русской раскладкой. Схожих букв между этими языками достаточно много, так что шанс ошибиться абсолютно есть. Автозаполнение будет вставлять международный идентификатор и всё будет отменно трудиться. Но вот если вы испробуете вызвать способ либо получить класс через рефлексию — получите ошибку, которую будет не так легко найти. Скорее, вы будете искать её в ином, так как наименования будут визуально совпадать — утратите время и силы.

Я хотел добавить ещё один пример, в котором два на внешний вид идентичных идентификатора будут содержать наружно одинаковые буквы из различных алфавитов, но по коду различные и посему идентификаторы будут также различные. И принудить объяснить, отчего код вызывает ошибку компиляции. Но это было бы слишком жестоко — на вид подвох никак не подметить.

Дальше — мы вызываем статический способ достаточно подлинно — приводим null к типу Main. И это не вызовет ошибки времени выполнения! Дело в том, что вызов статических способов разрешается на этапе компиляции и зависит только от типа объекта.

6)Скомпилируется ли данный код и если да — то каким будет итог?

Byte[] Byte[] = {{0}};
System.out.println(Byte);
System.out.println(Byte.class);
System.out.println(Byte.length);
System.out.println(new Byte("8"));
Результат

[[Ljava.lang.Byte;@6f171e30 (хэш может быть иным)
class java.lang.Byte
1
8
Пояснение

Выходит, вот вам ещё один пример кода, тот, что на 1-й взор вводит в заблуждение. Для начала вас должно смутить казалось бы следующее два раза друг за ином объявление массива из оболочек для байтов. Впрочем если вы наблюдательно постигали язык, вы обязаны знать, что есть два метода объявления массива — с квадратными скобками до имени и позже. Больше того, никто не мешает нам применять эти два метода единовременно — перед вами легко двумерный массив.

Дальше вас должно смутить имя массива, которое всецело совпадает с именем класса. Java разрешает называть локальные переменные классами стандартной библиотеки, при этом они будут перекрывать классы. Впрочем компилятор у Джавы разумный. Дюже разумный. Он сообразит, что позже оператора new может следовать только класс, да и перед .class — тоже. И в том контексте будет подразумеваться именно класс Byte, правда имя экземпляра должно его перекрывать.

Выводы

Мне нравится Java за её жёсткую стандартизованность. Спецификация определяет фактически все тонкости с дюже огромный точностью. А значит — изучив эти тонкости один раз — вы сумеете трудиться с ними всюду.

Хочу добавить, что скрин кода над катом взят из IDEA. Схему подсветки я сам разработал и использую её превосходства по максимуму. Если кому понравилась — могу скинуть.

Я подготавливался по нескольким различным системам тестирования. Для начала, отличным сайтом будетQuizful. Тесты «Java-Основы» и «Java-средний ярус» содержат дюже огромное количество пригодных задач. Дальше — дюже отменны тесты от ExamLab — гонял по ним SCJP 6. И наконец — для самого экзамена —Enthuware.

Позже прохождения бесчисленных тестов и решения задач «в уме» я стал отрадно удивлён, каким удобным и результативным стало написание кода. Тесты дюже отлично классифицируют и организуют познания. Я стал предвидеть в уме многие ошибки, а также выбирать лучшие решения. И не пожалел, что выучил некоторую часть API, правда вначале считал что не следует помнить наименования способов и классов назубок. Я считаю Java одним из наилучших языков в мире и рекомендую тесты для его углублённого постижения. А Java SE 7 Programmer I был вовсе не трудным — без труда набрал 96 из 100 баллов. Кто собирается сдавать — могу дать пару советов — напишите в личку.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB