Thinking in Java by Bruce Eckel
Apr 29, 2017 · 6 minute read · CommentsКак же долго я читал эту книгу, года три, не меньше. Разные заходы, на разных языках. И наконец дочитал.
Поэтому это обычная рецензия с краткими заметками. Множество людей уже до меня ее прочитали, разобрали и законспектировали. Оставлю маленькое напоминание о том, как сложно это было. Надо бы повторить.
Заметки:
-
finalize() - вызывается только в процессе garbage collection. Т.е может так и не быть вызванным. Его полезно использовать например для обьектов, что открывают файлы - перед уничтожением обьекта файл должен быть закрыт.
-
A package contains a group of classes, organized together under a single namespace.
-
When you compile a .java file, you get an output file for each class in the .java file. Each output file has the name of a class in the .java file, but with an extension of .class. Thus you can end up with quite a few .class files from a small number of .java files.
-
The private keyword means that no one can access that member except the class that contains that member, inside methods of that class. Other classes in the same package cannot access private members, so it’s as if you’re even insulating the class against yourself.
-
The trick is to use the classes without soiling the existing code. In this chapter you’ll see two ways to accomplish this. The first is quite straightforward: you simply create objects of your existing class inside the new class. This is called composition, because the new class is composed of objects of existing classes. You’re simply reusing the functionality of the code, not its form.
-
The second approach is more subtle. It creates a new class as a type of an existing class. You literally take the form of the existing class and add code to it without modifying the existing class. This technique is called inheritance, and the compiler does most of the work. Inheritance is one of the cornerstones of object-oriented programming, and has additional implications that will be explored in the Polymorphism chapter.
-
A third relationship, which is not directly supported by Java, is called delegation. This is midway between inheritance and composition, because you place a member object in the class you’re building (like composition), but at the same time you expose all the methods from the member object in your new class (like inheritance)
-
Wouldn’t it be much nicer if you could just write a single method that takes the base class as its argument, and not any of the specific derived classes? That is, wouldn’t it be nice if you could forget that there are derived classes, and write your code to talk only to the base class? That’s exactly what polymorphism allows you to do. However, most programmers who come from a procedural programming background have a bit of trouble with the way polymorphism works.
-
Once you learn about polymorphism, you can begin to think that everything happens polymorphically. However, only ordinary method calls can be polymorphic. For example, if you access a field directly, that access will be resolved at compile time
-
If a method is static, it doesn’t behave polymorphically. Static methods are associated with the class, and not the individual objects.
-
Each inner class can independently inherit from an implementation. Thus, the inner class is not limited by whether the outer class is already inheriting from an implementation. Without the ability that inner classes provide to inherit—in effect—from more than one concrete or abstract class, some design and programming problems would be intractable. So one way to look at the inner class is as the rest of the solution of the multiple-inheritance problem. Interfaces solve part of the problem, but inner classes effectively allow “multiple implementation inheritance.” That is, inner classes effectively allow you to inherit from more than one non-interface.
-
The Java container library takes the idea of “holding your objects” and divides it into two distinct concepts, expressed as the basic interfaces of the library:
- Collection: a sequence of individual elements with one or more rules applied to them. A List must hold the elements in the way that they were inserted, a Set cannot have duplicate elements, and a Queue produces the elements in the order determined by a queuing discipline (usually the same order in which they are inserted).
- Map: a group of key-value object pairs, allowing you to look up a value using a key. An ArrayList allows you to look up an object using a number, so in a sense it associates numbers to objects. A map allows you to look up an object using another object. It’s also called an associative array, because it associates objects with other objects, or a dictionary, because you look up a value object using a key object just like you look up a definition using a word. Maps are powerful programming tools.
-
There are two types of List:
- The basic ArrayList, which excels at randomly accessing elements, but is slower when inserting and removing elements in the middle of a List.
- The LinkedList, which provides optimal sequential access, with inexpensive insertions and deletions from the middle of the List. A LinkedList is relatively slow for random access, but it has a larger feature set than the ArrayList.
-
One solution is what I call the Adapter Method idiom. The “Adapter” part comes from design patterns, because you must provide a particular interface to satisfy the foreach statement. When you have one interface and you need another one, writing an adapter solves the problem. Here, I want to add the ability to produce a reverse iterator to the default forward iterator, so I can’t override. Instead, I add a method that produces an Iterable object which can then be used in the foreach statement. As you see here, this allows us to provide multiple ways to use foreach:
-
Exception handling, two methods - Termination vs. resumption
-
Exception guidelines Use exceptions to:
- Handle problems at the appropriate level. (Avoid catching exceptions unless you know what to do with them.)
- Fix the problem and call the method that caused the exception again.
- Patch things up and continue without retrying the method.
- Calculate some alternative result instead of what the method was supposed to produce.
- Do whatever you can in the current context and rethrow the same exception to a higher context.
- Do whatever you can in the current context and throw a different exception to a higher context.
- Terminate the program.
- Simplify. (If your exception scheme makes things more complicated, then it is painful and annoying to use.)
- Make your library and program safer. (This is a short-term investment for debugging, and a long-term investment for application robustness.)