CDCDAC Prep
PGCP-ACCMCE (CCE Modules Exam)•~22 min read

Object-Oriented Programming with Java Study Notes

Topic 1 (Free Preview)

Java Platform, JVM Architecture, JDK and Primitive Types

Topic 1 notes are 100% free. Topics 2–15 are available on the CDACPrep app.

Open in App

Study Notes

Free high-yield revision notes for Java Platform, JVM Architecture, JDK and Primitive Types

Java Platform, JVM Architecture, JDK and Primitive Types

Every Java program goes through the same journey: you type source code into a .java file, the JDK's compiler turns it into bytecode stored in .class files, and a Java Virtual Machine (JVM) loads and runs that bytecode. Knowing each step explains everyday messages such as "cannot find symbol" or "integer number too large".

This chapter introduces Java and its features, the JVM architecture, the JDK tools, the structure of a Java class and the eight primitive data types that every other topic builds on.

1. Introduction to Java

Java is a general-purpose, class-based, object-oriented programming language. It was created at Sun Microsystems by a team led by James Gosling and released publicly in 1995. Oracle acquired Sun in 2010 and today Java is developed in the open through the OpenJDK project.

Since Java 10 (2018) a new version appears every six months. Some versions are designated long-term support (LTS) releases, which companies keep using for years: Java 8 (2014), Java 11 (2018), Java 17 (2021) and Java 21 (2023). This course uses the behaviour of Java 8, 11 and 17; where a feature appeared in a particular version it is marked.

This course uses Java SE (Standard Edition), the core language and libraries on which the enterprise (Jakarta EE) and embedded (Java ME) platforms are built.

The central idea of Java is that a program is compiled once into bytecode, an instruction set for an imaginary computer (the JVM), instead of into machine code for one particular processor. Any machine that has a JVM can then run that bytecode.

text
Hello.java  --javac (compile)-->  Hello.class (bytecode)  --java (JVM)-->  program runs

Here is the smallest useful program. Save it as Hello.java:

java
public class Hello {
    public static void main(String[] args) {
        int a = 2, b = 3;
        System.out.println("Sum = " + (a + b));
    }
}
text
$ javac Hello.java      (creates Hello.class)
$ java Hello            (runs the class; note: no .class extension)
Sum = 5

2. Features of Java

The features below are often called the Java "buzzwords". Each one has a concrete technical reason.

FeatureWhat it means in practice
Simple and familiarC/C++-like syntax, but no pointers, no operator overloading, no manual free/delete
Object-orientedPrograms are built from classes and objects; all code lives inside classes
Platform independent and portableThe same .class files run on any operating system that has a compatible JVM ("write once, run anywhere")
Architecture neutralPrimitive sizes are fixed by the language: an int is 32 bits on every machine
RobustStrong static type checking, no pointer arithmetic, array bounds checks, exception handling and automatic garbage collection
SecureBytecode is verified before it runs and programs cannot read arbitrary memory addresses
Interpreted and high performanceBytecode is first interpreted, and frequently executed ("hot") code is compiled to native code by the JIT compiler
MultithreadedThreads and synchronization are built into the language and libraries
Distributed and dynamicRich networking libraries; classes are loaded on demand while the program runs

Platform independence needs a JVM per platform. The bytecode is the same everywhere, but the JVM itself is a native program, built separately for Windows on x64, Linux on ARM and so on.

Architecture neutral means the mini-lab in section 7 prints the same sizes (int 32 bits, long 64 bits, char 16 bits) on every machine, unlike C or C++ where the size of int can vary.

3. JDK, JRE and JVM

These three terms are related but different:

  • JVM (Java Virtual Machine): the engine that loads, verifies and executes bytecode, manages memory and runs the garbage collector. It does not compile .java files.
  • JRE (Java Runtime Environment): a JVM plus the standard class libraries (java.lang, java.util, ...). Enough to run programs, not to develop them.
  • JDK (Java Development Kit): a JRE plus development tools such as the compiler. You install a JDK to write Java.

A compact way to remember it: JDK = JRE + development tools and JRE = JVM + class libraries. From Java 11 Oracle no longer ships a separate JRE download; developers install a JDK, and the jlink tool (Java 9+) can build a trimmed runtime image for deployment.

Using the JDK tools

ToolUsage
javacCompiler: checks source code and writes .class files
javaLauncher: starts a JVM and runs a class's main method
javadocGenerates HTML API documentation from /** ... */ comments
jarPackages class files and resources into a .jar archive
javapDisassembler: shows the members and bytecode inside a class file
jshellInteractive shell for trying statements (Java 9+)
jdbCommand-line debugger

The commands you use daily:

text
javac Hello.java                  compile; writes Hello.class next to the source
javac -d out Hello.java           compile; writes class files under the out directory
java Hello                        run class Hello from the current directory
java -cp out Hello                run Hello, searching the out directory for classes
java Hello.java                   Java 11+: compile in memory and run a single-file program
javap -c Hello                    show the bytecode of Hello.class
  • javac takes a file name (with .java); java takes a class name (no .class).
  • The classpath (-cp / -classpath option, or the CLASSPATH environment variable) lists the directories and JAR files where java and javac look for user classes. It is different from PATH, which only lets the shell find the javac and java programs, and from JAVA_HOME, which tells other tools where the JDK is installed.
  • Every class in a source file, public or not, produces its own .class file.

Running javap -c Hello on the class from section 1 shows real bytecode (abridged):

text
     0: iconst_2          push int 2
     1: istore_1          store it in local variable a
     ...
     9: iadd              add the two ints

The class file contains such JVM instructions plus metadata (a constant pool, field and method descriptions), not native machine code and not your source text.

Compile-time errors versus runtime errors

javac checks names, types, access rules and whether local variables are assigned before use. A mistake such as using an undeclared variable is a compile-time error ("cannot find symbol") and no class file is produced. A runtime error happens in code that compiled successfully, for example integer division by zero throwing ArithmeticException while the program runs.

4. JVM architecture

A JVM has three main subsystems, plus an interface to native code.

text
 .class files
      |
 Class loader subsystem    load -> link (verify, prepare, resolve) -> initialize
      |
 Runtime data areas        shared:     method area, heap
                           per thread: JVM stack, PC register, native method stack
      |
 Execution engine          interpreter, JIT compiler, garbage collector
      |
 Native method interface   JNI + native libraries

4.1 Class loader subsystem

Classes are loaded on demand, the first time the program needs them. Using a class goes through three phases:

  1. Loading: a class loader finds the binary .class data and creates the JVM's internal representation of the class.
  2. Linking:
  • Verification: the bytecode verifier checks that the code is well-formed and type-safe (for example, it does not jump outside a method or misuse an int as a reference). This is a key part of Java's security.
  • Preparation: memory for static variables is allocated and set to default values (0, false, null).
  • Resolution: symbolic references (names of other classes, methods and fields) are replaced by direct runtime references, eagerly or lazily.
  1. Initialization: static variable initializers and static { } blocks run, top to bottom, exactly once per class.

There are three built-in class loaders, arranged parent to child:

LoaderLoadsJava 8 name
BootstrapCore classes such as java.lang.String (part of the JVM itself)Bootstrap
PlatformOther Java SE platform modulesExtension class loader
Application (system)Your classes and libraries on the classpathApplication class loader

Loaders use parent delegation: a loader first asks its parent to load a class and only searches itself if the parent cannot find it. That is why your own class named java.lang.String can never replace the real one.

java
public class LoaderDemo {
    public static void main(String[] args) {
        ClassLoader app = LoaderDemo.class.getClassLoader();
        System.out.println(app.getName());              // Java 9+
        System.out.println(app.getParent().getName());
        System.out.println(String.class.getClassLoader());
    }
}
text
app
platform
null

String prints null because the bootstrap loader is implemented inside the JVM and has no Java object.

The order of initialization is visible with a static block:

java
public class InitDemo {
    static int count = 10;

    static {
        System.out.println("static block: count = " + count);
        count = 20;
    }

    public static void main(String[] args) {
        System.out.println("main: count = " + count);
    }
}
text
static block: count = 10
main: count = 20

The class must be initialized before main (a static method) can run, so the static block prints first.

4.2 Runtime data areas

AreaShared or per threadHolds
Method areaShared by all threadsPer-class data: runtime constant pool, field and method information, method bytecode. Textbooks also place static variables here.
HeapShared by all threadsAll objects and arrays created with new; managed by the garbage collector
JVM stackOne per threadOne frame per method call: local variables, operand stack and return information
PC registerOne per threadAddress of the bytecode instruction the thread is currently executing
Native method stackOne per threadFrames for native (non-Java) methods called through JNI

When a method is called, a new frame is pushed on the calling thread's stack; when it returns, the frame is popped and its local variables disappear. Objects stay on the heap as long as something still refers to them. Running out of stack (for example, recursion with no stopping condition) throws StackOverflowError; running out of heap throws OutOfMemoryError.

java
public class DeepRecursion {
    static int depth = 0;

    static void dive() {
        depth++;
        dive();               // no base case: every call adds a frame
    }

    public static void main(String[] args) {
        try {
            dive();
        } catch (StackOverflowError e) {
            System.out.println("Stack overflow after " + depth + " calls");
        }
    }
}

The number printed depends on the JVM and its stack size setting (our run with default settings printed about 55,000).

Implementation note: before Java 8, HotSpot (the most widely used JVM) stored class metadata in a heap region called PermGen; since Java 8 it uses native memory called Metaspace. The JVM specification describes logical areas, not fixed physical regions.

4.3 Execution engine

  • The interpreter reads bytecode instructions one by one and executes them. It starts quickly but is slower for code that runs millions of times.
  • The JIT (just-in-time) compiler watches which methods run often and compiles these hot methods into native machine code while the program runs, so later calls run at native speed. This is why Java is described as both interpreted and compiled.
  • The garbage collector finds heap objects that can no longer be reached from any live reference and reclaims their memory. Garbage collection is covered in detail in a later topic.

4.4 Native method interface

The Java Native Interface (JNI) lets Java code call functions written in languages such as C, stored in native libraries (.dll, .so).

5. Structure of a Java class

A source file is organized in a fixed order:

  1. an optional package declaration (at most one, and it must come first);
  2. any import declarations;
  3. one or more type declarations (classes, interfaces, enums).

Inside a class, members can appear in any order, but a conventional layout is: fields, constructors, then methods.

java
package com.example;                      // 1. package declaration (optional)

import java.util.ArrayList;               // 2. import declarations
import java.util.List;

/** A student with a list of marks. */    // documentation comment, read by javadoc
public class Student {                    // 3. class declaration
    private static int count;             // static (class) variable: one copy
    private final String name;            // instance variables: one copy per object
    private final List<Integer> marks = new ArrayList<>();

    public Student(String name) {         // constructor: initializes a new object
        this.name = name;
        count++;
    }

    public void addMark(int mark) {       // instance method
        marks.add(mark);
    }

    public double average() {
        int total = 0;                    // local variable
        for (int m : marks) {
            total += m;
        }
        return marks.isEmpty() ? 0 : (double) total / marks.size();
    }

    public static void main(String[] args) {   // entry point
        Student s = new Student("Asha");
        s.addMark(70);
        s.addMark(85);
        /* block comment */
        System.out.println(s.name + " " + s.average() + " " + count);
    }
}
text
$ javac -d out Student.java
$ java -cp out com.example.Student
Asha 77.5 1

Because of the package, javac -d out writes the class to out/com/example/Student.class, and the launcher is given the fully qualified class name com.example.Student.

Rules to remember:

  • A public top-level class must be saved in a file with exactly the same name, including case: public class Student belongs in Student.java. Otherwise javac reports "class Student is public, should be declared in a file named Student.java". A file may contain several top-level classes, but at most one of them can be public.
  • Comments: // to end of line, /* ... */ for a block, and /** ... */ for documentation comments.
  • Import only lets you use a short name (ArrayList instead of java.util.ArrayList). It does not copy code like a C #include and does not load the class. Classes in java.lang (such as String, System, Math) and classes in the same package need no import.

The main method

For Java 8, 11 and 17 the launcher looks for exactly this method:

text
public static void main(String[] args)
PartWhy it is needed
publicThe launcher, which is outside your class, must be able to call it
staticIt is called without first creating an object of the class
voidIt returns nothing to the launcher (use System.exit(status) to return a status code)
mainThe name the launcher searches for; Java is case-sensitive, so Main is a different method
String[] argsThe command-line arguments that follow the class name

String... args is also accepted, the parameter can have any name, and the order static public is legal. If no suitable main exists, the program still compiles, but the launcher stops with an error such as "Main method not found in class Hello". (Java 25 adds simpler instance main methods for small programs; this course uses the classic form.)

6. Primitive data types

Java has exactly eight primitive types. A primitive variable stores the value itself. Everything else (String, arrays, your own classes) is a reference type, whose variables hold a reference to an object; reference types are covered in topic 3.

TypeSizeRangeDefault (field)Example literal
byte8 bits, signed-128 to 1270(byte) 10
short16 bits, signed-32,768 to 32,7670(short) 300
int32 bits, signed-2,147,483,648 to 2,147,483,647042, 0x2A, 0b101010, 052
long64 bits, signedabout -9.22 x 10^18 to 9.22 x 10^180L8_000_000_000L
float32 bits, IEEE 754about ±3.4 x 10^38, 6-7 significant digits0.0f3.14F
double64 bits, IEEE 754about ±1.8 x 10^308, 15-16 significant digits0.03.14, 1.8e1
char16 bits, unsigned0 to 65,535 ('\u0000' to '\uffff')'\u0000''A', '\n', '\u0041'
booleannot precisely definedtrue or falsefalsetrue

For a signed integer type of b bits the range is -2^(b-1) to 2^(b-1) - 1; for byte, -2^7 to 2^7 - 1 = -128 to 127. Java has no unsigned keyword; char is the only unsigned primitive.

6.1 Integer types: byte, short, int, long

int is the default choice for whole numbers; use long when values can exceed about 2.1 billion.

Integer literals are of type int unless they end in L. Writing long big = 8000000000; fails with "integer number too large" because 8000000000 does not fit in an int; 8000000000L is a long literal. Prefer uppercase L, since lowercase l looks like the digit 1.

Literals can be written in several bases, and (since Java 7) underscores may separate digits:

java
public class Literals {
    public static void main(String[] args) {
        int decimal = 42;
        int hex = 0x2A;           // base 16
        int octal = 052;          // leading 0 means base 8
        int binary = 0b10_1010;   // base 2, Java 7+
        long big = 8_000_000_000L;
        System.out.println(decimal + " " + hex + " " + octal + " " + binary);
        System.out.println(big);
    }
}
text
42 42 42 42
8000000000

Overflow wraps around. Integer arithmetic uses fixed-width two's-complement, so going past the maximum silently wraps to the minimum; no exception is thrown:

java
public class Overflow {
    public static void main(String[] args) {
        int max = Integer.MAX_VALUE;
        System.out.println(max);
        System.out.println(max + 1);           // wraps to the minimum
        long safe = max + 1L;                  // long arithmetic: no overflow
        System.out.println(safe);
    }
}
text
2147483647
-2147483648
2147483648

Math.addExact(max, 1) throws ArithmeticException instead, when overflow must be detected.

6.2 Floating-point types: float and double

Floating-point literals such as 3.14 are double by default. A float needs an F suffix: float rate = 3.14F; (without it, the assignment fails to compile because double to float loses precision). double is the normal choice.

Binary floating point cannot represent most decimal fractions exactly, so results can carry small errors; avoid float/double for exact money values.

java
public class FloatingPoint {
    public static void main(String[] args) {
        System.out.println(0.1 + 0.2);
        System.out.println(10.0 / 0);          // floating-point division by zero
        System.out.println(0.0 / 0);
        float f = 1.1F;
        System.out.println(f * 2);
    }
}
text
0.30000000000000004
Infinity
NaN
2.2

Floating-point division by zero gives Infinity or NaN (not a number), whereas integer division by zero throws ArithmeticException.

6.3 char

A char is an unsigned 16-bit UTF-16 code unit. Java uses Unicode rather than ASCII, so a char can hold letters from Hindi, Tamil or Chinese as well as English. Characters can be written as literals ('A'), escape sequences ('\n', '\t', '\'', '\\') or Unicode escapes ('\u0041' is 'A'). Characters outside the Basic Multilingual Plane, such as many emoji, need two char values (a surrogate pair), so one char is not always a complete character.

Because a char is numeric underneath, it takes part in arithmetic. In an expression, a char is promoted to int; but ++ on a char variable keeps the type char:

java
public class CharDemo {
    public static void main(String[] args) {
        char c = 'A';
        System.out.println(c + 1);       // char promoted to int: 65 + 1
        c++;                             // still a char
        System.out.println(c);
        char fromCode = 97;              // constant 97 fits in char
        System.out.println(fromCode);
        System.out.println((int) 'z');
    }
}
text
66
B
a
122

6.4 boolean

A boolean has only two values, true and false. Unlike C and C++, Java does not treat integers as booleans: if (1) does not compile, and you cannot cast between boolean and any numeric type. The language does not specify a size for boolean, and Java has no sizeof operator.

6.5 Default values and local variables

Fields (static and instance variables) and array elements receive the default values listed in the table automatically. Local variables (declared inside a method) get no default; the compiler rejects any read of a local that might not have been assigned. This is called definite assignment, and it is explained with examples in topic 2.

java
public class Defaults {
    static int count;
    static double rate;
    static boolean flag;
    static char letter;
    static String text;                  // a reference type: default null

    public static void main(String[] args) {
        System.out.println(count + " " + rate + " " + flag);
        System.out.println((int) letter + " " + text);
    }
}
text
0 0.0 false
0 null

The char default '\u0000' is an invisible character, so the program casts it to int to show 0.

7. Mini-lab: sizes and ranges of every primitive type

Each primitive has a matching wrapper class in java.lang (Byte, Short, Integer, Long, Float, Double, Character, Boolean) whose constants describe the type. Wrapper classes are studied in topic 9; here they simply report the limits.

java
public class PrimitiveRanges {
    public static void main(String[] args) {
        System.out.println("byte   " + Byte.SIZE + " bits  " + Byte.MIN_VALUE + " to " + Byte.MAX_VALUE);
        System.out.println("short  " + Short.SIZE + " bits  " + Short.MIN_VALUE + " to " + Short.MAX_VALUE);
        System.out.println("int    " + Integer.SIZE + " bits  " + Integer.MIN_VALUE + " to " + Integer.MAX_VALUE);
        System.out.println("long   " + Long.SIZE + " bits  " + Long.MIN_VALUE + " to " + Long.MAX_VALUE);
        System.out.println("float  " + Float.SIZE + " bits  max " + Float.MAX_VALUE);
        System.out.println("double " + Double.SIZE + " bits  max " + Double.MAX_VALUE);
        System.out.println("char   " + Character.SIZE + " bits  " + (int) Character.MIN_VALUE + " to " + (int) Character.MAX_VALUE);
        System.out.println("smallest positive double " + Double.MIN_VALUE);
    }
}
text
byte   8 bits  -128 to 127
short  16 bits  -32768 to 32767
int    32 bits  -2147483648 to 2147483647
long   64 bits  -9223372036854775808 to 9223372036854775807
float  32 bits  max 3.4028235E38
double 64 bits  max 1.7976931348623157E308
char   16 bits  0 to 65535
smallest positive double 4.9E-324

Note the trap in the last line: Double.MIN_VALUE is the smallest positive double, not the most negative one (that is -Double.MAX_VALUE). Extend the lab: try assigning 128 to a byte and read the compiler message.

8. Common mistakes and misconceptions

Mistake or beliefWhat is actually true
"The JVM compiles my .java file"javac (a JDK tool) compiles source; the JVM runs bytecode, and its JIT compiles hot bytecode to native code at runtime
"Java is platform independent, so the JVM is too"Bytecode is portable; each platform needs its own native JVM
Running java Hello.classThe launcher takes a class name: java Hello
"import loads or copies the class"It only allows a short name at compile time
"Overflow throws an exception"Integer overflow wraps silently; floating-point overflow gives Infinity
"char is an 8-bit ASCII byte"char is a 16-bit unsigned UTF-16 code unit
Public class Hello in Test.javaCompile error: the file must be named Hello.java

9. Key points

  • javac compiles source into platform-neutral bytecode; a JVM on each platform loads and runs it.
  • JDK = JRE + development tools; JRE = JVM + class libraries.
  • JVM architecture: class loader subsystem (bootstrap, platform, application loaders; load, link, initialize), runtime data areas (shared method area and heap; per-thread stack, PC register, native method stack), execution engine (interpreter, JIT, garbage collector) and JNI.
  • Source file order: package, imports, types; a public class lives in a file of the same name; public static void main(String[] args) is the entry point.
  • Eight primitives: byte 8, short 16, int 32, long 64, float 32, double 64, char 16 unsigned, boolean. Integer literals are int (use L), floating literals are double (use F).
  • Fields get defaults (0, 0.0, '\u0000', false, null); local variables do not.
Ready to test what you learned?

Practice 20 MCQs with Instant Explanations

Solve exam-level questions for Object-Oriented Programming with Java with scoring, option checking, and detailed answers.

Go to Practice MCQs