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.
Do not use package access (which, stupidly, is the default if you don't specify any access modifier) for variables or methods. It's okay for classes though. Explicitly make everythingprivate
,public
, or (for methods, if you must)protected
.
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
).
All variable, method, class, interface, and package names should follow the standard Java naming scheme (never mind that Java already messed this up withinstanceof
,println
, 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"
(BufferCorruptionException, 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 really must.)
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 thoroughly 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).
Fully parenthesize every expression containing multiple operators. Don't rely on complex precedence rules to produce the correct parenthesization. Either you or your reader, or both, will get it wrong.
Avoid using labels, andbreak
, andcontinue
.
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 to do what can be done with overridding or overloading methods, especially if you find yourself writing a lot ofinstanceof
tests to check for every subtype of a type, you are still asleep in C-land. Wake up and smell the Java.
Avoidswitch
statements; they are a fertile home for bugs. All programs can be written using onlyif, for
, andwhile
for flow control.
Methods
Do not try to make methods 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.
Make as many method parameters as you canfinal
.
Make as many local variables as you canfinal
.
Each method should have at most onereturn
statement (unless it would seriously harm the method's logic to do so).
Methods should never contain 'magic numbers' aside from 0, 1, and possibly 2; all 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 a very 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.)
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 classes 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 collection classes already provided in Java rather than rolling your own (unless you have an extremely 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.
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 a README 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.)
Comments
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 it is doing too much; redesign your architecture. If a CRC 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.
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.
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 are repeating chunks, 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 about 3 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()
; use a preemptive scheduler class to force preemption on all JVMs instead.
Do not usenotify()
. UsenotifyAll()
instead 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()
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 non-abstract class should have amain()
method containing test code for the rest of the class. Such test-codemain()
methods are also very useful for showing prospective users of the class how to use it. (A class can be an Applet and still contain amain()
method.)
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 your own system's 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 or creating temporary variables for constants inside 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 to defeat it, or to reschedule it, or to alter the heap size, or monkey with any other system variable unless you really know what you're doing.
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".
Style
Read a good Java book. (Unfortunately, there are only a few.)
Study the source code provided with the JDK or with almost any Java book. Laugh your head off. Then resolve never to produce such terrible code yourself.