A variable is a named location in the computer's memory that stores a value. You give it a name, declare what type of value it holds, and then use that name throughout your program to read or modify the value.
Imagine a shelf of labelled boxes. You have a box labelled "age" that only fits numbers, another labelled "name" that only fits text, and one labelled "passed" that only fits true/false. Each box has: a name (the label), a type (what it can hold), and a value (what's inside right now). You can look inside, change the contents, and refer to the box by its label anywhere in your program.
Variable Declaration Syntax
// FORM 1: Declare and initialise in one line (most common) int age = 20; double gpa = 3.75; String name = "Ahmed"; boolean passed = true; // FORM 2: Declare first, assign later int marks; // declared β memory reserved, not yet assigned marks = 95; // now assigned System.out.println(marks); // OK: 95 // FORM 3: Multiple variables of the same type int x = 5, y = 10, z = 15; // FORM 4: Update (reassign) an existing variable int score = 50; score = 75; // old value 50 is gone, replaced with 75 score = score + 10; // score is now 85
How Variables Work in Memory
When you declare int age = 20;, Java does three things: allocates memory (4 bytes for an int), labels that memory location "age", and stores the value 20 in those bytes. The variable name is just a human-readable alias for a memory address.
Java has exactly 8 primitive data types. They are the most basic building blocks β not objects, just raw values. Knowing their sizes and ranges is frequently tested in exams.
| Type | Category | Size | Default Value | Literal Example |
|---|---|---|---|---|
byte | Integer | 1 byte | 0 | byte b = 42; |
short | Integer | 2 bytes | 0 | short s = 1000; |
int | Integer | 4 bytes | 0 | int n = 100000; |
long | Integer | 8 bytes | 0L | long l = 9_000_000_000L; |
float | Floating-point | 4 bytes | 0.0f | float f = 3.14f; |
double | Floating-point | 8 bytes | 0.0d | double d = 3.14159; |
char | Character | 2 bytes | '\u0000' | char c = 'A'; |
boolean | Logical | JVM-dependent | false | boolean b = true; |
Integer Types β Deep Dive
// byte: perfect for small values (e.g., age, percentage) byte age = 20; byte max = 127; // maximum byte value // byte overflow β what happens if you go beyond 127? byte b = 127; b++; // b becomes -128! Wraps around (overflow) // short: rarely used directly, saves memory in large arrays short year = 2024; // int: the DEFAULT integer type in Java β use this for most cases int students = 500; int maxInt = Integer.MAX_VALUE; // 2,147,483,647 int minInt = Integer.MIN_VALUE; // -2,147,483,648 // long: for values beyond int range β MUST add L suffix long population = 7_800_000_000L; // world population long bytes = 9_223_372_036_854_775_807L; // Long.MAX_VALUE // Note: underscores _ in literals (Java 7+) for readability β ignored by compiler int million = 1_000_000; // same as 1000000
When an integer goes beyond its maximum value, it doesn't throw an error β it silently wraps around to the minimum value. This is called integer overflow. Example: byte b = 127; b++; β b becomes -128, not 128. Always choose a type with sufficient range for your data.
Floating-Point Types β float vs double
// double is DEFAULT for decimal literals β use it for most cases double pi = 3.14159265358979; double price = 99.99; double temp = -273.15; // float needs 'f' or 'F' suffix β otherwise ERROR float f1 = 3.14f; // β correct float f2 = 3.14; // β ERROR: "possible lossy conversion from double to float" float f3 = (float) 3.14; // β explicit cast also works // Precision comparison: float precision = 1.0f / 3.0f; double precDouble = 1.0 / 3.0; System.out.println(precision); // 0.33333334 (7 digits, then error) System.out.println(precDouble); // 0.3333333333333333 (15 digits) // Special double values: double inf = 1.0 / 0.0; // Infinity (no exception! division by zero for doubles) double nan = 0.0 / 0.0; // NaN (Not a Number) System.out.println(inf); // Infinity System.out.println(nan); // NaN
Use double by default for any decimal computation β it has more precision and is the default type for decimal literals. Use float only when memory is extremely limited (e.g., large arrays of coordinates in graphics/gaming). In financial calculations, use neither β use BigDecimal to avoid floating-point rounding errors.
The char Type β Characters and Unicode
// char stores ONE character β ALWAYS single quotes char grade = 'A'; char digit = '7'; // the CHARACTER '7', not the number 7 char space = ' '; char newline = '\n'; // escape sequence inside char // char uses UNICODE β stores a 16-bit number internally char c = 'A'; System.out.println((int) c); // prints 65 (Unicode value of 'A') System.out.println((char) 65); // prints 'A' // TRICKY: char arithmetic (char + int = int!) char ch = 'A'; System.out.println(ch + 1); // prints 66 (int, not 'B'!) System.out.println((char)(ch + 1)); // prints 'B' (cast back to char) // Common Unicode values (memorise for exams): // 'A' = 65, 'Z' = 90 // 'a' = 97, 'z' = 122 // '0' = 48, '9' = 57 // ' ' = 32 (space)
The boolean Type
// boolean stores ONLY true or false β no 0/1, no "yes"/"no" boolean isAdult = true; boolean hasPassed = false; // Most common use: storing the result of a comparison int score = 75; boolean passed = (score >= 50); // true boolean distinction = (score >= 90); // false boolean isEven = (score % 2 == 0); // false (75 is odd) System.out.println(passed); // true System.out.println(distinction); // false // TRAP: cannot assign 0 or 1 like C/C++ boolean b = 1; // β COMPILE ERROR in Java! (works in C/C++ not Java) boolean b = true; // β correct
A literal is a fixed value written directly in your code. Different types of literals need different syntax:
// INTEGER LITERALS int decimal = 255; // decimal (base 10) β default int octal = 0377; // octal (base 8) β starts with 0 int hex = 0xFF; // hex (base 16) β starts with 0x int binary = 0b11111111; // binary (base 2) β starts with 0b (Java 7+) // All four represent the value 255! long big = 9_000_000_000L; // L suffix = long literal // FLOATING-POINT LITERALS double d = 3.14; // double by default double sci = 1.5e10; // scientific: 1.5 Γ 10ΒΉβ° float f = 3.14f; // f or F suffix = float literal double d2 = 3.14d; // d suffix optional for double // CHARACTER LITERALS char c1 = 'A'; // character char c2 = 65; // integer literal assigned to char (='A') char c3 = '\u0041'; // Unicode literal (='A') // STRING LITERALS (not primitive β but good to know) String s = "Hello"; // String in double quotes String empty = ""; // empty String // BOOLEAN LITERALS boolean t = true; boolean f2 = false;
Java has official naming conventions. They are not enforced by the compiler but are followed by every professional Java developer. University examiners may specifically ask about these.
| What | Convention | Examples |
|---|---|---|
| Variables | camelCase β start lowercase, capitalise each new word | age, firstName, totalMarks, isLoggedIn |
| Methods | camelCase β same as variables | calculateArea(), getName(), isValid() |
| Classes | PascalCase β capitalise EVERY word including first | Student, BankAccount, HelloWorld |
| Constants | ALL_CAPS with underscores | MAX_SIZE, PI, MIN_VALUE |
| Packages | all lowercase, reverse domain | com.university.app, java.util |
// β Bad β violates conventions, hard to read: int A = 20; // single letter (meaningless) double MyGPA = 3.5; // PascalCase for variable (wrong) int total_marks = 90; // snake_case (Python style, not Java) // β Good β follows Java conventions: int age = 20; double myGpa = 3.5; int totalMarks = 90; final double PI = 3.14159; // constant β ALL_CAPS final int MAX_STUDENTS = 100; // constant β ALL_CAPS
The scope of a variable is the region of code where it is accessible. A variable exists only inside the { } block where it was declared. Once that block ends, the variable is gone from memory.
public class ScopeDemo { static int classVar = 100; // class-level: accessible everywhere in class public static void main(String[] args) { int x = 10; // local to main β only exists inside main { // inner block int y = 20; // local to this inner block System.out.println(x); // β x is in scope System.out.println(y); // β y is in scope System.out.println(classVar); // β class variable in scope } // y goes out of scope here β DESTROYED System.out.println(x); // β still in scope // System.out.println(y); // β COMPILE ERROR: y is out of scope } static void otherMethod() { // System.out.println(x); // β x from main is not accessible here System.out.println(classVar); // β class variable accessible } }
Sometimes you need to convert a value from one type to another. Java handles this in two ways: automatically (widening) when there's no risk of data loss, and manually (narrowing) when you accept the risk of data loss.
β Widening Conversion β Automatic, Safe
Java automatically converts a smaller type to a larger type. This is always safe because the larger type can hold all values of the smaller type without losing any information.
Also: char β int β long β float β double
int i = 100; long l = i; // int β long: automatic, safe. l = 100L float f = l; // long β float: automatic. f = 100.0f double d = f; // float β double: automatic. d = 100.0 int x = 50; double result = x + 3.14; // int x is automatically widened to double // result = 53.14 char ch = 'A'; int code = ch; // char β int: automatic. code = 65 (Unicode of 'A') System.out.println(code); // 65
β‘ Narrowing Conversion β Explicit Cast Required
Going from a larger type to a smaller type is risky β you might lose data. Java forces you to explicitly write a cast to acknowledge you know the risk. Syntax: (targetType) value
// double β int: decimal part TRUNCATED (not rounded!) double pi = 3.99999; int approx = (int) pi; // approx = 3 (not 4! decimal just dropped) double d = 9.7; int i = (int) d; // i = 9 (NOT 10 β truncated, not rounded) // long β int: value may be completely wrong if too large long big = 9_999_999_999L; int small = (int) big; // small = 1410065407 (garbage! overflow) // int β byte: only keeps last 8 bits int n = 130; byte b = (byte) n; // b = -126 (overflow wraps around) // int β char: int code = 65; char ch = (char) code; // ch = 'A' // WITHOUT CAST β compile error: double x = 3.14; int y = x; // β ERROR: possible lossy conversion from double to int int y = (int) x; // β explicit cast β y = 3
(int) 9.9 gives 9, not 10. The decimal part is simply dropped. If you want rounding, use Math.round(9.9) which gives 10L. This is a very common exam trap.
β’ Converting Between String and Numbers
// String β int String s = "42"; int n = Integer.parseInt(s); // n = 42 // String β double String d = "3.14"; double pi = Double.parseDouble(d); // pi = 3.14 // String β long long l = Long.parseLong("9999999999"); // int β String (three ways) int x = 100; String way1 = String.valueOf(x); // "100" β preferred String way2 = Integer.toString(x); // "100" String way3 = "" + x; // "100" β concatenation trick // DANGEROUS: parsing non-numeric string int bad = Integer.parseInt("hello"); // β NumberFormatException at runtime!
Declare a variable as final to make it a constant β a value that cannot be changed after the first assignment. Attempting to reassign a final variable causes a compile error.
// Declare and initialise final (constant) variables final double PI = 3.14159265358979; final int MAX_STUDENTS = 100; final String UNIVERSITY_NAME = "Abu Dhabi University"; // Attempting to reassign β compile error: PI = 3.0; // β ERROR: cannot assign a value to final variable PI // final can be declared first, assigned once later: final int x; x = 42; // β first assignment allowed x = 43; // β ERROR: second assignment not allowed // Why use final? // 1. Prevents accidental modification of important values // 2. Communicates intent: "this value never changes" // 3. Can enable compiler optimisations
Since Java 10, you can use var instead of writing the explicit type. The compiler infers the type automatically from the value you assign. The type is still fixed at compile time β var is not dynamic like JavaScript's var.
// Before Java 10: explicit type required int age = 20; double gpa = 3.75; String name = "Ahmed"; // Java 10+: var β compiler infers the type var age2 = 20; // inferred as int var gpa2 = 3.75; // inferred as double var name2 = "Ahmed"; // inferred as String // var is still STATICALLY typed β type is fixed once assigned var x = 10; // x is int x = 20; // β still int, fine x = "hello"; // β ERROR: x is int, cannot assign String // var CANNOT be used without initialisation: var y; // β ERROR: cannot use 'var' on variable without initialiser // var is ONLY for local variables β not for fields or parameters
float f = 3.14;β Error! 3.14 is a double literal. Need3.14for cast:(float)3.14.long l = 9999999999;β Error! 9999999999 exceeds int range. Need9999999999L.char c = "A";β Error! char uses single quotes'A', not double quotes.boolean b = 1;β Error! Java booleans accept onlytrueorfalse, not 0/1.int x = 3.14;β Error! Cannot assign double to int without explicit cast.- Using uninitialized local variable β
int x; System.out.println(x);β compile error. Assign a value before use. - Assuming (int) rounds β
(int) 9.9 = 9, NOT 10. Narrowing truncates, never rounds. - Integer overflow is silent β
byte b = 127; b++;gives -128, not 128. No error or warning.
| Statement | Operation | Result | Note |
|---|---|---|---|
int i = 100; | Declare int | i = 100 | 4 bytes allocated |
long l = i; | Widening intβlong | l = 100L | Automatic, safe |
double d = 9.9; | Declare double | d = 9.9 | 8 bytes allocated |
int x = (int) d; | Narrowing doubleβint | x = 9 | Decimal TRUNCATED! |
byte b = 127; | Declare byte | b = 127 | Max byte value |
b++; | Increment at max | b = -128 | OVERFLOW! Wraps around |
char c = 'A'; | Declare char | c = 'A' (65) | Stores unicode 65 |
int code = c; | Widening charβint | code = 65 | Gets unicode value |
Integer.parseInt("42") | Stringβint conversion | 42 | Wrapper method |
- Size order: "Best Smart Intelligent Learners Find Deep Concepts" β byte, short, int, long, float, double, char (widening direction)
- Suffixes:
Lfor long (large),ffor float (fractional small),dfor double (optional) - char quotes: char = 'single' (one character). String = "double" (text). Never mix them.
- Narrowing truncates: "(int) cuts off the decimal like scissors" β 9.9 becomes 9, never 10.
- byte range: -128 to 127 β remember 127 = 2β· - 1
- int range: about Β±2.1 billion β Β±2 Γ 10βΉ
- final = permanent: "final decision β you can't change your mind"
Q: List all primitive data types in Java. Give the size and default value of each.
Answer: Java has 8 primitive types: byte (1 byte, default 0), short (2 bytes, default 0), int (4 bytes, default 0), long (8 bytes, default 0L), float (4 bytes, default 0.0f), double (8 bytes, default 0.0d), char (2 bytes, default '\u0000'), boolean (JVM-dependent, default false). Note: default values apply only to class-level fields, not local variables β local variables must be explicitly initialised.
Q: What is type casting in Java? Explain widening and narrowing conversion with examples.
Answer: Type casting is the process of converting a value from one data type to another.
Widening (implicit): Converting a smaller type to a larger type β done automatically by Java because no data is lost. Example: int i = 100; double d = i; β i (4 bytes) is automatically widened to d (8 bytes), d = 100.0.
Narrowing (explicit): Converting a larger type to a smaller type β must be done with explicit cast syntax (type) because data may be lost. Example: double pi = 3.14; int x = (int) pi; β x = 3 (decimal part truncated). Note: narrowing truncates, it does NOT round. Without the explicit cast, Java gives a compile error.
Q: What is the output of: byte b = 127; b++; System.out.println(b);
Answer: The output is -128. This demonstrates integer overflow. byte has a maximum value of 127. Incrementing beyond 127 wraps around to the minimum value of -128. Java does not throw an exception β the overflow happens silently. This is why choosing the correct data type for your data range is important.
MCQ Bank
(int) 9.99?(int) Math.round(9.99) which gives 10.float f = 3.14f;float f = 3.14;double d = 3.14;long l = 100L;float f = 3.14; causes "possible lossy conversion from double to float". The literal 3.14 is a double by default. To assign to float you need either the f suffix: 3.14f, or an explicit cast: (float)3.14.char c = 'A'; System.out.println(c + 1);char + int, the char is automatically promoted to int first. 'A' has Unicode value 65. 65 + 1 = 66. The result is an int (66), not a char. To get 'B', you need: (char)(c + 1).long l = 10000000000;long l = 10000000000L;long l = 10_000_000_000;int l = 10000000000L;L suffix, 10000000000 is treated as an int literal β but it's larger than Integer.MAX_VALUE (2,147,483,647), causing a compile error "integer number too large". The L suffix tells Java to treat it as a long literal. Option C is missing the L suffix.Integer.parseInt("hello") cause?Integer.parseInt() compiles fine but throws a NumberFormatException at runtime when the String cannot be parsed as an integer. "hello" has no numeric value, so it crashes at runtime. Always validate input before parsing!staticconstfinalimmutablefinal makes a variable a constant. Once assigned, it cannot be changed β reassignment causes a compile error. Note: const is a reserved word in Java but is NOT used β it exists only as a placeholder. Java uses final for constants.type name = value;. All local variables must be initialised before use.final = constant. Cannot reassign. Use ALL_CAPS naming for constants.var (Java 10+) infers type from initializer. Still statically typed. Cannot use without initializer.Integer.parseInt(str) and String.valueOf(num). parseInt throws NumberFormatException on non-numeric input.Easy
Byte.MIN_VALUE, Integer.MAX_VALUE, etc.char c = 'A';. Print: (1) the char itself, (2) its integer (Unicode) value, (3) the next 4 letters by casting (char)(c+1) through (char)(c+4).final double constants: BOILING_POINT = 100.0, FREEZING_POINT = 0.0, ABSOLUTE_ZERO = -273.15. Print them. Try reassigning one to see the error.float f = 3.14; / long l = 9999999999; / char c = "Z"; / boolean b = 1; / int x = 2.5;int Total_Marks = 90;, double MyGPA = 3.5;, String STUDENT_NAME = "Ali";, final int maxsize = 100;int count = 5; double ratio = 0.75; String msg = "Hello"; using var. Then prove the type is still fixed by attempting to assign a String to the int variable.Medium
double d = 9.99;. Cast to float, then to long, then to int, then to short, then to byte. Print at each step. Note where data loss occurs and why.(int) 9.9 (truncation) and (int) Math.round(9.9) (rounding) for these values: 0.4, 0.5, 0.6, 2.49, 2.50, 9.99. Create a formatted table of results.Integer.SIZE, Double.BYTES). Format as a table: Type | Bits | Bytes | Example Value.Hard
Integer.toBinaryString(). Explain why -1 in binary is all 1s (two's complement). Research and comment your findings.Prove your data type mastery without looking at notes.
- Write from memory: the 8 primitive types in order of size, their byte sizes, and default values.
- Explain to yourself (out loud): why does Java have both float and double? When would you realistically choose float over double today?
- Predict without running: what does
System.out.println((byte)(200));output? Trace the bit-level calculation. - Challenge: what is
Integer.MAX_VALUE + 1? Predict, then run, then explain the result using your understanding of overflow.