
Java and Swift are the dominant languages for mobile development on their respective platforms. Java powers Android (along with Kotlin), while Swift is Apple’s language for iOS, macOS, and other Apple platforms. If you’re building mobile apps or choosing a development path, understanding both helps you make informed decisions.
Quick Comparison
| Aspect | Java | Swift |
|---|---|---|
| Released | 1995 | 2014 |
| Created by | Sun Microsystems (now Oracle) | Apple |
| Primary platform | Android, enterprise backends | iOS, macOS, watchOS, tvOS |
| Typing | Static, strong | Static, strong |
| Compilation | Bytecode (JVM) | Native binary (LLVM) |
| Memory management | Garbage collected | Automatic Reference Counting (ARC) |
| Null handling | Nullable by default | Optionals (null safety) |
| OOP model | Class-based with interfaces | Class-based with protocols |
| Functional features | Lambdas (Java 8+) | First-class functions, closures |
Syntax Comparison
Hello World
Java:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Swift:
print("Hello, World!")
Swift requires no class wrapper or main method for simple programs. The minimal syntax reflects its design as a modern language.
Variables and Constants
Java:
int count = 10;
final int maxSize = 100; // Constant
String name = "Alice";
double price = 19.99;
var items = new ArrayList<String>(); // Type inference (Java 10+)
Swift:
var count = 10 // Mutable
let maxSize = 100 // Constant (immutable)
var name: String = "Alice" // Explicit type
var price = 19.99 // Type inferred as Double
var items: [String] = [] // Array of strings
// Swift encourages using let (constants) by default
let pi = 3.14159 // Prefer let when value won't change
Swift uses var for variables and let for constants. Type inference is pervasive. The language encourages immutability by making let the preferred choice.
Functions
Java:
public int add(int a, int b) {
return a + b;
}
public String greet(String name) {
return "Hello, " + name;
}
// Overloading
public int add(int a, int b, int c) {
return a + b + c;
}
Swift:
func add(a: Int, b: Int) -> Int {
return a + b
}
func greet(name: String) -> String {
return "Hello, \(name)"
}
// Argument labels (external vs internal names)
func greet(person name: String) -> String {
return "Hello, \(name)"
}
// Called as: greet(person: "Alice")
// Default parameters
func greet(name: String, greeting: String = "Hello") -> String {
return "\(greeting), \(name)"
}
// Omit argument label with underscore
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// Called as: add(1, 2) instead of add(a: 1, b: 2)
Swift functions have argument labels that make call sites read like sentences. This is a key difference from Java’s positional-only parameters.
Classes
Java:
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String greet() {
return "Hi, I'm " + name;
}
}
Swift:
class User {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func greet() -> String {
return "Hi, I'm \(name)"
}
}
// Usage
let user = User(name: "Alice", age: 30)
print(user.greet())
print(user.name) // Direct property access, no getter needed
Swift properties are accessed directly. No explicit getters and setters needed unless you want custom behavior. Initializers use init instead of the class name.
Structs (Value Types)
Swift has first-class support for value types:
struct Point {
var x: Double
var y: Double
func distance(to other: Point) -> Double {
let dx = x - other.x
let dy = y - other.y
return (dx * dx + dy * dy).squareRoot()
}
// Mutating method (modifies self)
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var p1 = Point(x: 0, y: 0)
var p2 = p1 // Creates a copy, not a reference
p2.x = 10 // Only p2 changes, p1 remains at (0, 0)
Java doesn’t have value types in the same way. Records (Java 16+) provide immutable data classes but are still reference types.
Null Safety
This is where Swift shines compared to Java.
Java: Null by Default
String name = null; // Allowed
int length = name.length(); // NullPointerException at runtime
// Optional helps but isn't enforced
Optional<String> maybeName = Optional.ofNullable(getName());
String value = maybeName.orElse("Unknown");
Swift: Optionals
var name: String = nil // Compile error! String can't be nil
var name: String? = nil // Optional String, can be nil
// Must unwrap to use
if let unwrapped = name {
print(unwrapped.count) // Safe, unwrapped is String
}
// Guard let for early exit
func processName(_ name: String?) {
guard let name = name else {
print("No name provided")
return
}
// name is now String (not optional) for rest of function
print(name.uppercased())
}
// Optional chaining
let length = name?.count // Returns Int? (nil if name is nil)
// Nil coalescing
let displayName = name ?? "Unknown" // "Unknown" if name is nil
// Force unwrap (use sparingly)
let length = name!.count // Crashes if name is nil
Swift’s type system distinguishes between values that can be nil and those that cannot. The compiler forces you to handle the nil case.
Error Handling
Java Exceptions
public String readFile(String path) throws IOException {
return Files.readString(Path.of(path));
}
try {
String content = readFile("data.txt");
System.out.println(content);
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
Swift Error Handling
enum FileError: Error {
case notFound
case permissionDenied
case corrupted
}
func readFile(path: String) throws -> String {
// If file doesn't exist:
throw FileError.notFound
}
// Using do-catch
do {
let content = try readFile(path: "data.txt")
print(content)
} catch FileError.notFound {
print("File not found")
} catch {
print("Error: \(error)")
}
// Convert to optional
let content = try? readFile(path: "data.txt") // Returns String?
// Force try (crashes on error)
let content = try! readFile(path: "data.txt") // Use carefully
Swift uses throws and try keywords similar to Java, but errors are typically enums that conform to the Error protocol rather than exception classes.
Memory Management
Java: Garbage Collection
The JVM tracks object references and periodically frees unused memory. You don’t manage memory manually, but GC pauses can affect performance.
public void createObjects() {
List<String> list = new ArrayList<>();
// When list goes out of scope, GC will eventually free it
}
Swift: Automatic Reference Counting (ARC)
Swift counts references to each object and frees memory immediately when the count reaches zero. No GC pauses, but you must handle reference cycles.
class Person {
var name: String
var apartment: Apartment? // Strong reference
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deallocated")
}
}
class Apartment {
var number: Int
weak var tenant: Person? // Weak reference to avoid cycle
init(number: Int) {
self.number = number
}
}
// Without weak, Person and Apartment would reference each other
// and never be deallocated (retain cycle)
Swift requires explicit weak or unowned references to break cycles. This is more manual than Java’s GC but provides deterministic memory management.
Protocols vs Interfaces
Java:
public interface Drawable {
void draw();
default void printDescription() {
System.out.println("A drawable object");
}
}
public class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing circle");
}
}
Swift:
protocol Drawable {
func draw()
}
// Protocol extensions provide default implementations
extension Drawable {
func printDescription() {
print("A drawable object")
}
}
class Circle: Drawable {
func draw() {
print("Drawing circle")
}
}
// Protocols can be used as types
func render(items: [Drawable]) {
for item in items {
item.draw()
}
}
// Protocol-oriented programming
protocol Vehicle {
var numberOfWheels: Int { get }
func drive()
}
struct Car: Vehicle {
let numberOfWheels = 4
func drive() { print("Driving car") }
}
struct Bicycle: Vehicle {
let numberOfWheels = 2
func drive() { print("Riding bicycle") }
}
Swift protocols are more powerful than Java interfaces. They can be adopted by structs, enums, and classes. Protocol extensions enable “protocol-oriented programming,” a pattern Swift encourages.
Enums
Java:
public enum Direction {
NORTH, SOUTH, EAST, WEST;
public Direction opposite() {
switch (this) {
case NORTH: return SOUTH;
case SOUTH: return NORTH;
case EAST: return WEST;
case WEST: return EAST;
default: throw new IllegalStateException();
}
}
}
Swift:
enum Direction {
case north, south, east, west
var opposite: Direction {
switch self {
case .north: return .south
case .south: return .north
case .east: return .west
case .west: return .east
}
}
}
// Enums with associated values (very powerful)
enum NetworkResult {
case success(data: Data)
case failure(error: Error)
}
func handleResult(_ result: NetworkResult) {
switch result {
case .success(let data):
print("Got \(data.count) bytes")
case .failure(let error):
print("Error: \(error)")
}
}
// Enums with raw values
enum Planet: Int {
case mercury = 1, venus, earth, mars
}
let earth = Planet.earth.rawValue // 3
Swift enums can have associated values, making them algebraic data types. This enables patterns like Result types that Java handles with separate classes.
Collections
Java:
// List
List<String> list = new ArrayList<>();
list.add("apple");
String first = list.get(0);
// Map
Map<String, Integer> map = new HashMap<>();
map.put("apple", 5);
Integer count = map.get("apple");
// Set
Set<String> set = new HashSet<>();
set.add("apple");
Swift:
// Array
var list = ["apple", "banana"]
list.append("cherry")
let first = list[0]
// Dictionary
var map = ["apple": 5, "banana": 3]
map["cherry"] = 7
let count = map["apple"] // Optional Int
// Set
var set: Set<String> = ["apple", "banana"]
set.insert("cherry")
// Collection operations
let doubled = [1, 2, 3].map { $0 * 2 } // [2, 4, 6]
let evens = [1, 2, 3, 4].filter { $0 % 2 == 0 } // [2, 4]
let sum = [1, 2, 3].reduce(0, +) // 6
Swift collections use cleaner syntax with square brackets. Functional operations like map, filter, and reduce are built in with closure syntax.
Platform and Ecosystem
Java
- Android development: Primary language (with Kotlin)
- Enterprise backends: Spring Boot, Jakarta EE
- Cross-platform: Runs on any OS with JVM
- Mature ecosystem: Millions of libraries on Maven Central
- IDE support: IntelliJ IDEA, Eclipse, VS Code
Swift
- Apple platforms: iOS, macOS, watchOS, tvOS
- Server-side: Vapor, Kitura (growing but smaller than Java)
- Cross-platform: Limited (Linux support, no Windows officially)
- Ecosystem: Swift Package Manager, smaller than Java’s
- IDE: Xcode (required for Apple development)
Performance
Compilation: Swift compiles to native machine code, while Java compiles to bytecode that the JVM interprets and JIT-compiles. Swift apps start faster with no runtime warmup.
Runtime performance: Both can be fast. Swift has an edge for CPU-bound tasks due to native compilation. Java’s JIT can produce highly optimized code for long-running applications.
Memory: Swift with ARC typically uses less memory than Java with GC. No garbage collector overhead.
Mobile apps: Swift iOS apps generally feel snappier than Java Android apps due to native compilation and platform optimization.
Use Cases
Choose Java When:
Android development: Java (or Kotlin) is required for native Android apps.
Enterprise applications: Java dominates backend development with mature frameworks and tooling.
Cross-platform needs: Write once, run on Windows, Linux, macOS, and more.
Large team projects: Extensive Java developer talent pool worldwide.
Choose Swift When:
iOS/macOS development: Swift is Apple’s preferred language for all Apple platforms.
Apple ecosystem integration: Best access to Apple frameworks, APIs, and features.
Performance-critical iOS apps: Native compilation and tight platform integration.
Modern language features: Swift has optionals, value types, and protocol-oriented programming built in.
Code Example: Simple App Structure
Java (Android Activity)
public class MainActivity extends AppCompatActivity {
private TextView textView;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
button = findViewById(R.id.button);
button.setOnClickListener(v -> {
textView.setText("Button clicked!");
});
}
}
Swift (iOS ViewController)
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
@IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func buttonTapped(_ sender: UIButton) {
label.text = "Button tapped!"
}
}
// Or with SwiftUI (modern approach)
struct ContentView: View {
@State private var message = "Hello"
var body: some View {
VStack {
Text(message)
Button("Tap me") {
message = "Button tapped!"
}
}
}
}
Both frameworks follow similar patterns. SwiftUI represents Apple’s modern declarative approach, similar in concept to Jetpack Compose on Android.
Learning Path
If you’re deciding which to learn:
Learn Java first if: You want Android development, plan to work in enterprise software, or want the most job opportunities overall.
Learn Swift first if: You’re focused on Apple platforms, have a Mac for development, or prefer a more modern language syntax.
Learn both if: You want to do cross-platform mobile development or maximize your career options in mobile.
Summary
Java and Swift serve different ecosystems but share similar goals: type safety, object-oriented design, and developer productivity.
Java offers cross-platform compatibility, a massive ecosystem, and dominance in Android and enterprise development. It’s the pragmatic choice for maximum job opportunities.
Swift provides modern language features, null safety, and the best experience for Apple platform development. It’s the clear choice for iOS and macOS apps.
Mobile developers often benefit from knowing both, since native development requires platform-specific languages. Many concepts transfer between them.
Related: Java vs Kotlin | Java vs Python | Introduction to Object-Oriented Programming | Java Exception Handling
Sources
- Apple. “The Swift Programming Language.” docs.swift.org/swift-book
- Oracle. “Java Documentation.” docs.oracle.com/en/java
- Stack Overflow. “Developer Survey 2024.” stackoverflow.com/survey


