OK, this is the third thing this day; I’ma start a new entry which I’ll update occasionally.
Now this one is not so much incorrect, but still, the irony just slays me:
int key []; // brackets after name (legal but less readable)
// spaces between the name and [] legal, but bad
Spaces before the [] are bad? Like the spaces they use in String [] args
in just about every main method in the book?
The mind, it reels.
OK, not a WTF as such, but still:
[…] Instance init blocks are often used as a place to put code that all the constructors in a class should share. That way, the code doesn't have to be duplicated across constructors.
I’d like to dispute the “often” in there, given that I had never heard of instance init blocks before (nor seen then used), ever. (Though the syntax and usage is reasonable based on what I did know about static init blocks.)
I’m also distinctly unimpressed that one of the questions in the self-test in Chapter 2 relies on overloading method resolution information that will be taught in Chapter 3.
More String nonsense
Creating String objects
Oh dear! More nonsense about how String objects work:
In Java, strings are objects. Just like other objects, you can create an instance of a String with the new
keyword, as follows:
String s = new String();
This line of code creates a new object of class String, and assigns it to the reference variable s
. So far, String objects seem just like other objects. Now, let's give the String a value:
s = "abcdef";
As you might expect, the String class has about a zillion constructors, so you can use a more efficient shortcut:
String s = new String("abcdef");
And just because you'll use strings all the time, you can even say this:
String s = "abcdef";
There are some subtle differences between these options that we'll discuss later, but what they have in common is that they all create a new String object, with a value of "abcdef"
, and assign it to a reference variable s
.
Except that the first pair of statements created two String objects: one for the empty string (which got assigned to the s
variable), and then another one containing "abcdef"
, which then also got assigned to s
(leaving the object that s
previously referred to, the empty string, not referred to any more (and presumably eligible for garbage collection).
One string object and two string objects seems like a difference to me.
Understanding Strings
…Oh dear, and a bit later, they say:
[…] If you really, really get the examples and diagrams, backward and forward, you should get 80 percent of the String questions on the exam correct.
I think I do; thanks! It just doesn’t seem to me as if the authors did….
Substring index values
[…] If the [substring()
method] call has two arguments, the substring returned will end with the character located in the nth position of the original String where n is the second argument. Unfortunately, the ending argument is not zero-based, so if the second argument is 7, the last character in the returned String will be in the original String's 7 position, which is index 6 (ouch). […]
Ouch indeed. Wouldn’t explaining that the indexes run from beginIndex, inclusive, to endIndex, exclusive, have been easier than invoking one-based counting?
Or using wording such as “the substring returned will end with the character immediately before the index given by the second argument”.
Why StringBuilder is better than String, or, When objects get added to the String constant pool
Introduction
[…] if you choose to do a lot of manipulations with String objects, you will end up with a lot of abandoned String objects in the String pool. (Even in these days of gigabytes of RAM, it's not a good idea to waste precious memory on discarded String pool objects.) On the other hand, objects of type StringBuffer and StringBuilder can be modified over and over again without leaving behind a great effluence of discarded String objects.
Is this really the reason?
As I understand it, every string literal will be added to the String constant pool, but results of methods such as String.concat()
will not.
So
String s = "Hello ";
s += "world, ";
s += "how are ";
s += "you?";
Would end up with four objects in the String constant pool (the four string literals), one String object on the heap (with value Hello world, how are you?
) pointed to by s
, and two String objects on the heap produced by the += operator (with values Hello world,
and Hello world, how are
) which are not pointed to by anything and are eligible for garbage collection.
While if you did
StringBuilder s = new StringBuilder(26);
s.append("Hello ");
s.append("world, ");
s.append("how are ");
s.append("you?");
Would end up with… tada, four! objects in the String constant pool, as before! Since we still have four String literals in the source.
You’d avoid the two temporary String objects produced by the concatenation in the first example, but those are (if I understand correctly) on the heap and eligible for garbage collection, not “wast[ing] precious memory” as a case of “discarded String pool objects”. Sure, those are “discarded String objects”, but they’re not clogging up the scarce String pool resource and never getting freed. So you’re still polluting your scarce resource just as much as before, just using a bit less of a less-scarce one (the heap).
…and if you had used the new StringBuilder();
constructor instead, you’d have a discarded char[]
lying around on the pool from when the StringBuilder had to resize from its default 16 to 34 characters upon appending the “how are ” bit. So on this particular example, you save one heap object with StringBuilder compared to String.
Using StringBuilder and StringBuffer
String x = "abc";
x = x.concat("def");
System.out.println("x = " + x); // output is "x = abcdef"
We got a nice new String out of the deal, but the downside is that the old String "abc" has been lost in the String pool, thus wasting memory. If we were using a StringBuffer instead of a String, the code would look like this:
StringBuffer sb = new StringBuffer("abc");
sb.append("def");
System.out.println("sb = " + sb); // output is "sb = abcdef"
…which would still lose “the old String "abc"” to the String pool, as I understand it, since it would be added to the String pool at the point where the compiler saw the String literal "abc"
in the source code: it would create a String object in the constant pool (or find an existing String object with that value in the pool), and then pass that String object to the constructor of StringBuffer. You’re not doing the constant pool any favours here.
Lenient: I don’t think that means what you think it means.
The API for DateFormat.parse() explains that by default, the parse()
method is lenient when parsing dates. Our experience is that parse()
isn't very lenient about the formatting of Strings it will successfully parse into dates; take care when you use this method!
I wonder whether they’re using the same definition of “lenient” as the API; it doesn’t mean that it can take stuff such as “last Thursday” or “ye 13th of the monthe of Maye of the Year of Our Lord 1492”, but rather that it can take stuff such as “2001-2-31” and turn it into “2001-03-03”. As the API says, it does have to be parsable as a date; but you can omit leading zeroes or talk about the 31st of February.