Effective Java
Rate it:
Open Preview
Kindle Notes & Highlights
77%
Flag icon
Besides offering excellent concurrency, ConcurrentHashMap is very fast. On my machine, the intern method above is over six times faster than String.intern (but keep in mind that String.intern must employ some strategy to keep from leaking memory in a long-lived application).
Robert
Yea, comparing to String.intern isn't that helpful. Would have been more helpful to compare with synchornizedMap and even regular HashMap.
78%
Flag icon
Map<K, V> m = Collections.synchronizedMap(new HashMap<>()); Set<K> s = m.keySet();  // Needn't be in synchronized block     ... synchronized(m) {  // Synchronizing on m, not s!
Robert
What happens if the map is modified between `m.keySet()` and `synchronized(m)`?
Robert
· Flag
Robert
Because the keySet is just a view, and so it's okay if the "set" changes between when you "fetch" it and use it.
78%
Flag icon
In the presence of multiple threads, lazy initialization is tricky. If two or more threads share a lazily initialized field, it is critical that some form of synchronization be employed, or severe bugs can result (Item 78).
Robert
A bit more specificity would be helpful here. I think it's safe to say that the initialization of the field is often idempotent (i.e., the extra initialization shouldn't have side effects other than inefficient), and redundant initialization should only be a problem if the it's creating objects that depend on a mutuable shared, non-thread-safe fields (should be rare as well as bad). Edit: this case is mentioned as acceptable later.
78%
Flag icon
If you need to use lazy initialization for performance on a static field, use the lazy initialization holder class idiom. This idiom exploits the guarantee that a class will not be initialized until it is used [JLS, 12.4.1].
Robert
Beware, this lazy initialization applies at the class level! That is, the first access of FieldHolder initializes everything in the FieldHolder class. So, if you have multiple fields you want to use this pattern on, and you put them in one holder class, all of the fields in it will be initialized at the same time (potentially costly). If that's a problem, you have to create a holder for each field (uglier).
79%
Flag icon
Two variants of the double-check idiom bear noting. Occasionally, you may need to lazily initialize an instance field that can tolerate repeated initialization. If you find yourself in this situation, you can use a variant of the double-check idiom that dispenses with the second check. It is, not surprisingly, known as the single-check idiom. Here is how it looks. Note that field is still declared volatile
Robert
Ah, here's the exception I asked about on the earlier statement (about a cheaper strategy if the operation is idempotent: https://www.goodreads.com/notes/37646821-effective-java/74-robert/d28eee38-5bdd-40cf-95e9-8eaffc16c5be).
79%
Flag icon
If you don’t care whether every thread recalculates the value of a field, and the type of the field is a primitive other than long or double, then you may choose to remove the volatile modifier from the field declaration in the single-check idiom. This variant is known as the racy single-check idiom. It speeds up field access on some architectures, at the expense of additional initializations (up to one per thread that accesses the field).
Robert
> up to once per thread. Only for threads started before the field was initialized (for the most part) right?
79%
Flag icon
The security issues described in previous editions of this book turned out to be every bit as serious as some had feared. The vulnerabilities discussed in the early 2000s were transformed into serious exploits over the next decade, famously including a ransomware attack on the San Francisco Metropolitan Transit Agency Municipal Railway (SFMTA Muni) that shut down the entire fare collection system for two days in November 2016
Robert
Hmm, do I vaguely remember this?
80%
Flag icon
Therefore, deserializing the set causes the hashCode method to be invoked over 2100 times. Other than the fact that the deserialization is taking forever, the deserializer has no indication that anything is amiss. Few objects are produced, and the stack depth is bounded. So what can you do defend against these problems? You open yourself up to attack whenever you deserialize a byte stream that you don’t trust. The best way to avoid serialization exploits is never to deserialize anything. In the words of the computer named Joshua in the 1983 movie WarGames, “the only winning move is not to ...more
Robert
So it's quite clear that Java made a bad decision when it chose to use this method of serialization (and perhaps a bad decision to even have Serialiizable all together). We've also heard about a handful of other bad decisions throughout this book (poor method names/overloadings, unnecessary synchronization, etc.). Is Java ever willing to create a version that at least begins to trim off some of this legacy? How about deprecating some of those for at least one major version, and removing the, a major version or two later? The cost will be high, but I'm sensing the cost will be higher if it does nothing; it will slowly fade away as alternatives with most of the benefits and few of its drawbacks pop up and gain traction (Kotlin replacing Java, Swift replacing ObjC).
80%
Flag icon
There are other mechanisms for translating between objects and byte sequences that avoid many of the dangers of Java serialization, while offering numerous advantages, such as cross-platform support, high performance, a large ecosystem of tools, and a broad community of expertise.
Robert
The case is certainly strong and clear to not use Java serialization. But even if the other reasons pale in comparison, I think it's still worth calling out the importance of cross-platform support. More than ever, systems are and will not be Java-only (whether because of evolution over time, or interoperability with non-Java services).
80%
Flag icon
If you accept the default serialized form and later change a class’s internal representation, an incompatible change in the serialized form will result. Clients attempting to serialize an instance using an old version of the class and deserialize it using the new one (or vice versa) will experience program failures. It is possible to change the internal representation while maintaining the original serialized form (using ObjectOutputStream.putFields and ObjectInputStream.readFields), but it can be difficult and leaves visible warts in the source code.
Robert
To be fair, this is a problem not specific to Java serialization. As classes evolve, compatibility with data in older forms requires some handling and warts. Yea, some handle it better or more gracefully than others. I've said this before elsewhere (and I have a feeling a subsequent item will recommend this[1]): consider separating the code that performs serialization from the class that it's serializing (where "serialization" can refer to the process of converting Java data into non-Java, cross-platform structured data formats). This keeps class logic cleanly separated from serialization code/mess. Consider also that you may have to deal with exporting/importing data from several sources (read: have multiple serialization formats) — database and clients, and even multiple types of clients — not to mention the possibility that you may wish to migrate from one format to another. This is very analogous to (and related to, actually) keeping string formatting separate[2] from the data/logic class (where it was even recommended that `toString()` could be used for "serialization"). [1] Yup —kinda —Item 90 [2]https://www.goodreads.com/notes/37646821-effective-java/74-robert/cd025f84-7dcb-4402-86a3-13ae1acf2050?ref=rnlp
83%
Flag icon
The source of the problem is that Period’s readObject method is not doing enough defensive copying. When an object is deserialized, it is critical to defensively copy any field containing an object reference that a client must not possess.
Robert
"Critical" only when there's incentive to attack and when there's something for you to lose. For example, in cases where the program only serves one person (e.g.,, it's a personal finance desktop app), someone attacking the app is only corrupting their own data. I say this to support a more general point: There are situations where defensiveness isn't necessary and doesn't justify the cost. Defensiveness can increase code complexity (which can increase possibility of more bugs and cost of maintenance), performance (but don't ignore caveats about optimization), etc.. So, it's okay to opt not to be defensive after you consider what you're defending and defending against.
1 3 Next »