interface MyInterface {
default void greet() { System.out.println("Hello"); }
static void help() { System.out.println("Help"); }
}
If a class implements two interfaces that define the same default method, it must resolve the conflict explicitly:
interface A {
default void hello() {
System.out.println("Hello from A");
}
}
interface B {
default void hello() {
System.out.println("Hello from B");
}
}
class C implements A, B {
@Override
public void hello() {
// explicitly choose or combine
A.super.hello();
B.super.hello();
}
}
List<Integer> numbers = List.of(5, 10, 3, 8, 12);
Optional<Integer> maxV = numbers.stream()
.reduce(Math::max);
System.out.println("Max value: " + maxV.orElse(null));
Compiler can infer local variable types:
var list = new ArrayList<String>();
Type inference happens at compile time
The type is still static — var does not make Java dynamically typed like JavaScript or Python.
You must initialize the variable
This works:
var x = 10;
But this does not:
var x;
x = 10; // Error: cannot infer type
var can only be used for local variables
You can use it in methods, constructors, or blocks — not for:
Fields (class-level variables)
Method parameters
Return types
The inferred type is fixed
Once inferred, it cannot change.
var name = "Alice";
name = 42; // ❌ Error: incompatible types
switch is an expression — returns a value.
-> arrow syntax: no break needed.
String result = switch (day) {
case "MONDAY", "FRIDAY" -> "Workday";
case "SATURDAY", "SUNDAY" -> "Weekend";
default -> "Unknown";
};
Use yield when a branch needs a block:
String mood = switch (temperature) {
case 30 -> "Hot";
case 20 -> "Mild";
case 10 -> "Cold";
default -> {
String msg = "Unknown temperature: " + temperature;
yield msg; // returns this value from the block
}
};
It combines type checks and casts neatly.
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
Match types:
String format(Object obj) {
return switch (obj) {
case Integer i -> "int " + i;
case Long l -> "long " + l;
case String s -> "string " + s.toUpperCase();
case null -> "null value";
default -> "unknown type";
};
}
With conditions:
String result = switch (obj) {
case String s && s.length() > 5 -> "Long string: " + s;
case String s -> "Short string: " + s;
case null -> "Null";
default -> "Other type";
};
A records are immutable by design and
public record Point(int x, int y) {}
This one line automatically generates:
A final class
private final fields x and y.
A canonical constructor
x() and y() accessor methods
equals(), hashCode(), and toString()
Point p = new Point(10, 20);
System.out.println(p); // prints: Point[x=10, y=20]
System.out.println(p.x()); // 10
System.out.println(p.y); // 20
Note:Though the fields are private final, they can be directly accessed like p.x, p.y. The compiler actually calls the accessor methods.
Custom constructors:
Canonical constructors (same parameters as the header)
Compact constructors (omitting parameter list)
Overloaded constructors
Override accessor methods
Custom accessor methods
public record Point(int x, int y) {
// Compact constructor
public Point {
if (x < 0 || y < 0)
throw new IllegalArgumentException("Coordinates must be positive");
}
// Overloaded constructor
public Point(int value) {
this(value, value);
}
// Override accessor method
@Override
public int x() {
System.out.println("Accessing x");
return x;
}
// Custom method
public double distanceFromOrigin() {
return Math.sqrt(x * x + y * y);
}
}
The key design principle of records is “data transparency” — everything that defines the state of the record must appear in the header.
Record definition: record Point(int x, int y) {}
The record header is this part: (int x, int y)
Adding another instance field is illegal:
record Point(int x, int y) {
private int z; // ❌ illegal
}
static (shared across all instances) fields are allowed
record Point(int x, int y) {
static int count = 0;
}
interface Shape {
double area();
}
record Circle(double radius) implements Shape {
public double area() {
return Math.PI * radius * radius;
}
}
Enum constants are static and final.
Enums are type-safe;
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class TestEnum {
public static void main(String[] args) {
Day today = Day.WEDNESDAY;
System.out.println(today); // WEDNESDAY
}
}
All enums automatically inherit from java.lang.Enum, giving you useful methods:
values() → returns an array of all enum constants.
valueOf(String name) → returns the enum constant with the specified name.
ordinal() → returns the position of the enum constant (starting from 0).
Enums can have fields, constructors, and methods.
enum Day {
MONDAY("Work day"),
SATURDAY("Weekend"),
SUNDAY("Weekend");
private final String description;
// Constructor (private by default)
Day(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
public class TestEnum {
public static void main(String[] args) {
Day day = Day.SATURDAY;
System.out.println(day + " is a " + day.getDescription());
}
}
Day day = Day.FRIDAY;
switch(day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> System.out.println("Work day");
case SATURDAY, SUNDAY -> System.out.println("Weekend");
}
enum Operation {
ADD {
public double apply(double x, double y) { return x + y; }
},
SUBTRACT {
public double apply(double x, double y) { return x - y; }
};
// abstract method
public abstract double apply(double x, double y);
}
System.out.println(Operation.ADD.apply(3, 5)); // 8.0
Key words: sealed, permits, non-sealed
A sealed class can be extended only by permitted classes.
Extended class should either be final or non-sealed
public sealed class Shape permits Circle, Rectangle, Freeform {}
public final class Circle extends Shape {}
public final class Rectangle extends Shape {}
public non-sealed class Freeform extends Shape {} // open-ended
Once you seal the hierarchy, the compiler knows all possible subtypes → switch doesn’t need a default.
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
static double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
};
}
Useful for HTML, JSON, or SQL inside Java code:
String html = """
<html>
<body>
<p>Hello, world!</p>
</body>
</html>
""";
System.out.println(html);
String json = """
{
"name": "Alice",
"age": 30
}
""";
Text blocks preserve line breaks. If you want to avoid a line break at the end of a line, you can use a backslash \:
String text = """
This is a single \
line string.
""";
System.out.println(text);
Output: This is a single line string.
Text blocks themselves are just strings, so you can still use them with String.format or concatenation:
String name = "Alice";
String greeting = """
Hello, %s!
Welcome to Java Text Blocks.
""".formatted(name);
System.out.println(greeting);
Output:
Hello, Alice!
Welcome to Java Text Blocks.
import static java.lang.Math.sqrt;
public class Test {
public static void main(String[] args) {
double result = sqrt(16); // no Math. prefix
System.out.println(result);
}
}
👉 Only affects static members: fields, constants, and methods.
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
System.out.println(sqrt(25)); // 5.0
System.out.println(pow(2, 3)); // 8.0
System.out.println(PI); // 3.14159...
double result = sqrt(pow(3, 2) + pow(4, 2));
System.out.println(result); // 5.0
}
}
import static org.junit.jupiter.api.Assertions.*;
public class TestExample {
@org.junit.jupiter.api.Test
void testSum() {
int sum = 2 + 3;
assertEquals(5, sum); // no Assertions.assertEquals needed
}
}
import static java.time.DayOfWeek.*;
DayOfWeek today = MONDAY; // instead of DayOfWeek.MONDAY