Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
COMP 1600 Coding Standards
This page gives guidelines for coding Java in COMP 1600. Their emphasis is on readability and consistency across the department. Coding standards are partly a matter of style, partly a matter of convention, and partly a matter of practicality. There are sometimes reasons to violate a particular set of standards, but those reasons must be strong and justifiable.
Click on the links below to jump to the corresponding section.
- Braces and indentation
- Whitespace
- Variables
- Constants
- Methods
- Classes
- Exceptions and interfaces
- Loops
- Comments
- Practices to avoid
Braces and Indentation
Any block of code should be indented. Such blocks include the bodies of classes, methods, selection statements, and loops. A more controversial question is where the surrounding curly braces should go. The guiding principle is consistency: You should always follow the same pattern in your code. Any of the three following styles is acceptable.
Note that Java allows braces to be omitted in the case of single line if
statements, else
statements, and loops. Many experienced programmers will always omit braces in this case to make code shorter. Some experienced programmers insist on using braces in all circumstances to avoid logic errors if a second line of code is added later. As a beginner programmer, it is wise to use braces always, but eventually you should make a thoughtful decision about whether you want to keep doing so.
K&R style - Java API variant
The K&R style, named for its use in Kernighan and Ritchie's book The C Programming Language, puts the opening brace on the same line as the header. Furthermore, the ending brace of an if
statement is on the same line as the matching else
if there is one. This style is commonly seen in Java because it was used for the code in the Java API. Many software developers prefer this style because it takes up very little space. The original K&R style put the opening brace of a method (a function in C) on its own line, but the Java API variant treats methods like like any other block.
public class Wombat { private int annoyance = 0; public void sing(String song) { if (song.equals("Stairway to Heaven")) { System.out.println("No Stairway allowed!"); ++annoyance; } else { System.out.println("*sings \"" + song + "\"*"); annoyance = Math.max(0, annoyance - 1); } } public int getAnnoyance() { return annoyance; } }
K&R style - Stroustrup variant
A very slight difference from the Java API variant is the Stroustrup variant, named for Bjarne Stroustrup, the creator of C++. The most significant difference between the two is that the Stroustrup variant always puts a closing brace on its own line instead of crowding the else
. Another feature of the variant is that very short methods are sometimes put on a single line.
public class Wombat { private int annoyance = 0; public void sing(String song) { if (song.equals("Stairway to Heaven")) { System.out.println("No Stairway allowed!"); ++annoyance; } else { System.out.println("*sings \"" + song + "\"*"); annoyance = Math.max(0, annoyance - 1); } } public int getAnnoyance() { return annoyance; } }
Allman style
The Allman style, named after Eric Allman, always puts braces on their own lines. This style makes the beginnings and ends of blocks very clear, but the cost of this clarity is code that takes up more space. Another difficulty with this style is that, with default preferences, Eclipse always puts an opening brace on the same line as the header of a block.
public class Wombat { private int annoyance = 0; public void sing(String song) { if (song.equals("Stairway to Heaven")) { System.out.println("No Stairway allowed!"); ++annoyance; } else { System.out.println("*sings \"" + song + "\"*"); annoyance = Math.max(0, annoyance - 1); } } public int getAnnoyance() { return annoyance; } }
Whitespace
For the most part, Java ignores whitespace. However, blank lines should be used to enhance readability. Members are commonly listed at the top of a class, followed by a blank line before the methods begin. A single blank line between methods is common.
Inside methods, it is also common to declare local variables at the top, followed by a single blank line. Within a method, most lines of code will not be separated by a blank line, but a single blank line can be used to break up a long section of code into two related sections. Breaking up code in this way is similar to dividing sentences into paragraphs when writing.
Even though it's possible to put everything on one line, multiple executable lines of code should not be put on the same line. Likewise, there is never a reason to have repeated blank lines anywhere in your code.
Variables
Picking names
Local variables and member variables should be named to reflect their contents. Variable names are essentially unlimited in length (although the JVM you use may limit this length to thousands of characters). A tremendously long variable name can be hard to read, but abbreviations can be worse. You want the meaning of your code to be obvious to others and to yourself when you come back days or weeks later. Good variable names reduce the need for comments.
Imagine you are writing a program that sells fruit. Consider the following names for a variable that keeps track of the number of apples.
Name | Attributes |
---|---|
a |
Too short, gives no useful information |
apps |
Too short, vague, could mean applications or appetizers |
cntr |
Too short, vague, could mean center |
counter |
Not bad, but counting what? |
theVariableUsedToCountApples |
Too long for no good reason |
appleCounter |
Very clear |
apples |
Concise and clear, unless there are multiple apple quantities
such as applesSold and applesBought |
Camel case
Mathematics is filled with one letter variables, partly because there is a history of writing mathematics on chalkboards and paper. Clarity is more important than brevity with variables in computer programs. Some variables need more than one word to be descriptive; however, spaces are not allowed in variable names. Instead, the Java convention is to use camel case. In camel case, multi-word variables and methods start with a lowercase letter and then use an uppercase letter to mark the beginning of each new word. It is called camel case because the uppercase letters are reminiscent of the humps of a camel.
Using camel case, variables could be named lastValue
, symbolTable
, and fivePointPalmExplodingHeartTechnique
.
To avoid confusion, abbreviations are usually treated as words in camel case. For example, variables might be named textToHttp
or xmlParser
even though HTTP and XML are normally written in all uppercase.
Constants
Constants in Java are marked with the final
keyword. By a convention dating back to the C language and earlier, the names for constants are written in all uppercase letters. Thus, constants have names like PI
, TRIANGLE_SIDES
, and UNIVERSAL_GRAVITATIONAL_CONSTANT
. Obviously, this third example is for illustration purposes only.
Because they are written all in uppercase, constants cannot use camel case. As you can see above, the words in a constant's name are separated by underscores. Any number of underscores can be used in a Java identifier. In fact, a Java identifier can be made up entirely of underscores, but doing so would go against the principle of readability.
Methods
A method name should describe what the method does. The name usually contains a verb. Like variables, methods should be written in camel case. Examples are print()
, makeHamSandwich()
, and destroyEarth()
.
One common kind of method is an accessor, which reads a value from an object without changing it. These methods are also called getters, since they get a value. It is typical for the name of an accessor to start with get
, as in getXVelocity()
, getColor()
, or getMeaningOfLife()
. One exception to this convention is when the accessor returns a boolean
value. In those situations, it is common for the accessor to start with is
, as in isHungry()
, isToxic()
, or isJohnMalkovich()
.
Another common kind of method is a mutator, which changes a value inside an object. These methods are also called setters, since they set a value. It is typical for the name of a mutator to start with set
, as in setYVelocity()
, setAge()
, or setDetonationTime()
.
Classes
By convention, the names of classes in Java should start with a capital letter. Because a class must be saved inside of a file with the same name, those files should start with a capital letter as well. As with all Java identifiers, spaces are not allowed in class names. Capitalize the beginning of each new word in the class name, using camel case that starts with an upper case letter. Examples of class names are Wombat
, LinkedList
, and AtomicBomb
.
In general, classes should not contain static members. These global variables allow methods and different objects to share state in ways that make code unpredictable in general and unsafe for multi-threaded applications. Because they cannot be changed, one reasonable exception is constants, which are both static
and final
.
In general, all members inside of classes should be marked private
. Thus, encapsulation is preserved and the data inside of objects cannot be changed in unpredictable ways. Members should generally not be marked protected
. Although this keyword can be used to simplify code inside of some child classes, OOP purists object to its use since it can break code that works inside of parent classes.
Most methods will be marked public
, although there are many cases in which a method could be marked protected
or private
.
In Java, it is legal to define members and methods without using any of the three visibility modifiers. When none is used, the default is not public
but package-private. Anything that is package-private can be used within the package but is otherwise private
. Use of package-private is discouraged. You should mark every member and every method explicitly with public
, protected
, or private
.
Exceptions and Interfaces
Exception and interface names follow the same rules as class names and start with a capital letter. Most exception names end with Exception
, as in ArrayIndexOutOfBoundsException
or OxygenDepletedException
. Many interfaces end with able
to indicate the presence of some ability, such as Comparable
or Runnable
, but many also do not.
Exception handling
The body of a catch
block should never be left empty. If you leave it empty, the error will fail silently. In other words, the error will happen, but it will die without doing anything else. It would be better to mark the method with a throws
so that the error could be handled by the calling method.
try { Scanner in = new Scanner(new File("input.txt")); } catch(FileNotFoundException e) {} // Never do this!
At the very least print a stack trace so that it is clear what the error is. A better way to handle the problem is as follows.
try { Scanner in = new Scanner(new File("input.txt")); } catch(FileNotFoundException e) { System.out.println(e.getStackTrace()); // Now we see the problem }
You should also never have a single generic exception handler. It is much better to handle each exception separately.
try { juggleDynamite(); // Can throw ExplodedHandsException lookAtSword(); // Can throw LostEyeException smellEther(); // Can throw FaintException } catch (Exception e) { System.out.println("Something went wrong!"); // Never do this! }
Instead, you should have a handler for each exception that deals with each one individually.
try { juggleDynamite(); // Can throw ExplodedHandsException lookAtSword(); // Can throw LostEyeException smellEther(); // Can throw FaintException } catch (ExplodedHandsException e) { System.out.println("Ack! My hands!"); } catch (LostEyeException e) { System.out.println("Oh! My eye!"); } catch (FaintException e) { System.out.println("*Falls onto the ground*"); }
A finally
block marks code that will always be executed, regardless of whether or not an exception was raised. One good place to use them is to close a file.
Scanner in = null; try { in = new Scanner(new File("input.txt")); // Do stuff } catch (FileNotFoundException e) { System.out.println(e.getStackTrace()); // Now we see the problem } finally { if (in != null) in.close(); }
No matter what happens, exception or not, the file will get closed.
Loops
Although variables should be descriptive, i
, j
, and k
are recognized as common index variables for loops. Do not be afraid of using them, but feel free to use another variable like row
or column
if it makes the code more readable.
Don't run a for
loop backwards unless there is a good reason to do so.
Never put a semicolon after the header of a while
loop. There are situations when it is correct to put a semicolon after the header of a for
loop, but they are rare and usually detract from readability.
Comments
Code is confusing by nature. Don't hesitate to add comments as you write your code so that you can follow what it means. Comment anything that would be confusing to anyone else or even to yourself if you came back to the program after a week. It's better to comment a little bit more than necessary rather than to come back to some code later and have no idea what what it does.
You should use block comments at the top of every file describing the class it contains and giving your name, course number, assignment, and due date. You should use block comments to explain what each method does. Use single line comments to explain any confusing line of code.
/* This class contains a static method that prints the hailstone sequence for a given integer n. * If the hailstone sequence converges to 1 for every positive integer, the Collatz conjecture is true. * * Author: David Bowie * Course: COMP 1600 * Assignment: Project 4 * Date: 2/29/2014 */ public class Collatz { /* * Prints the sequence of numbers starting at n and going down to 1. * If n is even, the next number in the sequence is n / 2. * If n is odd, the next number in the sequence is 3n + 1. */ public static void findSequence(int n) { System.out.println(n); // Print start while (n != 1) { if (n % 2 == 0) // Check if even n /= 2; else n = 3*n + 1; System.out.println(n); // Print next number } } }
As a beginner, block comments will be appropriate to explain classes and methods. As you become more advanced, you should use a feature of Java called documentation comments for these situations. On the surface, documentation comments look like block comments with an extra asterisk after the first slash, but they have a special syntax which allows HTML documentation pages to be generated automatically using a program called javadoc
. The previous program with documentation comments might look like the following.
/** * This class contains a static method that prints the hailstone sequence for a given integer n. * If the hailstone sequence converges to 1 for every positive integer, the Collatz conjecture is true. * Course: COMP 1600 * Assignment: Project 3 * * @author David Bowie * @version 1.0, 02/29/2014 */ public class Collatz { /** * Prints the sequence of numbers starting at n and going down to 1. * If n is even, the next number in the sequence is n / 2. * If n is odd, the next number in the sequence is 3n + 1. * * @param n the starting point of the sequence */ public static void findSequence(int n) { System.out.println(n); // Print start while (n != 1) { if (n % 2 == 0) // Check if even n /= 2; else n = 3*n + 1; System.out.println(n); // Print next number } } }
Practices to Avoid
Many practices should be avoided because they make the code harder to read or increase the likelihood of bugs. Below we have a short (and non-exhaustive) list of such practices.
Comparing boolean variables to true or false
When used in the condition of an if
statement or loop, there is no need to compare a boolean
variable to true
or false
since they are already boolean
values. Consider the following example.
boolean isEven = (x % 2 == 0); if (isEven == true) // Don't do this System.out.println("Your number is even!");
It is cleaner to put the variable in the condition without any other comparison, as follows.
boolean isEven = (x % 2 == 0); if (isEven) // Simpler and easier to read System.out.println("Your number is even!");
Another reason to avoid the comparison is because it is not an error to use assignment (=
) with a boolean
in a condition instead of comparison (==
). Consider the following code which will say that any number is even.
boolean isEven = (x % 2 == 0); if (isEven = true) // Always true because = was used instead of == System.out.println("Your number is even!");
Using numbers in place of character literals
It's true that every character literal in Java has a Unicode value that can be interpreted as an integer. These values can be added, subtracted, and compared against other numbers. Some programmers are tempted to use the number instead of the literal. Doing so is always wrong. The result is less readable, and it's easy to get the wrong value.
if (character >= 97 && character <= 122) // Don't do this! System.out.println(character + " is a lower case letter!");
Always use the character literal, given between single quotes, to represent the character.
if (character >= 'a' && character <= 'z') // Much clearer System.out.println(character + " is a lower case letter!");
Using break or continue in loops
The keywords break
and continue
can be used, respectively, to leave a loop immediately or start the next iteration. They should be avoided so that the loop has only one entry point and one exit point. Otherwise, the path of execution taken through a loop becomes harder to reason about. It is always possible to rewrite a loop so that all important termination conditions are checked at the top. Consider the following example.
boolean isPalindrome = true; for (int i = 0; i < word.length()/2; ++i) if (word.charAt(i) != word.charAt(word.length() - i - 1)) { isPalindrome = false; break; //don't do this! }
In this case, the break
keyword is being used for efficiency. There is no need to keep checking additional characters once we know the word is not a palindrome. Instead, this check can be incorporated into the loop condition as follows.
boolean isPalindrome = true; for (int i = 0; i < word.length()/2 && isPalindrome; ++i) // Condition included here if (word.charAt(i) != word.charAt(word.length() - i - 1)) isPalindrome = false;
Systems programmers might argue that there are some cases where a break
or a continue
might make a difference in terms of performance. Empirically, these arguments do not usually hold. Furthermore, the optimizing Java compiler and the dynamic JVM runtime environment might ultimately treat both pieces of source code the same.
Using System.exit()
Using System.exit()
seems like a simple way to end a program. However, it shuts down the entire JVM unceremoniously. It is like a giant break
, jumping out of all execution. It is better to structure a program so that it reaches the end of the main()
method naturally.
A program that is structured so that all paths ultimately reach the end of the main()
method is easier to reason about, but it is just as important to remember that System.exit()
kills the entire program and all threads. If some background threads are reading or writing data, they are killed immediately. Setting the default close operation on a JFrame
to JFrame.EXIT_ON_CLOSE
is equivalent to calling System.exit()
. Instead, set the default close operation to JFrame.DISPOSE_ON_CLOSE
, which will allow all the GUI threads to finish naturally.
In the narrow case of command line scripting, it can be argued that System.exit()
allows a Java program to return an error code that can be stored by a shell script. However, this case is less and less important, and Java has much more detailed forms of error reporting such as exceptions.
Using Math.pow() to square values
Unlike some languages, Java does not provide exponentiation as a built-in numerical operator. Instead, the static method Math.pow()
can be used to raise arbitrary bases to arbitrary powers. However, Math.pow()
should not be used to square numbers. It is less efficient and less readable to write Math.pow(x,2)
instead of x*x
. Furthermore, Math.pow()
takes double
values and returns a double
value. Thus, it cannot be used to square or cube int
values without a cast.
Writing your own code instead of using libraries
Avoiding Math.pow()
when squaring numbers is a special case. In general, you should use all the standard Java libraries rather than writing your own code. There is no need to write a general method to raise values to powers when Math.pow()
already exists. You should not write your own linked list class when the JCF LinkedList
class is there for you to use. The example above that tests to see if a character value is between 'a'
and 'z'
would be better replaced by Character.isLowerCase()
. You should never write a sorting method when you could use Arrays.sort()
or similar.
There are a few exceptions to this rule. Because you are here to learn, your professors may often require you to write code for a problem that is already solved by a library. In these cases, you must follow your professor's instructions, even if there is a simple way built into the Java API. Furthermore, there are some cases when you want to do something more specific than what the Java API does. For example, testing to see if a character is between 'a'
and 'z'
is different from using Character.isLowerCase()
, which will also report true
for 'ä'
.
Misusing shortcuts
Incrementing a variable in Java is a very common operation. Expressions like i++
and ++i
pop up so
often that it is easy to forget exactly what they mean. Programmers occasionally forget that they are both shorthand for i = i + 1
and begin to think of them as a fancy way to write i + 1
.When confused, a programmer might write something like the following.
int i = 14; i = i++;
At first glance, it may appear that the second line of code really means i = i = i + 1
. Assigning
i
an extra time is pointless, but it does no harm. However, remember that the postfix version gives back
a copy of the original value, before it has been incremented. In this case, i
will be incremented, but then
its original value will be stored back into itself. In the code given above, the final value of i
is still 14
.
Using lowercase L or uppercase O as variables
You should not use lowercase L (l
) as a variable. Depending on your editor, it can be almost indistinguishable from the number one (1
). Four levels of nested loops are uncommon, but when they happen, programmers are tempted to use i
, j
, k
, and then l
. Use any other single letter instead of l
.
Uppercase O (O
) is similarly objectionable because it looks like the number zero (0
). If you are following conventions for making local variables, you would never create a variable starting with an uppercase letter in the first place, but even lowercase O (o
) throws off the eye.