Java for beginners, part 1: variables, strings, control flow, methods and arrays
Advertisement
This is the second post of the Java series, after What is Java?. Here you learn the core of the language: variables, text, decisions, loops, methods and arrays. Part 2 covers object-oriented Java, and part 3 the advanced and modern features. Every program below was compiled with javac -Xlint:all and run on OpenJDK 21, with no warnings. Type them in, run them, change them.
Program structure and comments
A Java file contains one or more classes; the public class has the same name as the file. Execution starts in main.
public class Greeting {
// a single-line comment
/* a multi-line
comment */
public static void main(String[] args) {
String name = "Asha";
int year = 2026;
System.out.println("Hello, " + name + "! The year is " + year + ".");
System.out.printf("%s scored %d (%.1f%%)%n", name, 45, 90.0);
}
}
Hello, Asha! The year is 2026.
Asha scored 45 (90.0%)
+ joins text with other values. printf uses format characters (%s string, %d integer, %.1f one decimal, %n newline, %% a percent sign), as in C.
Variables and data types
A variable has a type, a name and a value. Names start with a letter, _ or $, use camelCase by convention (totalPrice), and cannot be keywords. Use final for constants (final double TAX = 0.18;, written in capitals).
public class Variables {
public static void main(String[] args) {
int count = 10;
long population = 8_000_000_000L; // L for long literals; underscores help reading
double price = 19.99;
float ratio = 0.5f; // f for float literals
char grade = 'A';
boolean passed = true;
String city = "Pune";
final double TAX = 0.18;
var inferred = 42; // 'var' (Java 10+): type inferred as int
var message = "inferred as String";
System.out.println(count + " " + population + " " + price + " " + ratio + " " + grade + " " + passed);
System.out.println(city + " " + TAX + " " + inferred + " " + message);
System.out.println(Integer.toBinaryString(10) + " " + Integer.toHexString(255) + " " + 0b1010 + " " + 0x1F);
}
}
10 8000000000 19.99 0.5 A true
Pune 0.18 42 inferred as String
1010 ff 10 31
var is only for local variables with an initial value; the variable is still statically typed. Variables declared inside a method are local and must be given a value before use; the compiler refuses to compile otherwise.
Type conversion and casting
Widening (small type to bigger) happens automatically. Narrowing needs an explicit cast, which may lose data.
public class Casting {
public static void main(String[] args) {
int i = 7;
double d = i; // widening: 7.0
double pi = 3.99;
int truncated = (int) pi; // narrowing: 3 (the decimals are cut, not rounded)
long big = 3_000_000_000L;
int wrapped = (int) big; // too big for int: wraps around
char c = 'A';
int code = c; // 65
char next = (char) (c + 1); // 'B'
System.out.println(d + " " + truncated + " " + Math.round(pi) + " " + wrapped + " " + code + " " + next);
String s = "123";
int n = Integer.parseInt(s) + 1;
String back = String.valueOf(n);
System.out.println(n + " " + back.length());
try {
Integer.parseInt("12x");
} catch (NumberFormatException e) {
System.out.println("NumberFormatException: " + e.getMessage());
}
}
}
7.0 3 4 -1294967296 65 B 124 3
NumberFormatException: For input string: "12x"
Casting 3 billion to int wrapped it to a negative number: a silent, classic bug. Use Math.round to round rather than cast, Math.toIntExact to fail loudly on overflow, and Integer.parseInt (inside try) to convert text to numbers.
Strings
String is a class (not a primitive), and strings are immutable: methods return new strings. Compare strings with equals, never ==, which compares object identity (whether they are the same object in memory).
public class Strings {
public static void main(String[] args) {
String a = "hello";
String b = "hello";
String c = new String("hello");
System.out.println((a == b) + " " + (a == c) + " " + a.equals(c) + " " + "HELLO".equalsIgnoreCase(a));
String s = " Hello, Java World ";
System.out.println("[" + s.trim() + "] " + s.length() + " " + s.trim().toUpperCase());
String t = s.trim();
System.out.println(t.charAt(0) + " " + t.indexOf("Java") + " " + t.substring(7, 11) + " " + t.replace("Java", "Python"));
System.out.println(t.contains("World") + " " + t.startsWith("Hello") + " " + String.join("-", "a", "b", "c"));
String[] parts = "a,b,,c".split(",");
System.out.println(parts.length + " " + String.join("|", parts));
System.out.println("apple".compareTo("banana") + " " + "b".repeat(3) + " " + " ".isBlank());
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 5; i++) sb.append(i).append(i < 5 ? "," : "");
System.out.println(sb + " " + sb.reverse());
String text = """
Text blocks (Java 15+)
keep line breaks
""";
System.out.print(text);
}
}
true false true true
[Hello, Java World] 21 HELLO, JAVA WORLD
H 7 Java Hello, Python World
true true a-b-c
4 a|b||c
-1 bbb true
1,2,3,4,5 5,4,3,2,1
Text blocks (Java 15+)
keep line breaks
a == b was true only because Java reuses identical string literals from a pool; a == c was false because new String makes a separate object. This is why == on strings is unreliable and equals is the rule. For building text in a loop, StringBuilder is far more efficient than repeated +, which creates a new string each time.
Operators
Arithmetic (+ - * / %), comparison (== != < > <= >=), logical (&& || !, which short-circuit), assignment (= += -= *= /= ++ --), bitwise (& | ^ ~ << >>) and the conditional condition ? a : b.
public class Operators {
public static void main(String[] args) {
int a = 17, b = 5;
System.out.println(a / b + " " + a % b + " " + (double) a / b + " " + -17 / 5 + " " + -17 % 5);
int x = 5;
int y = x++ + ++x; // 5 + 7
System.out.println(x + " " + y);
x += 3; x *= 2;
System.out.println(x + " " + (x > 10 && x < 30) + " " + (x > 100 || !(x > 0)));
System.out.println((5 & 3) + " " + (5 | 3) + " " + (5 ^ 3) + " " + (5 << 2) + " " + (-16 >> 2) + " " + (-16 >>> 28));
String size = x > 20 ? "big" : "small";
System.out.println(size);
}
}
3 2 3.4 -3 -2
7 12
20 true false
1 7 6 20 -4 15
small
Integer division truncates toward zero (-17 / 5 is -3), and the remainder takes the sign of the dividend (-17 % 5 is -2). x++ uses the value, then increments; ++x increments, then uses the value.
Decisions: if and switch
public class Decisions {
static String grade(int marks) {
if (marks >= 90) return "A";
else if (marks >= 75) return "B";
else if (marks >= 60) return "C";
else return "D";
}
public static void main(String[] args) {
System.out.println(grade(95) + grade(80) + grade(65) + grade(30));
int day = 3;
String name = switch (day) { // switch expression (Java 14+)
case 1, 7 -> "weekend";
case 2, 3, 4, 5, 6 -> {
String tmp = "week";
yield tmp + "day";
}
default -> "unknown";
};
System.out.println(name);
switch (day) { // classic switch: needs break
case 3:
System.out.println("Wednesday");
break;
default:
System.out.println("other");
}
Object o = "text";
if (o instanceof String str && str.length() > 2) { // pattern matching (Java 16+)
System.out.println("long string: " + str);
}
}
}
ABCD
weekday
Wednesday
long string: text
The modern switch expression with -> has no fall-through and returns a value, so prefer it over the classic form, where forgetting break runs into the next case.
Loops
public class Loops {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) System.out.print(i * i + " ");
System.out.println();
int n = 5, fact = 1;
while (n > 1) fact *= n--;
System.out.println("5! = " + fact);
int tries = 0;
do { tries++; } while (tries < 3);
System.out.println("tries = " + tries);
int[] data = {4, 8, 15, 16, 23, 42};
int sum = 0;
for (int v : data) sum += v; // enhanced for ("for each")
System.out.println("sum = " + sum);
outer:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) continue; // skip j == 2
if (i == 3) break outer; // leave both loops
System.out.print(i + "" + j + " ");
}
}
System.out.println();
}
}
1 4 9 16 25
5! = 120
tries = 3
sum = 108
11 13 21 23
break leaves the nearest loop, continue skips to the next round, and a label lets break leave an outer loop. Use for when you know the count, while when you don't, and do-while when the body must run at least once.
Methods
A method has a return type, a name, parameters and a body. Java passes arguments by value: primitives are copied, and for objects the reference is copied (so a method can change the object's contents but cannot make the caller's variable point somewhere else).
public class Methods {
static int square(int n) { return n * n; }
static double average(int... numbers) { // varargs
if (numbers.length == 0) return 0;
int sum = 0;
for (int v : numbers) sum += v;
return (double) sum / numbers.length;
}
static String describe(int n) { return "int " + n; }
static String describe(double d) { return "double " + d; } // overloading
static String describe(String s) { return "String " + s; }
static void tryToChange(int number, int[] array) {
number = 99;
array[0] = 99;
}
static long factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1); }
public static void main(String[] args) {
System.out.println(square(7) + " " + average(4, 8, 9) + " " + average());
System.out.println(describe(5) + ", " + describe(2.5) + ", " + describe("hi"));
int number = 1;
int[] array = {1, 2};
tryToChange(number, array);
System.out.println(number + " " + array[0]);
System.out.println(factorial(10));
}
}
49 7.0 0.0
int 5, double 2.5, String hi
1 99
3628800
Overloading means several methods with the same name and different parameter lists (Java picks by the argument types; unlike Python, this is allowed). The final test shows pass-by-value: the int stayed 1, but the array's contents changed because the method received a copy of the reference to the same array.
Arrays
An array has a fixed length and one element type. Indexes start at 0, and a bad index throws an exception instead of corrupting memory as in C.
import java.util.Arrays;
public class ArrayDemo {
public static void main(String[] args) {
int[] marks = new int[5]; // five zeros
int[] scores = {70, 85, 60, 90, 75};
System.out.println(Arrays.toString(marks) + " length " + scores.length);
int[] sorted = scores.clone();
Arrays.sort(sorted);
System.out.println(Arrays.toString(sorted) + " search 85 at " + Arrays.binarySearch(sorted, 85));
System.out.println(Arrays.equals(scores, sorted) + " " + Arrays.stream(scores).max().getAsInt() + " " + Arrays.stream(scores).average().getAsDouble());
int[][] grid = {{1, 2, 3}, {4, 5, 6}};
System.out.println(grid[1][2] + " " + Arrays.deepToString(grid));
int[] copy = Arrays.copyOf(scores, 7);
System.out.println(Arrays.toString(copy));
try {
System.out.println(scores[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException: " + e.getMessage());
}
}
}
[0, 0, 0, 0, 0] length 5
[60, 70, 75, 85, 90] search 85 at 3
false 90 76.0
6 [[1, 2, 3], [4, 5, 6]]
[70, 85, 60, 90, 75, 0, 0]
ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
Never print an array directly (System.out.println(scores) prints an unhelpful identity code); use Arrays.toString. The size is fixed at creation; for a list that grows, use ArrayList (part 2).
Reading input with Scanner
import java.util.Scanner;
public class Input {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Name: ");
String name = in.nextLine();
System.out.print("Two numbers: ");
int a = in.nextInt();
int b = in.nextInt();
System.out.println("Hello " + name + ", sum = " + (a + b));
in.close();
}
}
With the input lines Asha and 12 30, it printed Name: Two numbers: Hello Asha, sum = 42 (the prompts ran together because the input was piped). Remember the classic trap: after nextInt(), the newline stays in the buffer, so a following nextLine() returns an empty string; call in.nextLine() once to consume it. Check in.hasNextInt() before reading when input might be invalid.
A first look at classes
Everything in Java lives in classes. Part 2 covers them fully, but this is the shape:
public class Counter {
private int count; // field (state), hidden from outside
public Counter(int start) { this.count = start; } // constructor
public void increment() { count++; } // method (behaviour)
public int getCount() { return count; }
public static void main(String[] args) {
Counter c = new Counter(10); // create an object
c.increment();
c.increment();
System.out.println(c.getCount());
Counter other = new Counter(0);
System.out.println(other.getCount() + " " + (c == other));
}
}
12
0 false
new creates an object, each object has its own count, and private hides the field so that other code must use the methods.
Common mistakes
- Comparing strings with
==instead ofequals. - Integer division where a decimal result was wanted (
a / bwith two ints). - Silent overflow and narrowing casts.
- A missing
breakin a classicswitch. - Reading past the end of an array (
lengthis one more than the last index). - Mixing
nextInt()andnextLine(). - Printing arrays directly.
- Using
=in a condition (if (x = 5)does not compile for ints, butif (flag = true)does for booleans).
Practice
Write programs that: print a multiplication table; reverse a string without StringBuilder.reverse; check for a palindrome; find the largest and second largest in an array; count vowels in a sentence; and print the first 20 Fibonacci numbers. Then read marks from the user into an array and print the average and the grade of each, using a grade method like the one above.
Next
Continue with part 2: classes, inheritance, interfaces, collections, exceptions and files. To compare how the same loops and conditions look in other languages, see Python flow control and the C guide.