Access
All non-
finalvariables should beprivate.
A
final staticvariable can bepublic, since it is a constant.
All methods, except those in the public API of the class, should be
private. Those to be used by subclasses must beprotected; try to have as few of them as possible since they can also be used by packaged classes as well.
Variables and methods usually shouldn't have package access (which, stupidly, is the default if you don't specify any access modifier). Classes though should almost always have package access except for those few that need to be
public. In general, make everything as private as possible.
Minimize the use of
staticnon-finalvariables. Class-globals are a Bad Thing.
Minimize the use of object-globals. They are a Bad Thing, Too. Instead, write your methods to explicitly import as many of them as possible as parameters, even though the methods are in the same class. This keeps your methods as encapsulated as possible.
Names
All names should be meaningful. If a variable has no special meaning then don't be afraid to use its type as its name. For example:
private DataFormat dataFormat
No variable, method, class, interface, or package name should use abbreviations (for example,
num, obj, cls, and, most especially,Impl). (Try to ignore Java's own breaking of this rule: for example,File.mkdirs(), System.out.println(), andSystem.currentTimeMillis(); maybe Sun will live these down one day.) Common acronyms likeURLandTCPConnectionare ok; they would be impossible if written out in full.
All variable, method, class, interface, and package names should follow the standard Java naming scheme (never mind that Java already messed this rule up with
instanceof,println,Hashtable, and the names of allColorconstants):
aaaa.aaaa...for packages, AaaaAaaa...for classes and interfaces, aaaaAaaa...for methods and variables, AAAA_AAAA_...for constants.
No variable name should have an underscore (unless it is a constant, in which case it must be in all uppercase).
Do not invent another name for a parameter simply because the obvious choice clashes with a variable. Use the same name and disambiguate the two of them with
this. A name should be chosen with great care; it should capture the essence of the variable. Don't undo all that work by making up fake variants of the name as well.
Don't name a thread reference
runnerorkicker. Call it what it is:thread.
Avoid using negative boolean variable names; use the positive version instead. For example, instead of
notReady, notFull, andnotBuffereduseisReady, full, andbuffered.
Boolean variable names should be predicative verbs (that is, they should make sense when prefixed with `if')
(done, isReady, isHighSpeed);
other variable names should be nouns
(pixelSetting, characterBuffer, frame);
accessor methods should be prefixed with `get' and `set', and query methods should be prefixed with `is'
(getInstance(), setBuffer(), isValid());
other method names should be verbs or adverbial phrases
(initializeScreen(), createBuffer(), run());
interface names should be adjectives
(Runnable, Cloneable, Observable);
class names should be nouns or noun phrases
(DoubleBufferedApplet, WidgetFactory, Component)
exceptions should end with the word "Exception"
(BufferCorruptedException, TooSmallToSpawnException).
Statements
Of the primitive types, you should only use
boolean, int, long, anddouble. Do not usebyte, char, short, andfloat. (Unfortunately we can't get rid ofintin favor oflongthanks to two bad Java design decisions---longoperations aren't thread-safe andlongs can't be used as array indexes or to controlswitchstatements.)
Do not use the ternary operator.
Do not use the binary shift operators.
Do not use the bitwise operators. (Unless you're writing some ultra-high performance bitshift-based application and you understand exactly what the jit engine does and you really know what you're doing.)
Do not compare Strings with
==.
Always use braces to indicate scope in a
do-whilestatement.
Try to rewrite
do-whilestatements intowhilestatements if you can do so without mangling your code. The fewer different types of statements you use, the lower the chance that you will confuse yourself, or your reader (and, no, that's not the Java interpreter).
Try to rewrite long methods with multiple
returnstatements into ones with onereturnstatement at the end if you can do so without mangling your code. Long multi-returnmethods are hard to understand. because you must consider every exit point. Better yet, once your methods are short, you don't have to care about multiple exit points; if you have conditions that should immediately exit, then simply exit. Short methods are easier to understand, regardless of the number ofreturns.
Fully parenthesize every expression containing multiple operators. Don't rely on complex precedence rules to produce the correct parenthesization. You think you are being efficient and clever, but the interpreter doesn't care and either you or your reader, or both, will get it wrong.
Avoid using labels and
continue. Be careful withbreak; it can be useful, but used too much it can turn your code into spaghetti.
Switch statements
Do not use "fall-through" cases in
switchstatements. If the code being shared is significant then make it into a separate method----even if it's only a line or two.
Always provide a
defaultcase forswitchstatements. If you don't expect it to ever be fired, write code to report that it did fire (and therefore that there is a bug).
Always put the
defaultcase last in theswitchstatement.
Always include a
breakstatement for thedefaultcase, even if it is last in the list of cases. Future programmers may rearrange the cases and not notice the missingbreak.
If you find yourself writing a
switchstatement with a lot ofinstanceoftests to check for every subtype of a type, wake up and smell the Java. Use overloaded methods instead; let the interpreter do the work. If you find yourself writing multipleswitchstatement with the same or similar code in each branch all over your program, try the state pattern instead. If you find yourself writing aswitchstatement with a lot ofinstanceoftests to manage every possible combination of subtypes of two type hierarchies, try the visitor design pattern (use double-dispatch.)
Avoid
switchstatements; bugs love 'em. All programs can be written using onlyif, for, andwhilefor flow control. Bottom line: if you think you're being forced to useswitchstatements, or a long line ofif-elsestatements, your automatic response should be to ask "What am I doing wrong?"
Methods
Do not try to make a method do more than one well-defined task, and its name should mirror that task. If you cannot name a method easily then either you don't know what you're doing and should stop for thought, or it is doing too much---break it up.
If a method is longer than about half a page (including comments), it's probably too long and should be broken up into several methods.
If a method requires more than about 3 or 4 unrelated parameters it is probably too big; break it up.
Do not avoid one-line methods on the grounds of "efficiency." A one-line method is perfectly reasonable, especially if it allows you to clean up other methods that use it. Further, using methods lets subclasses override them to provide added functionality. That isn't posible with straight code. Finally, small methods make identifying efficiency improvements easier than large bloated methods.
Make as many method parameters as you can
final.
Make as many local variables as you can
final.
Methods should never contain `magic numbers' aside from single digits 0-9, and possibly 10. All other constants should be safely hidden in
final staticvariables.
Never use 0 and 1 when you really mean
trueandfalse.
The last statement in a
finalizemethod should always be:super.finalize().
If you override
toString()you should also overridehashCode().
If you override
equals()give some thought to what you'll do about checking whether the superclass portion of the objects will test equal as well.
Methods and Variables
Class variables and methods should always be accessed or executed through their class, not through an object. Using nothing at all is equivalent to access through an object, since the Java interpreter adds
thisas a prefix.
Constructors
(Almost always) provide at least one constructor and make sure that it initializes object variables to a consistent state.
(Almost always) provide a zero-parameter constructor if you also provide non-zero-parameter constructors. Otherwise someone may subclass your class one day and end up with an obscure error. (Java adds an empty zero-parameter constructor if you don't have any constructors at all to partly avoid this problem; but it doesn't do a complete job.) Further, having a zero-parameter constructor in all your classes makes them easier to turn into JavaBeans, if you later need to do so (however, to be beans they will also have to implement
Serializable, be thread-safe, and consistently name any accessor/setter methods).
If you have multiple constructors, avoid code duplication (and subsequent maintenance nightmares) by making one of them the setter of all variables and pass special cases to it from the others. You can also create a special method to handle initializations and execute it from each constructor. If you go this last route, make sure the initializer cannot throw exceptions---it would have nowhere to throw them to. Also, make sure that the initializer is
protectedandfinal, otherwise someone may accidentally override it in a subclass, and that is the version that your constructors will execute.
Classes
Do not try to make a class do more than one well-defined task, and its name should mirror that task. If you cannot name a class easily, or if you cannot describe its purpose in one short declarative sentence, it is doing too much; break it up.
If a class has more than about a page's worth of method descriptions it's probably too big and should be broken into several classes.
Do not subclass standard classes (
Thread, String, Vector, Hashtable, etc); use composition rather than extension (that is, use aVector, don't be aVector; implementRunnable, don't be aThread).
Do not reinvent the wheel: use the standard
java.util.Collectionclasses rather than rolling your own (unless you have an extremely specialized and demanding application).
Avoid extension as much as possible. Use interfaces and composition and delegation instead.
If you must extend, try to make your superclasses as abstract as possible. For example, common but widely variable methods in subclasses should be made
abstractin the superclass rather than be made into dummy methods; that forces subclasses to explicitly implement them.
Files and Packages
Put every class in its own file. Use explicit
importstatements to name all collaborator classes.
No
importstatement should use the global class descriptor (the '*'); all imports should be explicit. Importing more than 10 or so classes is a warning sign that your class is trying to do too much. Pay attention to it.
Make sure that every
imported class is used.
Use packages to group related classes together, not to create a single umbrella for a bunch of unrelated classes (sole exception: a toolbox package).
Write a
ReadMefile for every package of classes to give the reader an overview of the package. (Unfortunately, Java has no mechanism for this common problem. Java's poor support of modules larger than classes is one of its weakest aspects.)
Documentation
Every class should start with a CRC: the name of the class, the responsibilities of the class (what the class does), and the collaborators of the class (which other classes the class must work with to do its job). If a CRC lists more than 2 or 3 collaborators the class is doing too much; redesign your architecture. If the class's responsibility cannot be expressed in a short declarative sentence, the class is doing too much; break it up.
At the top of every class there should be a description of its overall function, a list of all its
publicmethods (that is, its API) together with descriptions of their function, and a list of all classes it collaborates with to accomplish its function together with descriptions of those classes. You can waive this requirement if you use javadoc religiously to publish your class APIs.
At the top of every method there should be a description of its function, any class-global or object-global variables it depends on, and any special actions it takes (for example, any exceptions it throws).
At the top of every non-trivial chunk of code within a method there should be a description of its overall function. Further, if you find yourself having to comment a piece of code inside a method seriously consider making it a new method.
Write comments as you code; each level should proceed in a stepwise manner. From topmost level down to the lowest and simplest line of code the reader should be able to read your comments only and fully understand your program without also having to read the code itself. A well-commented program is like a well-lit room; bugs hate it.
Don't ever let the code and the comments disagree.
In comments and other documentation, always refer to methods by appending parentheses to their names to show that they are methods.
Use
/**comments for class-level comments,/*for method-level comments, and//for declaration-level and statement-level comments.
Don't make spelling or grammatical errors.
Layout
Indent all scopes consistently.
Comments should also obey the local scope.
Indent statement continuations.
Do not mix spaces and tabs in one line. Different people use different tab settings and that completely messes up your layout.
Do not use tabs inside a line (that is, between two pieces of text---for example a declaration and a comment); use spaces instead. (See above.)
Put a space after each comma in any statement (declarations,
forloops, and comments).
Put a space on each side of every binary operator, including assignments.
Collect declarations of related variables together and use a group comment to describe them and their relationships to each other. Separate that group from other groups with blank lines.
Separate declaration-only statements from executable statements by at least one blank line.
Separate each related chunk of code from other chunks with blank lines. If there is a repeating chunk, seriously consider putting it in its own method.
Separate each method's
returnstatement from the rest of the method with a blank line.
If you find yourself writing code nested deeper than 2 levels, you've gone too deep and are about to drown. Rewrite to use a separate method or methods.
Separate methods from each other either with several blank lines or with a blank line and a solid line (for example: "
////////...").
Within classes, place declarations before constructors before methods. Within methods, place declarations before executables except for temporary variables that should be declared in or near the loops that use them. Within declarations, place
publicbeforeprivatevariables, unless that would hamper the organization of variables by relation or function, in which case go with relation instead.
Exceptions
Don't ever catch all exceptions [with
catch (Exception exception)] and then not handle them. More than anything else you could do this shows that you just got off the C boat, and have no idea how to program in Java.
Do not place
tryblocks around every statement that can throw an exception. Place the entire block of code in atryblock and handle all exceptions at the end of the block.
Catch as many exceptions as you can and handle them in one block. Try to avoid client classes from knowing of class-specific exceptions and therefore having to write exception-handling code unless the exceptions are something the client classes must know about to do their job adequately.
Create your own meaningfully named exceptions rather than using the generic exception classes.
Exceptions should be rare---not commonplace. (Exception! I can't find the disk drive! Exception! The machine is on fire!) Don't use exceptions to handle normal events you can easily predict (for example, end-of-file).
When dealing with files, always use a
finallyclause to close them.
Do not place exception-throwing code in a
finallyclause.
Threads
Do not use
stop(), suspend(), destroy(), orresume()on threads. They are all deadlock-prone. Use flags instead.
Do not use
yield(); instead, use a preemptive scheduler class to force preemption on all JVMs.
Do not use
notify(). Instead, usenotifyAll()and have eachwait()ing thread check its condition to see if it is the one that should awaken.
Embed all
wait()s inside ofwhiles that test their wake up condition. Don't rely on aniftest; the condition may change between the time the test was passed and thewait()was re-encountered.
Synchronize all thread-unsafe methods even if you are presently using only one thread. One day you will add multithreading, forget all about the lack of safety, and blow your foot off.
Do not fail to synchronize methods on the theory that data corruption "will never happen" and, besides, "synchonization makes things take longer." Subtle bugs make things take even longer.
Responsiveness
Do not use busy-waits. Use
sleep()orwait()andnotifyAll()instead.
Use multithreading to improve apparent responsiveness. If you have a long task, spin it off into a separate thread and return immediately for more processing.
Robustness
Every package should have a
TestMeclass containing unit test code for the rest of the class. Such test-code classes are also very useful for showing prospective users of each class in the package how to use it.
![]()
main()methods and major inter-class interface methods should always validate their input---even when being executed from classes you wrote. One day someone will modify your code and break the invariants by mistake.
Use assertions to continually validate your methods' inputs.
Use repeatable pseudo-random number sequences when debugging probabilistic code by using the same seed over and over.
Portability
Do not embed system-specific constants into your programs (for example, do not assume that files must be separated by "/" or by "\" or by ":"). Use Java's
Systemproperties to make your code portable. That's what it's for.
Optimization
Optimization for amateurs: don't.
Optimization for experts: not yet.
Optimization for gurus: buy a faster machine.
In these days of quite smart optimizing interpreters and ever faster machines, the only reasonable case where hand optimization might still be warranted is for some tricky cases of tail recursion. Do not use tail recursion instead of a simple loop (unless the method becomes deeply inelegant without the recursion).
Do not try to unravel a multiply-recursive method to an iterative one where you manage the stack yourself. That's the interpreter's job, and if you try it you will either bungle it, get in the interpreter's way, or horribly mangle your code---or all three.
Do not do trivial "optimizations" like unrolling loops, creating temporary variables for constants inside loops, or counting down instead of up in loops. That's the interpreter's job, and it is quite good at it. In fact, it's better than you.
Do not try to do Java's garbage collection for yourself, or defeat it, or reschedule it, or alter the heap size, or monkey with any other system variable unless you really know what you're doing. And you probably don't.
If you ever feel the urge to optimize, lie down. It will pass. If it doesn't pass, then first make sure your program is correct in all respects. Then profile your code. Then figure out why the hottest spots are so hot. Before performing trivial optimizations (and mangling your code) look for a better algorithm. Only if all else fails (and you've had another good long lie-down) and you really, really, really know what you're doing, should you try any real "optimizations".
Throw out all the above optimization rules if your boss puts a gun to your head and says that your program must run in five seconds and not five minutes. However, once all the carnage is over and the dust settles, remember that you or later programmers will have to maintain that gross disgusting system for several years into the future, so refactor it to make it cleaner, even if it slows down a bit. Next year's machines will be twice as fast as this year's, and then you can put a gun to your boss's head to get new machines.