Java Conventions

Java syntax is writing code in a way that allows it to compile. Conventions are things that don't affect whether the code will compile, but are just generally accepted as part of writing good Java code, and are things that you should adhere to ensure you are writing good, readable code.

Curly Braces

In Java curly braces are used to compartmentalize blocks of code. Lots of languages use curly braces, and Java has its a convention for how to use them. An opening curly brace should be placed at the end of the line that opened it, and the closing curly brace should be on a line by itself at the same level of indentation as the line with the opening curly brace.

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}
⬆ back to top
Indentation

Indentation is an important way to help distinguish blocks of code. You should indent your program one tab further every time you open a curly brace { and indent one tab less every time you close a curly brace }.

A tab should generally be 3 or 4 spaces, and you can set the size of your tab in your compiler. The jGrasp default is 3 spaces

Take these two examples:

DON'T DO THIS
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
System.out.println("Hello world!");
}
}
}

This method has no indentation, and is super hard to read as a result. Now look at the second method below:

DO THIS
public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        if (i % 2 == 0) {
            System.out.println("Hello world");
        }
    }
}

Indentation makes code easier to read. It helps the reader see what code is nested where, and where structures like if statements and loops start and end.

You can actually have jGRASP fix all your indentation for you! To the right of the Undo button is the Generate CSD button, which will automatically indent your code and add some structural annotations on your code, which can be removed with the next button to the right, the Remove CSD button.

⬆ back to top
Long Lines and Wrapping

To keep our code nice and compact and improve readability, lines of code should ideally max out at 80 characters, and should never exceed 100 characters in length. Lines that are too long should be broken up and wrapped to the next line.

Note that in jGrasp, you can check your line length by putting your cursor at the end of the line and check the bottom right corner. There should be something that says "Col:" and then the number of characters in the line.

To break up a long comment, you can simply split the comment into two or more lines:

// THIS IS A BAD COMMENT
// this is a bad comment because it is just so gosh darn long that it just makes it so hard to read

// THIS IS A GOOD COMMENT
// this is a good comment because when it reaches the point where it is too long,
// the comment just moves to the next line

Breaking up lines of code is a little more complicated. Choosing where to break up your lines can be a little arbitrary, but it's usually best to break the line after things like commas and operators. IMPORTANT: You cannot break a String up between lines. If you want to break a String up, you need to use two separate Strings that are concatenated. There are two conventions for wrapping a line of code:

  1. You can leave a hanging indent of two tabs relative to the start of the first line, like so:
// Because the String to be printed is so long, it gets wrapped to the next line
public static void printReallyLongLine() {
System.out.println("wow this line is so long it's like unreasonably long like" +
        "honestly it's so long why even");
}
  1. Or, if the long line has something in parentheses, like a method header, you can align the code inside the parentheses, like so:
public static void thisMethodHeaderIsReallyLong(int thing, int otherThing,
                                            int moreThings, int anotherThing)

Note that these are very bad method and variable names, and you should not use these as an example. These are just examples of long lines.

⬆ back to top
Spacing

It might seem trivial, but spacing is very important for writing readable code. Here are some examples of Java conventions for good spacing for your code:

Code Good Spacing Bad Spacing
Class Header public class GoodSpacing { public class BadSpacing{
Method Header public void method(int param, int param2) { public void method (int param,int param2){
Method Call method(7, 2); method(7,2);
for loop for (int i = 0; i < 5; i++) { for(int i=0;i<5;i++){
while loop while (test) { while(test){
Array Initialization int[] arr = new int[5]; int [] arr=new int [5];
Variable Initialization int x = -5 * j + y % 2; int x=-5*j+y%2;

Note the spacing in expressions. Generally, you should always leave a space on either side of an operator (+, -, =, etc.). The only exceptions are ++, --, and using - to express negative numbers (i.e. -5 ).

⬆ back to top
Spacing Between Methods

Similar to spacing within lines of code, we want to ensure that we have spacing in the structure of our code to keep our code looking readable. You should always include a blank line between methods to make sure your code is easy to read through.

DO THIS
public class GoodSpacing {
    public static void main(String[] args) {
        ...
    }

    public static void method() {
        ...
    }

    public static void anotherMethod() {
        ...
    }
}
⬆ back to top

Names

Naming Conventions

We have certain conventions for how we name things in Java. Here are the conventions we use for different kinds of names in Java:

  • Method & Variable Names: These should be camelCased, a convention where the first letter of every word after the first is uppercased.
  • Class Names: These should be PascalCased, a subset of camel casing where the first letter of the first word is also uppercased.
  • Constant Names: These should be SCREAMING_CASED, in all uppercase with words separated by underscores.
⬆ back to top
Descriptive Names

The name of a variable should describe the value it stores and what it represents. Similarly, the name of a method should describe the task of the method. As a general rule of thumb, class and variable names should be nouns, and method names should be verbs. Abbreviations should not be used unless they are generally accepted abbreviations or very obvious, like num for number. It's usually best to avoid abbreviations. Only the following one-letter variable names should be used:

  • Graphics g & Random r: These are okay only for these objects only because they're common Java conventions.
  • x and y (for coordinates): These are what we actually call Cartesian coordinates, so they're actually great variable names for this purpose (but, again, only for coordinates). Similarly, r is a good variable name for describing Polar coordinates (unfortunately there's no theta key, but if you can get the character it's also acceptable for a polar coordinate (a little more work than it's probably worth)).
  • Loop variable names: We use i-j-k for loop variable names because it is general programmer convention for for loops. i should be used for outer-loops, j for once-nested loops, k for twice nested loops, etc. Generally, if we use i-j-k convention for loop varaible names, we don'e want to mix them with descriptive loop variable names. Descriptive loop variable names are okay, as is using i-j-k convention, but we want be consistent with one or the other. i-j-k convention should be used like this:
for (int i = 0; i < 10; i++) {
for (int j = 0; j < i; j++) {
    for (int k = i; k > j; k++) {
        System.out.print("*");
    }
    System.out.print("+");
}
System.out.println();
for (int j = 0; j < i; j++) {
    System.out.print("#");
}
System.out.println();
}
⬆ back to top

Methods

Technically, you could write an entire program in that program's main method. However, this would be an incredibly bad idea. Your main method could end up being thousands of lines long. It's generally considered good practice to factor your code into methods. Here are a few reasons why:

  1. Methods Reduce Redundancy: Often, you will want the exact same or very similar tasks multiple times in your program. Rather than writing the same code multiple times, which would be redundant, you can factor that code into a method that can be called throughout your program to perform that task. We can even use parameters and returns to create methods that have even more functionality to further reduce redundancy. Remember, you should never be copying and pasting code.
  2. Methods Represent Tasks: Even if code isn't redundant, it can still be a good idea to factor it into a method. Methods should represent a concrete task. A good example is printing an intro to your program; it's something you'll only do once, but it's a distinct task, so it makes sense to factor it into a method. An important aspect of this is making main a concise summary of the program. main runs the program, but it shouldn't be cluttered with all of the sub-tasks required to do that. Those tasks should each be factored into their own methods, whether or not they're redundant. Factoring code into methods with distinct tasks also makes it easier to reuse your code. If there's a method where you perform 2 tasks, and then later only want to perform only one, you couldn't call your existing method because you don't want to perform both tasks. It would be better to structure each task into its own method to make it more reusable.

A few things to avoid:

  • Trivial methods do so little that their existence is pointless. Methods with just one print statement or one method call are good examples. One-line methods can be non-trivial if you are factoring a common calculation into a method, for instance, but with methods with so little code, you should generally consider whether or not the method is improving your program.
  • Avoid cases where you have unnecessary parameters and returns. Methods only need to return a value if you plan on catching and using that value when you call your method. Otherwise, your method should just have a void return type. Similarly, you should only pass in parameters that you need. Parameters that you never use in your method or whose value could be calculated from other parameters are unnecessary. If you pass a parameter into your method but never use the value passed in (i.e. you immediately set its value to something else), you might want to consider whether you need that parameter at all, or if that value could be a local variable.
⬆ back to top

Printing

There are a few basic rules you should follow for printing in Java:

  • You should always print a blank line using System.out.println(). Printing an empty String (System.out.println("")) is considered bad style; it makes the intention less clear.
  • \n should never be used in print and println statements.
  • Rather than have a bunch of adjacent print statements, you should combine them into one where you can. System.out.println("**") is much preferred to System.out.print("**"); System.out.println();
⬆ back to top

Variables

Variables are used to store values so that we can access the same values in multiple places without having to do the same computations to get that value every time. However, there are some important things to consider when using variables:

⬆ back to top
Scoping

You should declare your variables in the smallest scope necessary. If a variable only needs to keep its value through one iteration of a loop, you should declare it in the loop. If a variable needs to keep track of something across multiple iterations of a loop, you should declare it outside the loop. If you have a variable that you need to use in multiple places throughout your program, it's generally a good idea to declare it in main and pass it where it's needed using parameters and returns.

⬆ back to top
Constants

Some values are used all across your program. This is where it's good to make a class constant. Constants are unchanging values that exist everywhere in your program and represent some important value in your program. For example, the Math class has a constant for the value of PI, because it's an important value that is used often in the class and needs to have the same value everywhere. Constants also make code easier to change. Rather than having to change a value everywhere it is used, you can just change the value of the constant to change that value everywhere in the program that the constant is used. Constants should always be declared as public static final <CONSTANT_NAME>. The final keyword means that they cannot be changed after declaration.

⬆ back to top

Types

Primitives (vs Wrapper Classes)

Every primitive type has an Object that is called a wrapper class that essentially represents the same value, but as an object, for example:

  • Integer for int
  • Double for double
  • Boolean for boolean

While these do represent the same values and can for all intents and purposes be used the same, you should always use the primitive type over the wrapper class whenever possible. There are some cases where you need to use the wrapper class (making an ArrayList or other data structure to contain a primitive type), but you should always prefer using the primitive type when possible.

⬆ back to top
int vs double

ints and doubles both represent numbers and technically, anything that can be represented by an int can also be represented by a double. However, just because it can doesn't mean it should. You should always use the type that best represents the value you are storing; a percentage might make sense as a double, but a count should always be a whole number and should therefore be an int. Make sure to consider what your variables are representing before making them all doubles when they shouldn't be.

⬆ back to top
boolean

Similarly, there are a few different ways you can represent a true/false (or yes/no) value. You could represent it with an int as a 1 or a 0, or even with a String as "yes" or "no" or "true" or "false", however, there's a better type to represent that. You're representing one of two values, and that is exactly what a boolean should be used for. booleans represent a true or false value, and should always be used when you're saving some variable that represents one of two states.

⬆ back to top

Loops

Something important to first consider is if you actually need a loop. Loops should be used to perform repeated actions. If you only want to do something once, then there's no point in having a loop, since you could just include the code without the loop and it would do the same thing.

DON'T DO THIS
public static int square (int num) {
    int square = num;
    for (int i = 0; i < 1; i++) {
        square *= num;
    }
    return square;
}

In this case, we're using a loop to perform an action that we only need to do once. We can replace this entire loop with the code inside it and that would not change anything about what happens when we run the code.

DO THIS
public static int square (int num) {
    int square = num;
    square *= num;
    return square;
}

Similarly, if you only want to do something once after a bunch of repetitions, you should not include that code in the loop, because it's not actually repeating. For example:

DON'T DO THIS
for (int i = 0; i <= 5; i++) {
    System.out.print("*");
    if (i == 5) {
        System.out.println(" done");
    }
}

In this code they are only printing "done" at the end of the last iteration, so they should just pull that out of the loop, like this:

DO THIS
for (int i = 0; i <= 5; i++) {
    System.out.print("*");
}
System.out.println(" done");

Always make sure you are using the right kind of loop. for loops should be used when you know how many times you want to perform a repeated action (these are very helpful for String and array traversals). while and do-while loops are great for when you aren't sure how many times your loop will run.

The difference between a while and do-while loop is that a do-while is guaranteed to run at least once and then function as a while loop. A while loop may never run, but a do-while loop is guaranteed to run at least once.

⬆ back to top

Conditionals

if/else Structure Choice

Each set of if/else if/else branches can go into at most 1 branch, and an else branch guarantees that the program will go into a branch. When using conditionals in your program, you should use a structure that best matches the min and max number of branches you want to execute. For instance, take the following program:

DON'T DO THIS
int x = r.nextInt(5) - 2; // range from -2 to 2
if (x < 0) {
    System.out.println("positive number generated");
}
if (x > 0) {
    System.out.println("negative generated");
}
if (x == 0) {
    System.out.println("0 generated");
}

The program as it is currently structured could go into 0-3 branches. However, because x can only be one value, the program logically should only go into 1 branch, so it would be better to use else ifs, like so:

int x = r.nextInt(4);
if (x < 0) {
    System.out.println("positive number generated");
} else if (x > 0) {
    System.out.println("negative number generated");
} else if (x == 0) {
    System.out.println("0 generated");
}

This ensures that the program will go into a maximum of 1 branch. However, this structure could still go into 0 branches, and the program should go into exactly 1. There are 3 possibilities and one of them must be true every time. The best way to structure this program would be to write a conditional structure that goes into exactly 1 branch:

DO THIS
int x = r.nextInt(4);
if (x > 0) {
    System.out.println("positive number generated");
} else if (x < 0) {
    System.out.println("negative number generated");
} else { // x == 0
    System.out.println("0 generated");
}

Note that all three of these programs do the same thing externally. However, the last program is the best stylistically because the other structures imply to anyone reading the code that x could fall into none of the branches, which we know is not possible.

⬆ back to top
Useless Branches

You should never write code that does nothing, and conditionals are a good example of this. Remember, every conditional doesn't need to have an else if or else branch; you should only write these branches when they're needed. Take the following code:

DON'T DO THIS
System.out.print("How many numbers? ");
int nums = console.nextInt();
int max = 0;
for (int i = 0; i < nums; i++) {
System.out.print("Input a positive integer: ");
int n =  console.nextInt();
if (n > max) {
    max = n;
} else {
    max = max;
}
}

In the else branch, all that's happening is setting max = max, which does nothing. Since this line of code does nothing, we can remove it:

DON'T DO THIS
if (n > max) {
max = n;
} else {
}

However, now the else branch is empty, and can be removed completely:

DO THIS
if (n > max) {
max = n;
}

Similarly, sometimes you have nothing in your if branch and only want to execute code if the condition is false. In that case, you should structure your code with the opposite condition. Take the conditional from the previous example. If it had been structured like this:

DON'T DO THIS
if (n <= max) {
} else {
max = n;
}

We again have an empty branch, but can't remove it and have just an else. Instead, just use an if branch with the opposite condition, which eliminates the need for the empty branch.

DO THIS
if (n > max) {
max = n;
}
⬆ back to top
Factoring

Conditionals are used to separate chunks of code that should be executed under specific conditions. If code is repeated between all the branches, then that means that that code should be executed regardless of the condition. Take the following code:

DON'T DO THIS
int num = console.nextInt();
if (num % 2 == 0) {
    System.out.println("Your number was even.");
    return num;
} else {
    System.out.println("Your number was odd.");
    return num;
}

In both branches of the conditional, the return statement is the same. The only thing that is actually differs based on the condition is the printed statement, so that is all that should be inside the conditional. The redundant code should be factored out below the conditional:

DO THIS
int num = console.nextInt();
if (num % 2 == 0) {
    System.out.println("Your number was even.");
} else {
    System.out.println("Your number was odd.");
}
return num;
⬆ back to top
Boolean Zen

booleans represent a true or false value, and an equality test also returns a true or false value, so there's no reason to test if a boolean is equal to true or false. For instance, instead of:

DON'T DO THIS
if (test == true) {
    // do something
}

We can actually just use test directly:

DO THIS
if (test) {
    // do something
}

Similarly, if we want to execute code when test is false, then we want to execute when !test is true, so instead of testing:

DON'T DO THIS
if (test == false) {
    // do something
}

We should just do the following instead:

DO THIS
if (!test) {
    // do something
}

Similarly, we can use boolean zen to concisely return a boolean based on a test as well. Look at this code:

DON'T DO THIS
if (test) {
    return true;
} else {
    return false;
}

This code returns true when test is true, and false when test is false; it basically just returns the value of test. The entire conditional can be replaced with one line of code:

DO THIS
return test;

We can also use boolean zen to make giving values to boolean variables more concise. Check out this code:

DON'T DO THIS
int age = console.nextInt();
boolean canDrive;
if (age >= 16) {
    canDrive = true;
} else {
    canDrive = false;
}

age >= 16 returns a boolean value, and if it's true canDrive is set to true, and if it's false canDrive is set to false. This is the same situation as the return, so instead of the conditional we can just set canDrive directly equal to age >= 16:

DO THIS
int age = console.nextInt();
boolean canDrive = age >= 16;
⬆ back to top

Using Objects (Scanners, Random, etc.)

It's generally good to try to minimize the number of objects your program creates. For example, rather than creating a Scanner that takes in user input in every method where your program needs to take in user input, it would be better to create one Scanner that takes in user input in main and pass that Scanner as a parameter to all of the methods that need it. The same goes for Random objects, Graphics objects, and any other objects that do the same thing throughout your program. See the following two programs for examples:

DON'T DO THIS
// BAD
public static void main(String[] args) {
    Scanner console = new Scanner(System.in);
    ...
    method1();
}

public static void method1() {
    Scanner console = new Scanner(System.in);
    ...
}
DO THIS
// GOOD
public static void main(String[] args) {
    Scanner console = new Scanner(System.in);
    ...
    method1(console);
}

public static void method1(Scanner console) {
    ...
}
⬆ back to top

Arrays

Arrays can be used to store multiple values, but with great power comes great responsibility. Arrays should be used to store related values. They should not be used to store multiple independent values. For instance:

DON'T DO THIS
int[] numbers = new int[3];
System.out.print("What day of the month is it (1-31)? ");
numbers[0] = console.nextInt();
System.out.print("What is the temperature today? ");
numbers[1] = console.nextInt();
System.out.print("How old are you? ");
numbers[2] = console.nextInt();

This is an example of cramming a bunch of data into an array even though that data has nothing to do with each other.

On a similar note, sometimes it can make sense to return an array, but just because you can return an array doesn't mean you should use it as a way to hack returning multiple values from a method.

⬆ back to top
Unrolling Arrays

Often when using arrays, you need to access data from a certain range of indices (usually the entire array). To do this, you should always use a loop to traverse the indices of the array, rather than "unrolling" the array by manually accessing each index of the array. Even if you know exactly how many items are in your array, using a loop is better stylistically. It makes your code much more flexible; you could change the size of your array without having to change the way you access the array's elements!

DON'T DO THIS
double[] temperatures = {32,41,39,58};

// BAD APPROACH: unrolling
double avgTemp1 = 0;
avgTemp1 += temperatures[0];
avgTemp1 += temperatures[1];
avgTemp1 += temperatures[2];
avgTemp1 += temperatures[3];
avgTemp1 = avgTemp / temperatures.length;
DON'T DO THIS
// Also a bad approach. Even though it's all in one line,
// it's still manually unrolling the array and therefore inflexible
double avgTemp2 = temperatures[0] + temperatures[1] + temperatures[2] + temperatures[3];
avgTemp2 = avgTemp2 / temperatures.length;
DO THIS
// GOOD APPROACH: array traversal
double avgTemp = 0;
for (int i = 0; i < numbers.length; i++) {
    avgTemp += temperatures[i];
}
avgTemp = avgTemp / temperatures.length
⬆ back to top

Creating Objects

Fields

The current state of your object is stored in its fields. Because of this, the fields of your objects must always be declared private. This ensures that no client of your object will be able to directly change the state of your object in a way that you haven't allowed them to.

⬆ back to top
Constructors

A line of code where you are creating a variable, like int num = 1, is doing 2 things. It declares a variable num and then sets the value of num to 1. These two steps are the variable declaration and initialization:

int num; // declaration
num = 1; // initialization

Normally, we combine these into one line of code, but fields must exist in the scope of the entire class but must also be initialized in the constructor. For that reason, we put all the field declarations at the top of the program underneath the class header, and all field initializations should be done in the constructor.

public class SomeClass {
    private int someField;

    public SomeClass() {
        someField = 1;
    }
}
⬆ back to top

Contributors