Definition

This guide has a variety of do’s and don’ts for recommended style in Java programming!

Maintaining clean programmatic style is important for a variety of reasons:

  • It helps to keep your code readable, scalable, and is useful for collaboration in which others reading your code have expectations for conventional formats.
  • It helps to practice clean coding paradigms like “one change, one place” and avoid security exploits like restricting access to data members.
  • For the above reasons, it’s important for job and internship recruiters, which means that sloppy code can tank your chances of nailing the interview!

As such, it’s not just about making code that works, but also code that is well documented with clear identifiers, helper methods where needed, and a variety of other concerns that follow.


Basics

Definition

The following style advice applies to pretty much every programming language, but is demonstrated herein in Java.

Spacing & Indentation

Debug

First things first: USE ONLY SPACES TO INDENT (or only tabs, though that’s less preferable), but NOT a mixture of both.

A couple of things you can use to verify this:

  • Google how to use your chosen IDE to replace all tabs with spaces for indentation.
  • In many development environments, you can search for tabs by enabling the “regular expression” search option and finding \t, making sure to replace these with 4 spaces wherever found.

Bad

Sloppy indentation makes code hard to follow and difficult to understand what statements belong to what blocks.

Good

All blocks of code between curly brackets should be indented by 4 spaces, and adding 4 more in each nested block.


Bad

for (int i = 0; i < something; i++) {
for (int j = 0; j < somethingElse; j++) {
  System.out.println(i);
 System.out.println(j);
}
}

Good

for (int i = 0; i < something; i++) {
    for (int j = 0; j < somethingElse; j++) {
        System.out.println(i);
        System.out.println(j);
    }
}

Bad

New lines/spaces should be used tactically; you should never have more than 2 new lines in a row for any reason.

Good

Use spaces sparingly, generally to separate only key blocks of code and typically variable declarations at the top of some block from where they’re used.


Bad

String s = "test";
 
int a = 5;
 
char b = 'B';
 
for (int i = 0; i < s.length(); i++) {
 
    if (s.charAt(i) == b) {
        // ... do something
 
    }
 
}

Good

String s = "test";
int a = 5;
char b = 'B';
 
for (int i = 0; i < s.length(); i++) {
    if (s.charAt(i) == b) {
        // ... do something
    }
}

Variables & Naming

Bad

Programming isn’t like math in that you should feel bound to small variable names in an equation that are defined elsewhere.

Good

Names of variables/methods/constants etc. should clearly indicate their purpose—use as much room as you need, though try to find a good balance between parsimony and clarity. (Ignore the weird parameter new-lines, I had to fit it into these tiny boxes)


Bad

public static double d (double vi, double vf, double t) {
    return ((vi + vf)/2)*t;
}

Good

public static double getDistance (double velocityInitial,
                                  double velocityFinal,
                                  double time) {
    return ((velocityInitial + velocityFinal)/2)*time;
}

Bad

Declaring multiple variables with the same modifiers and types can get tedious…

Good

Instead, use the comma-declaration syntax, which allows you to borrow the first variable’s modifiers and types to declare the others!


Bad

public int length = 10;
public int width  = 10;
public int height = 20;

Good

public int length = 10,
           width  = 10,
           height = 20;

Bad

Literals (numbers like 5, 3.2, or String literals like “test”) that may need to be changed later (especially if they are repeated often) can be hard to find and change accurately if they are left without being named.

Good

Instead, make these static constants that are easy to change in a single location, which also adds to the clarity of your code since you’ve now associated some meaning to a value.


Bad

if (someVar < 3 || someVar > 10) {
    // ... do something
}

Good

private static int LOWER_BOUND = 3,
                   UPPER_BOUND = 10;
// ...
if (someVar < LOWER_BOUND || someVar > UPPER_BOUND) {
    // ... do something
}

Conditionals

Bad

If you already have a boolean condition, you should never compare it to the boolean literals.

Good

Either use the boolean itself or use the negation operator ! to flip as needed.


Bad

int inty = 2;
boolean booly = inty > 3;
if (booly == false) {
    // ... do something
}

Good

int inty = 2;
boolean booly = inty > 3;
if (!booly) {
    // ... do something
}

Bad

Sometimes our if-ladders (viz., long sequences of if-then statements) get out of control complicated when they’re only comparing some variable to a set of literals, like the following:

Good

That’s a lot of wasted typing just to have behavior that triggers when a value obtains one of a number of those expected! Instead, use a switch statement, which compares the given value to a variety of cases; whichever is matched executes the code block below it until the first break; statement is encountered.


Bad

int inty = 2;
if (inty == 0) {
    // ... do something 0
} else if (inty == 1) {
    // ... do something 1
} else if (inty == 2) {
    // ... do something 2
} else {
    // ... do something otherwise
}

Good

int inty = 2;
switch (inty) {
    case 0:
        // ... do something 0
        break;
    case 1:
        // ... do something 1
        break;
    case 2:
        // ... do something 2
        break;
    default:
        // ... do something otherwise
        break;
}

Advanced

Definition

This section is devoted to more class-design and architectural stylistic concerns.

Class Design

Bad

Public fields are risky to expose to users, as they may change their values outside of your (the class designers’) control, leading to unpredictable or insecure behavior.

Good

Fields should be made private, and controlled / manipulated only through public methods called by the class’ user.


Bad

public class Person {
 
    // ...
    public int age;
    public String name;
    // ...
 
}

Good

public class Person {
 
    // ...
    private int age;
    private String name;
 
    public int getAge () { return this.age; }
    // ...
 
}

Bad

Using static variables as globally-accessible between method calls is generally a mistake — it can lead to unpredictable behavior and bugs, especially if your methods are called in parallel (multi-threading is covered in your OS class, and exposes a risk that might not be obvious now).

Good

Static constants are OK, but if you need pieces of data to be passed between method calls, simply create a private helper method with all of the parameters needed.


Bad

public class SomeClass {
 
    // [X] Don't do this
    private static int someInt = 0;
 
    public static int someMethod1 (int someIntParam) {
        // ...
        someInt += someIntParam;
        // ...
    }
 
    public static int someMethod2 (int someIntParam) {
        // ...
        someInt -= someIntParam;
        // ...
    }
 
}

Good

public class SomeClass {
 
    // Hard to show a fix on an abstract example, just
    // don't use static variables!
 
}

Inheritance

Bad

Maintaining fields in multiple classes along some inheritance chain is both wasteful for memory, and confuses which class is meant to maintain what pieces of information.

Good

Store necessary fields in a single class (generally, the highest in the inheritance chain as-possible), and then expose only the methods necessary to manipulate or access those.


Bad

public class Pet {
 
    private String name;
 
}
public class Dog extends Pet {
 
    private String name;
 
}

Good

public class Pet {
 
    private String name;
 
    public String getName () { return this.name; }
 
}
public class Dog extends Pet {
    // ...
}

Bad

Defining the same methods in multiple places of the inheritance chain does not keep code DRY (don’t repeat yourself), and also compromises the one-change, one-place principle.

Good

For any methods that share the same behavior, define these in the superclass, and then simply use the inheritance chain or the super keyword (where appropriate) to employ the Superclass’ definition.


Bad

public class Pet {
 
    public void speak () {
        System.out.println("*silence*");
    }
 
}
public class Fish extends Pet {
 
    public void speak () {
        System.out.println("*silence*");
    }
 
    public void begForFood () {
        this.swimAround();
        this.speak();
    }
 
}

Good

public class Pet {
 
    public void speak () {
        System.out.println("*silence*");
    }
 
}
public class Fish extends Pet {
 
    public void begForFood () {
        this.swimAround();
        super.speak();
    }
 
}

Hidden on the original page

The section below was commented out of the Fall 2021 course notes, so students never saw it. It is preserved here because the material is complete and usable.

Bad

 

Good

 

…more to come!