Thornton Rose
Published as "Good Java Style: Part 1", 2/22/2001, and "Good Java Style: Part 2", 2/28/2001, Gamelan.com.
Copyright © 2001, Thornton Rose.
Thanks to April Rose, David Thurmond, and John Straw for their reviews.
Having worked as a software developer and consultant for many years, I have seen a large amount of code in a variety of programming languages. It has run the gamut from elegant to ugly, and unfortunately much of it has been ugly. With this article, I hope to persuade you, and my fellow developers, that we should give as much attention to the style of our code as we give to the user interface and other visible parts of an application. I will explain why we should care about how our code looks, then illustrate elements of good style for Java.
Even though Java is used to write programs rather than prose, it is still used to express thoughts and ideas. And, in addition to conveying information, those thoughts and ideas must actually do something. Worrying about good style may seem like a waste of time, but it behooves us to write our code such that the thoughts and ideas it expresses are exceptionally clear.
Here are several reasons for using good style:
Writing code with good style also provides the following benefits:
Writing Java with good style is not hard, but it does require attention to detail. Here are some general guidelines to follow:
Tabs vs. spaces is one of several religious issues related to writing code, and I am not going to suggest that there is only one right way. I espouse using spaces, because it ensures that my code will look the same in my editor as it does in your editor, and vice versa. If you feel that using spaces instead of tabs just ain't right, then by all means use tabs.
Indent style, or the placement of braces ("{" and "}") and the associated indentation of code, is another of the religious issues related to writing code. There are several indent styles common to C-style languages like Java, and I am not going to suggest that one of them is superior. In most of the example code in this article I use what is usually referred to as K&R style. If you don't like K&R, by all means use another style.
There are many ways that a Java source file can be organized. Here is one that works well:
Example 1 - Bad File Organization |
---|
package org.rotpad; import java.awt.*; import javax.swing.event.*; import org.javacogs.*; import javax.swing.*; import java.awt.event.*; class Foo { ... } public class RotPad extends JFrame { ... } |
Example 2 - Good File Organization |
---|
package org.rotpad; // Java classes import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; // JavaCogs classes import org.javacogs.*; /** * RotPad is a simple GUI application for performing rotation ciphers on plain * text. * * @author Thornton Rose * @version 1.0 */ public class RotPad extends JFrame { ... } //----------------------------------------------------------------------------- /** * Foo is ... * * @author Thornton Rose * @version 1.0 */ class Foo { ... } |
A complex class can have a large number of imports, which can get unruly, especially if you prefer to import individual classes instead of whole packages (e.g. java.awt.*). To get a handle on the imports, organize them as follows:
Be sure to comment the third-party and application classes, particularly those that do not have obvious names. Use end-of-line comments, or put a comment at the beginning of the section. Also, if you really want to be a perfectionist, order each group of imports alphabetically.
Example 1 - Bad Import Style |
---|
import java.util.*; import javax.swing.*; import java.awt.event*; import com.gensym.com.*; import javax.swing.table.*; import com.pv.jfcx.*; import java.awt.*; import com.melthorn.util.*; |
Example 2 - Good Import Styles |
---|
import java.awt.*; import java.awt.event*; import java.util.*; import javax.swing.table.*; import com.gensym.com.*; // BeanXporter import com.pv.jfcx.*; // ProtoView import com.melthorn.util.*; // Utilities |
// Java classes import java.awt.*; import java.awt.event*; import java.util.*; import javax.swing.table.*; // BeanXporter import com.gensym.com.*; // ProtoView GUI components import com.pv.jfcx.*; // Application classes import com.melthorn.util.*; |
Organizing a Java source file without organizing the classes in it would not gain you much in the way of style. Here's how to organize the classes in your source files:
Example 1 - Bad Class Style |
---|
// RotPad -- GUI app. for ROT ciphering public class RotPad extends JFrame { private static final String TRANSFORM_ROT13 = "ROT13"; private static final String TRANSFORM_ROT13N5 = "ROT13N5"; private static final String TRANSFORM_ROTASCII = "ROT-ASCII"; private void jbInit() throws Exception { ... } public static final String TITLE = "RotPad"; public static final String VERSION = "1.0"; public static void main(String[] args) { ... } public RotPad() { ... } private JPanel jPanel1 = new JPanel(); private JPanel jPanel2 = new JPanel(); private BorderLayout borderLayout1 = new BorderLayout(); ... } |
Example 2 - Good Class Style |
---|
/** * RotPad is a simple GUI application for performing rotation ciphers on plain * text. * * @author Thornton Rose * @version 1.0 */ public class RotPad extends JFrame { // Public constants public static final String TITLE = "RotPad"; public static final String VERSION = "1.0"; // Private constants private static final String TRANSFORM_ROT13 = "ROT13"; private static final String TRANSFORM_ROT13N5 = "ROT13N5"; private static final String TRANSFORM_ROTASCII = "ROT-ASCII"; // GUI components [JBuilder generated] private BorderLayout borderLayout1 = new BorderLayout(); private JPanel jPanel1 = new JPanel(); private JPanel jPanel2 = new JPanel(); ... /** * Construct a new instance of this class. */ public RotPad() { ... } /** * Initialize UI components. [JBuilder generated] */ private void jbInit() throws Exception { ... } ... //-------------------------------------------------------------------------- /** * Start the application. */ public static void main(String[] args) { ... } } |
Some classes have a large number of fields, which can get difficult to maintain if they are not organized well. Organize them as follows:
final
and static final
).
Additionally, use the following guidelines for writing field declarations:
Example 1 - Bad Field Style |
---|
public class CustomerSearchDialog extends JDialog { private JLabel firstNameLabel = new JLabel(); private JLabel lastNameLabel = new JLabel(); public static final RESULT_SELECT = 1; private Vector results = new Vector(); // Search results. private DefaultTableModel tableModel = new DefaultTableModel(); public static final RESULT_CANCEL = 0; // ... } |
Example 2 - Good Field Style |
---|
/** * ... */ public class CustomerSearchDialog extends JDialog { /** * Indicates that search was cancelled; returned by showDialog() when * user clicks cancel button. */ public static final RESULT_CANCEL = 0; /** * Indicates that a customer was selected; returned by showDialog() when * user clicks select button. */ public static final RESULT_SELECT = 1; private Vector results = new Vector(); // Search results. private DefaultTableModel tableModel = new DefaultTableModel(); // Grid model. // GUI fields. [JBuilder] private JLabel firstNameLabel = new JLabel(); private JLabel lastNameLabel = new JLabel(); // ... } |
Use the following guidelines for writing method declarations:
Example 1 - Bad Method Style |
---|
public int getTypeCount (String custType) { ... } static public getInstance(){ ... }; public void showRange() throws RangeException { ... } |
Example 2 - Good Method Styles |
---|
/** * Return the single instance of this class. */ public static CalculationEngine getInstance() { return instance; } /** * Calculate the consumption coefficient. */ public float calculateConsumptionCoefficient(int base, float variance, int iterations) throws RangeException { // ... } |
/** * Calculate the consumption coefficient. */ public float calculateConsumptionCoefficient( int base, float variance, int iterations) throws RangeException { // ... } |
/** * Calculate the consumption coefficient. */ public float calculateConsumptionCoefficient(int base, float variance, int iterations) throws RangeException { // ... } |
Use the following guidelines for writing blocks and statements:
if
).
} // end if
), particularly with long or nested blocks.
case
clauses in a switch
block.
if
, for
, or while
, put
whitespace before the "(".
Variables used in for
loops are the exception to putting
variables at the beginning of a block. The loop variable(s)
may be declared in the initialization part of the for
statement,
e.g. for (int i = 0; ...).
Putting a comment at the end of a block can help you track down accidentally deleted closing braces. Finding those in a large source file can sometimes drive you nearly crazy.
Example 1 - Bad Block Style |
---|
try{ for(int i=0;i<5;i++){ ... } int threshhold=calculateThreshhold(); float variance=(threshhold*2.8)-1; int c=0; if (threshhold<=15) c=calculateCoefficient(); switch(c){ case 1: setCeiling(c*2); break; case 2: setCeiling(c*3); break; else: freakOut(); } }catch(Exception ex){ ... } |
Example 2 - Good Block Style |
---|
try { int threshhold = 0; float variance = 0.0; int coefficient = 0; // Prepare 5 cycles. for (int i = 0; i < 5; i ++){ prepareCycle(i); } // Calculate the threshhold and variance. threshhold = calculateThreshhold(); variance = (threshhold * 2.8) - 1; // If the threshhold is less than the maximum, calculate the coefficient. // Otherwise, throw an exception. if (threshhold <= MAX_THRESHHOLD) { coefficient = calculateCoefficient(); } else { throw new RuntimeException("Threshhold exceeded!"); } // Set the ceiling based on the coefficient. switch (coefficient) { case 1: setCeiling(coefficient * 2); break; case 2: setCeiling(coefficient * 3); break; else: freakOut(); } // end switch } catch(Exception ex) { ... } // end try |
There are two type of comments that you can put in your Java code: Javadoc
comments (also called documentation comments) and implementation comments.
Javadoc comments can be extracted by the javadoc
tool to produce
API documentation. Implementation comments are those comments that explain the
how and why of the code. Use the following guidelines for commenting your Java
code:
Also, keep in mind that good comments are helpful; bad comments are a nuisance.
Example 1 - Bad Comment Style |
---|
// applyRotAscii() -- Apply ASCII ROT private void applyRotAscii(){ try{ int rotLength = Integer.parseInt(rotationLengthField.getText().trim()); // get rot len RotAscii cipher = new RotAscii(rotLength); // new cipher textArea.setText(cipher.transform(textArea.getText())); // transform }catch(Exception ex){ /* Show exception */ ExceptionDialog.show(this, "Invalid rotation length: ", ex); } } |
Example 2 - Good Comment Style |
---|
/** * Apply the ASCII rotation cipher to the user's text. The length is retrieved * from the rotation length field, and the user's text is retrieved from the * text area. * * @author Thornton Rose */ private void applyRotAscii() { int rotLength = 0; // rotation length RotAscii cipher = null; // ASCII rotation cipher try { // Get rotation length field and convert to integer. rotLength = Integer.parseInt(rotationLengthField.getText().trim()); // Create ASCII rotation cipher and transform the user's text with it. cipher = new RotAscii(rotLength); textArea.setText(cipher.transform(textArea.getText())); } catch(Exception ex) { // Report the exception to the user. ExceptionDialog.show(this, "Invalid rotation length: ", ex); } } |
In conclusion, I have one final thought for you on the subject of code style. No matter what guidelines you follow, and no matter how fervent your beliefs about things like indent style, remember that when you write code your overall goal should be to make the code understandable and maintainable by someone else.