Java는 10년 이상 Android SDK의 주요 언어였던 객체 지향 프로그래밍 언어입니다. Google이 공식적으로 Kotlin으로 전환했음에도 불구하고 Java는 생태계의 중요한 부분으로 남아 있습니다. 레거시 프로젝트, 엔터프라이즈 애플리케이션 및 Google Play의 수백만 줄의 코드가 계속 Java에서 실행됩니다. JetBrains Developer Ecosystem 보고서(2025)에 따르면 약 35%의 Android 프로젝트가 여전히 Java를 사용합니다. 최신 트렌드에 대한 자세한 내용은 JetBrains 공식 보고서를 참조하세요.
주요 내용
Java는 Sun Microsystems가 1995년에 만든 범용 프로그래밍 언어입니다. 주요 원칙은 "Write Once, Run Anywhere"입니다. 컴파일된 Java 바이트코드는 JVM 가상 머신이 설치된 모든 플랫폼에서 실행됩니다. 모바일 개발의 맥락에서 Java는 2008년 Google이 출시한 Android SDK의 첫 번째 공식 언어가 되었습니다.
Java 아키텍처는 세 가지 구성 요소를 기반으로 합니다: JDK(Java Development Kit) — 개발자 도구 세트, JRE(Java Runtime Environment) — 런타임 환경, JVM(Java Virtual Machine) — 가상 머신. Android는 자체 JVM 구현인 Android Runtime(ART)을 사용하며, 버전 5.0(Lollipop)에서 Dalvik을 대체했습니다. ART는 dx/d8 유틸리티를 통해 Java 바이트코드에서 컴파일된 DEX 바이트코드를 실행합니다.
Java의 주요 기능은 가비지 컬렉터(GC)를 통한 자동 메모리 관리입니다. 개발자가 명시적으로 메모리를 해제할 필요가 없습니다. GC가 사용되지 않는 객체를 추적하여 제거합니다. Android ART에서는 모바일 장치의 제한된 리소스에 최적화된 Concurrent Mark Sweep(CMS) 및 Generational GC가 사용됩니다. 이는 개발자의 부담을 줄여주지만 UI 스레드에서 가비지 수집 중 "지연"이 발생할 수 있습니다.
Android는 OS의 새 버전마다 확장되는 Java API의 하위 집합을 지원합니다. Android 12(API 31)는 변수 캡처가 포함된 람다, JPMS 모듈 시스템 및 java.net.http HTTP 클라이언트를 포함한 Java 11 지원을 추가했습니다. Android 14(API 34)는 D8/R8 디슈가링을 통해 Java 17에 대한 실험적 지원을 도입했습니다. 일부 기능(레코드, sealed 클래스)은 이전 버전과 호환되는 코드로 변환됩니다.
| Java 버전 | Android API (min) | Android 버전 | 주요 기능 |
|---|---|---|---|
| Java 7 | API 19 | 4.4 KitKat | try-with-resources, multi-catch |
| Java 8 | API 24 | 7.0 Nougat | 람다, Stream API, Optional |
| Java 11 | API 31 | 12 | HTTP Client, Local-Variable Syntax |
| Java 17 | API 34 | 14 | Records, Sealed Classes (D8) |
Android SDK에는 Java 컴파일러, dex 유틸리티(d8), aapt(Asset Packaging Tool), 에뮬레이터 및 android.* 및 java.* 라이브러리가 포함됩니다. 개발자는 Java로 코드를 작성하고 .class 파일(JVM 바이트코드)로 컴파일한 다음 d8이 이를 .dex 파일(Dalvik Executable)로 변환하고 리소스 및 AndroidManifest.xml과 함께 APK에 패키징합니다.
Gradle은 Android 프로젝트 빌드 시스템입니다. build.gradle(Groovy) 또는 build.gradle.kts(Kotlin DSL) 파일은 종속성, SDK 버전, 플레이버 및 서명을 설명합니다. Android Gradle Plugin(AGP) 버전 8.x는 최신 Java 기능을 이전 버전과 호환되는 코드로 변환하는 디슈가를 자동화합니다. 예를 들어 java.time.LocalDate는 개발자 소스 코드의 변경 없이 API 21+ 호환 코드로 변환됩니다.
// Java로 만든 간단한 Android Activity
package com.example.myapp;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.textView);
tv.setText("Hello from Java!");
}
// 버튼 클릭 처리를 위한 메서드
public void onButtonClick(View view) {
TextView tv = findViewById(R.id.textView);
tv.setText("Button clicked");
}
}MainActivity 클래스는 AppCompatActivity를 확장합니다. 이는 이전 버전과의 호환성을 위한 AndroidX의 기본 클래스입니다. onCreate 메서드는 Activity가 생성될 때 호출되며 setContentView를 통해 레이아웃을 설정합니다. findViewById는 Java 변수를 XML 레이아웃의 요소에 바인딩합니다. onButtonClick 메서드는 XML의 android:onClick 속성을 통해 버튼에 바인딩됩니다.
Java의 Activity 생명 주기는 콜백 시퀀스입니다: onCreate → onStart → onResume(활성), onPause → onStop → onDestroy(종료). 개발자는 리소스 관리를 위해 이러한 메서드를 재정의합니다. onCreate — 초기화, onPause — 데이터 저장, onDestroy — 리소스 해제. Android는 화면 회전, 백그라운드 전환 및 전화 수신 시 이러한 메서드를 호출합니다.
// 상태 관리가 있는 Activity 생명 주기
public class DetailActivity extends AppCompatActivity {
private String itemId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
itemId = getIntent().getStringExtra("ITEM_ID");
loadData(itemId);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("item_id", itemId);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
itemId = savedInstanceState.getString("item_id");
loadData(itemId);
}
private void loadData(String id) {
// 식별자로 데이터 로드
}
}onSaveInstanceState 메서드는 화면 회전 또는 작업 전환 시 Activity 상태를 저장합니다. Bundle에는 키-값 쌍(단순 유형, Parcelable 또는 Serializable 객체)이 포함됩니다. onRestoreInstanceState는 저장된 상태를 복원합니다. 이 패턴은 Android에 매우 중요합니다. 시스템이 메모리 부족 시 Activity를 제거하고 사용자가 돌아올 때 복원할 수 있기 때문입니다.
Java 구문은 C 스타일이며, 강력한 형식 지정, 객체 지향입니다. 각 .java 파일에는 파일 이름과 일치하는 이름의 public 클래스가 하나 포함됩니다. Java는 클래스 외부의 함수를 지원하지 않습니다. 모든 것이 메서드입니다. 진입점은 콘솔 애플리케이션의 경우 정적 메서드 public static void main(String[] args)이거나 모바일의 경우 Android Activity 생명 주기 메서드입니다.
Java는 유형을 기본형(int, double, boolean, char, byte, short, long, float)과 참조형(클래스, 인터페이스, 배열, enum)으로 나눕니다. 기본형은 스택에 저장되고 값으로 전달됩니다. 객체는 힙에 저장되고 참조로 전달됩니다. 오토박싱은 기본형을 자동으로 래퍼 객체(Integer, Double, Boolean)로 변환하지만 추가 객체를 생성합니다. Android에서 이는 UI 성능에 중요합니다.
// Java 데이터 유형 및 구조 예제
public class DataTypes {
// 기본형
int count = 42;
double price = 19.99;
boolean isActive = true;
char grade = 'A';
// 참조형
String name = "Android";
Integer boxed = 100; // autoboxing
List<String> items = new ArrayList<>();
// 제네릭을 사용한 메서드
public <T> T getFirst(List<T> list) {
return list.isEmpty() ? null : list.get(0);
}
// 고정 값 세트를 위한 Enum
public enum Status {
PENDING, LOADING, SUCCESS, ERROR
}
}제네릭 List<T>는 컬렉션에 유형 안전성을 제공합니다. getFirst 메서드는 모든 유형의 요소를 반환하며 구체적인 유형은 호출 지점에서 결정됩니다. Enum Status는 고정된 상수 세트가 있는 클래스로 컴파일되며 각 인스턴스는 싱글톤입니다. Android에서는 메모리 소비로 인해 enum이 덜 자주 사용됩니다. ProGuard/R8은 가능한 경우 enum을 int 상수로 최적화합니다.
Android 개발을 위한 Java와 Kotlin의 비교는 Google이 Kotlin을 우선 언어로 선언한 2019년 이후 주요 주제 중 하나였습니다. Kotlin은 Java와 완전히 호환됩니다. 둘 다 JVM 바이트코드로 컴파일되고, 서로 호출할 수 있으며 동일한 라이브러리를 사용할 수 있습니다. 그러나 코드 작성 방식은 크게 다릅니다.
Kotlin은 Java의 장황함을 제거합니다. data class는 getter, setter, equals, hashCode, toString 및 copy가 포함된 Java 클래스의 50+ 줄을 대체합니다. 널 안전성은 Kotlin의 유형 시스템에 내장되어 있어 NullPointerException이 사실상 불가능합니다. 확장 함수를 사용하면 상속 없이 기존 클래스에 메서드를 추가할 수 있습니다. 스마트 캐스트는 검사 후 자동으로 유형을 변환합니다.
| 측면 | Java | Kotlin |
|---|---|---|
| 널 안전성 | @Nullable / @NonNull 어노테이션 | String? / String — 내장 |
| Data class | Lombok으로 50+ 줄 | data class User(val name: String) |
| 확장 | 정적 유틸리티 클래스 | fun String.isEmail(): Boolean |
| 코루틴 | AsyncTask / ExecutorService | suspend + Structured Concurrency |
| 람다 | FunctionalInterface + ::method | 기본적으로 람다 |
| DI 어노테이션 | @Inject, @Module, @Provides | 유사하지만 코드가 적음 |
성능 면에서 Java와 Kotlin은 동일합니다. 둘 다 JVM 바이트코드로 컴파일되고 Android ART가 동일하게 실행합니다. 차이점은 APK 크기에 있습니다. Kotlin은 kotlin-stdlib 라이브러리에 약 200-400KB를 추가합니다. Java 프로젝트는 추가 런타임이 없어 APK 크기가 더 작지만 Java Stream API 및 Optional을 사용하면 디슈가링으로 인해 APK 크기가 증가할 수 있습니다.
Android용 Java 개발자 도구에는 Android Studio, Gradle, 프로파일러 및 에뮬레이터가 포함됩니다. Android Studio는 IntelliJ IDEA Community를 기반으로 하는 공식 IDE이며 Java 자동 완성 편집기, 코드 분석기(lint), 리팩토링 및 시각적 레이아웃 편집기(Layout Editor)를 갖추고 있습니다.
Android Gradle Plugin(AGP) 버전 8.x는 빌드를 관리합니다. 클래스로의 Java 컴파일, 디슈가링, ProGuard/R8 축소, APK/AAB 패키징을 담당합니다. R8은 ProGuard를 대체하는 업데이트된 디슈가링 및 난독화 도구입니다. 빌드 변형(debug/release + product flavors)은 build.gradle을 통해 구성됩니다. Gradle Daemon은 캐싱을 통해 후속 빌드 속도를 높입니다.
// build.gradle (app) — Java 모듈 구성
android {
compileSdk 34
defaultConfig {
applicationId "com.example.myapp"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
testImplementation 'junit:junit:4.13.2'
}compileOptions 블록은 컴파일을 위한 Java 버전을 지정합니다. Java 17은 AGP 8.x에서 사용할 수 있습니다. 최소 SDK(minSdk)는 애플리케이션이 실행되는 최소 Android 버전을 결정합니다. Target SDK는 애플리케이션이 테스트된 버전입니다. compileSdk와 targetSdk의 차이: compileSdk는 컴파일용 API이고 targetSdk는 런타임 동작입니다.
Android에서 Java의 한계는 Google의 Kotlin 전환으로 이어졌습니다. NullPointerException은 Java Android 애플리케이션에서 가장 흔한 예외입니다. 유형 시스템이 nullable 참조와 non-null 참조를 구분하지 않기 때문입니다. Google은 androidx.annotation 패키지의 @Nullable 및 @NonNull 어노테이션을 권장하지만 컴파일러는 이를 확인하지 않습니다. lint만 확인합니다.
코루틴의 부재는 또 다른 한계입니다. Java에서 비동기 작업은 콜백, AsyncTask(API 30부터 사용 중단), ExecutorService 또는 RxJava가 필요합니다. 이는 콜백 지옥과 화면 회전 시 메모리 누수로 이어집니다. Structured Concurrency가 있는 Kotlin 코루틴은 언어 수준에서 이 문제를 해결합니다. 수동 관리 없이 Activity가 제거될 때 코루틴이 취소됩니다.
Java의 보일러플레이트 코드는 equals/hashCode, Parcelable 구현 및 RecyclerView용 ViewHolder를 수동으로 작성할 때 오류의 원인입니다. Android KTX 라이브러리와 Jetpack Compose는 Kotlin을 지향합니다. 새로운 Android API(Room, Navigation, Compose)는 기본적으로 Kotlin으로 예제를 게시하지만 Java 버전도 존재합니다.
자주 묻는 질문
Java는 Sun Microsystems(1995)의 객체 지향 프로그래밍 언어로 JVM 가상 머신에서 실행됩니다. 2019년까지 Android SDK의 주요 언어였습니다. 모바일 개발에서 레거시 프로젝트, 서버 측(Spring) 및 엔터프라이즈 애플리케이션에 사용됩니다. 강력한 형식 지정, 자동 가비지 수집 및 바이트코드의 크로스 플랫폼이 특징입니다.
Google은 2019년 Kotlin을 Android의 우선 언어로 선언했습니다. 새로운 AndroidX 라이브러리, Jetpack Compose, 아키텍처 권장 사항 및 문서는 기본적으로 Kotlin으로 제공됩니다. Java는 완전히 지원되지만 API는 Kotlin 우선 방식으로 설계됩니다. Java 프로젝트는 더 많은 보일러플레이트 코드가 필요하며 새로운 언어 기능을 먼저 받지 못합니다.
Android는 Java 8을 완전히 지원하고 Java 11은 Android 12+부터 부분적으로, Java 17은 D8 디슈가링을 통해 실험적으로 지원합니다. Android Gradle Plugin(AGP) 8.x는 desugar를 통해 최신 바이트코드를 이전 API와 호환되는 코드로 변환합니다. 개발자는 Java 11+로 작성하고 람다, Optional 및 DateTime API를 사용할 수 있지만 레코드 및 sealed 클래스는 D8을 통해서만 사용할 수 있습니다.
Kotlin은 더 적은 코드가 필요합니다. data class는 Java 보일러플레이트의 수십 줄을 대체하고 널 안전성이 유형에 내장되어 있으며 코루틴이 비동기 처리를 단순화합니다. Java는 초보자에게 더 간단한 구문, 검증된 라이브러리 생태계 및 안정적인 도구를 제공합니다. 런타임 성능은 동일합니다. 둘 다 Android ART가 실행하는 JVM 바이트코드로 컴파일됩니다.
Android Studio는 Java 및 Kotlin을 지원하는 Android 개발용 표준 IDE입니다. 에뮬레이터, 프로파일러(CPU, 메모리, 네트워크), Layout Inspector, APK Analyzer가 포함됩니다. IntelliJ IDEA는 서버 측 Java에 적합합니다. Eclipse ADT는 사용 중단되었으며 Google이 지원하지 않습니다. Android Studio는 내장된 변환기를 통해 Java 코드를 Kotlin으로 마이그레이션하는 도구도 제공합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.