Build tools
Compiling by hand stops working as soon as you have dependencies. Use Maven or Gradle.
Maven — pom.xml, declarative, ubiquitous:
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
</dependencies>mvn compile
mvn test
mvn package # builds a jarGradle — build.gradle, more concise, the Android standard:
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}Testing with JUnit
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void addsTwoNumbers() {
assertEquals(7, Calculator.add(3, 4));
}
@Test
void rejectsNegativeDeposit() {
Account a = new Account();
assertThrows(IllegalArgumentException.class, () -> a.deposit(-5));
}
}Test the logic that would be expensive to get wrong — pricing, validation, date handling — rather than chasing a coverage number.
Project layout
src/
main/java/com/example/App.java
main/resources/
test/java/com/example/AppTest.java
pom.xmlPackage names follow reverse domain order: com.example.project.
Where to go next
Spring Boot — the dominant Java backend framework. Dependency injection, REST endpoints, database access, and security with very little boilerplate.
JDBC / JPA / Hibernate — talking to databases. Learn plain JDBC with
PreparedStatement first so you understand what an ORM is doing for you
(and, as in every language, never concatenate user input into SQL).
Kotlin — runs on the same JVM, interoperates with all Java libraries, and removes much of Java's verbosity. The default for new Android work.
Modern Java features — if you learned Java 8, look at records, sealed
classes, pattern matching for switch, text blocks, and virtual threads
(Java 21). The language has changed considerably.
Habits worth forming
- Prefer immutability:
finalfields, records,List.of(). - Program to interfaces (
List, notArrayList) in declarations. - Return empty collections, never
null. - Use
Optionalfor "might not exist" return values. - Write
@Overrideevery time you override. - Keep methods short and named for what they do.
- Let exceptions travel to a layer that can actually handle them.