Effective Java

Školení, která pořádám

The book Effective Java is like a Java Bible for many programmers. It is a good book, I've learned a lot from it and I've spotted only a couple of unclear or wrong parts.

From a different point of view, it might also serve as a “What Went Wrong” description of the Java language and libraries. Let me quote some paragraphs from the book without further comments.

Item 1

For example, the constructor BigInteger(int, int, Random), which returns a BigInteger that is probably prime, would have been better expressed as a static factory method named BigInteger.probablePrime.

Unfortunately, the standard collection implementations such as HashMap do not have factory methods as of release 1.6, but you can put these methods in your own utility class.

Item 2

Unfortunately, the JavaBeans pattern has serious disadvantages of its own. Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction.

The newInstance method always attempts to invoke the class’s parameterless constructor, which may not even exist. You don’t get a compile-time error if the class has no accessible parameterless constructor.

Item 5

This program gets the right answer, but it is much slower than it should be, due to a one-character typographical error. The variable sum is declared as a Long instead of a long.

Item 7

Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems.

Don’t be seduced by the methods System.gc and System.runFinalization. They may increase the odds of finalizers getting executed, but they don’t guarantee it. The only methods that claim to guarantee finalization are System.runFinalizersOnExit and its evil twin, Runtime.runFinalizersOnExit. These methods are fatally flawed and have been deprecated.

If an uncaught exception is thrown during finalization, the exception is ignored, and finalization of that object terminates.

The four classes cited as examples of the explicit termination method pattern (FileInputStream, FileOutputStream, Timer, and Connection) have finalizers that serve as safety nets in case their termination methods aren’t called. Unfortunately these finalizers do not log warnings. Such warnings generally can’t be added after an API is published, as it would appear to break existing clients.

If a subclass implementor overrides a superclass finalizer but forgets to invoke it, the superclass finalizer will never be invoked.

Item 8

The equals implementation for Timestamp does violate symmetry and can cause erratic behavior if Timestamp and Date objects are used in the same collection or are otherwise intermixed. This behavior of the Timestamp class was a mistake and should not be emulated.

For example, java.net.URL’s equals method relies on comparison of the IP addresses of the hosts associated with the URLs. Translating a host name to an IP address can require network access, and it isn’t guaranteed to yield the same results over time. This can cause the URL equals method to violate the equals contract and has caused problems in practice.
Note: The defined behavior for equals is known to be inconsistent with virtual hosting in HTTP.

Item 11

The Cloneable interface was intended as a mixin interface for objects to advertise that they permit cloning. Unfortunately, it fails to serve this purpose. Its primary flaw is that it lacks a clone method, and Object’s clone method is protected.

If a class implements Cloneable, Object’s clone method returns a field-by-field copy of the object; otherwise it throws CloneNotSupportedException. This is a highly atypical use of interfaces and not one to be emulated.

The general intent is that, for any object x, the expression x.clone() != x will be true, and the expression x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements. While it is typically the case that x.clone().equals(x) will be true, this is not an absolute requirement.

Item 12

For example, consider the BigDecimal class, whose compareTo method is inconsistent with equals.

Item 14

Several classes in the Java platform libraries violate the advice that public classes should not expose fields directly. Prominent examples include the Point and Dimension classes in the java.awt package. Rather than examples to be emulated, these classes should be regarded as cautionary tales. The decision to expose the internals of the Dimension class resulted in a serious performance problem that is still with us today.

Item 15

This was not well understood in the early days of the Java platform, so the String class does have a copy constructor, but it should rarely, if ever, be used.

It was not widely understood that immutable classes had to be effectively final when BigInteger and BigDecimal were written, so all of their methods may be overridden. Unfortunately, this could not be corrected after the fact while preserving backward compatibility. If you write a class whose security depends on the immutability of a BigInteger or BigDecimal argument from an untrusted client, you must check to see that the argument is a “real” BigInteger or BigDecimal, rather than an instance of an untrusted subclass.

There are several classes in the Java platform libraries, such as java.util.Date and java.awt.Point, that should have been immutable but aren’t.

Item 16

There are a number of obvious violations of this principle in the Java platform libraries. For example, a stack is not a vector, so Stack should not extend Vector. Similarly, a property list is not a hash table, so Properties should not extend Hashtable.

In the case of Properties, the designers intended that only strings be allowed as keys and values, but direct access to the underlying Hashtable allows this invariant to be violated.

Item 17

Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run.

If you decide to implement Serializable in a class designed for inheritance and the class has a readResolve or writeReplace method, you must make the readResolve or writeReplace method protected rather than private. If these methods are private, they will be silently ignored by subclasses.

Item 19

There are several constant interfaces in the Java platform libraries, such as java.io.ObjectStreamConstants. These interfaces should be regarded as anomalies and should not be emulated.

Item 25

For example, it is illegal to create an array of a generic type, a parameterized type, or a type parameter.

It also means that you can get confusing warnings when using varargs methods in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. If the element type of this array is not reifiable, you get a warning. There is little you can do about these warnings other than to suppress them, and to avoid mixing generics and varargs in your APIs.

Item 27

It would be nice if the language did the same kind of type inference when invoking constructors on generic types as it does when invoking generic methods.

Item 28

It doesn’t seem right that we can’t put an element back into the list that we just took it out of.

Item 29

The next thing to notice is that the value type of the favorites Map is simply Object. In other words, the Map does not guarantee the type relationship between keys and values, which is that every value is of the type represented by its key. In fact, Java’s type system is not powerful enough to express this.

Item 30

Enum constructors aren’t permitted to access the enum’s static fields, except for compile-time constant fields. This restriction is necessary because these static fields have not yet been initialized when the constructors run.

Item 37

Inexplicably, the authors of the ObjectOutputStream API did not take advantage of the Serializable interface in declaring the write method. The method’s argument type should have been Serializable rather than Object. As it stands, an attempt to call ObjectOutputStream.write on an object that doesn’t implement Serializable will fail only at runtime, but it didn’t have to be that way.

Item 40

When in doubt, look to the Java library APIs for guidance. While there are plenty of inconsistencies – inevitable, given the size and scope of these libraries – there is also a fair amount of consensus.

Item 41

The behavior of this program is counterintuitive because selection among overloaded methods is static, while selection among overridden methods is dynamic.

The confusing behavior demonstrated by the previous example came about because the List<E> interface has two overloadings of the remove method: remove(E) and remove(int).

The rules that determine which overloading is selected are extremely complex. They take up thirty-three pages in the language specification, and few programmers understand all of their subtleties.

For example, the String class exports two overloaded static factory methods, valueOf(char[]) and valueOf(Object), that do completely different things when passed the same object reference. There is no real justification for this, and it should be regarded as an anomaly with the potential for real confusion.

Item 42

Because of the unfortunate decision to retrofit Arrays.asList as a varargs method in release 1.5, this program now compiles without error or warning. Running the program, however, produces output that is both unintended and useless.

Every invocation of a varargs method causes an array allocation and initialization.

Item 52

More generally, if a concrete class has no associated interface, then you have no choice but to refer to it by its class whether or not it represents a value. The Random class falls into this category.

Item 55

Consider the getSize method in the java.awt.Component class. The decision that this performance-critical method was to return a Dimension instance, coupled with the decision that Dimension instances are mutable, forces any implementation of this method to allocate a new Dimension instance on every invocation.

The “semantic gap” between what the programmer writes and what the CPU executes is far greater than in traditional statically compiled languages, which makes it very difficult to reliably predict the performance consequences of any optimization.

Item 59

One example of an exception that fails this test is CloneNotSupportedException. It is thrown by Object.clone, which should be invoked only on objects that implement Cloneable. In practice, the catch block almost always has the character of an assertion failure. The checked nature of the exception provides no benefit to the programmer, but it requires effort and complicates programs.

Item 63

For example, instead of a String constructor, IndexOutOfBoundsException could have had a constructor that looks like this… Unfortunately, the Java platform libraries do not make heavy use of this idiom, but it is highly recommended.

Item 66

The libraries provide the Thread.stop method, but this method was deprecated long ago because it is inherently unsafe – its use can result in data corruption.

Item 67

Let the client synchronize externally where it is appropriate. In the early days of the Java platform, many classes violated these guidelines. For example, StringBuffer instances are almost always used by a single thread, yet they perform internal synchronization.

Item 71

Prior to release 1.5, the double-check idiom did not work reliably because the semantics of the volatile modifier were not strong enough to support it.

Item 73

In an ironic twist, the ThreadGroup API is weak from a thread safety standpoint. To get a list of the active threads in a thread group, you must invoke the enumerate method, which takes as a parameter an array large enough to hold all the active threads. The activeCount method returns the number of active threads in a thread group, but there is no guarantee that this count will still be accurate once an array has been allocated and passed to the enumerate method. If the thread count has increased and the array is too small, the enumerate method silently ignores any threads for which there is no room in the array.

Item 74

Relying on the default deserialization mechanism can easily leave objects open to invariant corruption and illegal access.

Item 75

It will dictate the serialized form forever. This is not just a theoretical problem. It happened to several classes in the Java platform libraries, including BigInteger.

Item 76

Note also that defensive copying is not possible for final fields. To use the readObject method, we must make the start and end fields nonfinal.

Do not use the writeUnshared and readUnshared methods. They are typically faster than defensive copying, but they don’t provide the necessary safety guarantee.

I didn't finish reading the book so I might add other interesting quotes later.

Jakub Vrána, Osobní, 15.11.2013, comments: 6 (new: 0)

Comments

ikona v6ak OpenID:

#1: There is such static factory, try java.math.BigInteger.probablePrime(256, new java.util.Random())

#17: I hope the design is good. A compile-time error or warning would be better, though.

ikona Jakub Vrána OpenID:

#1: Yeah, it was added later. The crazy constructor is still there though. Also, is it a responsibility of BigInteger class to generate numbers which are probably prime?

ikona v6ak OpenID:

The constructor can't be removed. It should have been deprecated. The documentation about it is very funny as of Java 7. There are two such constructors. None of them is deprecated. One of them contains a recomendation about using probablyPrime (see http://docs.oracle.com/javase/7/docs/api/….util.Random%29 ). The second one does not (see http://docs.oracle.com/javase/7/docs/api/….util.Random%29 ).

The good point is anybody can use the BigInteger.probablePrime factory and many programmers will probably do so. It differs from my first understanding of this article. I really wondered when reading this article, because I had used it recently.

There is one non-mentioned weird point, though. When you need to specify certainty, you need the weird constructor, because there is no alternative BigInteger.probablePrime that accepts certainty argument.

J.:

jezisi maria clovece vy mate ale problemy. keby sa kazdy drzal dokonaleho kodu doteraz by sme na telefonovanie pouzivali velke vytacacie polkilove bazmeky namiesto super duper iphonov. pan vrana prebudte sa konecne, IT naozaj NIE JE o tom co tu pisete.

Ori:

Bez optimalizacie by si ten super duper ajfon ani nemal a keď ta článok nezaujíma tak sa zdrž komentára

Hido:

Preco by sa mal zdrzat, komentare nemusia a zvacsa niesu len pozitivne.

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.