views:

1924

answers:

29

In the spirit of

what are common mistakes we should avoid for Java?

Example: use the == operator for comparing two strings come from external sources rather than .equals method.

+11  A: 

Use the == operator for comparing two strings come from external sources rather than .equals method.

The == operator compares object references. It doesn't check if the content of the strings are equal, only if they point to the same objects.

Ahmed
Actually most of the time this will work.
cletus
Isn't == operator, compare two object references?
Ahmed
In case strings were init like a = "Hello"; b = "Hello" they will reference the same object.
Mykola Golubyev
It is - and in many (though I would not say most) cases equals strings are in fact references to the same object; for example, whenever you use compile-time literals.
Michael Borgwardt
But for different init, they will refer two object reference? and so problem while comparing.
Ahmed
Yes but in well implemented JVM a string object is created just once, so references to equal strings will be refering to the same object.
piotrsz
They will not be equal when they come from external sources in any current JVMs unless you call .intern() on the string first. e.g. comparing strings read from a GUI textbox,read from network, result of a database query, etc. you really want to use .equals() to compare.
nos
There is some bad advice in these comments. Using == for string comparisons because it will work in "a well implemented JVM" is leaving yourself open to the fact that your code might run in a "bad" JVM. Follow the language design (.equals) and you won't have to worry about this. Don't use tricks just because they'll work in "most" JVMs.
matt b
@matt b: You're right, and not only does this trick depend on a "good" JVM, but I am pretty sure you will get the wrong behavior where there is more than one ClassLoader. Think e.g. of a JEE application with a WAR module and several EJB modules where each of them has its own ClassLoader. I think there you would get a = "Hello"; b = "Hello"; but a == b would evaluate to false if the two declarations were in separate modules.
Robert Petermeier
@piotsrz: This is completely wrong. "ab" == (new String("a") + new String("b")) will always evaluate to false (yes FALSE!) unless the JVM is broken.
Stephen C
+15  A: 

The most common mistake I've noticed so far is that C++ programmers treat Java like C++ and write C++ like java code. They still do not forget about 'destructors' and 'template magic'. Can't realize that code can be small and self expressed. Instead they recreate some abstractions that they are familiar with.

Another mistake I've found is that thinking that you don't have to worry about memory management leads to hanged objects and thus to memory leaks. Nevertheless Java manage memory memory leaks are still there.

Overriding equals() but not hashCode().

There are a lot more that are highlighted in the Effective Java 2nd edition.

Mykola Golubyev
*C++ programmers treat Java like C++ and write C++ like java code*. I believe it is the opposite in general. What happens is that Java programmers write Java in C++. I am not generalizing, but I have seen more beautiful Java code written by C++ programmers than the opposite.
AraK
Of course good C++ programmers are good in any language. But unfortunately many people think of language as of its syntax and libraries. And after switching to another language they don't change their habits.
Mykola Golubyev
+5  A: 

Using the array-based read() methods in java.io's Readers and InputStreams, and expecting them to read everything they were told to - most of these will return an int containing the number of bytes/characters actually read (an arbitrary number, for large arrays often less than what was asked), and you have to loop until you're done.

This can be quite puzzling since it usually works fine with small arrays, and fails seemingly at random only with larger ones.

Michael Borgwardt
+1  A: 

Writing to a file and having the program end without closing the OutputStream or Writer - data still in internal stream buffers can end up not being actually written to the file and getting lost.

Michael Borgwardt
+10  A: 
  1. empty catch blocks
  2. switch case without break
  3. constructor with return type
  4. calling String.split(String regex) and not escaping special char
  5. most of the methods(like replace) on String retun new object and does not modify the original
  6. confusion over pass by value and pass by reference
prateek urmaliya
I would call that a mistake. Even if it's one of those pesky "// only for the compiler" or "// can never happen" catch blocks, what does it cost you to at least put an ex.printStackTrace(); or logger.error(ex); in there?
Robert Petermeier
@RobertAgreed answer updated
prateek urmaliya
If you have a place where you put a "This might never happen", then stand by it an wrap the exception that can never happen inside a new Error object or a RuntimeException at the very least.
Mario Ortegón
How can you have a constructor with a return type? This isn't allowed by Java.
Steve Kuo
@Steve yes that's right and that's what qualifies it to be called a mistake,the default constructor will be called while developer thinks he already implemented one but that's just another method.
prateek urmaliya
A: 

Rookies often forget to call close() method on objects with an underlying resource such as a file handle or a database connection, leaving this job to the finalize() method.

Maurice Perry
A: 

Java methods throws an expception are not placed inside the try..catch block.

adatapost
I don't think compile-time errors count
finnw
+28  A: 
  • Ignoring Sun's code conventions for Java
  • Using java.net.URL objects as keys in a java.util.Map map or java.util.Set
  • Forgetting to make sure that system resources (network or database connections, streams) are freed by calling close() in a finally block
  • Assuming that you can't produce a memory leak in Java because it has garbage collection
  • Reinventing the wheel by not knowing the Java API and "standard" libs like Commons
  • Using outdated data structures like java.util.Vector which are only still part of the API for compatibility reasons (this doesn't actually cause any harm, I think, but it should be avoided)
  • Treating the Gang-of-four book as religion and over-using design patterns (this seems to apply to Java developers much more than to anyone else, at least judging by my own personal experience)
  • Making your code produce NullPointerExceptions by not being consistent about when a reference can be null and when not; this includes both the optimistic school of programming where nothing is checked for null equality as well as the over-compensating style where basically everything is checked for null
  • Using the Singleton design pattern in an environment with (potentially) multiple class loaders like e.g. JEE applications
  • Starting threads in code that is called from EJBs
  • Using non-ASCII characters in your code; yes, Java supports unicode files, but in my experience some will get corrupted when exchanged between different machines with different file systems, operating systems and encodings

Addendum:

  • The problem with java.net.URL objects is that the method URL#hashCode() performs DNS lookups, which is a) slow and b) simply not what you would expect. See this site and this site for more details. I think this is still the case in Java 6.
  • The methods provided by classes like Vector and Hashtable are all synchronized, which is not needed in most cases. They have been replaced by java.util.ArrayList and java.util.HashMap, where synchronization can be added if it is actually needed by using Collections.synchronizedXXX(). It is not really harmful to use the old classes but it has drawbacks and no advantages. The only excuse I can think of is that you use an API that uses these classes, but then you should create a facade or an adapter which converts to the new data types and send all calls to that API through this facade.
Robert Petermeier
Using outdated data structures like java.util.Vector which are only still part of the API for compatibility reasons (this doesn't actually cause any harm, I think, but it should be avoided)Can you comment on this a bit more why is it outdated?
Hamza Yerlikaya
whats the problem with using java.net.URL as a key?
matt b
from java doc for url ->Two hosts are considered equivalent if both host names can be resolved into the same IP addresses;it does a dns look up and same url might turn out to be not equal. since multiple ip's can server for a single domain.
Hamza Yerlikaya
@matt b: the problem is that the method `URL#hashCode()` performs a DNS lookups, which is a) slow and b) simply not what you would expect.See http://www.sworddance.com/blog/2007/09/09/code-review-4-always-read-the-documentationcode-aka-javaneturl-is-evil/ and http://daniel.gredler.net/2008/04/24/urlhashcode-considered-harmful/ for more details. I think this is still the case in Java 6.
Robert Petermeier
@Hamza: The methods provied by classes like Vector and Hashtable are all synchronized, which is not needed in most cases. They have been replaced by ArrayList and HashMap, where synchronization can be added if it is actually need by using Collections.synchronizedXXX().
Robert Petermeier
+1 for the Sun conventions, I hate see Java code formatted as C# ( as much as I hate seeing C# formatted as Java )
OscarRyz
+5  A: 
  • Forgetting to call flip() on a buffer
  • Being unaware of Bimap (from the google collections library) and maintaining two maps (inverses of each other) instead.
  • Not specifying a character encoding when creating a Reader or Writer, relying on the host default instead.
  • Appending to a String in a loop using the overloaded + operator, instead of using a StringBuilder or StringBuffer
  • Persisting in using micro-optimisations that worked a decade ago but are now useless or harmful, e.g. using CharArrayWriter in place of StringBuilder, or using abstract classes where interfaces are more appropriate.
finnw
A: 

Null pointers.

Compilers can't check this. It only occurs at runtime. Either you or user will discover it.

Ahmed
So are what are you saying? That null pointers should not be used ??? They should never be passed / returned? They should always be tested for?
Stephen C
Actually, the new version of Eclipse (optionally) does quite a bit of checking for possible null pointers. It's not foolproof, of course, but it's pretty nice. @Stephen: I would your code should be designed to work with null pointers, or you should write it in such a way that it's impossible to get a null pointer, or you should check for null values that shouldn't be null, and then throw a more descriptive error.
MatrixFrog
@MatrixFrog: re "designed to work with null pointers". My view is that a method that should only accept a null arg, return a null value, etc when it is >>meaningful<<, and that these cases should be clearly documented. An unexpected null is a BUG and should result in an (unchecked) exception. IMO, it is reasonable for this to be a plain NPE without an informative message. The bug stacktrace is for the developer to look at, and he/she should be able to figure out the root cause by looking at the source code.
Stephen C
Isn't this a common problem for languages, doesn't seem that specific to Java.
Ben Daniel
@Ben: Some functional languages have no predefined null value. If you want to use the equivalent of 'null' you typically need to declare your type as a union with an empty branch. So no, this problem is not common to all languages.
Stephen C
+8  A: 

Seeing you have to deal with a checked exception you don't care about and writing:

try {
....
} catch (Exception e) { }

Handle your exceptions properly (and catch the correct type) or at least re-throw them.

Remember the difference between Thread.run() and Thread.start(). Thread.run() simply invokes the thread's run() method. Thread.start() is what actually causes a new thread to begin executing concurrently.

Falaina
A: 
  • Closing streams and connections at the end of the try block and not in the finally block.
  • Not reading the JavaDoc and heeding the words 'Note that this implementation is not synchronized' on Collections and Formats and such.
  • Not being aware of what actions belong in the AWT Event Dispatch Thread (painting of components) and which do not (longer processes like IO and remote calls) and how to navigate between the two (SwingWorker).
  • attempting myDouble == Double.NaN
akf
+13  A: 

Anything that PMD or FindBugs would warn you about.

matt b
Sweet! Hadn't heard of PMD before.
Michael Deardeuff
Awesome resources. I hadn't heard of those before either. I wish there was a way to favorite a comment.
Mike Nelson
You can bookmark the link
matt b
+5  A: 
  • Implementing finalize(). Relying on finalization to do cleanup is usually a big mistake.
  • Calling gc(). This almost always makes your program run slower.
  • Writing new String(str) or new String("hi"). This creates unnecessary copies of the String arguments.
  • Using concrete classes (e.g. java.util.HashMap) instead of interfaces (e.g. java.util.Map) in an API. This makes your API less flexible / reusable, and with a modern JVM there is no speed advantage.
  • Declaring a class as final for (supposed) efficiency reasons. With a modern JVM it should make no difference.
  • Declaring a method as throwing java.lang.Exception. Don't be lazy: declare the actual (checked) exceptions that are actually thrown.
  • Any use of non-private static variables. Globals are bad.
  • Overuse of private static variables, whether or not they are wrapped as Singletons.
  • Returning null or an error value instead of throwing an exception to indicate an exceptional condition.
  • Throwing exceptions in situations that are not exceptional; e.g. as a way to break out of a loop. Creating and throwing an exception in Java is expensive.
  • Thinking that JNI will solve their problems. JNI is difficult to use, and a notorious source of bugs, including JVM crashes.
Stephen C
+1  A: 
  • Overriding equals() and not hashcode(), and viceversa.
Mauricio
Duplicate answer.
Stephen C
A: 

Failing to handle or declare Checked xceptions.

Randell
Compile time error. Unless you mean unchecked exceptions ... in which it is OK to not declare or handle them.
Stephen C
The "empty catch block" which has been mentioned already is not "failing to handle" the exception, as far as the compilers concerned. But it is definitely failing to handle it. At least in a meaningful way.
MatrixFrog
@MatrixFrog: agreed - but as you say, this just makes this answer duplicative.
Stephen C
+1  A: 

Suppressing Warnings from the compiler! The number of times that ended up biting me has made me learn that warnings should always be treated as errors!

Molex
A: 

Accessing a static variable from a non-static method. Or accessing a non-static variable from a static method.

Randell
@Randell: IMO, there is nothing wrong with accessing a static variable from a non-static method. Please explain what you think is wrong with it.
Stephen C
Accessing a non-static variable from a static method is a compilation error.
Stephen C
+6  A: 

Not knowing about Soft and Weak references. I don't know how many memory leaks would have been avoided in code I've seen by properly using these types of references.

Some resources:

http://www.ibm.com/developerworks/java/library/j-jtp11225/index.html

http://www.ibm.com/developerworks/java/library/j-jtp01246.html

Mario Ortegón
+1  A: 

Serializing a Swing object and sending it somewhere else. The API specifically warns against this, since Swing objects aren't guaranteed to be binary-compatible across different versions of the library, but I've seen people try to do it anyway (including a professor who really should have known better, in a coding problem on an exam).

Meredith L. Patterson
+3  A: 

String concatenation.

Anytime you see something like:

String s = "initial";
for (int i = 0; i < 100; i++)
  s += "Something"

alarm bells should start ringing. Conversely, some people have gotten the idea that String concatenation is always bad, and always manually do it themselves. That is they will do:

final String s;
final StringBuffer sb = new StringBuffer();
sb.append("String 1");
sb.append(variable);
sb.append("String 2");
s = sb.toString();

instead of

final String s = "String 1" + variable + "String 2";

despite the fact that the compiler generates basically the same code underwater, in fact the compiler produces slightly smaller code, since it won't emit and save sb.

Paul Wagland
A: 

Method dispatching:

null matches every type.

If you have two overloaded methods and pass null, you need to be careful which method will be executed.

I tripped over this myself a while back: http://stackoverflow.com/questions/377203/java-method-dispatch-with-null-argument

Yang
A: 

To add to the above:

  1. Failing to synchronize getters which access mutable data in classes which need to be thread safe.
  2. Using magic numbers where enums would be better.
  3. Overusing inheritance where interfaces would be better.
Paul Rayner
A: 

If you try to Serialize something that doesn't implement Serializable, the serialization will silently fail.

BlueRaja - Danny Pflughoeft
+2  A: 
JuanZe
About anemic classes: I'm aware that this is not a failure of the language. The lack of good OO knowledge leads to code like that. Programmers sometimes forget that OO also is applied to actual code and is not only to think about problems.
JuanZe
I would say: Forgetting to read Effective Java
nanda
A: 

Some java specific, and coding in general issues:

  • Holding a synchronized lock while notifying others of a change, usually a message broadcast. This is a very easy way to introduce a deadlock.

  • Overusing protected variables. It's used with the optimistic opinion that someone may need to extend the class, but not extend the behavior, rather hijack the parent implementation; it's essentially an argument against encapsulation.

  • Starting a thread in a constructor that uses the calling object. This can lead to the thread using a partially constructed object.

reccles
A: 

A semi-colon (;) after the condition of an if or while

if (a == b) ; {
    ...
}
Carlos Heuberger
A: 
  1. Calling overridable methods in constructors.
  2. Thinking of finalize() as some sort of destructor.
  3. Believing that there can't be memory leaks in Java.
  4. Implementing Cloneable without overriding the clone() method (or using clone() instead of copy constructors).
Helper Method
A: 

Using a KeyListener to detect changes in a text component like JTextField. It will not work correctly for example when pasting text into the component. You should use DocumentListener instead. I've seen surprisingly many bugs caused by this. In general you should avoid using "low-level" listeners like MouseListener if there are better, higher level alternatives available.

Carlos