Pull to refresh

Вопросы и задания для русскоязычной книги Thinking in Java (Философия Java) Брюса Эккеля

Reading time11 min
Views177K


На вопрос «С какой книги начать изучение Java?» очень часто отвечают: Thinking in Java Брюса Эккеля.
Но вот беда — эта книга (4-е издание) не содержит в конце каждой из глав вопросы и практикум…

Именно того, что способствует наиболее эффективному изучению языка!

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

Почему бы не поделиться этими заметками-вопросами с Вами?

Приведен список составленных на данный момент вопросов.
Вопросы типа «Что такое внутренний класс?» отсутствуют. Такие определения, на мой взгляд, четко формируются по мере чтения главы и не требуют скучного и банального вопроса для подкрепления.

А также отсутствуют вопросы, наподобие «что такое stack? каковы особенности его работы?». Они являются базовыми для многих языков и для всего программирования в целом. Поэтому старался их избегать.

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

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

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


Глава 10. Внутренние классы
Вопросы:
1. К каким полям и методам внешнего класса имеет доступ внутренний класс? Как изменится ответ, если класс объявить статическим?
2. Напишите код, создающий объект внутреннего класса. Как изменится этот код, если внутренний класс будет объявлен как статический?
3. Приведите код, позволяющий вызвать метод внешнего класса из метода внутреннего класса.
4. В чем заключается ограничение внутреннего класса с точки зрения реализации им интерфесов?
5. Должен ли внутренний класс реализовывать тот же интерфейс, что и внешний?
6. Напишите код реализации фабрики с помощью анонимного класса.

Глава 11. Коллекции объектов
Вопросы
1. Что такое интерфейс Collection?
Ответ
Интерфейс Collection — корневой интерфейс, описывающий общую функциональность всех последовательных контейнеров (с. 301).

2. Могут ли контейнеры использоваться для хранения примитивов?
Ответ
Примитивы в качестве элементов контейнера выступать не могут. В контейнер можно поместить только ссылки на объекты. Однако механизм автоупаковки (Autoboxing) автоматически преобразует примитив в объектный тип. Следовательно программист может «забыть» про вышеупомянутое ограничение.
(ответ дан совместно с MrD)


3. Какой метод добавления элементов в контейнер ArrayList является предпочтительным:
Arrays.asList или Collections.addAll и почему?
Ответ
Collections.addAll является предпочтительным в силу его большей производительности (с. 282).

4. Перечислите несколько методов, доступных при работе с ArrayList.
Ответ
«contains(Object o)» – проверить наличия элемента в массиве.
«indexOf(Object o)» – получить индекс элемента.
«subList(index1, index2)» — скопировать часть элементов в новый контейнер.
«containsAll(Object[] o)» — проверка наличия элементов в контейнере.

5. Что возвращает метод remove(), вызванный для контейнера типа Queue?
Ответ
Метод Queue.remove() не только удаляет головной элемент очереди, но и возвращает его строковое значение.

6. Какие операции производятся быстрее при работе с LinkedList по сравнению с ArrayList?
Ответ
Вставка и удаления элементов из середины списка в случае использования LinkedList осуществляется быстрее (с. 291).

7. Какие типы контейнеров можно реализовывать с помощью LinkedList?
Ответ
Стек, очередь, двустороннюю очередь.

8. Чем семейство контейнеров Set отличается семейства контейнеров List? (спасибо MrD за правку)
Ответ
В семействе контейнеров Set данные могут храниться только в одном экземпляре (с. 294)

9. Произойдет ли исключение при попытке добавить в Set элемент, который уже в нем присутствует?
Ответ
Исключения не произойдет.

10. Чем контейнер PriorityQueue отличается от Queue?
Ответ
Элементы в PriorityQueue можно сортировать по определенным правилам.

11. Для чего используется Comparator в применении к PriorityQueue?
Ответ
Comparator позволяет задавать правила сортировки элементов PriorityQueue.

12. Что означают префиксы «Tree» и «Hash», «LinkedHash», например, для типа контейнера Set (TreeSet, HashSet, LinkedHashSet)
Ответ
Префикс «Tree» означает, что элементы контейнера хранятся в отсортированном порядке
(например, в алфавитном порядке или по возрастанию) Префикс «Hash» означает, что контейнер реализует хэширование для ускорения выборки. Префикс «LinkedHash» означает, что контейнере элементы хранятся в порядке вставки, и при этом обеспечивается быстрый доступ (c. 309)

13. Поддерживает ли контейнер типа «Map» интерфейс Iterable?
Ответ
Контейнер типа «Map» интерфейс Iterable не поддерживает (с. 305) Список классов, поддерживающих интерфейс Iterable:
docs.oracle.com/javase/1.5.0/docs/api/java/lang/Iterable.html

14. Работает ли синтаксис foreach для контейнера типа «Map»?
Ответ
Для контейнера типа «Map» синтаксис foreach не работает, потому что им не поддерживается интерфейс iterable

15. В чем заключается основное преимущество использования итератора для доступа к элементам контейнера?
Ответ
Итератор может быть использован для контейнеров различного типа (с. 290).

16. Какие новые возможности предоставляются итератором ListIterator по сравнению с обычным итератором?
Ответ
ListIterator предоставляет последовательный доступ к элементам не только из начала в конец контейнера, но и наоборот (с. 290).


Упражнения
11. 1. Создайте класс Fruit. Класс должен содержать поле int weight, конструктор с инициализацией этого поля и метод printWeight(), печатающий значение weight.
В методе main добавьте несколько объектов Fruit в контейнер List. Вызовите printWeight() для каждого из объектов следующими способами:
А) с помощью метода get();
Б) с помощью итератора.

Ответ
// Fruit.java
public class Fruit {
	private int weight = 0;
	
	public Fruit(int weight) {
		this.weight = weight;
	}
	
	public void printWeight() {
		System.out.println("Weight is: " + weight);

	}
}
// Apply.java
import java.util.*;

public class Apply {
	
	public static void main(String[] args) {
		List<Fruit> fruits = Arrays.asList(new Fruit(10), new Fruit(20));
		
		// a
		System.out.println("Task a: ");
		for (Fruit f : fruits) {
			f.printWeight();
		}
		
		// b
		System.out.println("Task b: ");
		Iterator<Fruit> it = fruits.iterator();
		
			while(it.hasNext()) {
				it.next().printWeight();
			}
	}
}



11.2. Класс Fruit из 11.1 поместите в контейнер map. Пусть в качестве ключа будет указано имя владельца, в качестве значения – объект типа Fruit. Реализуйте перебор каждого объекта Fruit по ключу и вызов метода printWeight() для найденного объекта: с помощью foreach; с помощью iterator.

Ответ
Спасибо quarantino за компактный вариант решения с итератором.
класс Fruit — см. задание 11.2

import java.util.*;
import java.util.Map.Entry;

public class Apply {
	
	public static void main(String[] args) {
		
		// Используем HashMap, так как сортировка не нужна
		Map<String, Fruit> fruit = new HashMap<String, Fruit>();
		
		fruit.put("Bob",  new Fruit(10));
		fruit.put("Mary", new Fruit(20));
		
		// перебор с помощью foreach
		System.out.println("With foreach");
		for (String key : fruit.keySet()) {
			fruit.get(key).printWeight();
		}
		
		// перебор с использованием итератора
		System.out.println("With iterator");
		Iterator<Entry<String, Fruit>> it = fruit.entrySet().iterator();
		while (it.hasNext()) {
			it.next().getValue().printWeight();
		}
	}
}





11.3. Используя Comparator для PriorityQueue обеспечьте сортировку строковых переменных по размеру.

Ответ
Примечание: пример взят из интернета.
// StringComparator.java
import java.util.Comparator;

public class StringComparator implements Comparator<String> {

	@Override
	public int compare(String s1, String s2) {
		if (s1.length() < s2.length()) {
			return -1;
		}
		if(s1.length() > s2.length()) {
			return 1;
		}
		
		return 0;
	}

}

// PriorityQueue.java

import java.util.Comparator;
import java.util.PriorityQueue;

public class PriorityQueueUse {
	
	public static void main(String[] args) {
		Comparator<String> comparator = new StringComparator();
		
		PriorityQueue<String> queue = new PriorityQueue<String>(10, comparator);
		queue.add("abcde");
		queue.add("abc");
		queue.add("abcdefghi");
		queue.add("a");
      
		
		// Элементы в очереди НЕ размещены в порядке возрастания длины String
		System.out.println("Before removing:");
		System.out.println(queue);
		
		// Но элементы удаляются из очереди В порядке возрастания длины String
		System.out.println();
		System.out.println("Look at removing order: ");
        while (queue.size() != 0)
        {
            System.out.println(queue.remove());
        }	
	}
}



Перевод упражнений из оригинала книги:
11.4. Создайте новый класс Gerbil с полем int gerbilNumber. Пусть поле инициализируется конструктором. Создайте также метод hop(), который печатает «Gerbil’s number which is hopping is: » и значение переменной gerbilNumber. Разместите объекты Gerbil в контейнер ArrayList. Реализуйте метод get() для прохода по списку с вызовом метода hop() для каждого из этих объектов.

11.5. Модифицируйте SimpleCollection.java так, чтобы использовать метод Set для переменной «с». Модифицируйте innerclasses/Sequence.java таким образом, чтобы туда можно было бы добавлять любое количество элементов.

11.6. Напишите класс Generator, в котором есть список имен героев ваших любимых фильмов (объекты типа String). Пусть каждый объект этого списка возвращается посредством метода next(). При достижении конца созданного списка необходимо вернуться обратно в начало списка.
Используйте класс Generator для заполнения контейнеров типа ArrayList, LinkedList, HashSet, LinkedHashSet, TreeSet. Напишите универсальный метод, который выводил бы содержание каждого из массивов.

11.7. Модифицируйте ListFeatures.java таким образом, чтобы в нем использовались объекты типа Integers вместо объектов типа Pets. Объясните различия в результатах работы, возникшие из-за этой модификации.

11.8. Повторите упражнение 11.7 для ListFeatures.java, но замените Pets на Strings.

11.9. Создайте класс, который генерирует инициализированный массив объектов другого класса. С помощью этого массива заполните контейнер типа List. Создайте еще один контейнер, используя subList() для List. Затем удалите элементы, которые вы извлекли с помощью subList(), из контейнера, из которого вы их извлекали.

11.10. Шутливое упражнение на использование различных контейнеров и их методов (придумано самостоятельно)
Пусть у нас есть список из расходов, изначально состоящий из 3-х элементов: «food», «medicine», «entertainment». Необходимо записать эти элементы в контейнер List, причем порядок их следования не должен измениться.

Что же произошло дальше?
1) Мы вдруг поняли, что забыли добавить в список расходы на выплату кредитов. Необходимо разместить запись «taxes» между «food» и «medicine» без использования индексов записей напрямую.
2) Жена притащила дополнительный отдельный список расходов с записями: «coat», «restaurant». Делать нечего: прибавить расходы в конец списка (чтобы не обольщалась).
3) Однако, теща притащила нам еще пару пунктиков: «theater», «home decoration» и «medicine». Может быть, какой-то из этих пунктов у Вас уже был. На всякий случай копируем содержание первого контейнера в новый контейнер типа Set и после, не задумываясь, добавляем эти пунктики в конец списка.
4) Не долго думая, мы создали ассоциативный динамический список расходов map<String, Integer>, где Integer – планируемые затраты на каждый из пунктов расходов (суммы выбирайте самостоятельно).
5) Мы резко разбогатели решили увеличить сумму всех затрат на 30%. Напишите метод, который это сделает за вас.
6) Стоп! Довольно с нас «home decoration!» И, пожалуй, на coat уходит слишком много. Занесите эти пункты в новый List и передайте методу, который, считав этот лист, в контейнере Map отыщет соответствующие статьи расходов и уменьшит их на … 80%!
7) Свалим-ка часть расходов на тещу. Причем, сделаем ей сюрприз! Сгенерим два случайных числа, которые будут определять начальный и конечный индексы, все расходы между которыми будут записаны в отдельный список List (который впоследствии будет теще подсунут). Не забудем обновить Map, выкинув из него соответствующие статьи расходов.
8) Напишем метод, который определит, на что уходит самая малая сумма расходов, напечатаем название этих расходов и с гордостью удалим этот элемент из списка.
9) Однако, к нам подвалили дополнительные расходики: «food» и «restaurant», оформленные в виде очередного массива! Проверим, есть ли они в нашем списке эти пункты? И если хотя бы один расход есть, то ни в коем случае не будем их добавлять! Если же ничего этого нет, то (ничего не поделаешь), добавляем сразу оба эти пункта (как я проголодался!).
10) Мы сошли с ума! Прочитаем из списка Map все Integer суммы оставшихся у нас расходов, запишем их в «какой-нибудь» контейнер, который сам их нам рассортирует по возрастанию, потом в ярости перемешаем и удалим с проходом в обратном порядке, неистово применив для этого специальный итератор.






Глава 17. Параллельное выполнение
1. Объясните разницу между методами start() и run() для потоков.
Ответ
start() – метод, вызов которого запускает поток. Метод не требует определения.
Run() – метод, исполняемый после вызова start(). Требует переопределения и содержит код-задачу, с целью выполнения которой был запущен поток.

2. Удалит ли сборщик мусора поток, ссылка на который не сохранена ни в какой переменной:
New Thread(new ThreadTask()).start;

Ответ
Каждый поток самостоятельно себя регистрирует, и сборщик мусора не вправе его удалить (с. 560).

3. Чем отличается поток-демон от обычного потока?
Ответ
После завершения всех основных потоков, потоки-демоны также завершают свою работу. При этом не имеет значения, выполнили эти потоки свои задачи или нет (с. 568).

4. Пусть поток-демон генерирует новый поток. Является ли этот поток обычным или же потоком-демоном?
Ответ
Такой метод является потоком-демоном (с .571).

5. Назначение метода join(), вызываемого для потока.
Ответ
Метод thread.join() для потока может быть вызван другим (текущим) потоком. В результате текущий поток приостанавливает свою работу. Текущий поток возобновит свою работу, когда поток thread.join() завершит свою задачу (с. 576)

6. Что такое атомарные операции с точки зрения прерываний? Приведите примеры атомарных и неатомарных операций.
Ответ
Атомарные операции – это, как правило, простые операции, которые осуществляются без возможности прерывания. То есть нет возможности получить промежуточное состояние поля.
Примеры атомарных операций: присваивание, возврат значения.
Примеры неатомарных операций: инкремент, декремент (с. 579)

7. Для чего необходимо использовать ключевое слово synchronized?
Ответ
Для предотвращения конфликтов между потоками, связанных с доступом к общим ресурсам (с. 581)

8. Дайте определение мьютекса.
Ответ
Мьютекс — механизм взаимоблокировки, реализуемый следующим образом. Код помещается в секцию, называемую секцией блокировки. Этот код должен быть пройдет за раз только одним потоком. Другой поток в период этого прохода не может получить доступ к секции блокировки (с. 581).

9. Расскажите, как можно задать доступ к разделяемому ресурсу с помощью методов, объявленных как synchronized.
Ответ
Разделяемый ресурс можно поместить внутрь объекта. Далее любой метод, получающий доступ к ресурсу, объявляется как synchronized. Пусть некая задача выполняется потоком внутри одного из таких методов. При этом все прочие потоки не могут зайти ни в один synchronized метод. Они смогут это сделать лишь после того, как первый поток не завершит работу с этими методами (с. 582).

10. Для чего предназначен объект Lock?
Ответ
Объект lock предназначен для явного управления мьютексами. Такой объект, явно созданный в программе, позволяет установить или снять блокировку (с. 583).

11. Следует ли за вызовом lock() размещать блок try-catch?
Ответ
Размещать такой блок следует. Более того, желательно добавить секцию finally. В этой секции следует вызвать unlock(). Это позволяет гарантировать снятие блокировки в случае возникновения исключения.

12. В чем состоит ограничение при использовании synchronized по сравнению с Lock?
Ответ
Используя synchronized нельзя получить блокировку с неудачным исходом. Также нельзя получить блокировку в течение некоторого промежутка времени, после которого может последовать отказ (с. 584).


Упражнения
1. Реализуйте интерфейс Runnable в произвольном классе.
Внутри переопределенного метода run() напечатайте сообщение. После этого вызовите yield(). Повторите два вышеописанных действия три раза, затем завершите метод run().
Поместите в начале конструктора сообщение, оповещающее о начале выполнения конструктора. Поместите также сообщение, оповещающее о завершении выполнения потока. Создайте несколько таких задач и решите их с помощью нескольких потоков (с. 826 оригинала).
2. В соответствии с формой generics/Fibonacci.java создайте задачу, которая генерирует последовательность из n чисел Фибоначчи. Причем n должен передаваться конструктору этой задачи. Создайте несколько таких задач и решите их с помощью нескольких потоков (с. 804 оригинала).
3. Повторите упражнения 1 и 2, используя различные типы executors (с. 804 оригинала).
4. Модифицируйте упражнение 2 таким образом, чтобы Callable суммировал все значения чисел Фибоначчи. Сделайте несколько таких задач и выведите результаты решения этих задач (с. 807 оригинала).



Вопросы и упражнения будут добавляться периодически, по мере их составления и перевода. О каждом таком добавлении будет написано в конце статьи (разверните «Обновления») и написано в комментариях.

Не гарантирую, что главы будут добавляться последовательно. Например, после вопросника к 11 главе может появиться вопросник к 17-й.

Предлагаю Вам тоже принять участие в составлении вопросов и практикума по главам книги!
Прошу желающих принять участие написать мне в «диалог». Сразу сообщу, что вопросы и упражнения по главам, идущим до №10 публиковать не буду (материал уже изучен).

Полезная информация:
В англоязычном оригинале Thinking in Java после параграфов даются упражнения (Спасибо, iflista)
В англоязычной книге «Annotated Solution Guide for Thinking in Java» приводятся решения этих упражнений (Спасибо, WraithOW).

Ссылки:
Образовательный материал и задания по Java с сайта Oracle (eng)
docs.oracle.com/javase/tutorial/java/index.html

Обновления
Update 1 (15.02.2014): Переработаны вопросы по главе 11, добавлены ответы к вопросам. Добавлено 3 упражнения к главе 11 (Спасибо, soundie за идею позаимствовать часть их них из предущих изданий)
Update 2 (18.02.2014): Добавлены часть вопросов и упражнений к главе 17. Оставшаяся часть будет опубликована позднее.
Only registered users can participate in poll. Log in, please.
Ваше мнение об упражнениях по программированию, сформулированных в шутливой форме
24.01% Упражнения все-таки должны формулироваться строгим языком79
75.99% Люблю такие упражнения, потому что «строгих упражнений» полным-полно и порой хочется их чем-нибудь разбавить250
329 users voted. 98 users abstained.
Only registered users can participate in poll. Log in, please.
Полезно ли будет переводить упражнения, опубликованные в англоязычном оригинале книги и приводить их в статье?
91.08% Да, было бы очень полезно!286
8.92% Приводить эти упражнения не нужно28
314 users voted. 74 users abstained.
Only registered users can participate in poll. Log in, please.
Нужно ли приводить решения упражнений из книги Annotated Solution Guide for Thinking in Java?
82.82% Да, чтобы для поиска решений не залезать в книгу241
17.18% Нет. Мне не трудно в книгу залезть50
291 users voted. 81 users abstained.
Tags:
Hubs:
+17
Comments21

Articles

Change theme settings