Tutorials

A JavaScript Guide to Java

Key Takeaways

  • JavaScript and Java share familiar syntax, but Java constrains variable types at compile time; JavaScript variables can hold different types of values over time.
  • JavaScript runs in hosts such as browsers and Node.js. Java commonly runs as bytecode on a JVM; both execution pipelines can involve just-in-time compilation.
  • Java methods belong to types, and ordinary class members don't require explicit access modifiers. JavaScript supports private class elements with #name.
  • Java arrays have fixed length but mutable elements. ArrayList provides a resizable, typed collection; HashMap provides key-based lookup.

If you know JavaScript, familiar loops, constructors, and collection operations give you a head start in Java. The main adjustments are Java's static type system, methods attached to types, collection APIs, and runtime—not learning programming again.

This guide pairs small examples so you can translate concepts without assuming the languages are interchangeable.

Running Code: Compilation, Interpretation, and the JVM

The useful distinction isn't “JavaScript is interpreted; Java is compiled.” JavaScript engines parse and execute source code and commonly use just-in-time compilation (JIT): compiling code while the program runs. Java's javac compiler produces JVM bytecode, not native machine code. A conventional JVM can interpret bytecode and JIT-compile frequently executed code.

JavaScript source
  -> engine inside a browser or Node.js
  -> execution, often with JIT compilation

Java source (.java)
  -> javac
  -> JVM bytecode (.class)
  -> JVM interpretation and/or JIT compilation

The Java launcher documentation describes these execution modes. For more background, see how Java's JIT compiler works.

Different Environments: Browsers, Node.js, and the JVM

A JavaScript engine implements the language; its host supplies environment-specific APIs. Browsers provide the DOM for interacting with documents, while Node.js supplies server-side facilities. Browser APIs aren't automatically available in Node.js, and JavaScript isn't browser-only. MDN explains this engine-and-host boundary.

For Java, the JVM and Java libraries provide the usual runtime environment. Bytecode portability still requires a compatible JVM and dependencies; it doesn't remove differences in operating systems, file paths, or native libraries.

Hello World: Compile and Run Your First Java File

Use a maintained Java Development Kit (JDK), such as JDK 25, which Oracle identifies as an LTS release. Make sure both commands are installed and available on your terminal's PATH:

java --version
javac --version

For JavaScript, save this as script.js and run node script.js with Node.js installed:

console.log("Hello world");

For Java, save the following complete program as HelloWorld.java. The filename matches the public class name.

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}

From that directory, compile it and launch the resulting class:

javac HelloWorld.java
java HelloWorld

javac produces HelloWorld.class. In the launch command, use the class name without its extension. Alternatively, source-file launch compiles and runs this source directly:

java HelloWorld.java

Source-file launch is convenient for these examples; it doesn't replace the separate compilation workflow. Complete Java listings below use the same file layout. Fragments explicitly identify where they belong. For a longer walkthrough, follow our Java beginner tutorial.

Defining Variables: Java vs JavaScript Syntax

In JavaScript, values have types, but a variable isn't restricted to one value type. In Java, each variable has a compile-time type that constrains what you can assign.

let name = "Sam";
name = 42; // Valid JavaScript.
// Fragment: place inside main.
String name = "Sam";
// name = 42; // Compile-time error.
var count = 1; // The compiler infers int.

Java's local-variable var infers a static type from the initializer; it doesn't introduce dynamic typing.

Scope, Reassignment, and Mutation

JavaScript's let and const are block-scoped. let permits reassignment; const doesn't. var isn't block-scoped: inside a function, its scope is the function. See MDN's variable declarations and scope rules.

Java's final has a useful parallel with const: once assigned, the variable can't be reassigned, but a referenced object may remain mutable.

const numbers = [1, 2];
numbers[0] = 9;
console.log(numbers[0]); // 9
// numbers = []; // Reassignment would fail.
// Fragment: place inside main.
final int[] numbers = {1, 2};
numbers[0] = 9;
System.out.println(numbers[0]); // 9
// numbers = new int[0]; // Reassignment would fail.

Primitive Types, References, and Missing Values

Java distinguishes primitive types, such as int, long, double, and boolean, from reference types, such as String, arrays, and collection classes. JavaScript's number covers both integer and floating-point values; bigint is a separate numeric type.

Java reference variables can hold null; primitive variables can't. Java has no JavaScript-style undefined. Java local variables must be assigned before use, while fields and array elements receive default values, including null for references. These distinctions are defined in the Java types and variables specification.

In JavaScript, let value; initializes value to undefined, and reading a nonexistent object property also returns undefined. That's different from reading a let binding before its declaration executes, which throws.

What Static Typing Does—and Doesn't—Guarantee

Java catches many incompatible assignments and method calls before execution. It doesn't eliminate null dereferences, invalid input, failed I/O, or logic bugs. You still need validation, tests, and error handling.

TypeScript adds static checking to a separate language that typically emits JavaScript. Its type annotations are erased; they don't validate runtime data or make JavaScript itself statically typed.

Functions in JavaScript, Methods in Java

This JavaScript function expression creates a function value and assigns it to a variable. Functions can also be passed as arguments or returned from other functions.

const printName = function (name) {
    console.log(name);
};

printName("Sam");

The corresponding Java declaration is an instance method:

// Fragment: declare inside a class.
public void printName(String name) {
    System.out.println(name);
}

String specifies the parameter type; void means the method returns no value. Instance methods operate on an instance, while static methods belong to the class. Java methods belong to types rather than existing as JavaScript-style standalone functions; the class and method rules define that structure.

Classes and Access Modifiers

Both languages support classes and constructors, but JavaScript classes use prototype-based inheritance. JavaScript also supports private fields and methods with # names:

class Person {
    #age;
    #name;

    constructor(age, name) {
        this.#age = age;
        this.#name = name;
    }

    describe() {
        return this.#name + " (" + this.#age + ")";
    }
}

console.log(new Person(30, "Sam").describe());

Save the Java counterpart as Person.java and run java Person.java:

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public String describe() {
        return name + " (" + age + ")";
    }

    public static void main(String[] args) {
        System.out.println(new Person(30, "Sam").describe());
    }
}

Both examples print Sam (30). Java constructors use the class name and have no return type. For the JavaScript side, revisit our guide to JavaScript classes.

Java's Four Access Levels

For ordinary class members, Java offers these access levels:

  • public: accessible wherever the declaring type and module rules permit.
  • protected: accessible within the package and from subclasses, with additional restrictions on cross-package access.
  • Package-private: no access modifier; accessible within the same package, a named grouping of Java types.
  • private: accessible within the enclosing top-level class and its nested types.

Java doesn't require an access modifier on every member, and defaults depend on declaration context. JavaScript's #private elements provide encapsulation, but not Java's package/protected access model.

Objects, Dictionaries, and Java HashMap

A JavaScript object literal can act as a small dictionary:

const dict = { a: 2, b: 6, c: 5 };
console.log(dict.a); // 2

const map = new Map([["a", 2], ["b", 6], ["c", 5]]);
console.log(map.get("a")); // 2

Objects aren't simply JavaScript's version of HashMap. Object property keys are strings or symbols; other keys are coerced to property keys. Ordinary objects also have prototypes and property behavior. JavaScript's dedicated Map collection accepts keys of any value type and iterates entries in insertion order.

Java's Map<String, Integer> describes a map with string keys and integer-wrapper values. Map is the interface—the collection contract—and HashMap is an implementation. Save this complete map-and-list example as CollectionsDemo.java:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class CollectionsDemo {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("a", 2);
        map.put("b", 6);
        map.put("c", 5);
        System.out.println(map.get("a")); // 2

        ArrayList<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        System.out.println(numbers.get(0)); // 1

        int[] fixed = {1, 2, 3, 4};
        fixed[0] = 9;
        System.out.println(fixed.length); // 4
    }
}

Run it with java CollectionsDemo.java. The angle brackets are generics: type parameters that let the compiler check collection usage. Java generic collections use reference types, so you write Integer, not int; autoboxing converts the inserted integer values to wrappers. Our guide to Java wrapper classes and autoboxing explains that conversion.

HashMap lookup and insertion are typically average-case constant time when hashes distribute keys well. Key behavior depends on equals() and hashCode(), and iteration order isn't guaranteed. Use a sequence when position matters.

Java Arrays vs ArrayList

A JavaScript array can resize and hold heterogeneous values. Java arrays have a fixed length and a declared component type; fixed length doesn't mean immutable elements. ArrayList provides a resizable sequence with an element type checked through generics.

const arr = [1, 2, 3, 4];
arr.push(5);
console.log(arr.length); // 5

The Java program above demonstrates both alternatives:

OperationJavaScript ArrayJava arrayJava ArrayList
Read first elementarr[0]fixed[0]numbers.get(0)
Replace first elementarr[0] = 9fixed[0] = 9numbers.set(0, 9)
Element countarr.lengthfixed.lengthnumbers.size()
Appendarr.push(5)Cannot resize the existing arraynumbers.add(5)

Start with ArrayList when your collection grows or shrinks. Choose an array when its fixed-size shape fits the problem; see our Java arrays tutorial for more examples.

Loops: Familiar Structure, Different Collection Syntax

The basic for loop translates almost directly. For values in a sequence, JavaScript uses for...of; Java uses the enhanced for loop.

for (let i = 0; i < 5; i++) {
    console.log(i);
}

for (const value of [1, 2, 3, 4]) {
    console.log(value);
}
// Fragment: place inside main.
for (int i = 0; i < 5; i++) {
    System.out.println(i);
}

for (int value : new int[] {1, 2, 3, 4}) {
    System.out.println(value);
}

Don't substitute JavaScript's for...in for value iteration: it visits enumerable property keys. MDN's language overview covers these loop forms alongside functions and classes.

Exceptions: Java Adds Checked Error Contracts

Both languages support try, catch, finally, and throwing errors. Java additionally has checked exceptions: when an operation can throw one, you must catch it or declare it with throws. Unchecked exceptions don't have that compile-time requirement.

For example, Files.readString declares IOException. Save this as FileExample.java, create a small message.txt alongside it, and run java FileExample.java:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class FileExample {
    public static void main(String[] args) {
        try {
            String message = Files.readString(Path.of("message.txt"));
            System.out.println(message);
        } catch (IOException e) {
            System.err.println("Cannot read message.txt: " + e.getMessage());
            System.exit(1);
        }
    }
}

The Files API documentation specifies its failures. Catch exceptions where you can recover or report a useful failure; don't add empty catch blocks just to satisfy the compiler.

Promises and async/await Aren't Java's Concurrency Model

JavaScript promises represent eventual completion. An async function returns a promise, and await suspends that function's continuation without blocking its JavaScript execution thread. It doesn't automatically move CPU-heavy work to another thread.

Java offers threads, executors that schedule tasks, and asynchronous APIs such as CompletableFuture. Don't treat its get() or join() as equivalent to await: they can block the calling thread while waiting. Callback execution also depends on the method and executor, not a universal JavaScript-style event loop.

Java's virtual threads support scalable thread-per-task designs for workloads that spend time waiting for I/O. They don't make CPU-bound computation inherently faster. Follow the concurrency model of your chosen library, and explicitly handle failures, timeouts, and shared mutable state.

Common Mistakes

  • Comparing Java string contents with ==. For references, it checks identity; use String.equals() for string-content comparison.
  • Confusing fixed size with immutability. Array elements can change even though an array can't grow.
  • Assuming collections are automatically thread-safe. Neither HashMap nor ArrayList provides automatic synchronization.
  • Trusting types instead of validating boundaries. Static checking doesn't establish that a request, file, or database value satisfies your business rules.

Conclusion

Learn Java by translating one small JavaScript program at a time: declare its types, move behavior into methods, choose arrays or collections deliberately, and handle failure paths. Your existing programming knowledge transfers; the important work is learning where Java's contracts differ.

FAQ

Is Java similar to JavaScript?

They share familiar syntax for loops, expressions, and classes, but they're different languages. Java uses static typing and methods attached to types; JavaScript is dynamically typed and treats functions as first-class values.

Does JavaScript run only in a browser?

No. JavaScript also runs in hosts such as Node.js. The host determines which environment-specific APIs are available, so browser code that uses the DOM won't automatically work in Node.js.

Is Java compiled or interpreted?

Both descriptions can apply to its usual execution pipeline. The javac compiler produces JVM bytecode, and a conventional JVM can interpret that bytecode and JIT-compile frequently executed code to native machine code.

What is the Java equivalent of a JavaScript array?

Use a Java array for a fixed-length sequence or an ArrayList for a resizable sequence. Both have element-type constraints, unlike a JavaScript array, which can hold heterogeneous values.

What is the difference between a Java HashMap and a JavaScript object?

A Java HashMap is a map collection that supports generic key and value types and doesn't guarantee iteration order. A JavaScript object has string-or-symbol property keys and object behavior such as prototypes; JavaScript Map is the closer dedicated collection counterpart.

Further Reading

Written by Sam Erickson

Software engineer. I write every article on StackChief myself. Author of Mastering the Tech Interview. More about me