If you're completely new to Java, GitHub's Learning Lab Introduction to Java is a good place to build a solid foundation.

If you've already learned some Java in the past, GitHub's Learning Lab Intermediate NodeJS Course might be worth a try.

Getting Started

Running Java in the Terminal

System Properties

Java’s System.getProperty() method allows you to essentially pass in flags to the command line. Every property is a name-value string pair, and you can pass each one in with the following "-D" syntax

# passing in System Properties
$ java \
  -Dstreet=sesame \
  -Dnetwork=pbs animals.birds.BigBird

Within the Java file, you would access the input in the following way

String street = System.getProperty("street"); // "sesame"
String network = System.getProperty("network"); // "pbs"

Alternatively, you could create a properties object

Properties properties = new Properties(System.getProperties());

The Classpath

A path is an environment variable, it tells an application where it should look for a certain resource. For your shell, it's PATH, for Java, it's CLASSPATH.

On a Unix system, you can set the environment variable CLASSPATH like so:

Any package directories, located within ~/path/to/packages, (e.g. ~/path/to/packages/example/Greet.class) can now be imported with import example.Greet

Alternatively, you can use the -cp argument when running or compiling a program with java or javac on your terminal.


The javap command on bash lets you print a description of a compiled class. It will return a description if it is found in the classpath, so if the value returned isn’t expected, you know there must be a problem with your classpath


The javac command lets you compile a .java source file into a .class bytecode file. By default, it will store both files in the same directory, so it’s best to use the "-d" option to specify the directory the .class file should go into

# compile one file
javac -d /home/austin/Desktop
# compile 2 files
javac -d /home/austin/Desktop


The Java Archive jar utility is very similar in structure to Unix’s Tape Archive tar utility.

Some examples of ways to use the jar command are included below.

# create a jar file
jar -cvf Practice.jar bin/Practice.class
# list the contents within a jar
jar -tvf Practice.jar
# extract the contents of a jar
jar -xvf Practice.jar

In archive syntax, the meanings of those flags are as follows:

The Java API

Every Java implementation contains the following core packages. This is effectively the STL for Java, outlined below


In Java, strings are immutable. If you call a method that looks like it is changing the string, it is actually returning an entirely different string object.

Basic syntax outlined below

String words = "to be or not to be";
int numChars = words.length();
String firstname = "John";
String lastname = "Smith";
String fullname = firstname + " " + lastname;

// although in C++, strings can span multiple lines...
// ...they can't in Java without this type of workaroudn syntax
String multiline = "Roses are red\n" +
                   "Violets are blue";

C-Style Strings

These still exist in Java, and you can use the following syntax to create a C-style string, or even construct a java.String from a C-style string.

// create a C-string
char[] cstring1 = new char[] {'p','h','d'};
// convert it to C-string to a  java.String
String jeffrey_miller = new String(cstring1);
// convert the java.String to a new C-string
char[] cstring2 = jeffrey_miller.toCharArray();

Primitive Objects -> Strings

Using the valueOf() public static method from java.String we can actually turn primitive objects into a String pretty easily

String one = String.valueOf(1); // "1"
String pi = String.valueOf(3.14f); // "3.14"
String happy = String.valueOf(true); // "true"

Every Object has a built n .toString() method, so we can also convert an object itself to a string.

Date today = new Date();
// method 1
String ex1 = String.valueOf(today);
// method 2
String ex2 = today.toString();
// method 3
String ex3 = "" + new Date();

Comparing Strings

  1. You have two methods you can call on a string object. The .equals() method and the .equalsIgnoreCase() method

  2. Be careful with the == method. The code below would return "true" in C++, but "false" in Java, as the == operator compares identity in Java.

  3. The compareTo() method compares the lexical value of two strings. It checks if the input is alphabetically earlier or later in the dictionary to it.

// [part 1]
String one = "hello";
String two = "HELLO";
boolean same1 = one.equals(two); // false
boolean same2 = one.equalsIgnoreCase(two); // true

// [part 2]
String foo1 = "FOO";
String foo2 = String.valueOf(new char[] {'F','O','O'});
boolean same = (foo1 == foo2); // false

// part[3]
String str1 = "abc";
String str2 = "abcd";
String str3 = "123";

// comparisons
str1.compareTo(str2); // -1 (earlier in dictionary)
str2.compareTo(str1); // 1 (further in dictionary)
str3.compareTo(str1); // -48 (str3 comes before)
str3.compareTo(str3); // 0 (equal)

Searching Strings

Strings come with many useful methods built-in:

String name = "Matthew Smith";
boolean name.startsWith("Matt"); // true
boolean name.endsWith("Smith"); // true
int index = name.indexOf("hew"); // 4
char letter = name.charAt(4); // 'h'

Modifying Strings

String example = "aBcDe";
System.out.print(example.toUpperCase()); // "ABCDE"
System.out.print(example.toLowerCase()); // "abcde"
System.out.print(example.substring(2,5)); // "cDe"

String template = "Do you know NAME? I hear NAME is great.";
String output = template.replace("NAME","Aaron");
// "Do you know Aaron? I hear Aaron is great."

Date & Time

import java.util.*;
import java.text.*;

public class temp {
  public static void main (String[] args) {
    // Declare an rfc formatting object
    Format rfc = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssXXX");

    // Print the current time, in rfc3339 formatting
    Date now = new Date();
    // => 2019-08-01 07:00:26-07:00

    // Construct a DateTime from rfc3339 formatting
    Date moment = rfc.parse("2018-01-02 13:01:44+00:00")

import java.time.*;
import java.time.format.*;


LocalDate date1 =;
LocalDate date2 = LocalDate.parse("2018-04-05");

int year = date2.getYear(); // 2018
int month = date2.getMonthValue(); // 4
int day = date2.getDayOfMonth(); // 5

if (date1.isAfter(date2)){
  // true

// "2018-04-05"


LocalTime time1 =;
LocalTime time2 = LocalTime.parse("14:30:02");

OffsetTime time3 =;
OffsetTime time4 = OffsetTime.parse("14:30:02-08:00");

int hour = time2.getHour(); // 14
int minute = time2.getMinute(); // 30
int second = time2.getSecond(); // 2

LocalTime earliest = LocalTime.MIN;
LocalTime latest = LocalTime.MAX;

if (earliest.isBefore(latest)){
  // true

// "14:30:02"
// "14:30:02-08:00"

Formatting Timestamps

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class Timer {

    public static void main(String[] args) {

        /* Get the current time */
        LocalTime localTime =;

        /* Specify the formatting for the timestamp string */
        /* Read this Java documentation link to understand! */
        /* */
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SS");

        /* Generate the formatted timestamp string */
        String timestamp = String.format("[%s]", dateTimeFormatter.format(localTime));

        /* Print the formatted timestamp string */

It is worth noting that instances of DateTimeFormatter create from .ofPattern() are immutable and thread-safe. Because of this, instead of creating a formatter every time a thread needs it, you can instead create one single static formatter object to be shared among all threads.


LocalDateTime moment1 =;
LocalDateTime moment2 = LocalDateTime.parse("2018-08-04T14:32:11");

OffsetDateTime moment3 =;
OffsetDateTime moment4 = OffsetDateTime.parse("2018-08-04T14:32:11-08:00");

year = moment2.getYear(); // 2018
month = moment2.getMonthValue(); // 8
day = moment2.getDayOfMonth(); // 4
hour = moment2.getHour(); // 14
minute = moment2.getMinute(); // 32
second = moment2.getSecond(); // 11

if (moment1.isBefore(moment2)) {
  // false

// 2018-08-04T14:32:11
// 2018-08-04T14:32:11-08:00

A java array is an actual class, the Java.array class, and can be created one of two ways.

// preferred java style
int[] myArray;
// acceptable C-style
int myArray[];

You don’t have to specify the size of your array initially. As it stands, this is just an uninitialized object. You can instantiate the array in the following ways

int[] ArrayOne = new Array[10];
int size = 10;
int[] ArrayTwo = new Array[size];
int[] ArrayThree = {1, 3, 5, 7, 9};

Effectively, these two statements below are the same, by default, such that each index within the arrays is set to be null

int[] A = new int[3];
int[] A = {null, null, null};



The "For-Each" Loop

Copying an Array

Using System.arraycopy() we can copy the values of the source array to the destination array

String[] arrayOne = {"the", "quick", "brown", "fox"};
String[] arrayTwo = new String[3];
System.arraycopy(arrayOne, 1, arrayTwo, 0, 3);
// arrayTwo is now {"quick", "brown", "fox"}

Multidimensional Arrays

The syntax in java is much cleaner than it is in c++

Color[][][] rgb = new Color[256][256][256];
rgb[0][0][0] =;
rgb[255][255][0] = Color.yellow;


Contained in the library java.util.Scanner

Throws an InputMismatchException if it find an invalid next item when parsing.


Contained in the java.util.Formatter class

Uses the .format() method, so String.format(String input) will allow you to use the following syntax, similar to Python

%s refers to a String value

%d refers to an int value

Will throw IllegalFormatConversionException if the input does not match the specified value type.

String name = "Austin";
int age = 21;
String message = "My name is %s and I am %d years old.";

String output = String.format(message, name, age);
// My name is Austin and I am 21 years old.


Streams are contained in the library*. A stream is a flow of data, between a writer and a reader.

Implementations of InputStream and OutputStream


Uses the and System.out values to specify where to handle I/O on the terminal

The Reader and Writer classes will allow us to read in data if we are sure it is from a .txt file

We will use the inherited BufferedReader and BufferedWriter classes to improve the runtime of each.

Reader stdin = new BufferedReader(; // reads in data from std::cin
Writer stdout = new BufferedWriter(System.out); // writes output to std::cout

Customizing stdin, stdout, stderr

import java.lang.*;

public class Example {

  public static void main(String[] args) throws Exception {

  // initalize a PrintStream that writes to "stdout.txt"
  System.setOut(new PrintStream(new FileOutputStream("stdout.txt")));
  // write to stdout
  System.out.printf("This is stdout\n");

  // initalize a PrintStream that writes to "stderr.txt"
  System.setErr(new PrintStream(new FileOutputStream("stderr.txt")));
  // write to stderr
  System.err.printf("This is stderr\n");

  // initalize an input stream from "stdin.txt"
  System.setIn(new FileInputStream("stdin.txt"));

Resolving the Home Directory

import java.lang.*;

public class Example {

  public static void main(String[] args) throws Exception {

    String home = System.getProperty("user.home");
    // => "/Users/tommy"

    String old = "~/Documents/file.txt"
    // => "~/Documents/file.txt"

    String new = old.replaceFirst("^~", Matcher.quoteReplacement(home));
    // => "/Users/tommy/Documents/file.txt"


You can print formatted text to stdout with the following syntax

System.out.printf("The lucky number is, %d", 21);


The lucky number is 21

This class doesn’t actually contain the API for reading and writing data, just as a way to create the object of a file that can be accessed by readers and writers.

File myFile = new File("/Users/austintraver/Desktop/helloworld.txt");
boolean test1 = myFile.isFile(); // true
File myDirectory = new File("/Users/austintraver/Desktop");
boolean test2 = myFile.isDirectory(); // true

File relativePathDirectory = new File("../gamedata");
boolean test3 = myFile.isAbsolute(); // false
File relativeFile = newFile(relativePathDirectory, "filename.txt");

If any of these files are not found, the program will not throw a FileNotFoundException. You can, however, use the .exists() method to make sure that your code structure is valid. You can also use the .isFile() method, the .isDirectory() method, and the .isAbsolute() method. The following attributes of the file can also be returned to you:

File ifile = new File("../gamedata/sample.txt");
ifile.getAbsolutePath(); // /Users/austintraver/repo/src/../gamedata/sample.txt
ifile.getPath(); // ../gamedata/sample.txt

Be careful, because these will still print even if the file does not return true for the .exists() method, the .isFile() method, etc.

The .length() method will return an integer representing the size of a file (in bytes)

The .list() method will return a String[] object.

If you want a File[] object, use the .listFiles() method.

Note, this list will not be sorted.

Creating, Deleting, Changing Directories

Reading a File

To read in from a file, you will use a FileReader object To write to a file, you will use a FileWriter object

try {
  File directory = new File("../gamedata");
  File ifile = new File(directory, "sample.txt");
  FileReader fr = new FileReader(ifile);
  BufferedReader br = new BufferedReader(fr);
  String line = br.readLine();
  while(line != null) {
    System.out.printf("Next line is %s\n", line);
    line = br.readLine();
} catch (FileNotFoundException fnfe) {
    System.out.printf("IOException: %s", fnfe.getMessage());
} finally {

Writing a File

Use the FileWriter object, which accepts two arguments, a File and a boolean that specifies if we should append. The BufferedWriter class can be used to speed up the execution of FileWriter methods.

try {
  File directory = new File("../gamedata");
  File ofile = new File(directory, "test.txt");
  // "true" specifies for this writer to *append* to the file
  BufferedWriter writer = new BufferedWriter(
    new FileWriter(ofile, true));
  String line = "test";
} catch (FileNotFoundException fnfe) {
  System.out.printf("FileNotFoundException: %s\n", fnfe.getMessage());
} catch (IOException ioe) {
  System.out.printf("IOException: %s\n", ioe.getMessage());

Making an ArrayList of Files

The constructor for the ArrayList will only accept a Collection as an input.

Use the List method .listFiles() which returns a List[] object, pass that into the Arrays static method .asList which accepts an Array[Object] as an argument.

File inputdir = new File("../gamedata");
ArrayList<File> files = new ArrayList<File>(

Selecting a random element in a list, and then removing it

Take advantage of the Random class. You can create a Random object and use the .nextInt(lo,hi) method to call it.

List<String> myList = Lists.newArrayList("one", "two", "three", "four");
Random rand = new Random();
while (myList.size() > 0) {
  int index = rand.nextInt(myList.size());


Sun Microsystems developed JDBC, a single API for database access. JDBC allows a Java programmer to connect to any SQL database. Results from the database are returned as Java objects. The procedure to use JDBC goes as follows

// import the JDBC library
import java.sql.*;

public class Example {
  // [Member Variables] for class 'Example', used in calls to SQL Database

  // Connection,
  Connection connection = null;
  Statement select = null;
  PreparedStatement prepared_statement = null;
  String sqlServer = "jdbc:mysql://localhost";
  String sqlDatabase = "WeatherMeister";
  String sqlUsername = "root";
  String sqlPassword = "root";
  String connectionURL = sqlServer+"/"+sqlDatabase;

  // given the userID, return all recorded database
  public static String getSearchHistory(int userID) {
    try {
      // try to connect to the database with this username and password
      connection = DriverManager.getConnection(connectionURL, sqlUsername, sqlPassword);
      // using our connection in the database, create a statement
      // we can use this statement to execute a query to the database
      statement = connection.createStatement();
      statement.executeQuery("SELECT * FROM WeatherMeister.Users where userID < 10");
      statement.executeQuery("SELECT * FROM WeatherMeister.Users where userID < 10");


This is a static class that allows us to create a connection. It has two useful methods

  1. getDrivers(): show what available drivers we have.
  2. getConnection(String url, String username, String password): returns a Connection object, which represents our connection to the database. This will throw a SQLException if it can not find a driver that exists, or it cannot connect to the database.


A connection object represents a conection to a specific database in the SQL server. We can use it to send multiple consecutive queries to the same database. A SQL connection is created from the DriverManager class's getConnection() method.


A statement object represents a SQL statement. You can create a statement object by calling the Connection class's createStatement() method

A statement object can be used to execute a query to the server. To execute a query to be sent to the server, call the Connection class's executeQuery() method. The return type of this method is a ResultSet object, explained below.

The statement object can also be used to update data in the server. To execute a update to be sent to the server, call the executeUpdate() method. The return type of this method is an int, which represents the number of rows affected by the INSERT , UPDATE , or DELETE call that was made.


An object returned by calling a Statement object’s executeQuery() method. This object will contain the information that the SQL database returned from our executeQuery() call. We can access the data from each key-column by specifying either the key-column name or the key-column index, (starting at 1). Also, it’s important to call the wasNull() method after calling executeQuery() to see if a valid result was returned.

import java.sql.* ;

// Create a connection to the database
Connection connection = DriverManager.getConnection(
  "jdbc:mysql://localhost/DatabaseName", "myUsername", "myPassword");

// Create a statement object capable of executing queries and updates
Statement statement = connection.createStatement();

// Create a result object to store what is returned by statements
ResultSet result = null;

// Execute a query on the database
result = statement.executeQuery(
  "SELECT * FROM myTable WHERE userID > 10");

// Execute an update on the database
result = statement.executeUpdate(
  "INSERT INTO myTable VALUES ('Barack','Obama'), ('Donald', ’Trump')");

// Print the result, assuming it was not null
if (!result.wasNull()){

// Close the connection to the database

Datatypes SQL vs. java

SQL Type java Type
BIT boolean
INT int
REAL float
FLOAT double
DOUBLE double
VARCHAR java.lang.String
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp


The package contains the classes that pertain to communications and working with networked resources.


Sockets are an interface, which allows for a stream of data to be transmitted between two hosts (or two localhosts).

The Socket classes allow to work with low-level internet protocols, as well as high level web-oriented APIs that work with "uniform resource locators" (URLs). Most forms of I/O use streams, and a Socket is no exception. It’s just another type of stream.

Java’s most basic type of socket is the simple Socket class, which uses a reliable, lossless, and connection-oriented protocol known as the Transmission Control Protocol (TCP). After establishing a connection, two applications can send streams of data back and forth. The connection will remain online even if no data is being transmitted from either side.


A datagram is a unit of transfer sent from one network to another. Unlike other protocols, datagrams are sent without a connection established between the two networks. It’s like sending mail without a return address. No way of knowing if it delivered, no backup plan if the address it is being sent to doesn’t exist.

The DatagramSocket class uses a connectionless, unreliable protocol, known as the User Datagram Protocol (UDP)


The client is the one who initiates the conversation, and the server is the one who accepts the request.

A client can create a Socket and initiate a conversation with a server application at any time, but a server must create a ServerSocket in advance in order to be able to listen to new requests.

A ServerSocket will contain multiple Socket objects within it, one for each connection that it currently contains.

Forming a Connection

Each Socket needs a hostname and a port number in order to connect with a server. The hostname can be "" or just the ip address ""

A Socket can create the InputStream and OutputStream objects by calling its methods .getInputStream() and .getOutputStream() respectively.

These streams transfer binary data, so it’s useful to wrap them with the

try {
  // Create a socket for port 25, the SMTP port
  Socket mySocket = new Socket("", 25);

  // Use that socket to create a reader and a writer
  BufferedReader istream = new BufferedReader(mySocket.getInputStream());
  BufferedWriter ostream = new BufferedWriter(mySocket.getOutputStream());

  // Write something to the output stream

  // Read something from the input stream
  String response = istream.readLine();

  // Handle any exceptions that may occur
} catch (IOException ioe) {
  System.out.prinf("Error: %s", ioe.getMessage());
} catch (UnknownHostException uhe) {
  System.out.prinf("Error: %s", uhe.getMessage());

ServerThread Implementation

public class ServerThread extends Thread {

  // Create a web server, an input stream, and an output stream
  private BufferedReader bufferedReader;
  private PrintStream printStream;
  private WebServer webServer;

  // Create a directory to serve the html from
  private final String htmlDir = "/Users/tommy/Desktop/";

  public ServerThread(Socket socket, WebServer webServer) {

    // Create a reader and a writer, attach it to this webserver
    this.webServer = webServer;
    this.bufferedReader = new BufferedReader(
      new InputStreamReader(socket.getInputStream()));
    this.printStream = new PrintStream(
      new BufferedOutputStream(socket.getOutputStream()));

    // Start thread.
    // Start in constructor to ensure we can instantiate
    // BufferedReader and PrintWriter

  private void sendFile(File file) {

    // === BEGIN HTTP Headers ===
    this.printStream.print("HTTP/1.1 200 OK\n");
    this.printStream.print("Content-Type: text/html; charset=utf-8");
    this.printStream.print("\r\n\r\n"); // Terminate HTTP Headers
    // === END HTTP Headers ===

    InputStream inputStream = new FileInputStream(file);

    int b =; // 1 byte
    while (b != -1) {
      b =;
    // Flush the stream now that we're done reading input

  private void notFound() {

    // === BEGIN HTTP Headers ===
    printStream.print("HTTP/1.1 404 Not Found\n");
    printStream.print("Content-Type: text/html; charset=utf-8");
    printStream.print("\r\n\r\n"); // Terminate HTTP Headers
    // === END HTTP Headers ===

    InputStream inputStream = new FileInputStream(
      this.htmlDir + "/error.html");

    int b =
    while (b != -1) {
      b =;
    // Flush the stream now that we're done reading input

  public void run() {

    // Grab first header to see HTTP method
    // And file requested
    String line = this.bufferedReader.readLine();
    String[] data = line.split(" ");

    // Attempt to grab file
    File file = new File(this.htmlDir + data[1]);

    if (file.exists()) {
    else {

Sending Files To Client From A Web Servers

ServerSocket serverSocket = null;
// 6789 is the port number of the ServerSocket
serverSocket = new ServerSocket(6789);
// Wait for a browser HTTP GET request
while (true) {
  // 'Socket' is a bidirectional connection between the client and the server
  Socket socket = serverSocket.accept();
  // Create a thread connecting this server to the socket
  ServerThread serverThread = new ServerThread(socket,this);

Custom Iterator

I created an instantiator of the Iterator interface, called Range, which allows you to iterate through a range of numbers in a similar syntax to Python. If you're trying to implement a custom Iterator, then this code will be useful to get started.

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.lang.IllegalArgumentException;

public class Range implements Iterable<Integer> {
  public final int start, stop, step;

  // Construct a range of numbers starting at "0", ending at "stop"
  public Range(int stop) {

    this.start = 0;
    this.stop = stop;
    this.step = 1;


  // Construct a range of numbers, starting at "start", ending at "stop"
  public Range(int start, int stop) {

    this.start = start;
    this.stop = stop;
    this.step = 1;


  // Construct a range of numbers with a "step" component
  public Range(int start, int stop, int step) {

    // Handle edge case, step of magnitude `0` is specified
    if (step == 0) {
      throw new IllegalArgumentException("Range() arg 3 must not be zero");

    this.start = start;
    this.stop = stop;
    this.step = step;

  // Return an Iterator through the Range of Integers
  public RangeIterator iterator() {

    return new RangeIterator(this);



class RangeIterator implements Iterator<Integer> {

  // Constructor will provide an immutable Range object
  private final Range range;
  // The `current` value being iterated through
  private Integer current;

  // Constructor, establishes the Range and initial value
  public RangeIterator(Range range) {

    this.range = range;
    this.current = range.start;


  // Check if there is a next element to iterate to
  public boolean hasNext() {

    // For positive step, check that the upper bound has not been reached
    if (this.range.step > 0) {
      return (this.current + this.range.step < this.range.stop);

    // For negative step, check that the lower bound has not been reached
    else {
      return (this.current + this.range.step > this.range.stop);


  // Iterate to the next element, return its value
  public Integer next() {

    if (this.hasNext()) {
      this.current += this.range.step;
      return (this.current);

    // Edge case, there is no next element and this method was called
    else {
      throw new NoSuchElementException("Range() has no next element");




import java.util.*;

// Import these two functions into the namespace to save typing
import static java.lang.System.out;
import static java.lang.System.err;

public class test {
  public static void main(String[] args) {

    HashMap<Character, String> map = new HashMap<Character, String>();

    map.isEmpty(); // true

    map.put('a', "alpha");
    map.size(); // 1

    map.get('a'); // "alpha"

    map.put('b', "beta");
    map.replace('b', "bravo");

    HashMap<Character, String> innerMap = new HashMap<Character, String>();
    innerMap.put('c', "charlie");
    innerMap.put('d', "delta");

    // Put all of the objects in the inner HashMap into the outer HashMap
    out.printf("%d\n", map.size());

    // Make a set out of all the keys of the HashMap
    HashSet<Character> set = new HashSet<Character>(map.keySet());

    set.isEmpty(); // false
    set.size() // 4
    set.contains('a'); // true
    set.clear(); // empties the set
    set.add('e'); // add a new element to the set


Double-Brace Constructor Syntax

You can construct Java collection objects using the double brace {{ }} syntax, which makes the initialization of the values more concise.

  HashMap<String, Object> map = new HashMap<String, String>(){{
      put("title", "Ancient Map");
      put("description", "A rusty old map.");
      put("price", 19.99);

Unique IDs

The builtin UUID class from java.util.UUID, provides a convenient way to generate a unique 128-bit identifier for an object. This is useful when you're serializing an object, and inserting it into a database, where you need a way to reference it at a later point in time by your application.


Putting this here for future reference, doing MongoDB databases in Java is tricky.

package com.mongodb;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.diagnostics.logging.Loggers;
import org.bson.Document;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Connection {

    public static void main(String[] args) {
        String connectionString = System.getProperty("mongodb.uri");
        try (MongoClient mongoClient = MongoClients.create(connectionString)) {
            List<Document> databases = mongoClient.listDatabases().into(new ArrayList<>());
            databases.forEach(db -> System.out.println(db.toJson()));
package com.mongodb;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.InsertManyOptions;
import com.mongodb.diagnostics.logging.Loggers;
import org.bson.Document;
import org.bson.types.ObjectId;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import static java.util.Arrays.asList;

public class Create {

    private static final Random rand = new Random();

    public static void main(String[] args) {
        try (MongoClient mongoClient = MongoClients.create(System.getProperty("mongodb.uri"))) {

            MongoDatabase sampleTrainingDB = mongoClient.getDatabase("sample_training");
            MongoCollection<Document> gradesCollection = sampleTrainingDB.getCollection("grades");


    private static void insertOneDocument(MongoCollection<Document> gradesCollection) {
        gradesCollection.insertOne(generateNewGrade(10000d, 1d));
        System.out.println("One grade inserted for studentId 10000.");

    private static void insertManyDocuments(MongoCollection<Document> gradesCollection) {
        List<Document> grades = new ArrayList<>();
        for (int classId = 1; classId <= 10; classId++) {
            grades.add(generateNewGrade(10001d, classId));

        gradesCollection.insertMany(grades, new InsertManyOptions().ordered(false));
        System.out.println("Ten grades inserted for studentId 10001.");

    private static Document generateNewGrade(double studentId, double classId) {
        List<Document> scores = asList(
          new Document("type", "exam").append("score", rand.nextDouble() * 100),
          new Document("type", "quiz").append("score", rand.nextDouble() * 100),
          new Document("type", "homework").append("score", rand.nextDouble() * 100),
          new Document("type", "homework").append("score", rand.nextDouble() * 100)
        return new Document("_id", new ObjectId())
          .append("student_id", studentId)
          .append("class_id", classId)
          .append("scores", scores);