최근 수정 시각 : 2024-10-02 08:06:06

Java/문법

파일:상위 문서 아이콘.svg   상위 문서: Java
프로그래밍 언어 문법
{{{#!wiki style="margin: -16px -11px; word-break: keep-all" <colbgcolor=#0095c7><colcolor=#fff,#000> 언어 문법 C( 포인터 · 구조체) · C++( 자료형 · 클래스 · 이름공간 · 상수 표현식 · 특성) · C# · Java · Python · Kotlin · MATLAB · SQL · PHP · JavaScript · Haskell
마크업 문법 HTML · CSS
개념과 용어 함수 · 인라인 함수 · 고차 함수 · 람다식 · 리터럴 · size_t · 상속 · 예외 · 조건문 · 참조에 의한 호출 · eval
기타 == · === · deprecated · NaN · null · undefined · 모나드 · 배커스-나우르 표기법
프로그래밍 언어 예제 · 목록 · 분류 }}}


1. 개요2. 기본3. 편집 지침4. 메인 클래스
4.1. 메인 메서드4.2. 비메인 메서드
5. 클래스
5.1. 클래스 정의하기5.2. 상속
6. 변수 및 자료형
6.1. 변수 선언6.2. 원시 자료형
6.2.1. byte, short, char6.2.2. int, long
6.3. 참조 자료형6.4. 래퍼 클래스6.5. 컬렉션6.6. 일반화 변수 형식
7. 일반화 프로그래밍
7.1. 일반 메서드7.2. 와일드카드7.3. 바운딩7.4. 일반 클래스
8. 인터페이스
8.1. 인터페이스 정의하기8.2. 인터페이스 구현하기8.3. 함수형 인터페이스

1. 개요

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

2. 기본

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

[package 이름 명시 (필수는 아님)]
[import할 패키지 명시]
[주요 클래스 구현]
[부차적 클래스 및 함수 구현]

클래스 선언 시에 *.java 파일의 이름과 동일한 이름으로 클래스 이름을 정해야 한다. Main.java 파일에서는 주요 클래스의 이름이 Main이어야 한다. 부차적 클래스의 이름을 정할 때에는 상관없다.

하나의 *.java 파일 안에는 하나의 주요 클래스만 정의되어야 한다.

기본적인 문법들은 C와 거의 동일하다. 차이점을 찾으라면, 자바는 객체 지향 언어고 C는 절차 지향 언어인데, 객체 지향 언어에 속한 프로그래밍 언어들은 사물, 사람, 동물과 같이 주격에 속한 집단들의 특성을 서로 연관(상속)시킨다. 절차 지향 언어에 속한 프로그래밍 언어들은 이에 반대. for-문, while-문, switch문, if-else 문, 산술 연산자 등 여기에도 그런 것들을 다 적으면 중복되므로 C언어/문법을 참고.

3. 편집 지침

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

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

4. 메인 클래스

4.1. 메인 메서드

자바 프로그램을 실행하려면 어떤 클래스 안에 있는 메인 메서드가 필요하다.
#!syntax java
class Main {
	static public void main(String[] args) {
		System.out.println("Hello, World!");
    }
}

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

4.2. 비메인 메서드

메인 메서드가 들어있는 클래스에 다른 정적 (static) 메서드를 써넣는 게 가능하다. 양식은 접근자 static 반환형 이름(자료형 파라미터1, ...) {메서드 바디}

위에 있는 간단한 예제를 정적 메서드를 사용해서 구현해 보자.
#!syntax java
public class Namu {
  public static void main(String[] args) {
    printNamu();
  }
  public static void printNamu() {
    System.out.println("Namu Wiki");
  }
}

5. 클래스

5.1. 클래스 정의하기

당연하게도 커스텀 클래스를 만들수 있다. 예제를 통해 살펴보자. 클래스는 보통 각자 다른 .java 파일에 들어간다. 내부 클래스라는 개념이 있기는 하지만 그리 중요하진 않으므로 이 문단에서는 무시하자. 코딩을 한 번도 하지 않은 사람들에게 클래스의 가장 빠른 이해는 '사람'이다. 무슨 소리인가 싶으면 글을 계속 읽어보자.
#!syntax java
// Person.java
public class Person {
  private String name; // 사람의 이름. 모든 객체가 각자 name을 얻는다.
  private static String species = "Homo sapiens"; // 클래스에 하나만 있는 변수. 모든 Person 객체가 이걸 공유.
  
  // 생성자 메서드. 객체를 생성할때 new 키워드와 함께 사용된다.
  public Person(String name) {
    // 자신의 변수를 참조할 때 this를 쓸 수 있다.
    this.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"); // 새로운 객체를 생성하려면 new 키워드를 사용하자
    System.out.println(myPerson.getName()); // John
    System.out.println(Person.getSpecies()); // Homo sapiens
  }
}

클래스 안에 비정적 메서드는 'static'을 생략한다. static과 non-static 메서드의 차이점은 간단하다. static 메서드는 객체에 상관없이 호출이 가능하다. 하지만 non-static 메서드는 특정 개체를 대상으로 호출해야 한다. non-static 메서드를 호출하려면 object.method()로 호출, static 메서드는 class.method()로 호출하자.

5.2. 상속

상속을 하려면 extends 키워드를 사용하면 된다. 예제로 살펴보자. 상속이라는 개념이 다소 이해가 어려울 수 있는데 간단히 설명하자면 자웅동체인 부모님(...)이 모든 것이 똑같이 생긴 를 낳았다고 생각하면 된다. 즉 부모님이 힘이 세면 힘이 센 것을 유산 상속받듯이 상속받고, 부모님이 키가 크면 키가 큰 것을 상속받고... 물론 100% 모든 것을 상속받는 것은 아니고 인터페이스를 통해 받기 싫은 건 안 받을 수도 있긴 하다. 자세한 것은 상속 문서 참조.
#!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. 변수 및 자료형

6.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;
변수의 값을 이렇게 복사할 수도 있다.[1]

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

6.2. 원시 자료형

primitive type. 자바에선 원시 자료형이 몇 개가 있는데, 가장 중요한 것 몇 개를 뽑자면
  • int: 정수(-3, 2, 122...)
  • double: 소수(4.45, 1.0, 0.0...)
  • char: ASCII 문자(a, C, !...)
  • boolean: 불리언(참, 거짓)

원시 자료형 이름은 소문자로 시작한다. 대문자로 시작한다면 원시 자료형이 아닌 참조 자료형, 즉 객체이다. 대표적인 예가 문자열인 String.

다음은 두 정수의 합을 반환하는 메서드이다.
#!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
}

6.2.1. byte, short, char

byte는 -128 ~ 127까지를 저장할 수 있다. short는 더 늘어나 -32768 ~ 32767까지 저장이 가능하다.

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

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

6.2.2. int, long

int는 -2147483648 ~ 2147483647을 저장한다.

long은 -9223372036854775808 ~ 9223372036854775807을 저장한다. 그리고 long의 값이 int의 범위(-2147483648 ~ 2147483647)를 초과하면, 수 뒤에 l이나 L을 붙여야 한다.
#!syntax java
long namu = 1000000000000L;

6.3. 참조 자료형

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

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

6.4. 래퍼 클래스

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

6.5. 컬렉션

자바에서는 컬렉션을 통해 여러 개의 데이터를 저장할 수 있는 자료 구조를 제공한다. 컬렉션에는 저장된 데이터를 관리할 수 있는 내장 함수까지 구현되어 있다. 또한, 컬렉션 중에서 상속으로 구현된 것들도 있다.

컬렉션은 기본적으로 모든 데이터 형식을 사용할 수 있도록 일반화된 변수를 지원한다. 표에서 [컬렉션]<X>에 있는 X가 일반화된 변수를 뜻한다. 즉, X 대신에 int가 들어갈 수도 있고 String이 들어갈 수도 있고 사용자가 정의한 클래스가 들어갈 수도 있다.
컬렉션 상위 클래스 자료 구조
List - 리스트 기본형
ArrayList List 가변 배열 구조의 리스트
LinkedList List 단순 연결 리스트
Map - 맵 기본형
HashMap Map 값의 중복을 허용하지 않는 맵
LinkedHashMap Map 엔트리의 순서를 변경할 수 없는 맵
TreeMap Map 엔트리의 정렬이 가능한 맵

컬렉션 자료 구조에 대한 정렬이나 필터링에 필요한 함수는 Collections 클래스 (java.util.Collections)에서 제공하고 있다.

6.6. 일반화 변수 형식

일반화 변수 형식 (Generic Variable Type)은 어떠한 데이터 형식이던 매개 변수나 리턴값으로 사용할 수 있는 일반화된 자료형을 뜻한다. 자세한 내용은 아래 항목에서 설명하고 있으니 참조하면 된다.

7. 일반화 프로그래밍

자바에서의 일반화 프로그래밍에 대한 간단한 서술.

7.1. 일반 메서드

메서드를 정의할 때 파라미터 또는 반환값을 미리 정해두지 않고 싶을때 사용한다. 일반 자료형은 주로 T나 U같이 대문자 알파벳을 사용한다. 일반 메서드를 사용하려면 일반 자료형을 <> 사이에 넣어주자. 여러개의 자료형을 사용하고 싶다면 사이에 콤마를 넣어주면 된다.
#!syntax java
public static <T, U> T printAndReturn(T input1, U input2) {
  System.out.println(input1 + " " + input2);
  return input1;
}
public static void main(String[] args) {
  System.out.println(printAndReturn("Namu", 10));
  /* 결과 :
  Namu 10
  Namu
  */
}

7.2. 와일드카드

와일드카드라는 것도 있다. 일반형 자료와 비슷하지만 간결한 대신 좀 제한적인 개념. 와일드카드를 나타내는 기호는 물음표("?").
아래의 두 메서드는 똑같은 일을 하는데, 전자는 위에서 소개했던 방식이고, 후자는 와일드카드를 사용한 방식이다.
#!syntax java
public static <T> int getLength1(List<T> myList) {
  return myList.size();
}
public static int getLength2(List<?> myList) {
  return myList.size();
}

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

7.3. 바운딩

부등호 괄호[5] 안에 '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 메소드를 호출하는 꼴이 되어서 컴파일 오류가 발생하는 것이다.

7.4. 일반 클래스

객체 지향에 매우 충실한 언어답게 클래스를 짤 때도 일반화 프로그래밍이 가능하다. 클래스 이름 뒤에 일반 자료형(들)을 <>안에 넣어주면 된다. 예를 들면
#!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<>(44);
System.out.println(myWrapper1.getContent()); // Namu Wiki
System.out.println(myWrapper2.getContent()); // 44

8. 인터페이스

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

8.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이다. 어느 한 객체에 붙어있지도 않고 다른 변수를 대입할 수도 없다는 뜻이다.

8.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 {
// ...
}

8.3. 함수형 인터페이스

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


[1] 원시 자료형이 아닌 참조 자료형의 경우 객체의 참조가 복사된다. 원본 객체와 대입된 객체의 해시값을 출력해 보면 서로 같음을 알 수 있다. [2] 문자열 비교 시 A == B가 아닌 A.equals(B)를 사용하는 이유이기도 하다. [3] Object 클래스는 모든 클래스의 최상위 클래스이다. [4] String 클래스는 char 자료형을 추상화하였지만 래퍼 클래스는 아니다. [5] < >