자바 코드
1. 개요
1. 개요
자바는 1995년에 썬 마이크로시스템즈(현재 오라클)에서 공개한 객체 지향 프로그래밍 언어이다. 이 언어는 "한 번 작성하면, 어디서나 실행된다"는 철학을 바탕으로 설계되어, 다양한 플랫폼에서 동일하게 작동하는 것을 주요 특징으로 한다. 이는 자바 소스 코드가 자바 가상 머신 위에서 실행되는 바이트코드로 컴파일되기 때문에 가능하다.
자바는 주로 웹 애플리케이션, 엔터프라이즈 시스템, 데스크톱 애플리케이션 개발에 널리 사용된다. 특히 안드로이드 운영체제의 공식 개발 언어로 채택되면서 모바일 애플리케이션 개발 분야에서도 핵심적인 위치를 차지하고 있다. 강력한 메모리 관리와 풍부한 표준 라이브러리를 제공하여 대규모 소프트웨어 개발 프로젝트에 적합하다.
이 언어는 C++의 문법을 차용하지만, 복잡하고 위험한 기능인 포인터 연산과 명시적 메모리 할당을 제거하여 보다 안전하고 배우기 쉽게 만들었다. 또한 쓰레기 수집 기능을 통해 개발자가 직접 메모리를 관리할 부담을 줄였다. 자바의 이러한 설계 원칙은 웹 개발을 비롯한 다양한 컴퓨팅 환경에서 빠르게 확산되는 데 기여했다.
2. 기본 문법
2. 기본 문법
2.1. 변수와 데이터 타입
2.1. 변수와 데이터 타입
자바에서 변수는 데이터를 저장하기 위한 메모리 공간에 붙인 이름이다. 변수를 사용하려면 반드시 먼저 선언해야 하며, 선언 시 저장할 데이터의 종류를 나타내는 데이터 타입을 지정한다. 자바의 데이터 타입은 크게 기본형과 참조형으로 나뉜다. 기본형은 실제 값을 변수에 직접 저장하는 타입이며, 참조형은 객체의 주소를 저장하는 타입이다.
기본형 데이터 타입은 정수형, 실수형, 문자형, 논리형으로 구분된다. 정수형에는 byte, short, int, long이 있으며, 각각 저장할 수 있는 크기가 다르다. 실수형에는 float과 double이 있고, 문자형은 char, 논리형은 boolean이 있다. 이들은 자바 언어에서 미리 정의되어 있으며, 변수 선언 시 이 키워드를 사용하여 타입을 명시한다. 예를 들어, int age = 25;와 같이 선언과 동시에 초기값을 할당할 수 있다.
참조형 데이터 타입은 클래스, 인터페이스, 배열 등을 포함한다. 참조형 변수는 힙 메모리에 생성된 객체를 가리키는 주소를 값으로 갖는다. 예를 들어, String name = "Java";에서 String은 클래스 타입이며, 변수 name은 문자열 객체 "Java"의 참조를 저장한다. 모든 참조형 변수의 기본값은 null이다.
자바는 정적 타입 언어로, 변수의 타입은 컴파일 시점에 결정되며 한 번 선언된 변수의 타입은 변경할 수 없다. 이는 타입 안정성을 높여 런타임 오류를 줄이는 장점이 있다. 또한 형 변환을 통해 서로 다른 타입 간의 값을 변환할 수 있으나, 데이터 손실이 발생할 수 있는 묵시적 형 변환은 제한적으로 허용된다.
2.2. 연산자
2.2. 연산자
자바에서 연산자는 변수와 값에 대해 다양한 연산을 수행하기 위해 사용된다. 연산자는 피연산자의 개수에 따라 단항, 이항, 삼항 연산자로 구분되며, 수행하는 기능에 따라 산술, 할당, 비교, 논리, 비트, 기타 연산자 등으로 분류할 수 있다.
산술 연산자는 기본적인 수학 연산을 수행한다. 덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/), 나머지(%) 연산자가 있으며, 이들은 대부분의 프로그래밍 언어와 유사하게 동작한다. 특히 + 연산자는 숫자 간의 덧셈 외에도 문자열을 연결하는 결합 연산자로도 사용된다. 할당 연산자(=)는 오른쪽의 값을 왼쪽 변수에 저장하는 기본 연산이며, +=, -=와 같은 복합 할당 연산자를 통해 연산과 할당을 동시에 수행할 수 있다.
비교 연산자와 논리 연산자는 프로그램의 흐름을 제어하는 조건문과 반복문에서 핵심적인 역할을 한다. 비교 연산자(==, !=, >, <, >=, <=)는 두 값을 비교하여 참(true) 또는 거짓(false)의 불리언 결과를 반환한다. 논리 연산자(&&, ||, !)는 이러한 불리언 값에 대한 논리적 AND, OR, NOT 연산을 수행한다. 삼항 연산자(? :)는 조건에 따라 두 값 중 하나를 선택하는 간결한 조건부 할당을 가능하게 한다.
또한 자바는 비트 단위 연산을 위한 비트 연산자(&, |, ^, ~, <<, >>, >>>)를 제공한다. 이 연산자들은 정수 타입의 비트를 직접 조작할 때 사용되며, 특정 플래그를 설정하거나 암호화 알고리즘 등 저수준 프로그래밍에 활용된다. 인스턴스의 타입을 비교하는 instanceof 연산자와 객체의 참조를 비교하는 ==, != 연산자도 중요한 기타 연산자에 속한다.
2.3. 제어문 (조건문, 반복문)
2.3. 제어문 (조건문, 반복문)
자바에서 프로그램의 흐름을 제어하는 구문을 제어문이라고 한다. 제어문은 크게 조건에 따라 다른 코드 블록을 실행하는 조건문과 특정 코드 블록을 반복적으로 실행하는 반복문으로 나뉜다.
가장 기본적인 조건문은 if 문이다. if 키워드 뒤에 괄호 안에 논리식을 작성하고, 그 결과가 참일 경우 뒤따르는 코드 블록이 실행된다. else if와 else를 조합하여 여러 조건을 순차적으로 검사할 수 있다. 또 다른 조건문인 switch 문은 하나의 변수나 표현식의 값을 다양한 케이스와 비교하여 일치하는 경우의 코드를 실행한다. 각 case는 break 문으로 종료되며, 일치하는 것이 없을 때 실행될 default 케이스를 지정할 수 있다.
반복문에는 for 문, while 문, do-while 문이 있다. for 문은 초기화, 조건 검사, 증감식을 한 줄에 명시하여 정해진 횟수만큼 반복하는 데 주로 사용된다. while 문은 괄호 안의 조건이 참인 동안 코드 블록을 반복 실행하며, 선조건 검사를 한다. 이와 달리 do-while 문은 코드 블록을 먼저 한 번 실행한 후 조건을 검사하는 후조건 검사 방식을 취한다. 반복문 내에서는 break 문으로 반복을 즉시 종료하거나, continue 문으로 현재 반복을 건너뛰고 다음 반복으로 넘어갈 수 있다.
이러한 제어문들을 조합하여 복잡한 비즈니스 로직을 구현할 수 있으며, 특히 데이터 구조를 순회하거나 사용자 입력에 따라 다른 동작을 수행하는 프로그램을 작성하는 데 필수적이다.
2.4. 배열
2.4. 배열
배열은 자바에서 동일한 데이터 타입의 여러 값을 하나의 변수에 저장할 수 있도록 하는 자료 구조이다. 배열을 사용하면 인덱스를 통해 각 요소에 빠르게 접근할 수 있으며, 반복문과 함께 사용하여 대량의 데이터를 효율적으로 처리할 수 있다.
배열은 선언 시 데이터 타입과 함께 대괄호([])를 사용하여 정의한다. 배열을 생성할 때는 new 키워드를 사용하거나 중괄호({})를 이용한 리터럴 방식으로 초기화할 수 있다. 예를 들어, int[] numbers = new int[5];는 5개의 정수를 저장할 수 있는 배열을 생성하며, String[] names = {"Kim", "Lee", "Park"};은 문자열 배열을 선언과 동시에 초기값으로 채운다.
배열의 각 요소는 0부터 시작하는 인덱스로 접근한다. 배열의 길이는 length 속성으로 확인할 수 있으며, 이는 배열이 생성될 때 고정된다. 배열의 크기를 동적으로 변경할 수는 없지만, 더 큰 배열을 새로 생성하고 기존 데이터를 복사하는 방식으로 유사한 효과를 낼 수 있다. 다차원 배열, 즉 배열의 배열도 생성이 가능하여 행렬과 같은 구조를 표현하는 데 사용된다.
자바에서는 컬렉션 프레임워크에 포함된 ArrayList, Vector 등의 클래스가 내부적으로 배열을 활용하며, 더 유연한 크기 조정과 다양한 기능을 제공한다. 그러나 기본 배열은 여전히 성능이 중요한 저수준 연산이나 메서드의 매개변수로 고정된 크기의 데이터를 전달할 때 널리 사용된다.
3. 객체 지향 프로그래밍
3. 객체 지향 프로그래밍
3.1. 클래스와 객체
3.1. 클래스와 객체
클래스는 객체 지향 프로그래밍의 핵심 구성 요소로, 객체를 생성하기 위한 설계도 또는 틀에 해당한다. 클래스는 객체가 가지는 속성(필드)과 행동(메서드)을 정의한다. 예를 들어, '자동차'라는 클래스는 색상, 모델명 같은 필드와 주행, 정지 같은 메서드를 포함할 수 있다. 이렇게 정의된 클래스를 기반으로 생성된 실제 실체가 바로 객체이다.
객체는 클래스의 인스턴스라고도 불리며, new 키워드를 사용하여 생성한다. 각 객체는 클래스에 정의된 구조를 가지지만, 자신만의 고유한 상태(필드 값)를 가진다. 예를 들어, '자동차' 클래스로부터 생성된 '내 차' 객체와 '네 차' 객체는 서로 다른 색상과 모델명을 가질 수 있다. 이는 객체가 메모리에 할당된 독립적인 존재임을 의미한다.
클래스는 객체의 초기 상태를 설정하기 위한 특별한 메서드인 생성자를 포함할 수 있다. 생성자는 객체가 생성될 때 호출되며, 주로 필드의 초기값을 할당하는 데 사용된다. 또한, 클래스 내부의 필드와 메서드에 대한 접근을 제어하는 접근 제어자를 통해 캡슐화를 구현한다. 이를 통해 객체의 데이터는 보호되고, 외부와의 상호작용은 정의된 메서드를 통해서만 이루어지도록 할 수 있다.
실제 자바 프로그래밍에서는 수많은 클래스를 정의하고 객체를 생성하여 상호작용시킴으로써 복잡한 프로그램을 구성한다. 라이브러리에 미리 정의된 클래스를 사용하거나, 특정 요구사항을 해결하기 위해 새로운 클래스를 직접 작성하는 것이 일반적인 개발 과정이다.
3.2. 상속
3.2. 상속
상속은 자바의 객체 지향 프로그래밍 핵심 개념 중 하나로, 기존에 존재하는 클래스의 속성과 기능을 새로운 클래스가 물려받아 재사용하고 확장할 수 있게 하는 메커니즘이다. 이는 코드의 재사용성을 높이고, 계층 구조를 형성하여 유지보수를 용이하게 한다. 상속을 통해 새로운 클래스는 기존 클래스의 필드와 메서드를 그대로 사용할 수 있으며, 필요에 따라 새로운 멤버를 추가하거나 기존 메서드를 수정(오버라이딩)할 수 있다.
상속 관계에서 기존의 클래스를 부모 클래스 또는 슈퍼클래스라고 하며, 이를 물려받는 새로운 클래스를 자식 클래스 또는 서브클래스라고 한다. 자바에서는 extends 키워드를 사용하여 상속 관계를 정의한다. 예를 들어, class Student extends Person과 같이 작성하면 Student 클래스가 Person 클래스의 모든 public 및 protected 멤버를 상속받게 된다. 자바는 다중 상속을 지원하지 않으므로, 하나의 클래스는 오직 하나의 부모 클래스만 가질 수 있다.
상속의 주요 장점은 코드 중복을 줄이고, 계층적 분류를 자연스럽게 표현할 수 있다는 점이다. 예를 들어, 동물이라는 일반적인 클래스를 상속받아 포유류, 조류 등의 서브클래스를 만들고, 다시 포유류를 상속받아 개, 고양이 클래스를 정의하는 식으로 계층 구조를 구성할 수 있다. 이때 모든 하위 클래스는 공통된 특성(예: 이름, 나이 필드나 먹다() 메서드)을 상위 클래스에서 한 번만 정의하면 된다.
상속은 다형성과 밀접한 관련이 있다. 부모 클래스 타입의 참조 변수로 자식 클래스의 객체를 참조할 수 있으며, 이는 메서드 오버라이딩과 결합하여 프로그램의 유연성을 크게 향상시킨다. 또한, 모든 클래스의 최상위에는 Object 클래스가 존재하며, 명시적으로 다른 클래스를 상속받지 않는 클래스는 자동으로 Object 클래스를 상속받게 되어 toString(), equals(), hashCode()와 같은 기본 메서드를 사용할 수 있다.
3.3. 다형성
3.3. 다형성
다형성은 객체 지향 프로그래밍의 핵심 개념 중 하나로, 하나의 메시지가 서로 다른 객체에 전달될 때 각 객체의 타입에 따라 서로 다른 동작을 보이는 특성을 의미한다. 즉, 같은 이름의 메서드 호출이 객체의 실제 타입에 따라 다양한 형태로 실행될 수 있다. 이는 코드의 유연성과 확장성을 크게 높여준다.
자바에서 다형성은 주로 상속 관계와 메서드 오버라이딩, 그리고 참조 변수의 타입 변환을 통해 구현된다. 상위 클래스 타입의 참조 변수로 하위 클래스의 객체를 참조할 수 있으며, 이때 오버라이딩된 메서드를 호출하면 참조 변수의 타입이 아닌 실제 객체의 타입에 정의된 메서드가 실행된다. 이는 상위 클래스 참조를 통해 하위 클래스들의 구체적인 동작을 하나의 통일된 방식으로 처리할 수 있게 한다.
다형성을 효과적으로 활용하기 위해서는 인터페이스와 추상 클래스가 중요한 역할을 한다. 인터페이스는 구현해야 할 메서드의 명세를 정의함으로써, 서로 다른 클래스들이 동일한 인터페이스를 구현하면 같은 메서드 이름으로 다른 기능을 제공할 수 있게 한다. 이를 통해 시스템의 결합도를 낮추고, 새로운 기능 추가가 용이해진다.
실제 프로그래밍에서는 컬렉션 프레임워크에서 다양한 데이터 구조를 다루거나, 이벤트 처리 메커니즘에서 서로 다른 리스너 객체를 동일하게 취급할 때 다형성이 빈번히 사용된다. 또한, 디자인 패턴 중 전략 패턴이나 팩토리 메서드 패턴과 같은 많은 패턴들이 다형성 원리에 기반을 두고 설계된다.
3.4. 캡슐화
3.4. 캡슐화
캡슐화는 객체 지향 프로그래밍의 핵심 원칙 중 하나로, 객체의 데이터(필드)와 그 데이터를 처리하는 메서드를 하나의 단위, 즉 클래스 안에 묶는 것을 말한다. 더 나아가서는 객체의 내부 구현 세부 사항을 외부로부터 숨기고, 허용된 메서드를 통해서만 객체의 상태에 접근하도록 제한하는 정보 은닉의 개념을 포함한다. 이는 객체의 무결성을 보호하고, 코드의 유지보수성과 재사용성을 높이는 데 기여한다.
자바에서 캡슐화는 주로 접근 제어자를 통해 구현된다. private 접근 제어자는 필드나 메서드를 선언된 클래스 내부에서만 접근 가능하게 하여 외부의 직접적인 접근을 차단한다. 반면, public 메서드를 통해 이러한 private 필드의 값을 안전하게 읽거나 변경할 수 있도록 한다. 이러한 public 메서드를 게터와 세터라고 부르며, 객체의 상태를 조작하는 통로 역할을 한다.
캡슐화의 주요 이점은 첫째, 데이터 보호이다. 세터 메서드에 유효성 검사 로직을 추가하면 잘못된 데이터가 객체의 상태를 오염시키는 것을 방지할 수 있다. 둘째, 구현의 자유로움이다. 클래스의 내부 구현 방식을 변경하더라도 public으로 공개된 메서드의 인터페이스만 유지한다면, 이 클래스를 사용하는 다른 코드에는 영향을 미치지 않는다. 이는 모듈화와 코드 재사용에 매우 유리하다.
따라서, 자바 코드를 작성할 때는 필드를 private으로 선언하고, 필요한 경우 게터와 세터를 제공하는 것이 좋은 관행으로 여겨진다. 이는 단순히 데이터를 숨기는 것을 넘어서, 객체의 책임을 명확히 하고, 응집도를 높이며, 다른 객체와의 결합도를 낮추는 효과적인 설계 기법이다.
3.5. 추상화
3.5. 추상화
추상화는 복잡한 현실 세계의 개념을 핵심적인 속성과 동작만을 추려내어 단순화된 모델로 표현하는 객체 지향 프로그래밍의 핵심 원칙이다. 자바에서는 클래스와 인터페이스를 통해 추상화를 구현한다. 구체적인 구현 세부 사항을 감추고, 사용자에게 필수적인 기능만을 제공하는 인터페이스를 노출함으로써 코드의 복잡성을 낮추고 유연성을 높인다.
이를 실현하는 주요 도구는 추상 클래스와 인터페이스이다. 추상 클래스는 하나 이상의 추상 메서드를 포함할 수 있는 클래스로, 직접 인스턴스화될 수 없으며 하위 클래스에 의해 구체화되어야 한다. 반면 인터페이스는 모든 메서드가 추상 메서드인 완전한 추상화 계약을 정의하며, 다중 상속을 모방하는 데 사용된다.
추상화의 가장 큰 장점은 모듈화와 유지보수성 향상이다. 예를 들어, 다양한 데이터베이스를 사용하는 시스템에서 데이터 접근 계층을 인터페이스로 추상화하면, 실제 MySQL이나 오라클 데이터베이스 구현을 변경하더라도 상위 비즈니스 로직 코드를 수정할 필요가 없어진다. 이는 의존성 주입과 같은 고급 설계 패턴의 기초가 된다.
따라서 자바에서 추상화는 단순히 코드를 숨기는 것을 넘어, 시스템의 핵심 구조를 설계하고 변화에 유연하게 대응할 수 있는 틀을 제공하는 중요한 개념이다.
4. 핵심 클래스와 API
4. 핵심 클래스와 API
4.1. java.lang 패키지
4.1. java.lang 패키지
java.lang 패키지는 자바 프로그래밍 언어의 핵심 클래스들을 포함하는 패키지이다. 이 패키지는 자바 가상 머신에 의해 자동으로 임포트되므로, 개발자가 명시적으로 import 문을 작성하지 않아도 그 안의 클래스들을 사용할 수 있다. 이는 자바 애플리케이션의 기본적인 동작을 지원하는 필수적인 클래스들을 제공하기 위한 설계이다.
이 패키지에는 가장 기본적인 데이터 타입을 객체로 다루기 위한 래퍼 클래스들이 포함되어 있다. 예를 들어, Integer, Double, Boolean 등의 클래스는 각각 int, double, boolean 같은 기본형 값을 객체로 감싸는 역할을 한다. 또한, 모든 클래스의 최상위 부모 클래스인 Object와 문자열을 처리하는 String, StringBuilder 클래스도 여기에 속한다.
시스템과의 기본적인 상호작용을 담당하는 System 클래스도 java.lang 패키지의 중요한 구성원이다. 이 클래스를 통해 표준 입출력 스트림(표준 입력, 표준 출력, 표준 에러)에 접근하거나, 시스템 속성을 읽고, 가비지 컬렉션을 요청하는 등의 작업을 수행할 수 있다. 스레드 관련 기본 클래스인 Thread와 Runnable 인터페이스도 이 패키지에 정의되어 있다.
또한, java.lang 패키지는 프로그램 실행 중 발생하는 오류와 예외를 처리하기 위한 기본 예외 처리 클래스 계층 구조를 제공한다. Throwable 클래스를 최상위로 하여 Error와 Exception 클래스가 파생되며, RuntimeException과 같은 일반적인 예외 클래스들도 여기에 포함된다. 이 패키지는 자바 플랫폼의 기초를 이루며, 모든 자바 코드 작성의 토대가 된다.
4.2. 컬렉션 프레임워크
4.2. 컬렉션 프레임워크
자바 컬렉션 프레임워크는 다수의 객체를 효율적으로 저장, 관리, 검색하기 위한 통합된 아키텍처와 클래스 및 인터페이스의 집합이다. 이 프레임워크는 자바 플랫폼의 핵심 API 중 하나로, 자바 2 플랫폼부터 본격적으로 도입되었다. 배열과 달리 크기가 동적으로 변할 수 있으며, 다양한 데이터 구조를 표준화된 방식으로 제공함으로써 개발자가 직접 자료 구조를 구현하는 부담을 덜어준다.
컬렉션 프레임워크의 핵심은 java.util 패키지에 포함된 여러 인터페이스와 이를 구현한 클래스들이다. 주요 인터페이스로는 단일 요소의 순차적 나열을 위한 List, 중복을 허용하지 않는 집합을 위한 Set, 키와 값의 쌍으로 데이터를 저장하는 Map이 있다. 각 인터페이스는 특정 목적에 맞는 여러 구현 클래스를 가진다. 예를 들어, List의 대표적인 구현체로는 ArrayList와 LinkedList가 있다.
인터페이스 | 주요 구현 클래스 | 특징 |
|---|---|---|
순서가 있으며, 중복 허용 | ||
순서가 보장되지 않으며, 중복 불허 | ||
키와 값의 쌍으로 저장, 키는 중복 불허 |
이러한 컬렉션들은 제네릭을 사용하여 특정 타입의 객체만 저장하도록 타입 안정성을 보장한다. 또한 이터레이터 패턴을 구현한 Iterator 인터페이스를 통해 컬렉션 내 요소들을 안전하게 순회할 수 있다. 컬렉션 프레임워크는 알고리즘을 구현한 유틸리티 클래스인 Collections도 제공하여 정렬, 검색, 동기화 등의 공통 작업을 지원한다.
4.3. 입출력 (I/O)
4.3. 입출력 (I/O)
자바의 입출력(I/O)은 데이터의 흐름을 다루는 기능으로, 파일 시스템이나 네트워크를 통해 데이터를 읽고 쓰는 데 사용된다. 자바 I/O의 핵심은 스트림이라는 개념으로, 데이터의 흐름을 바이트 단위로 처리하는 바이트 스트림과 문자 단위로 처리하는 문자 스트림으로 크게 구분된다. 바이트 스트림의 기본 클래스는 InputStream과 OutputStream이며, 문자 스트림은 Reader와 Writer 클래스를 기반으로 한다. 이러한 클래스들은 java.io 패키지에 포함되어 있다.
파일 입출력을 위해서는 FileInputStream, FileOutputStream, FileReader, FileWriter와 같은 구체적인 클래스들이 사용된다. 예를 들어, 텍스트 파일을 읽을 때는 FileReader와 BufferedReader를 조합하여 효율적으로 처리하는 것이 일반적이다. 이때 버퍼를 사용하면 여러 바이트나 문자를 한 번에 읽어오기 때문에 시스템 호출 횟수를 줄여 성능을 향상시킬 수 있다.
또한, 자바는 더욱 직관적이고 강력한 파일 및 경로 조작 기능을 제공하는 java.nio.file 패키지(일명 NIO.2)를 도입했다. 이 패키지의 핵심 클래스인 Path, Files, Paths를 사용하면 파일의 속성 확인, 생성, 삭제, 복사, 이동 등 고급 파일 작업을 쉽게 수행할 수 있다. Files 클래스의 정적 메서드들은 기존의 스트림 기반 I/O보다 코드를 간결하게 작성할 수 있게 해준다.
작업 유형 | 주요 클래스/인터페이스 (java.io) | 주요 클래스/인터페이스 (java.nio.file) |
|---|---|---|
바이트 입력 |
|
|
바이트 출력 |
|
|
문자 입력 |
|
|
문자 출력 |
|
|
파일/경로 조작 |
|
|
입출력 작업 중에는 파일이 존재하지 않거나 권한이 없는 경우와 같은 예외 상황이 빈번히 발생할 수 있다. 따라서 자바 I/O 코드를 작성할 때는 반드시 예외 처리를 통해 IOException을 적절히 처리해야 한다. 이는 프로그램의 안정성과 신뢰성을 보장하는 중요한 부분이다.
4.4. 예외 처리
4.4. 예외 처리
자바에서 예외 처리는 프로그램 실행 중 발생할 수 있는 오류나 비정상적인 상황을 관리하는 메커니즘이다. 프로그램의 비정상 종료를 방지하고, 오류 발생 시 적절한 조치를 취할 수 있도록 설계되었다. 자바는 모든 예외를 객체로 처리하며, java.lang.Throwable 클래스를 최상위로 하는 예외 계층 구조를 가지고 있다.
예외 처리는 주로 try, catch, finally, throw, throws 키워드를 사용하여 구현한다. 오류가 발생할 가능성이 있는 코드 블록을 try 블록으로 감싸고, 발생한 예외의 타입에 맞는 catch 블록에서 해당 예외를 처리한다. finally 블록은 예외 발생 여부와 관계없이 반드시 실행되어야 하는 정리 코드를 작성하는 데 사용된다. 개발자가 의도적으로 예외를 발생시킬 때는 throw 문을, 메서드가 특정 예외를 던질 수 있음을 선언할 때는 throws 절을 사용한다.
자바의 예외는 크게 컴파일 시점에 체크되는 예외와 실행 시점에 발생하는 예외로 구분된다. 전자는 Exception 클래스의 하위 클래스 중 RuntimeException을 제외한 대부분으로, 반드시 처리하거나 선언해야 한다. 후자는 RuntimeException 클래스와 그 하위 클래스들(예: NullPointerException, ArrayIndexOutOfBoundsException) 및 Error 클래스의 하위 클래스들로, 명시적인 처리가 강제되지는 않는다. 효과적인 예외 처리는 프로그램의 안정성과 신뢰성을 높이는 데 기여한다.
5. 실행과 컴파일
5. 실행과 컴파일
5.1. JVM과 바이트코드
5.1. JVM과 바이트코드
자바 코드는 자바 가상 머신(JVM) 위에서 실행되도록 설계되었다. 자바 컴파일러는 소스 코드를 바이트코드라는 중간 형태로 변환한다. 이 바이트코드는 .class 파일에 저장되며, 기계어가 아니라 JVM이 이해할 수 있는 명령어 집합이다. 이러한 설계는 자바의 핵심 특징인 플랫폼 독립성을 가능하게 한다.
JVM은 이 바이트코드를 특정 운영체제와 하드웨어에 맞는 실제 기계어로 해석하거나 JIT 컴파일(Just-In-Time Compilation) 방식을 통해 실행 시점에 네이티브 코드로 변환하여 실행한다. 따라서 개발자는 윈도우, macOS, 리눅스 등 다양한 플랫폼에서 동일한 자바 바이트코드를 실행할 수 있다. JVM은 또한 메모리 관리와 가비지 컬렉션을 담당하여 프로그래머의 부담을 줄인다.
바이트코드의 구조는 스택 기반 가상 머신을 위해 설계되었다. 대부분의 명령어는 오퍼랜드 스택에서 값을 꺼내어(pop) 연산을 수행한 후 결과를 다시 스택에 넣는(push) 방식으로 동작한다. 이는 레지스터 기반 아키텍처와 구별되는 특징이다. 바이트코드는 디어셈블러 도구를 사용해 사람이 읽을 수 있는 형태로 다시 변환하여 분석할 수 있다.
JVM과 바이트코드 아키텍처는 자바 생태계의 확장성을 뒷받침한다. 코틀린, 스칼라, 클로저 같은 다른 JVM 언어들도 최종적으로는 자바 바이트코드로 컴파일되어 동일한 JVM 위에서 실행될 수 있다. 이는 다양한 언어로 작성된 라이브러리와 프레임워크가 상호 운용될 수 있는 기반을 마련한다.
5.2. 메인 메서드
5.2. 메인 메서드
자바 애플리케이션의 실행 시작점은 main 메서드이다. 이 메서드는 프로그램이 실행될 때 자바 가상 머신(JVM)에 의해 가장 먼저 호출되는 특별한 메서드이다. 모든 실행 가능한 자바 애플리케이션은 반드시 하나의 main 메서드를 포함해야 한다.
main 메서드는 특정한 형식으로 선언된다. 정확한 서명은 public static void main(String[] args)이다. 여기서 public은 접근 제어자로, JVM이 이 메서드에 접근할 수 있도록 한다. static 키워드는 이 메서드가 클래스의 인스턴스(객체)를 생성하지 않고도 호출될 수 있음을 의미한다. void는 메서드가 값을 반환하지 않음을 나타낸다. 매개변수 String[] args는 명령줄 인터페이스(CLI)를 통해 프로그램 실행 시 전달되는 문자열 인자들을 저장하는 배열이다.
실행 과정에서 JVM은 지정된 클래스 파일을 찾아 main 메서드를 실행한다. 만약 main 메서드가 올바른 형식으로 존재하지 않으면, 프로그램은 실행되지 않고 오류 메시지를 출력한다. 이 메서드 내부에는 프로그램의 주요 로직이 작성되거나, 다른 객체를 생성하고 메서드를 호출하는 코드가 위치하게 된다.
6. 코딩 관례와 스타일
6. 코딩 관례와 스타일
6.1. 네이밍 컨벤션
6.1. 네이밍 컨벤션
자바 코드를 작성할 때는 일관된 네이밍 컨벤션을 따르는 것이 중요하다. 이는 코드의 가독성을 높이고 유지보수를 용이하게 하며, 여러 개발자가 협업하는 프로젝트에서 특히 효과적이다. 자바 커뮤니티에서는 썬 마이크로시스템즈에서 제시한 규칙이 사실상의 표준으로 자리 잡았으며, 이후 오라클에 의해 유지되고 있다.
클래스와 인터페이스의 이름은 파스칼 케이스(PascalCase)를 사용하며, 각 단어의 첫 글자를 대문자로 쓴다. 예를 들어, String, ArrayList, Runnable이 이에 해당한다. 반면 변수와 메서드의 이름은 카멜 케이스(camelCase)를 사용하여 첫 단어는 소문자로 시작하고 이후 각 단어의 첫 글자를 대문자로 쓴다. userName, calculateTotalAmount, getInputStream 등이 그 예시이다.
상수는 모두 대문자로 표기하며, 단어 사이는 밑줄(_)로 연결한다. MAX_PRIORITY, PI, DEFAULT_TIMEOUT과 같은 형태이다. 패키지 이름은 모두 소문자로 작성하는 것이 원칙이며, 일반적으로 회사의 도메인 이름을 역순으로 사용한다. 예를 들어 com.example.project.util과 같은 형식을 따른다. 이러한 일관된 명명 규칙은 자바 소프트웨어 개발 생태계 전반에 걸쳐 코드의 품질을 보장하는 기초가 된다.
6.2. 주석
6.2. 주석
자바 코드에서 주석은 프로그램의 실행에는 전혀 영향을 주지 않으며, 소스 코드에 설명을 추가하기 위해 사용된다. 주석은 코드의 가독성을 높이고, 다른 개발자나 미래의 자신이 코드의 의도나 복잡한 로직을 이해하는 데 도움을 준다. 주석은 크게 한 줄 주석, 여러 줄 주석, 문서화 주석 세 가지 유형으로 나뉜다.
한 줄 주석은 //로 시작하며, 그 줄의 끝까지를 주석으로 처리한다. 이는 짧은 설명이나 변수 옆에 간단한 메모를 달 때 주로 사용된다. 여러 줄 주석은 /*로 시작하여 */로 끝나며, 여러 줄에 걸친 설명이 필요할 때 활용된다. 특히 코드 블록을 일시적으로 비활성화(주석 처리)하는 디버깅 목적으로도 자주 쓰인다.
가장 체계적인 주석은 문서화 주석으로, /**로 시작하여 */로 끝난다. 이 주석은 자바독 도구를 사용하여 API 문서를 자동으로 생성하는 데 사용된다. 클래스, 메서드, 필드 선언 바로 앞에 작성하며, @param, @return, @throws 등의 특별한 태그를 포함해 매개변수, 반환 값, 발생 가능한 예외에 대한 정보를 제공한다.
효과적인 주석 작성은 코딩 관례의 중요한 부분이다. '무엇을' 하는 코드인지 설명하기보다 '왜' 그렇게 작성했는지 설명하는 데 중점을 두어야 한다. 명백한 코드 내용을 그대로 번역한 주석은 피하고, 변경 사항이 생기면 반드시 주석도 함께 업데이트해야 한다. 잘 관리된 주석은 소프트웨어 유지보수 비용을 줄이고 협업 개발의 효율성을 크게 향상시킨다.
7. 여담
7. 여담
자바는 1995년 썬 마이크로시스템즈[7]에 의해 처음 공개된 이후, 웹 애플리케이션 백엔드부터 엔터프라이즈 시스템에 이르기까지 광범위한 분야에서 사용되는 대표적인 객체 지향 프로그래밍 언어이다. 특히 "한 번 작성하면 어디서나 실행된다"는 철학을 바탕으로 한 JVM 덕분에 다양한 운영체제와 플랫폼에서 높은 이식성을 보여주었다.
자바의 영향력은 안드로이드 앱 개발의 공식 언어로 채택되면서 더욱 확대되었다. 수많은 모바일 애플리케이션이 자바와 그 변형인 코틀린으로 개발되고 있으며, 이는 자바 생태계의 활력을 지속시키는 주요 동력이 되고 있다. 또한 대규모 금융 시스템이나 전자상거래 플랫폼과 같은 안정성이 요구되는 서버 측 소프트웨어 개발에서도 여전히 강세를 보이고 있다.
시간이 지나면서 자바는 C++나 C#과 같은 경쟁 언어들과 비교되기도 하며, 때로는 상대적으로 장황한 문법이나 성능에 대한 논쟁의 대상이 되기도 한다. 그러나 지속적인 JDK 업데이트를 통해 모듈화 시스템(JPMS)과 같은 현대적 기능을 도입하고, 가비지 컬렉션 성능을 개선하는 등 진화를 거듭하고 있다. 이처럼 자바는 초기 설계 철학을 유지하면서도 변화하는 프로그래밍 언어 트렌드와 개발자 요구에 적응해 나가고 있다.
