I love it when tools find problems in my code rather than waiting for me or QA to find them. It helps my code to have less bugs, be more performant, more "hygienic" and I can usually fix the problems found faster than if it's later in the dev cycle.
So this blog entry is a plug for static code analyzers such as Checkstyle, PMD, FindBugs etc. and focuses on some of the issues that they spot in my own code and that of others.
Best of all - if you use these tools regularly, little by little you start writing code that will pass their checks without thinking about it - and *bam!* you're a better programmer!
Anyway, I use Checkstyle, PMD and FindBugs regularly- as each of them has slightly different coverage ranges for different issues (I've still to get around to checking out Hammurapi) and here I list some of the more helpful issues that each one spots for me.
Checkstyle
- Missing Package Documentation - Just a basic reminder to document one's code. Nothing fancy - it's a good practice to be kind to other developers!
- Method Parameter should be final - A good practice to ensure your contract / interface is explicit about what's an "in" parameter versus "in-out"
- Spotting Magic Numbers - Such things should be either named constants or defined in some config / database somewhere. So easy to forget such "hard coding" beasts!
PMD
- Excessive class length - as I've mentioned elsewhere, classes that are too long are probably guilty of trying to do too much and should be refactored to smaller classes that "do one thing and do it well"
- Missing break in switch - one of those nasty ones where you can stare at the code for hours until it hits you that one of the lines doesn't have a break!
- Empty Catch block - aaaaggghhhhh
- Avoid catching Throwable - again, aaaaaggghhhhh - why do you want to catch Errors (e.g. out of memory problems) in addition to Exceptions?
- Non-Thread-Safe Singleton - boy was that a great addition to PMD! Saved my ass more than once!
- If all methods are static, consider using Singleton or a private constructor - just goodole code hygiene
- Unused Imports - again just good hygiene
- Avoid Protected Field in a final class - good point - the class is either extensible or not!
- Insufficient StringBuffer Declaration - again just a nice, neat, small performance booster
- Excessive method length - here a method is probably trying to do too much and should be refactored for maintainability
- Switch Statements should have default - good point if only to log an error or throw an Assertion Error
- Return from a finally block - finally blocks aren't intended for that!
FindBugs
- Equals method assumes argument is correct type - the signature of equals is boolean Equals(Object o) so if you override it, it's always a good idea to check the instanceof right after checking for null
- Inconsistent Synchronization on methods - Some objects are accessed by synchronized methods other's aren't exposing a multi-threading issue
- Unconditional wait() - so your code doesn't mind waiting forever? I bet your users do!
- Beware Tests for Floating Point Equality - you might want to consider Double or BigDecimal to avoid rounding errors
- Switch Statement found where one case falls through to next - are you sure you mean that?
And that's only the ones that I stop me in my tracks! There's so many others that are still quite useful.
My day-to-day dev environment set-up is that I use the Checkstyle plug-in with Eclipse to check my code as I write it, and then before I check-in code, I run PMD & FindBugs using Ant (FYI the PMD plugin to Eclipse is just too slow) to generate a few reports to scan and fix some of the biggest problems.
These tools are great and well-worth the investment of time to integrate to your development process! Once integrated it takes almost no effort or time to run them. Oh and since they're open source they are free too! It's like pair programming in XP without the other person ;-)
11 comments:
2. Method Parameter should be final - A good practice to ensure your contract / interface is explicit about what's an "in" parameter versus "in-out"
Final parameters have nothing to do with whether or not the object is modified by the method. For instance, maybe you have this method:
public void fu(final Object o);
You are perfectly free to call methods on o that modify it. The only thing you can't do is assign a different Object to what is basically a local variable o.
True - I should have given more context to make this less ambiguous (primitives vs. objects).
Also the primary reason to use "final" on method parameters is that the compiler will flag if you accidentally try to re-assign a new value to that object/primitive parameter within the method call (often a hard to find bug).
And I should have said the main reason to watch for this isn't about enforcing "in" vs. "in-out" for the consumer of the API as much as to aid the writer of the implementation in avoiding a silly mistake.
For more information on using "final" in such a manner check this link
Hardcore Java Sample Chapter
Thanks!
-Frank
Avoid catching Throwable
A good example of wanting to do this is in a transactional enviroment. For example if you have the following code....
try
{
openTransaction();
doSomethingThatUsesLotsOfMemory()
commitTransaction()
}
catch (Exception ex}
{
rollbackTransaction();
}
..you will find that the transaction is left open should there be an out of memory error or perhaps a class loading exception. This can leave you tool in a broken state forcing the user to restart unless you have special code to recognized a hanging transaction.
Avoid catching Throwable
A good example of wanting to do this is in a transactional enviroment. For example if you have the following code....
try
{
openTransaction();
doSomethingThatUsesLotsOfMemory()
commitTransaction()
}
catch (Exception ex}
{
rollbackTransaction();
}
..you will find that the transaction is left open should there be an out of memory error or perhaps a class loading error. This can leave you tool in a broken state forcing the user to restart unless you have special code to recognized a hanging transaction.
Regarding final method parms: why not just use Checkstyle's ParameterAssignment check instead? That way you detect when you've reused a method parm by mistake, without incurring the extra clutter of the final keyword.
Regarding final method parms: why not just use Checkstyle's ParameterAssignment check instead? That way you detect when you've reused a method parm by mistake, without incurring the extra clutter of the final keyword.
Hi Gerard -
The rules typically aren't hard and fast but in this case, how do you handle an OutOfMemoryError?
Can you release some resources?
Which ones? Why didn't you release them before?
Also if you catch Throwable you might get a VirtualMachineError. What can you do in that case?
Handling Errors in Java is extremely hard to do 100% correctly.
FYI the following little article has some good points on the few times it might be good -
Catch Throwable
FYI sometimes I've caught Throwable (and just logged it) and when I get flagged by Checkstyle etc. it forces me to think a bit more about whether it's the right thing to do and if it is, to put some good documentation of why in the code
-F
Hm... do you really need to check for null in the equals() method if the first thing you do is to check instanceof? A null pointer is, correct me if I'm wrong, not instanceof any object.
Another potential mistake: defining equals(Foo) instead of equals(Object), which is a completely new method rather than overriding the one in java.lang.Object.
Hi Frank,
I have used these tools for my Project and it's generating tons of comments.
Are you aware of any free tools which are capable of fixing some of these errors.
Thanks,
Neeraj Gupta
Hi Neeraj,
I don't know of any specific tools per se that will fix the issues, and to an extent that's the point - the individual developers should fix them bit-by-bit.
Clearly some of the issues are more important than others (e.g. catching Throwable vs. incomplete javadoc).
So I would follow the following procedure
1) Prioritize a top-10 or 20 most risky issues
2) Tweak your tools to only report on those issues
3) Assign them as bugs to developers
4) As the list diminishes widen the top-10 or 20 to a top-30 or 50 and repeat
To an extent I do that myself - some of the issues found are of lower priority to me than the tools suggest.
-Frank
Post a Comment