Access
All non-final
variables should beprivate
.
Afinal static
variable can bepublic
, since it is a constant.
All methods, except those in the public API of the class, should beprivate
. 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 bepublic
. In general, make everything as private as possible.
Minimize the use ofstatic
non-final
variables. 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 likeURL
andTCPConnection
are 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 withinstanceof
,println
,Hashtable
, and the names of allColor
constants):
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 withthis
. 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 referencerunner
orkicker
. Call it what it is:thread
.
Avoid using negative boolean variable names; use the positive version instead. For example, instead ofnotReady, notFull
, andnotBuffered
useisReady, 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 useboolean, int, long
, anddouble
. Do not usebyte, char, short
, andfloat
. (Unfortunately we can't get rid ofint
in favor oflong
thanks to two bad Java design decisions---long
operations aren't thread-safe andlong
s can't be used as array indexes or to controlswitch
statements.)
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 ado-while
statement.
Try to rewritedo-while
statements intowhile
statements 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 multiplereturn
statements into ones with onereturn
statement at the end if you can do so without mangling your code. Long multi-return
methods 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 ofreturn
s.
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 andcontinue
. 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 inswitch
statements. 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 adefault
case forswitch
statements. 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 thedefault
case last in theswitch
statement.
Always include abreak
statement for thedefault
case, 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 aswitch
statement with a lot ofinstanceof
tests 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 multipleswitch
statement with the same or similar code in each branch all over your program, try the state pattern instead. If you find yourself writing aswitch
statement with a lot ofinstanceof
tests to manage every possible combination of subtypes of two type hierarchies, try the visitor design pattern (use double-dispatch.)
Avoidswitch
statements; bugs love 'em. All programs can be written using onlyif, for
, andwhile
for flow control. Bottom line: if you think you're being forced to useswitch
statements, or a long line ofif-else
statements, 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 canfinal
.
Make as many local variables as you canfinal
.
Methods should never contain `magic numbers' aside from single digits 0-9, and possibly 10. All other constants should be safely hidden infinal static
variables.
Never use 0 and 1 when you really meantrue
andfalse
.
The last statement in afinalize
method should always be:super.finalize()
.
If you overridetoString()
you should also overridehashCode()
.
If you overrideequals()
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 this
as 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 implementSerializable
, 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 isprotected
andfinal
, 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 standardjava.util.Collection
classes 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 madeabstract
in 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 explicitimport
statements to name all collaborator classes.
Noimport
statement 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 everyimport
ed 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 aReadMe
file 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 itspublic
methods (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,for
loops, 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'sreturn
statement 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, placepublic
beforeprivate
variables, 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 [withcatch (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 placetry
blocks around every statement that can throw an exception. Place the entire block of code in atry
block 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 afinally
clause to close them.
Do not place exception-throwing code in afinally
clause.
Threads
Do not usestop(), suspend(), destroy()
, orresume()
on threads. They are all deadlock-prone. Use flags instead.
Do not useyield()
; instead, use a preemptive scheduler class to force preemption on all JVMs.
Do not usenotify()
. Instead, usenotifyAll()
and have eachwait()
ing thread check its condition to see if it is the one that should awaken.
Embed allwait()
s inside ofwhile
s that test their wake up condition. Don't rely on anif
test; 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. Usesleep()
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 aTestMe
class 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 System
properties 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.