나무모에 미러 (일반/밝은 화면)
최근 수정 시각 : 2025-04-26 14:55:50

Java/문법

파일:상위 문서 아이콘.svg   상위 문서: Java
프로그래밍 언어 문법
{{{#!folding [ 펼치기 · 접기 ]
{{{#!wiki style="margin: 0 -10px -5px; word-break: keep-all"
프로그래밍 언어 문법
C(포인터 · 구조체 · size_t) · C++(클래스 · 이름공간 · 상수 표현식 · 특성) · C# · Java · Python(함수 · 모듈) · Kotlin · MATLAB · SQL · PHP · JavaScript(표준 내장 객체) · Haskell(모나드) ·
마크업 언어 문법
HTML · CSS
개념과 용어
함수(인라인 함수 · 고차 함수 · 콜백 함수 · 람다식) · 리터럴 · 문자열 · 식별자(예약어) · 상속 · 예외 · 조건문 · 반복문 · 비트 연산 · 참조에 의한 호출 · eval · 네임스페이스 · 호이스팅
기타
#! · == · === · deprecated · GOTO · NaN · null · undefined · 배커스-나우르 표기법
}}}}}}
프로그래밍 언어 목록 · 분류 · 문법 · 예제


1. 개요2. 기본
2.1. C와 차이점
3. 메인 메서드4. 제어자
4.1. 접근 제어자4.2. 기타 제어자
5. 메서드6. 클래스
6.1. 유틸리티 클래스6.2. 데이터 클래스
6.2.1. Getter와 Setter
6.3. 상속6.4. Object
7. 변수 및 자료형
7.1. 변수 선언7.2. 원시 자료형
7.2.1. 정수7.2.2. 실수7.2.3. 문자
7.3. 참조 자료형
7.3.1. 래퍼 클래스
7.4. 배열
7.4.1. 컬렉션
8. 제네릭
8.1. 바운딩8.2. 클래스8.3. 메서드
8.3.1. 와일드카드
9. 예외
9.1. try
10. 인터페이스
10.1. 인터페이스 정의하기10.2. 인터페이스 구현하기10.3. 함수형 인터페이스

1. 개요

프로그래밍 언어 Java의 문법을 정리한 문서이다.

==# 편집 지침 #==
소스 코드로 예시를 들 때 아래와 같이 문법을 활용하여 소스 코드를 써 주시기 바랍니다.
\#!syntax java (소스 코드)

예시 코드는 아래와 같습니다.
#!syntax java
public class Main {
	public static void main(String[] args) {
		System.out.println("Hello, World!");
    }
}

2. 기본

기본적으로 자바의 코드의 구조는 다음과 같다.

[package 이름 명시 (필수는 아님)]
[import할 패키지 명시]
[클래스 구현]

클래스 중 public 접근 제어자가 붙은 주요 클래스는 *.java 파일과 동일한 이름으로 정해야 하며, 2개 이상 선언할 수 없다.[1] 그 외의 클래스는 상관없다.

2.1. C와 차이점

기본적인 문법들은 C와 거의 동일하므로 C(프로그래밍 언어)/문법을 참고. 여기서는 차이점을 서술한다.
#!syntax java
String[] strArray = { "hello", "world", "namuwiki" }

for (int i = 0; i < strArray.length; i++) {
  System.out.println(strArray[i]);
}

for (String s : strArray) {
  System.out.println(s);
}

3. 메인 메서드

자바 프로그램을 실행하려면 메인 메서드가 필요하다. top-level에 클래스만 선언할 수 있는 자바 특성 상 메인 메서드도 어떤 클래스에 속에 있어야 하며, 그 클래스를 메인 클래스라 한다.
#!syntax java
public class Main {
	public static void main(String[] args) {
		System.out.println("Hello, World!");
    }
}

자바로 콘솔에 뭔가를 띄우려면 System.out.println() 메서드를 사용한다.

4. 제어자

클래스, 메서드, 변수를 선언할 때 특성을 지정하는 키워드로 12개의 제어자가 있다.

4.1. 접근 제어자

생략 포함, 4개의 제어자가 있다. 생략은 default로 부른다.
해당 요소를 어디까지 접근할 수 있는지를 지정한다.

4.2. 기타 제어자

사용 여부에 따라 특성이 지정된다.

5. 메서드

자바에서 모든 함수는 클래스에 속해 있으므로 메서드라고 부른다.

수학에서는 수를 넣으면 그 수에 무언갈 더하거나 빼는 등의 연산을 거친 결과를 밷는 것을 함수라고 한다.
자바에서 함수(메서드)는 코드상에서 메서드를 실행(호출)하면 내부의 코드들이 실행되는 코드 묶음이다. 여기서 수학처럼 호출할 때 데이터를 넘길 수 있고, 메서드가 종료될 때 데이터가 반환될 수 있다.
메서드 받은 데이터를 매개변수라고 하고, 매개변수로 넘기는 값을 인자라고 한다.

메서드를 선언하려면 다음과 같이 작성한다.
#!syntax java
// 반환 타입과 메서드이름을 쓰고 뒤에 소괄호를 붙인다.
void name() { // void는 반환 값이 없음을 의미한다.
    // 메서드 내용
}


이름 앞에 제어자를 쓰거나, 매개변수를 지정할 수 있다.
#!syntax java
public void myMethod(int i) {
    // 메서드 내용
}


메서드를 종료하고 값을 반환하려면 return키워드를 사용한다. void 메서드는 생략할 수 있으며, 다른 메서드는 반드시 반환해야 한다.
#!syntax java
// 두 정수를 받고 더한 결과를 반환하는 메서드
public int sum(int a, int b) {
    return a + b;
}

받은 문자열이 비어있지 않을 때 출력하는 메서드
public void println(String text) {
    if (text == null || text.equals("")) return; // return은 값 반환 뿐만 아니라 메서드 종료도 하므로 text가 비어있을 경우 아래코드가 실행되기 전에 메서드가 종료된다.
    System.out.println(text);
    // return; 이 생략되었다.
}


메서드 안에서 변수를 선언할 경우, final을 제외한 다른 제어자를 쓸 수 없고 함수가 종료되면 변수는 소멸한다.
#!syntax java
public double getPI() {
    double pi = 3.14
    return pi; // 실제로는 return 3.14; 만으로 쓸 수 있다.
}

6. 클래스

클래스는 용도에 따라 크게 2가지로 나뉠 수 있다.

6.1. 유틸리티 클래스

정적 메서드 및 변수를 모아둔 클래스다. 예시로 java.lang.Math가 있다.

이 때 클래스의 역할은 메서드와 변수를 담아두는 상자에 비유할 수 있다.

(클래스명).(메서드명)으로 메서드를 호출하고, (클래스명).(변수명)으로 변수를 가져올 수 있다.

예시로 도형의 넓이를 계산해주는 클래스는 다음과 같다.
#!syntax java
// Utils.java
public class Utils {
	public static int getAreaOfSquare(int width, int height) {
		return width*height;
	}

    public static double getAreaOfTriangle(int base, int height) {
        return base*height/2.0;
    }

    public static double getAreaOfCycle(int radius) {
        return Math.pow(radius, 2)*Math.PI;
    }
}

6.2. 데이터 클래스

대부분이 비정적 메서드 및 변수로 이루어져 객체를 만들고 활용하는 클래스다. 예시로 java.lang.String, java.util.ArrayList가 있다.

String, ArrayList 클래스를 활용한 용어 정리[7]

데이터 클래스를 쉽게 이해하자면 커스텀 데이터 타입이다. 예시로 "사람"이라는 데이터 타입을 만들면 다음과 같다.
#!syntax java
// Person.java
public class Person {
    // 상술된 유틸리티 클래스처럼 정적 변수를 사용할 수 있다. 모든 객체가 공유하는 필드라고 생각하면 된다.
    // 추가로 final 키워드를 붙여 읽기 전용, 즉 상수로 선언했다. 보통 상수는 대문자로 표기하며 띄어쓰기는 _로 표현한다.
    private static final String SPECIES = "Homo sapiens";

    // 사람의 이름. 각 객체별로 다른 필드를 가져 공유되지 않는다.
    // 이 경우 final을 붙여도 되며, 생성자에서 한번 초기화하고 읽기 전용이 된다. 객체마다 값이 다를 수 있으므로 상수로 부르지 않는다.
    private String name;
  
    // 생성자. 객체를 생성할때 new 키워드와 함께 사용된다.
    public Person(String name) {
        // this는 객체 자기 자신을 가리킨다.
        this.name = name;
    }

    // 비정적 메서드. name 필드의 값을 리턴한다.
    public String getName() {
        return name; // this.name과 동일
    }

    // 마찬가지로 정적 메서드를 사용할 수 있다. 객체에 대하여 호출하는게 아니기에 비정적 변수 및 메서드를 직접 활용할 수 없다.(어떤 인스턴스의 필드를 가리키는지 모르기 때문.)
    public static String getSpecies() {
        return SPECIES;
    }
}

// Main.java
public class Main {
    public static void main(String[] args) {
        // Person 타입의 myPerson 변수를 만들고, new Person("John")이 만드는 인스턴스를 값으로 저장한다.
        Person myPerson = new Person("John"); // 새로운 객체를 생성하려면 new 키워드를 사용한다.
        System.out.println(myPerson.getName()); // John
        System.out.println(Person.getSpecies()); // Homo sapiens
    }
}

이해를 위한 다른 비유도 있다. 클래스를 붕어빵 틀, 인스턴스를 만들어진 붕어빵, 필드를 반죽 및 속으로 비유하고, 객체는 붕어빵을 가리키는 용어로 설정하여 이해할 수 있다.


단순히 데이터 타입 목적 외에도 정말 많은 목적이 있다. 무언갈 관리하는 클래스, 다른 앱과 통신하는 클래스 등이 있다.

6.2.1. Getter와 Setter

private 필드를 가져오는 메서드를 Getter, 설정하는 메서드를 Setter라고 한다.
#!syntax java
// 자동차 클래스
public class Car {
    private int speed = 0; // 속도
    private int gear = 0; // 기어. 0부터 차례대로 P, R, N, D를 뜻한다.
    private final List<Object> trunk = new ArrayList<>(); // 트렁크
    private String licensePlate = "12가 3456"; // 번호판

    public int getSpeed() {
        return speed;
    }
    public void setSpeed(int speed) {
        if (speed < 0) throw new IllegalArgumentException("속력은 음수가 될 수 없음"); // 잘못된 대입 방지
        this.speed = speed;
    }

    public String getGear() { // 알아보기 힘든 int를 문자열로 바꿔서 반환
        return switch (gear) {
            case 0 -> "P";
            case 1 -> "R";
            case 2 -> "N";
            case 3 -> "D";
            default -> throw new RuntimeException();
        };
    }
    public void setGear(String gear) {
        this.gear = switch (gear) {
            case "P" -> 0;
            case "R" -> 1;
            case "N" -> 2;
            case "D" -> 3;
            default -> throw new IllegalArgumentException("잘못된 변속");
        };
    }

    public List<Object> getTrunk() {
        return Collections.unmodifiableList(trunk); // List를 수정 불가로 바꿔서 반환
    }
    public void addObjectInTrunk(Object object) {
        if (object instanceof Person) throw new IllegalArgumentException("잘못된 물건"); // 사람을 트렁크에 넣을 수 없다.
        trunk.add(object);
    }
    public void removeObjectInTrunk(Object object) {
        if (!trunk.contains(object)) throw new IllegalArgumentException("존재하지 않는 물건"); // 없는 물건을 꺼낼 수 없다.
        trunk.remove(object);
    }

    public String getLicensePlate() {
        return licensePlate;
    }
    protected void setLicensePlate(String licensePlate) { // 아무나 번호판을 변경할 수 없다.
        this.licensePlate = licensePlate;
    }
}

6.3. 상속

상속을 하려면 extends 키워드를 사용하면 된다. 예제로 살펴보자. 상속이라는 개념이 다소 이해가 어려울 수 있는데 비슷한 필드, 메서드를 가진 자식 클래스들의 공통적인 부분을 부모 클래스로 만들고 상속하여 한번의 선언으로 여러 클래스에서 활용하는 기능이다.
#!syntax java
// Student.java
public class Student extends Person {
    private double gpa;

    public Student(String name, double gpa) {
        super(name); // 부모 클래스 Person의 생성자를 호출
        this.gpa = gpa;
    }
    public double getGPA() {
        return gpa;
    }
}
// Main.java
public class Main {
    public static void main(String[] args) {
        Student joe = new Student("Joe", 4.0);
        // Student는 Person이기도 하므로 Person의 getName() 메서드를 사용할 수 있다.
        System.out.println(joe.getName()); // Joe.
        System.out.println(joe.getGPA()); // 4.0
    }
}

자녀 클래스 내에서 상위 클래스의 메서드를 사용하고 싶다면 super 키워드를 사용하자. 참고로 이 예제에서 name을 사용하려고 하면 에러가 뜰 것이다. Person 클래스에서 name은 private 접근자를 가지는데, private 접근자를 가진 변수는 본 클래스 내에서만 직접 가져올 수 있다. Person 클래스에서 private String name;이 아니라 protected String name;을 쓰면 Student 클래스에서도 name을 자유롭게 사용할 수 있다.

6.4. Object

최상위 클래스인 Object 클래스는 모든 클래스의 부모로 모든 클래스는 Object 클래스를 상속받으며, 사용자가 직접 만든 클래스 또한 Object 클래스를 상속받는다.

Object 클래스의 대표적인 메서드

7. 변수 및 자료형

7.1. 변수 선언

#!syntax java
int namu;
namu라는 변수를 선언한다.

#!syntax java
namu = 3;
같이 namu의 값을 대입 수 있다.

#!syntax java
int namu = 3;
선언과 동시에 대입할 수도 있다.

#!syntax java
var namu = 3;
10 버전부터는 특정 자료형을 명시하지 않고 var로 대체할 수도 있다. 이 경우 컴파일러가 자동으로 자료형을 추론한다.

#!syntax java
System.out.println(namu);
변수의 값을 참조할 수도 있다.

#!syntax java
namu = namuwiki;
변수의 값을 이렇게 복사할 수도 있다.[11]

다음은 Java에서 기본적으로 제공하는 자료형이다.

7.2. 원시 자료형

primitive type. 자바에선 8개의 원시 자료형(혹은 기본 자료형)이 있다.
원시 자료형 이름은 소문자로 시작한다. 대문자로 시작한다면 원시 자료형이 아닌 참조 자료형이다. 대표적인 예가 문자열인 String.
원시 자료형은 null을 허용하지 않고, 실제 값을 저장한다.

다음은 두 정수의 합을 반환하는 메서드이다.
#!syntax java
public static int sum(int a, int b) {
    return a + b;
}
public static void main(String[] args) {
    int n1 = 10;
    int n2 = 12;
    System.out.println(sum(n1, n2)); // 22
}

7.2.1. 정수

4가지 타입이 있으며 기본인 int가 주로 사용된다.
var 키워드를 이용한 타입추론 시 int가 우선으로 적용된다.
long 타입 정수는 뒤에 l 또는 L을 붙여야 한다.
#!syntax java
long namu = 1000000000000L;

7.2.2. 실수

2가지 타입이 있으며 기본은 double이다.
float 타입 실수는 뒤에 f 또는 F를 붙여야 한다.
부동 소수점 방식으로 저장해서 정확한 연산이 불가능하다.[13]
절댓값이 최솟값이 있다.

7.2.3. 문자

char은 유니코드로 0 ~ 65535까지 저장이 가능하다. 예를 들어
#!syntax java
char namu = 'A';

면 namu는 65가 된다.A의 유니코드가 65기 때문이다.

7.3. 참조 자료형

원시 자료형 아닌 자료형은 참조 자료형이라고 하는데, 쉽게 말하면 데이터 클래스다. 문자열 자료형인 String이 대표적이다.

원시 자료형과는 다르게 비교 연산자의 작동 방식이 다른데, 값이 아닌 주소로 비교하는 방식을 사용한다.[14]

7.3.1. 래퍼 클래스

원시 자료형을 참조 자료형으로 변환한 클래스를 래퍼 클래스(Wrapper Class)라고 한다. 래퍼 클래스는 원시 자료형과 관련 내장 함수가 존재한다. Java 5 이상이면 동일한 데이터 형식을 공유하는 원시 자료형과 래퍼 클래스 사이에는 자동적으로 박싱과 언박싱을 할 수 있다.
원시 자료형 래퍼 클래스 데이터 형식
boolean Boolean 불 대수(true 또는 false)
byte Byte 1바이트 정수
short Short 2바이트 정수
int Integer 4바이트 정수
long Long 8바이트 정수
float Float 4바이트 실수
double Double 8바이트 실수
char Character[15] 문자

Java 4 이하는 수동으로 박싱과 언박싱을 해야 한다.
#!syntax java
int a = 2;
Integer b = new Integer(a);
int c = b.intValue();


원시 자료형과 다르게 null을 쓸 수 있고, 제네릭의 타입 매개변수로 쓰일 수 있지만, 연산속도가 느리다.

7.4. 배열

같은 타입의 데이터를 나열하고, 인덱스(index)를 부여한 자료 구조.

#!syntax java
int[] intArray;
String strArray[];
타입이나 변수에 '[]'를 추가하여 배열을 선언한다.

#!syntax java
int[] intArray = new int[3];
String[] strArray = {"hello", "world", "example"};
배열을 초기화할 때는 배열의 크기만 지정하거나 실제 배열의 내용을 지정한다. 크기만 지정할 경우, 모든 인덱스는 기본값이 저장된다.[16]

#!syntax java
System.out.println(strArray[0]); // 'hello'를 출력
System.out.println(strArray[1]); // 'world'를 출력
System.out.println(strArray[2]); // 'example'을 출력
변수 앞에 []를 붙이고 대괄호 안에 인덱스를 넣으면, 해당 인덱스에 맞는 값을 가져온다. 인덱스는 0부터 시작한다.

#!syntax java
intArray[0] = 1;
intArray[1] = 3;
intArray[2] = -10;
같은 방법으로 값을 대입할 수 있다.

#!syntax java
System.out.println(intArray[3]);
strArray[-1] = "text";
크기를 벗어나거나 음수 인덱스를 이용해 접근할 경우 예외를 발생시킨다.

7.4.1. 컬렉션

파일:자바 컬렉션.jpg
위 이미지는 추상 클래스를 생략한 모습이다.

자바에서는 컬렉션을 통해 여러 개의 데이터를 저장할 수 있는 자료 구조를 제공한다. 배열과 다르게 크기가 바뀔 수 있다.

컬렉션은 특정 타입의 데이터만 저장하기 위해 제네릭을 지원한다.

Iterable은 for-each문에 사용할 수 있도록 하는 인터페이스다.
컬렉션 자료 구조에 대한 정렬이나 필터링에 필요한 함수는 Collections 클래스 (java.util.Collections)에서 제공하고 있다.

8. 제네릭

타입을 미리 정해두지 않고 사용할 때 정하는 방식. 사용할 때 참조 자료형[18]을 매개변수로 받듯이 사용돼서 타입 매개변수라고 부른다. 타입 매개변수는 <>안에 넣어야 하며, 여러 개의 타입 매개변수를 사용하려면 사이에 콤마를 넣어주면 된다.
타입 매개변수의 이름은 암묵적인 규칙이 있다.

8.1. 바운딩

부등호 괄호[19] 안에 'extends' 또는 'super' 키워드를 사용하는 것. 이럼으로써 자료형에 약간 제약을 걸 수 있다. 예를 들어서 <T extends Person>는 Person 클래스 또는 Person을 상속하는 클래스를 뜻한다. 이때 상속은 직접적인 상속이 아니어도 된다. 예를 들어서 Person ← Student ← HighSchooler 이런 식으로 상속 관계가 있다면 HighSchooler도 T extends Person의 조건을 만족한다. 하지만 String 같은 자료형은 만족하지 못한다.

'super'는 그와 반대다. <? super Student>라 하면 Student 클래스 또는 그의 부모/조상 클래스들을 의미한다. super는 오직 와일드카드만이 쓸 수 있다.

0개 이상의 클래스와 한 개 이상의 인터페이스를 동시에 구현·상속하는 자료형을 받을 때 구분자로 '&'를 쓸 수 있다. 이때는 클래스가 맨 앞으로 가야 한다.

예로 리스트에 담긴 숫자의(부동 소수점) 합을 구하는 메서드를 보자.
#!syntax java
// 일반 자료형
public static <T extends Number> double sum1(List<T> list) {
  double sum = 0;
  for (T num: list) {
    sum += num.doubleValue();
  }
  return sum;
}
// 와일드카드
public static double sum2(List<? extends Number> list) {
  double sum = 0;
  for (Number num: list) {
    sum += num.doubleValue();
  }
  return sum;
}


바운딩 없이 메소드를 작성하였다면 컴파일 오류가 발생한다. 바운딩을 안 하게 될 시 T는 Object 클래스를 상속하는 클래스로 취급되어 Object 클래스에 정의된 메소드밖에 호출을 하지 못하는데, doubleValue 메소드는 Number 래퍼 클래스를 상속하는 유도 클래스의 메소드이므로, Object 클래스의 인스턴스에서 doubleValue 메소드를 호출하는 꼴이 되어서 컴파일 오류가 발생하는 것이다.

8.2. 클래스

클래스 이름 뒤에 타입 매개변수를 쓰면 된다.
#!syntax java
// Wrapper.java
public class Wrapper<T> {
  private T content;

  public Wrapper(T content) {
    this.content = content;
  }
  
  public T getContent() {
    return this.content;
  }
}
// ... 메인 메서드 안
Wrapper<String> myWrapper1 = new Wrapper<>("Namu Wiki"); // new Wrapper뒤에 <>를 붙여야한다.
Wrapper<Integer> myWrapper2 = new Wrapper<Integer>(44); // java 6 까지는 타입까지 표기해야 한다.
System.out.println(myWrapper1.getContent()); // Namu Wiki
System.out.println(myWrapper2.getContent()); // 44

8.3. 메서드

일반적인 클래스에서도 제네릭을 이용한 메서드를 사용할 수 있다. 제네릭을 제한하는게 아니라면 Object의 존재 때문에 자주 사용되지 않으며, 이 제한 역시 하술된 와일드카드가 더 많이 쓰인다.
#!syntax java
public static <T extends Exception> void throwRuntimeException(T e) {
  throw RuntimeException(e);
}
public static void main(String[] args) {
  throwRuntimeException(new IOException()); // IOException은 Exception의 자식 클래스이므로 인자로 사용 가능하다.
}

8.3.1. 와일드카드

아래의 두 메서드는 똑같은 일을 하는데, 전자는 위에서 소개했던 방식이고, 후자는 와일드카드를 사용한 방식이다.
#!syntax java
public static <T> int getLength1(List<T> myList) {
  return myList.size();
}
public static int getLength2(List<?> myList) {
  return myList.size();
}

보다시피 와일드카드가 읽기 약간 더 편하다. 따라서 가능하면 와일드카드를 사용하는 게 좋다. 물론 그냥 일반 자료형을 사용해야 할 때도 많다.

9. 예외

예외란 프로그램의 오류라고 생각하면 된다. 보통 0으로 나누거나, 배열을 벗어난 인덱스에 접근하거나 할 때 발생한다.
예외를 발생시키는 것을 예외를 던진다고 한다.

파일:자바 throwable.png
checked 예외는 catch 블록으로 처리하거나 throws 키워드로 넘겨야하며, unchecked 예외는 예외처리를 강제하지 않는다.

예외를 던지려면 throw 키워드를 사용한다.
#!syntax java
public void method() {
  RuntimeException e = new RuntimeException();
  throw e;
} 


throws 키워드를 사용하여 강제 예외 처리를 상위 메서드로 넘길 수 있다.
#!syntax java
public void method() thorws Exception {
  throw new Exception();
}

9.1. try

예외 처리를 위한 문법. try 단독으로 사용할 수 없고 catch나 finally 블록과 함께 사용해야 한다.

try-finally: 예외처리 목적보다는 finally의 무조건 실행 특징을 활용하는 문법이다.
#!syntax java
try {
  System.out.println("Hello, World!");
} finally {
  System.out.println("이 문자열은 반드시 출력된다.");
}


try-catch: 가장 일반적인 용법. 여러개의 catch를 쓸 수 있다.
#!syntax java
String str = "1234a";
try {
  int i = Integer.parseInt(str); // 문자열을 정수로 변환한다. 이 경우, a 때문에 NumberFormatException을 발생시킨다.
} catch(NumberFormatException _) {
  System.err.println("잘못된 문자열"); // 에러 메시지를 출력한다.
} catch (Throwable e) {
  e.printStackTrace(); // 오류 포함 모든 예외를 잡는다. 상술했듯 오류는 처리를 안하기에 잘 사용되지 않는다.
} catch (Error e) {
  e.printStackTrace(); // 이 catch 블록은 모든 오류를 잡지만, 위의 Throwable을 잡는 catch 블록 때문에 실제로는 작동하지 않는다.
}


try-catch-finally: Java 6까지, Closeable을 구현한 객체를 다룰 때 사용되던 용법.
#!syntax java
BufferedReader reader = null;
try {
  reader = new BufferedReader(new FileReader("example.txt")); // example.txt 파일을 가져온다.
  String line;
  while ((line = reader.readLine()) != null) {
    System.out.println(line); // 파일의 내용을 한줄 씩 출력한다.
  }
} catch (Exception e) {
  System.err.println("파일 읽기 실패: " + e.getMessage()); // 모든 예외를 잡는 catch 블록이다. 예외를 잡은 후, 예외 메시지를 출력한다.
} finally {
  if (reader != null) {
    try {
      reader.close(); // 무조건 실행되는 finally 블록의 특징을 활용하여 자원을 닫는다.
    } catch (IOException e) {
      System.err.println("파일 닫기 실패: " + e.getMessage());
    }
  }
}


try-with-resources: Java 7 부터, AutoCloseable[23]을 구현한 객체를 다룰 때 사용되는 문법. 마지막에 자동으로 close() 메서드를 호출한다. 위의 try-catch-finally의 코드를 try-with-resources로 바꾼 모습.
#!syntax java
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) { // 여러 객체를 선언하려면, 세미콜론(;)을 사용하여 구분한다.
  String line;
  while ((line = reader.readLine()) != null) {
    System.out.println(line);
  }
} catch (Exception e) {
  System.err.println("파일 읽기 실패: " + e.getMessage());
} // 이후 자동으로 reader.close(); 를 실행한다.

10. 인터페이스

인터페이스(Interface)는 일부 다른 언어에서는 프로토콜(Protocol)이라고도 한다. 상속과 다르면서도 미묘하게 비슷한 개념이다. 상속이 어느 클래스의 속성과 메서드를 '상속'한다면, 인터페이스는 어떤 클래스가 '구현'해야 한다.

10.1. 인터페이스 정의하기

'interface' 키워드를 사용한다. 인터페이스를 만들 때는 주로 추상 메서드만을 사용한다. 추상 메서드란 이 인터페이스/클래스에서 정의하지 않고, 구현/상속한 클래스에서 정의하는 것을 말한다. 클래스에서는 추상 메서드를 만들려면 abstract 키워드를 붙여야 하지만, 인터페이스에서는 abstract가 디폴트다. Java 8부터는 비추상 메서드를 'default' 키워드를 사용해 정의할 수 있다.
#!syntax java
// Incrementable.java
public interface Incrementable {
    String interfaceName = "Incrementable";
    public void increment();
    public default void printMessage() {
        System.out.println("디폴트 메서드");
    }
}

인터페이스 내의 변수 (예제의 "interfaceName")는 디폴트로 static final이다. 어느 한 객체에 붙어있지도 않고 다른 변수를 대입할 수도 없다는 뜻이다.

10.2. 인터페이스 구현하기

'implements' 키워드를 사용한다.
#!syntax java
public class Person implements Incrementable {
  /* 생략 */
  // Incrementable의 모든 추상 메서드를 정의
  public void increment() {
    age++;
  }
}

이제 Person은 Incrementable 인터페이스를 구현했다. 이제 다음과 같은 메서드에 Person 객체를 보낼 수 있다.
#!syntax java
public static void example(Incrementable implementation) {
  implementation.increment();
  implementation.printMessage();
}

단 한 개의 클래스를 상속할 수 있는 것과는 달리 하나의 클래스가 여러 인터페이스를 구현할 수 있다. 구현과 상속을 동시에 할 수도 있다.
#!syntax java
public class SubClass extends SuperClass implements IntOne, IntTwo, IntThree {
// ...
}

10.3. 함수형 인터페이스

Java 8에 추가된 개념. 단 하나의 추상 메서드를 가지는 인터페이스를 말한다. 람다식을 지원하는 Java 8부터는 매우 특별한 취급을 받는다.


[1] Main.java 파일에서는 주요 클래스의 이름이 Main이어야 한다.[2] 대표적으로 컬렉션[3] 객체의 경우, 객체의 필드는 수정할 수 있다.[4] 이하 "유틸리티 클래스'로 설명[5] 이하 "데이터 클래스"로 설명[6] 일반적으로 클래스를 배운다고 하면 이 방법을 배운다.[7] 다만, String은 조금 특수한 경우라서 자세히 따지면 다르니 주의한다.[8] 엄밀히 말하면 조금 다르다.[9] 나중에 getter와 setter를 도입하면 그동안 필드가 사용된 코드를 모두 손봐야 한다.[10] 메서드 이름 규칙에 의해 변수는 대문자로 시작한다.[11] 원시 자료형이 아닌 참조 자료형의 경우 객체의 참조가 복사된다. 원본 객체와 대입된 객체의 해시값을 출력해 보면 서로 같음을 알 수 있다. Java를 처음 학습하는 초보들이 흔하게 저지를 수 있는 실수이다.[12] 1구골은 10^100 이다.[13] 0.1 + 0.2 == 0.3이 false를 나타낸다.[14] 문자열 비교 시 A == B가 아닌 A.equals(B)를 사용하는 이유이기도 하다.[15] String 클래스와 다르다.[16] 참조 자료형은 null, boolean은 false, 나머지(int, char, long 등)는 0[17] 실제로 HashSet은 HashMap에서 값을 의미없는걸로 사용해서 작동한다.[18] 원시 자료형 사용 불가.[19] < >[20] 설정한 예외와 그 예외를 상속하는 예외를 모두 잡는다. 예를 들어 Exception으로 설정하면 모든 예외를 잡는다.[21] 프로그램을 종료시키는 메서드.[22] Java 7부터는 AutoCloseable이 추가되어 try-with-resources가 대신 사용된다.[23] Closeable도 AutoCloseable의 자식 클래스이므로 사용 가능하다.