Every variable in Java has a type that you must declare. This is the single biggest day-one difference from JavaScript, where a variable can hold any value at any time. In Java, int passed = 28; says "this slot holds whole numbers, forever." Try to put a string in it and the compiler refuses to build. That strictness feels heavy for ten seconds and pays off for the next ten years — types are why IntelliJ can rename a method across 300 test files safely, and why your test code fails at compile time instead of at 3am in production.
Declaring a variable — the shape
The shape is always Type name = value;:
String testName = "Login Test";
int statusCode = 200;
double responseTime = 1.25;
boolean isPassed = true;Read each one as: "this name holds this type, and its starting value is …". Once declared, the type cannot change. testName = 42; is a compile error.
In JavaScript you'd write let testName = "Login Test"; — the type is inferred, and you can later reassign testName = 42 without complaint. Java forbids this.
Primitive types — values stored directly
Java has eight primitive types. These hold the value itself in memory, not a pointer to an object. The four you'll use most in QA code:
int retries = 3; // whole number
double price = 29.99; // decimal number
boolean isLoggedIn = true; // true or false
char grade = 'A'; // single character — note SINGLE quotesThe other four are corner cases:
long timestamp = 1715000000L; // big whole number — note the L suffix
float score = 85.5f; // less-precise decimal — note the f suffix
short hour = 23; // small integer — rarely used
byte flag = 1; // tiny integer — rarely usedA few rules that matter:
longliterals need anLsuffix because Java assumes whole-number literals areintby default. Without the L,1715000000is fine, but1715000000000overflows theintrange and won't compile.floatliterals need anfsuffix because Java assumes decimal literals aredouble. Forget thefand you'll seeerror: incompatible types: possible lossy conversion from double to float.charuses single quotes ('A').Stringuses double quotes ("A"). They are different types —'A'is a 16-bit character,"A"is a one-character String object.
Reference types — String is the one that matters
Anything that isn't one of the eight primitives is a reference type — the variable stores a reference (a pointer) to an object somewhere in the heap. The reference type you'll use every day is String:
String url = "https://staging.myapp.com";
String env = "staging";
String userAgent = null; // a String reference can hold nullNote the capital S — String is a class, not a primitive. (Lowercase string is not a Java type and won't compile.) We'll cover Strings properly in chapter 8; for now, just know that text values go in a String.
var — let Java infer the type
Java 10 added the var keyword. It looks like JavaScript's let/const, but it's actually different: the type is still fixed at compile time — the compiler just figures it out from the right-hand side.
var count = 5; // inferred as int
var url = "https://x.com"; // inferred as String
var passed = true; // inferred as boolean
count = "five"; // ERROR — count is locked to int even though we used varvar is convenient for long generic types you'll meet later (Map<String, List<TestResult>>), but for simple QA code an explicit type usually reads better. A reviewer can tell at a glance that int retries holds a number; with var retries they have to chase the value to the right.
Naming conventions
Java has strong, almost-universal conventions:
- camelCase for variables and methods:
testCaseName,expectedResult,findElementById. - PascalCase for class names:
LoginPage,TestRunner,ApiClient. - UPPER_SNAKE_CASE for constants:
MAX_RETRIES,BASE_URL,DEFAULT_TIMEOUT.
These aren't rules the compiler enforces, but every IntelliJ inspection and code review will flag them if you break them. Lean into the conventions early.
Constants — final means "can't change"
When a value should never change after assignment, declare it final:
final int MAX_RETRIES = 3;
final String BASE_URL = "https://staging.myapp.com";
final double SLA_THRESHOLD = 2.0;
MAX_RETRIES = 5; // ERROR — cannot assign a value to final variable MAX_RETRIESfinal is Java's equivalent of JavaScript's const. Use it for any value that is configuration: timeouts, base URLs, retry counts. The compiler enforces immutability so a future maintainer can't accidentally reassign it halfway through a test.
Type casting — converting between types
Sometimes you need to move a value between types. There are two flavours:
Widening (small to bigger) is automatic and safe:
int retries = 3;
double retriesAsDouble = retries; // int → double, no cast needed
System.out.println(retriesAsDouble); // 3.0Narrowing (bigger to smaller) loses data and requires an explicit cast:
double responseTime = 1.85;
int responseTimeAsInt = (int) responseTime; // truncates — does NOT round
System.out.println(responseTimeAsInt); // 1, not 2The (int) in front is the cast operator. Note that casting to int truncates (drops the decimals) — it doesn't round. To round properly, use Math.round:
double rt = 1.85;
long rounded = Math.round(rt); // 2String ⇄ number conversion
Reading from a CSV, an API response, or a config file usually gives you values as Strings. To use them as numbers you must parse:
String statusFromCsv = "200";
int status = Integer.parseInt(statusFromCsv); // String → int
String priceFromCsv = "29.99";
double price = Double.parseDouble(priceFromCsv); // String → doubleThe reverse — turning a number into a String — is even simpler:
int code = 200;
String asString = String.valueOf(code); // preferred
String alsoAsString = "" + code; // works, a bit hackyInteger.parseInt("not a number") throws a NumberFormatException at runtime — exception handling lives in chapter 7. For now, only call parseInt on values you know are numeric.
A QA configuration block
A real-world snippet showing every variable type pulling its weight:
public class TestConfig {
public static void main(String[] args) {
final String BASE_URL = "https://staging.myapp.com";
final int MAX_RETRIES = 3;
final double SLA_THRESHOLD = 2.0;
final boolean IS_PRODUCTION = false;
String testEnv = "staging";
int currentRetry = 0;
double lastResponseTime = 1.85;
boolean isPassing = lastResponseTime < SLA_THRESHOLD;
System.out.println("Env: " + testEnv);
System.out.println("Base URL: " + BASE_URL);
System.out.println("Max retries: " + MAX_RETRIES);
System.out.println("Last response: " + lastResponseTime + "s");
System.out.println("SLA met? " + isPassing);
}
}Output:
Env: staging
Base URL: https://staging.myapp.com
Max retries: 3
Last response: 1.85s
SLA met? true
The eight primitives at a glance
Java primitive types — size, range, and a QA use
| Common | Less common | |
|---|---|---|
| Whole nums | int — 32-bit, range ±2.1B. Default for retries, HTTP status codes, indices. | long — 64-bit, range ±9.2 quintillion. Use for timestamps in milliseconds (Unix epoch). Suffix L: 1715000000L. |
| Decimals | double — 64-bit floating-point. Default for response times, prices, percentages. | float — 32-bit, less precision. Rarely used in QA code. Suffix f: 85.5f. |
| Boolean | boolean — true or false. Use for isPassing, isLoggedIn, isProduction flags. | (no second boolean — there's only one) |
| Character | char — single 16-bit character. Use single quotes: 'A'. Rare in QA — Strings cover most needs. | byte / short — small integers. Almost never used in test code; included for legacy compatibility. |
The takeaway: in QA code you'll use int, double, boolean, and String about 95% of the time. The other types exist; you can mostly ignore them until a specific situation demands them.
⚠️ Common mistakes
- Lowercase
stringinstead ofString.string name = "Alice";does not compile. Java's text type is the classString, capital S. Same trap withIntegervsinteger,Doublevsdouble— the wrapper classes are PascalCase. - Forgetting the
Lorfsuffix on numeric literals.long t = 1715000000000;fails because the literal is anintfirst and overflows. Fix:1715000000000L.float score = 85.5;fails because85.5is adouble. Fix:85.5f. - Expecting
(int) 1.85to round to 2. Casting truncates — you get 1. For rounding, useMath.round(1.85)(returns along), or(int) Math.round(1.85)if you need an int.
🎯 Practice task
Build a typed configuration block. 20-30 minutes.
- Create a file
LoadTestConfig.java(and a class with the matching name). - Inside
main, declare these variables with correct types:- a
Stringfor the API base URL - a
Stringfor the environment name (e.g. "staging") - an
intfor max concurrent users - a
doublefor the p95 latency SLA in milliseconds (e.g. 250.0) - a
booleanfor whether dry-run mode is on - a
final intconstant for default retries
- a
- Print each value with
System.out.printlnand a label, e.g."Env: " + env. - Read a value as a String —
String maxUsersInput = "150";— and convert it to anintwithInteger.parseInt. Print both the original string and the parsed int. - Read a decimal as a String —
String slaInput = "2.5";— and convert it to adoublewithDouble.parseDouble. Cast it to anintand print both: notice the cast truncates 2.5 to 2. - Compile and run with
javac LoadTestConfig.java && java LoadTestConfig. Confirm the output matches your declarations. - Stretch: try assigning the wrong type on purpose —
int users = "150";— and read the compiler error. Type errors at compile time are the whole point of static typing; getting comfortable reading those errors is half the skill of using a typed language.
You can now declare and convert every type you'll need for the next several chapters. The next lesson kicks off chapter 2 with control flow — if, switch, and the operators you use to build them.