2021. 1. 29.

11주차 과제: Enum

https://github.com/whiteship/live-study/issues/11

목표

자바의 열거형에 대해 학습하세요.

학습할 것 (필수)

  • enum 정의하는 방법
  • enum이 제공하는 메소드 ( values()와 valueOf() )
  • java.lang.Enum
  • EnumSet


enum 정의하는 방법

public enum Season {
    SPRING(3, 4, 5),
    SUMMER(6, 7, 8),
    AUTUMN(9, 10, 11),
    WINTER(12, 1, 2);

    Season(int i, int i1, int i2) {
        this.start = i;
        this.middle = i1;
        this.end = i2;
    }
    private final int start;
    private final int middle;
    private final int end;

    public int getStart() {
        return start;
    }

    public int getMiddle() {
        return middle;
    }

    public int getEnd() {
        return end;
    }
}
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class MySeasonTest {
    Season season;

    public MySeasonTest(Season season) {
        this.season = season;
    }

    public void showMeTheSeason() {
        System.out.println(season.getStart());
        System.out.println(season.getMiddle());
        System.out.println(season.getEnd());
    }

    @Test
    @DisplayName("main")
    public static void main(String[] args) {
        MySeasonTest mySeasonTest
                = new MySeasonTest(Season.WINTER);
        mySeasonTest.showMeTheSeason();
    }
}
> Task :MySeasonTest.main()
12
1
2

BUILD SUCCESSFUL in 415ms
3 actionable tasks: 2 executed, 1 up-to-date
  • Java의 enum 타입은 클래스
  • 상수이기에 변수명은 대문자
  • 암시적으로 java.lang.Enum을 상속(extends)
  • enum 생성자는 반드시 package-private, private 접근
  • enum 시작 부분에 정의된 상수를 자동으로 생성
  • enum 생성자를 직접 호출 불가능
  • 암시적으로 public static final

출처, 참고:
https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

https://docs.oracle.com/javase/specs/jls/se13/html/jls-8.html#jls-8.9.2

https://woowabros.github.io/tools/2017/07/10/java-enum-uses.html


enum이 제공하는 메소드 ( values()와 valueOf() )

    @Test
    @DisplayName("main")
    public static void main(String[] args) {
        MySeasonTest mySeasonTest
                = new MySeasonTest(Season.WINTER);

        // valueOf()
        Season tempSeason 
                = mySeasonTest.season.valueOf("SUMMER");
        System.out.println("tempSeason.getStart(): " 
                + tempSeason.getStart());

        // values()
        for (Season s :
                Season.values()) {
            System.out.println(s + ", " 
                    + s.getStart() + ", " 
                    + s.getMiddle() + ", " 
                    + s.getEnd());
        }
        
        // ordinal()
        for (Season s :
                Season.values()) {
            System.out.println(
                    s + " at index " + s.ordinal());
        }
    }
> Task :MySeasonTest.main()

// valueOf()
tempSeason.getStart(): 6

// values()
SPRING, 3, 4, 5
SUMMER, 6, 7, 8
AUTUMN, 9, 10, 11
WINTER, 12, 1, 2

// ordinal()
SPRING at index 0
SUMMER at index 1
AUTUMN at index 2
WINTER at index 3

BUILD SUCCESSFUL in 411ms
3 actionable tasks: 2 executed, 1 up-to-date
  • valueOf(): 지정한 이름의 enum 상수를 반환
  • values(): 해당 enum의 모든 상수를 저장한 배열을 생성하여 반환
  • ordinal(): enum 상수의 순서값 반환
  • toString(): enum 상수의 이름을 반환
  • name(): enum 상수의 이름을 반환

toString() 메소드가 사용자에게 더 친숙한 이름을 반환하므로 name() 메소드보다 우선적으로 사용해야 합니다. (Most programmers should use the toString() method in preference to this one, as the toString method may return a more user-friendly name.)

출처: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html#name()


Enum 바이트코드(ByteCode)

public enum Day {
    SUNDAY, MONDAY, TUESDAY
}
javap -c Day.class

Compiled from "Day.java"
public final class com.testcompany.Day extends java.lang.Enum<com.testcompany.Day> {
  public static final com.testcompany.Day SUNDAY;

  public static final com.testcompany.Day MONDAY;

  public static final com.testcompany.Day TUESDAY;

  public static com.testcompany.Day[] values();
    Code:
       0: getstatic       #13  // Field $VALUES:[Lcom/testcompany/Day;
       3: invokevirtual   #17  // Method "[Lcom/testcompany/Day;".clone:()Ljava/lang/Object;
       6: checkcast       #18  // class "[Lcom/testcompany/Day;"
       9: areturn

  public static com.testcompany.Day valueOf(java.lang.String);
    Code:
       0: ldc             #1  // class com/testcompany/Day
       2: aload_0
       3: invokestatic    #22 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast       #1  // class com/testcompany/Day
       9: areturn

  static {};
    Code:
       0: new             #1  // class com/testcompany/Day
       3: dup
       4: ldc             #32 // String SUNDAY
       6: iconst_0
       7: invokespecial   #33 // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic       #3  // Field SUNDAY:Lcom/testcompany/Day;
      13: new             #1  // class com/testcompany/Day
      16: dup
      17: ldc             #34 // String MONDAY
      19: iconst_1
      20: invokespecial   #33 // Method "<init>":(Ljava/lang/String;I)V
      23: putstatic       #7  // Field MONDAY:Lcom/testcompany/Day;
      26: new             #1  // class com/testcompany/Day
      29: dup
      30: ldc             #35 // String TUESDAY
      32: iconst_2
      33: invokespecial   #33 // Method "<init>":(Ljava/lang/String;I)V
      36: putstatic       #10 // Field TUESDAY:Lcom/testcompany/Day;
      39: invokestatic    #36 // Method $values:()[Lcom/testcompany/Day;
      42: putstatic       #13 // Field $VALUES:[Lcom/testcompany/Day;
      45: return
}
  • 놀랍게도 바이트코드 내부에 'public static final' 선언
  • values(), valueOf()도 미리 준비
  • 메소드의 형태로 static 필드에 선언

바이트 코드 설명

  • getstatic: 클래스의 static 필드값 가져오기
  • invokevirtual: 인스턴스 메서드 호출
  • checkcast: 객체의 타입 확인
  • areturn: 메소드 참조 반환 (Return reference from method)
  • ldc: 런타임 상수 풀에 아이템 푸시
  • aload_<n>: 지역변수 레퍼런스 로드
  • invokestatic: static 메서드 호출
  • new: 새로운 객체 생성
  • dup: 연산자 스택의 맨 위의 값 복사
  • iconst_<i>: int 상수 푸시
  • invokespecial: 생성자, private 메소드, 슈퍼 클래스의 메소드 호출
  • putstatic: static 필드 선언
  • return: Return void from method
여기서 관심을 가질 부분은 "Ljava/lang/String;"과 마지막의 "V"이다. 자바 바이트코드의 표현에서 "L;"은 클래스 인스턴스이다. 즉, addUser() 메서드는 java/lang/String 객체 하나를 파라미터로 받는 메서드이다. 이 사례의 라이브러리에서는 파라미터가 변경되지 않았으므로 이 파라미터는 정상이다. 위 메시지 마지막의 "V"는 메서드의 반환값을 나타낸다. 자바 바이트코드 표현에서 "V"는 반환값이 없음을 의미한다.

참고, 출처:
https://d2.naver.com/helloworld/1230

https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings

https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html


java.lang.Enum

public abstract class Enum<E extends Enum<E>>
        implements Constable, Comparable<E>, Serializable {
    private final String name;
    
    @NotNull
    public final String name() {
        return name;
    }
    
    private final int ordinal;
    
    @Range(from = 0, to = java.lang.Integer.MAX_VALUE)
    public final int ordinal() {
        return ordinal;
    }
    
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }
    
    public String toString() {
        return name;
    }
    
    ...
}
  • 모든 자바 열거형의 기본 클래스
  • Enum은 추상 클래스. Enum 클래스를 생성할 수 없다.
  • final 메소드로 선언되므로 수정이 불가능


EnumSet

// Creates an enum set containing all of the elements 
// in the specified element type.
// 명시한 요소 타입이 모두 들어간 enum 세트 생성
EnumSet<Season> myEnumSet = EnumSet.allOf(Season.class);
System.out.println("allOf(Season.class): " + myEnumSet);

// Creates an empty enum set with the specified element type.
// 명시한 요소 타입을 사용해 빈 enum 세트 생성
myEnumSet = EnumSet.noneOf(Season.class);
System.out.println("noneOf(Season.class): " + myEnumSet);

// Creates an enum set initially containing the specified element.
// 생성시 명시한 요소가 들어간 enum 세트 생성
myEnumSet = EnumSet.of(Season.SPRING, Season.AUTUMN);
System.out.println("of(Season.SPRING, Season.AUTUMN): " + myEnumSet);

// Creates an enum set with the same element type 
// as the specified enum set, initially containing 
// all the elements of this type that are not contained 
// in the specified set.
// 명시한 세트가 포함되지 않은 enum 세트 생성
myEnumSet = EnumSet.complementOf(myEnumSet);
System.out.println("complementOf(myEnumSet): " + myEnumSet);

// Creates an enum set initially containing all of the elements 
// in the range defined by the two specified endpoints.
// 지정된 두 요소 범위안의 enum 세트를 생성
myEnumSet = EnumSet.range(Season.SUMMER, Season.AUTUMN);
System.out.println("range(Season.SPRING, Season.AUTUMN): " + myEnumSet);
allOf(Season.class): [SPRING, SUMMER, AUTUMN, WINTER]
noneOf(Season.class): []
of(Season.SPRING, Season.AUTUMN): [SPRING, AUTUMN]
complementOf(myEnumSet): [SUMMER, WINTER]
range(Season.SPRING, Season.AUTUMN): [SUMMER, AUTUMN]

Process finished with exit code 0
  • enum 타입과 함께 사용하기 위한 특수 Set의 구현
  • 모든 요소는 enum 셋을 만들 때 명시적으로 또는 암시적으로 지정된 단일 enum 타입을 가져와야 함
  • 내부적으로 bit vector로 표시됨 (매우 간결하고 효율적)
  • 이 클래스의 공간 및 시간 성능은 기존의 int 기반 "비트 플래그"의 대안으로 고품질에 타입 안전으로 사용할 수 있을 만큼 충분히 우수해야 함

출처: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/EnumSet.html


2021. 1. 22.

10주차 과제: 멀티쓰레드 프로그래밍

https://github.com/whiteship/live-study/issues/10

목표

자바의 멀티쓰레드 프로그래밍에 대해 학습하세요.

학습할 것 (필수)

  • Thread 클래스와 Runnable 인터페이스
  • 쓰레드의 상태
  • 쓰레드의 우선순위
  • Main 쓰레드
  • 동기화
  • 데드락


Thread 클래스와 Runnable 인터페이스

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("Runnable thread");
    }

    public static void main(String args[]) {
        (new Thread(new MyRunnable())).start();
    }

}
public class MyThread extends Thread {

    public void run() {
        System.out.println("extends thread");
    }

    public static void main(String args[]) {
        (new MyThread()).start();
    }

}

출처: https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html


쓰레드의 상태

NEW - 아직 시작되지 않은 스레드 상태

RUNNABLE - Java 가상 머신에서 실행중인 스레드 상태

BLOCKED - 모니터 잠금을 기다리면서 차단 된 스레드 상태

WAITING - 다른 스레드가 특정 작업을 수행 할 때까지 무기한 대기중인 스레드 상태

TIMED_WAITING - 다른 스레드가 지정된 대기 시간까지 작업을 수행하기를 기다리는 상태

TERMINATED - 종료 된 스레드 상태

public class MyThread implements Runnable {
    @Override
    public void run() {
        // moving thread2 to timed waiting state
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("State of thread1 while it 
        called join() method on thread2 -"
        + MyTest.thread1.getState());
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class MyTest implements Runnable {
    public static Thread thread1;
    public static MyTest obj;

    public static void main(String[] args) {
        obj = new MyTest();
        thread1 = new Thread(obj);

        // thread1 created and is currently 
        // in the NEW state.
        System.out.println("State of thread1 after 
        creating it - " + thread1.getState());
        thread1.start();

        // thread1 moved to Runnable state
        System.out.println("State of thread1 after calling 
        .start() - " 
        + thread1.getState());
    }

    @Override
    public void run() {
        MyThread myThread = new MyThread();
        Thread thread2 = new Thread(myThread);

        // thread1 created and is currently in the NEW
        // state.
        System.out.println("State of thread2 after
        creating it - " + thread2.getState());
        
        thread2.start();

        // thread2 moved to Runnable state
        System.out.println("State of thread2 after calling
        .start() - " + thread2.getState());

        // moving thread1 to timed waiting state
        try {
            //moving thread1 to timed waiting state
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("State of thread2 after calling
        .sleep() - " + thread2.getState());

        try {
            // waiting for thread2 to die
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("State of thread2 when it has
        finished - " + thread2.getState());
    }
}
State of thread1 after creating it - NEW
State of thread1 after calling .start() - RUNNABLE
State of thread2 after creating it - NEW
State of thread2 after calling .start() - RUNNABLE
State of thread2 after calling .sleep() - TIMED_WAITING
State of thread1 while it called join() - WAITING
State of thread2 when it has finished - TERMINATED

Process finished with exit code 0

코드 출처: https://www.geeksforgeeks.org/lifecycle-and-states-of-a-thread-in-java/


쓰레드의 우선순위

public static int MIN_PRIORITY: 최소 우선 순위. 값: 1

public static int NORM_PRIORITY: 기본 우선 순위. 값: 5. 명시하지 않을 경우 기본값.

public static int MAX_PRIORITY: 스레드의 최대 우선 순위. 값: 10


public final int getPriority(): java.lang.Thread.getPriority() 주어진 스레드의 우선 순위를 리턴

public final void setPriority(int newPriority): java.lang.Thread.setPriority() 스레드의 우선 순위를 newPriority 값으로 변경. newPriority의 값이 minimum(1) maximum(10) 한계를 초과하면이 IllegalArgumentException 발생

public class ThreadDemo extends Thread {

    public void run() {
        System.out.println("Inside run method");
    }

    public static void main(String[] args) {
        ThreadDemo t1 = new ThreadDemo();
        ThreadDemo t2 = new ThreadDemo();
        ThreadDemo t3 = new ThreadDemo();

        // Default 5
        System.out.println("t1 thread priority : "
                + t1.getPriority());

        // Default 5
        System.out.println("t2 thread priority : "
                + t2.getPriority());

        // Default 5
        System.out.println("t3 thread priority : "
                + t3.getPriority());

        t1.setPriority(2);
        t2.setPriority(5);
        t3.setPriority(8);

        // t3.setPriority(21); will throw
        // IllegalArgumentException

        // 2
        System.out.println("t1 thread priority : "
                + t1.getPriority());

        // 5
        System.out.println("t2 thread priority : "
                + t2.getPriority());

        // 8
        System.out.println("t3 thread priority : "
                + t3.getPriority());

        // Main thread

        // Displays the name of
        // currently executing Thread
        System.out.println(
                "Currently Executing Thread : "
                + Thread.currentThread().getName());

        System.out.println(
                "Main thread priority : "
                + Thread.currentThread().getPriority());

        // Main thread priority is set to 10
        Thread.currentThread().setPriority(10);
        System.out.println(
                "Main thread priority : "
                + Thread.currentThread().getPriority());
    }
}
t1 thread priority : 5
t2 thread priority : 5
t3 thread priority : 5
t1 thread priority : 2
t2 thread priority : 5
t3 thread priority : 8
Currently Executing Thread : main
Main thread priority : 5
Main thread priority : 10

Process finished with exit code 0

코드 출처: https://www.geeksforgeeks.org/java-thread-priority-multithreading/


Main 쓰레드

Java 프로그램이 시작되면 하나의 스레드가 즉시 실행. 프로그램이 시작될 때 실행되는 스레드이기 때문에 일반적으로 프로그램의 메인 스레드라고 한다.

"자식"스레드가 생성되는 스레드

public class MyTestThread extends Thread {

    public static void main(String[] args) {
        // getting reference to Main thread
        Thread t = Thread.currentThread();
        // getting name of Main thread
        System.out.println(
                "Current thread: "
                        + t.getName());
        // changing the name of Main thread
        t.setName("Geeks");
        System.out.println(
                "After name change: "
                        + t.getName());
        // getting priority of Main thread
        System.out.println(
                "Main thread priority: "
                        + t.getPriority());
        // setting priority of Main thread to MAX(10)
        t.setPriority(MAX_PRIORITY);
        System.out.println(
                "Main thread new priority: "
                        + t.getPriority());
    }
}
Current thread: main
After name change: Geeks
Main thread priority: 5
Main thread new priority: 10

Process finished with exit code 0

코드 출처: https://www.geeksforgeeks.org/main-thread-java/


동기화

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

1. 동기화(synchronized) 메소드를 동일한 객체에서 두 번 호출하는 인터리빙(interleave - 스레드가 수행되는 순서)이 불가능해진다. 첫 스레드가 동기화 메소드를 실행 완료할 때까지 같은 블록의 동기화 메소드를 실행한 모든 스레드는 실행을 일시 중단하고 기다린다.

2. 동기화 메소드가 종료되면 같은 객체의 동기화된 메소드의 다음 호출 관계를 자동으로 설정. 모든 스레드에 객체의 상태 변경 사항을 보여준다.

생성자는 동기화가 불가능. 생성자를 동기화하는 것은 의미가 없습니다. 객체를 생성하는 스레드만이 접근할 수 있어야 하기 때문.

출처: https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html


데드락

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            
            // 교착상태를 만드는 부분
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");
        
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

코드 출처: https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html

동기화된 bowBack 메소드를 호출하지만 동기화된 bow 메소드 종료를 기다리기에 교착상태(Deadlock) 발생


2021. 1. 15.

9주차 과제: 예외 처리

https://github.com/whiteship/live-study/issues/9

목표

자바의 예외 처리에 대해 학습하세요.

학습할 것 (필수)

  • 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • Exception과 Error의 차이는?
  • RuntimeException과 RE가 아닌 것의 차이는?
  • 커스텀한 예외 만드는 방법


자바에서 예외 처리 방법 (try, catch, throw, throws, finally)

public class MyClass {
    public static void main(String[] args) {
        try {
        
            // 예외를 발생시킬 수 있는 코드
            int[] myArray = {1, 2, 3};
            System.out.println(myArray[3]);
            
        } catch (Exception e) {
        
            // 오류 발생 시 실행할 코드
            System.err.println(e.getMessage());
            
        } finally {
        
            // try 블록 종료 시 항상 실행되는 코드
            System.out.println("try catch block end");
        }
    }
}
Index 3 out of bounds for length 3
try catch block end

Process finished with exit code 0
public class MyClass {

    private static void checkAge(int age) 
            throws ArithmeticException {

        if (age < 18) {
            // throw를 사용해 특정 Exception으로 예외를 던진다.
            throw new ArithmeticException("Access denied");
        } else {
            System.out.println("Access granted");
        }
    }
    public static void main(String[] args) {
        checkAge(20);
    }
}
Exception in thread "main"
java.lang.ArithmeticException: Access denied
	at com.company.MyClass.checkAge(MyClass.java:7)
	at com.company.MyClass.main(MyClass.java:13)

출처: https://www.w3schools.com/java/java_try_catch.asp

throw / throws 비교

  • 메소드 안에서 예외를 사용 / 메소드의 블럭에서 예외를 받아 처리
  • 1개의 예외 선언 가능 / 여러개의 예외 선언 가능

https://www.w3schools.com/java/ref_keyword_throws.asp

https://docs.oracle.com/javase/tutorial/essential/exceptions/try.html

https://docs.oracle.com/javase/tutorial/essential/exceptions/catch.html

https://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html


자바가 제공하는 예외 계층 구조

출처: https://www.manishsanger.com/java-exception-hierarchy/

Throwable

  • 예외처리의 최상위 클래스
  • Serializable 인터페이스의 구현
  • throwable에는 생성 당시 스레드의 실행 스택 스냅샷이 포함

https://www.geeksforgeeks.org/throwable-class-in-java-with-examples/

Error

  • 예상치 못한 심각한 문제
  • 처리 기술로 복구할 수 없는 조건 (비정상적인 프로그램 종료의 원인)
  • 런타임에 발생 (ex 메모리 부족, 시스템 충돌 오류)

Exception

  • 포착하려는 조건의 문제
  • 런타임시 발생하는 조건으로 프로그램이 종료 될 수 있지만 try catch, throw 키워드로 복구 가능
  • checked / unchecked 예외 두 가지 범주

https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html


Exception과 Error의 차이는?

출처: https://www.geeksforgeeks.org/errors-v-s-exceptions-in-java/


RuntimeException과 RE가 아닌 것의 차이는?

Checked Exception

  • 반드시 예외를 처리해야 함
  • 컴파일 단계에서 확인

Unchecked Exception

  • 예외 처리를 강제하지 않음
  • 실행 단계에서 확인
  • RuntimeException 하위 예외


커스텀한 예외 만드는 방법

class MyException extends Exception {
    public MyException(String errorMessage) {
    	// 부모의 생성자 호출
        super(errorMessage); 
    } 
}

public class Main { 
    public static void main(String args[]) { 
        try
            throw new MyException("메세지"); 
        } 
        catch (MyException ex) { 
            System.out.println("Caught");
            System.out.println(ex.getMessage()); 
        }
    }
}


2021. 1. 8.

8주자 과제: 인터페이스

https://github.com/whiteship/live-study/issues/8

목표

자바의 인터페이스에 대해 학습하세요.

학습할 것 (필수)

  • 인터페이스 정의하는 방법
  • 인터페이스 구현하는 방법
  • 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
  • 인터페이스 상속
  • 인터페이스의 기본 메소드 (Default Method), 자바 8
  • 인터페이스의 static 메소드, 자바 8
  • 인터페이스의 private 메소드, 자바 9


인터페이스 정의하는 방법

public interface MyInterface extends MyInterface1, 
MyInterface2, MyInterface3 {
    void doExample(int i, double x);
    int doExampleSomething(String s);
}

출처: https://docs.oracle.com/javase/tutorial/java/IandI/interfaceDef.html

인터페이스 선언은 접근 지시자, interface 키워드, 인터페이스 이름, 쉼표로 구분된 상위 인터페이스 목록(있는 경우) 및 인터페이스 본문으로 구성

서브 클래스처럼 다른 인터페이스를 확장하거나 다른 클래스를 확장 가능

클래스는 하나의 다른 클래스만 확장할 수 있지만 인터페이스는 여러 인터페이스를 확장 가능

Interface Body

인터페이스 본문에는 추상 메소드, 기본 메소드, static 메소드가 포함될 수 있다.

인터페이스의 모든 추상, 기본, static 메소드는 암시적으로 공용이므로 public을 생략 가능

인터페이스에 정의된 모든 상숫값은 암시적으로 public, static 및 final


인터페이스 구현하는 방법

샘플 인터페이스, MyInterface

public interface MyInterface {
    public int isCompareTo(MyInterface other);
}

MyInterface 인터페이스 구현

public class MyRectangle implements MyInterface {

    // 가로 * 세로
    public int getArea() {
        return width * height;
    }

    // MyInterface 인터페이스 구현을 위한 메소드
    @Override
    public int isCompareTo(MyInterface other) {
        MyRectangle otherRect 
            = (MyRectangle)other;
        if (this.getArea() < otherRect.getArea())
            return -1;
        else if (this.getArea() > otherRect.getArea())
            return 1;
        else
            return 0;               
    }
}

출처: https://docs.oracle.com/javase/tutorial/java/IandI/usinginterface.html

MyInterface 인터페이스를 구현(implements)해 MyRectangle 클래스 안에서 사각형의 크기를 계산하는 메소드 isCompareTo()를 오버라이딩(overriding;재정의)

MyRectangle는 MyInterface을 구현(implements)하기 때문에 두 MyRectangle 객체의 크기를 비교 가능

Note: isCompareTo 메소드는 MyInterface 타입의 객체를 사용합니다. MyRectangle 타입 캐스팅은 컴파일러에 실제 객체가 무엇인지 알려줍니다. 다른 인스턴스(other.getArea())에서 직접 getArea를 호출하면 다른 인스턴스가 실제로 MyRectangle 인스턴스임을 이해하지 못하기 때문에 컴파일에 실패합니다.


인터페이스 레퍼런스를 통해 구현체를 사용하는 방법

새로운 인터페이스를 정의할 때 새로운 레퍼런스 데이터 타입으로 정의할 수 있다. 데이터 타입 이름을 사용할 수 있는 모든 곳에서 인터페이스 이름을 사용할 수 있다.

public Object findLargest(Object object1, Object object2) {
    MyInterface obj1 = (MyInterface) object1;
    MyInterface obj2 = (MyInterface) object2;
    if ((obj1).isLargerThan(obj2) > 0)
        return object1;
    else
        return object2;
}

object1을 MyInterface 타입으로 캐스팅하여 isLargerThan 메소드를 호출할 수 있다.

다양한 클래스에서 MyInterface을 구현(implementing)하려는 경우 두 객체가 동일한 클래스인 경우 클래스 중 하나에서 인스턴스화 된 객체를 findLargest()로 비교가 가능하다. 비슷하게 다음과 같이 비교할 수 있다.

public Object findSmallest(Object object1, Object object2) {
    MyInterface obj1 = (MyInterface) object1;
    MyInterface obj2 = (MyInterface) object2;
    if ((obj1).isLargerThan(obj2) < 0)
        return object1;
    else
        return object2;
}

public boolean isEqual(Object object1, Object object2) {
    MyInterface obj1 = (MyInterface) object1;
    MyInterface obj2 = (MyInterface) object2;
    if ((obj1).isLargerThan(obj2) == 0)
        return true;
    else
        return false;
}


인터페이스 상속

클래스와 인터페이스의 중요한 차이점은 클래스는 필드를 가질 수 있지만 인터페이스는 가질 수 없다. 추가로 클래스의 객체 생성이 가능하지만 인터페이스는 불가능 하다.

객체는 클래스에 정의된 필드의 상태를 저장한다. 자바 언어가 둘 이상의 클래스를 확장할 수 없는 이유는 여러 클래스에서 필드를 상속하는 기능인 다중 상속 문제를 방지하기 위한 것.

인터페이스에는 필드가 포함되지 않기 때문에 다중 상속으로 인해 발생하는 문제에 대해 걱정할 필요가 없다.

구현(implementation)의 다중상속은 여러 클래스에서 메소드의 정의를 상속하는 기능

다중상속에서 이름 충돌 및 모호성과 같은 문제가 발생하는데 컴파일러가 슈퍼 클래스의 같은 이름의 메소드를 만나면 어떤 멤버나 메소드를 실행하거나 접근해야 하는지를 결정할 수 없는 경우가 있다. 또한 슈퍼 클래스에 새 메소드를 추가하여 무의식적으로 이름 충돌을 일으킬 수 있다. 기본 메소드(Default methods)는 다중 상속의 한 형태를 소개한다. 클래스는 동일한 이름을 가진 기본 메소드를 포함한 인터페이스를 2개 이상 구현할 수 있다. 컴파일러는 특정 클래스가 사용하는 기본 메소드를 결정하는 규칙을 제공한다.

출처: https://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html


인터페이스의 기본 메소드 (Default Method), 자바 8

산업 표준 인터페이스를 발표하는 컴퓨터 제어 자동차 제조업체의 예를 들어 설명해 봅시다. 컴퓨터로 제어되는 자동차 제조업체가 비행과 같은 새로운 기능을 자동차에 추가하면 어떻게 될까요? 제조업체는 다른 회사(예: 전자 유도 기기 제조업체)가 소프트웨어를 적용하도록 특별히 새로운 메소드가 필요할 겁니다. 이러한 새로운 비행 관련 메소드를 어디에 선언해야 할까요? 만약 오리지널 인터페이스에 추가하면 해당 인터페이스를 구현한 프로그래머는 구현을 다시 작성해야 합니다. 만약 static 메소드로 추가하면 프로그래머는 필수 핵심 메소드가 아닌 유틸리티 메소드로 간주할 것입니다.

기본 메소드(default methods)를 사용하면 라이브러리의 인터페이스에 새 기능을 추가하고 이전 버전용으로 작성된 코드와 바이너리 호환성을 보장할 수 있습니다.

출처: https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

public interface MyInterface {
    default int getData(int i) {
        return i;
    }
}


인터페이스의 static 메소드, 자바 8

public interface MyInterface {
    static public int getData(String myString) {
        try {
            return 1;
        } catch (Exception e) {
            System.err.println("error");
            return -1;
        }
    }
}

클래스의 모든 인스턴스는 static 메소드를 공유

라이브러리에서 helper 메소드를 쉽게 구성할 수 있다.


인터페이스의 private 메소드, 자바 9

Java 9부터 인터페이스에 private 메소드와 private static 메소드 추가 가능

인터페이스 내부의 코드 재사용성을 향상

private 메소드는 인터페이스 내에서만 사용 가능

private static 메소드는 다른 static or non-static 인터페이스 메소드 안에서 사용 가능

private non-static 메소드는 private static 메소드 내부에서 사용 불가능

public interface MyInterface {
    private int add(int nums) {
        return nums + 1;
    }
}

출처: https://howtodoinjava.com/java9/java9-private-interface-methods/

링크1: 인터페이스의 장점에 대해 모르겠습니다. 납득좀 시켜주세요

링크2: 객체지향 개발 5대 원리: SOLID

2021. 1. 1.

7주차 과제: 패키지

https://github.com/whiteship/live-study/issues/7

목표

자바의 패키지에 대해 학습하세요.

학습할 것 (필수)

  • package 키워드
  • import 키워드
  • 클래스패스
  • CLASSPATH 환경변수
  • -classpath 옵션
  • 접근지시자


package 키워드

//in the Draggable.java file
package graphics;
public interface Draggable {
    . . .
}

//in the Graphic.java file
package graphics;
public abstract class Graphic {
    . . .
}

//in the Circle.java file
package graphics;
public class Circle extends Graphic
    implements Draggable {
    . . .
}

//in the Rectangle.java file
package graphics;
public class Rectangle extends Graphic
    implements Draggable {
    . . .
}

//in the Point.java file
package graphics;
public class Point extends Graphic
    implements Draggable {
    . . .
}

//in the Line.java file
package graphics;
public class Line extends Graphic
    implements Draggable {
    . . .
}
  • package 선언은 소스 파일 최상단에
  • 1개의 소스 파일은 1개의 패키지만 선언 가능
  • 1개의 소스 파일에 여러 타입을 넣는 경우 한 개의 타입은 'public'이며 소스 파일 이름과 동일해야 함
    ex) 'public class Circle'의 소스 파일 이름 'Circle.java', 'public interface Draggable'의 소스 파일 이름 'Draggable.java', 'public enum Day'의 소스 파일 이름 'Day.java'
  • 'public' 타입과 동일한 파일에 'private' 타입을 포함할 수 있지만('private' 타입이 작고 'public' 타입과 밀접한 관련이 있는 경우를 제외하고는 강력히 권장하지 않음) 'public' 타입만 외부에서 접근 가능

전 세계 프로그래머가 Java 언어를 사용하여 클래스와 인터페이스에 같은 이름을 사용할 가능성이 높다. 예) java.awt 패키지에 이미 Rectangle 클래스가 있을 때 Rectangle 클래스를 정의. 그래도 컴파일러는 두 클래스가 다른 패키지에 있을 경우 같은 이름을 허락한다. Rectangle 클래스의 정규화 이름(fully qualified name)에 패키지 이름이 포함된다. graphics 패키지의 Retangle 클래스의 정규화 이름은 'graphics.Rectangle', java.awt 패키지의 Retangle 클래스의 정규화 이름은 'java.awt.Rectangle'.

두명의 프로그래머가 동일한 패키지 이름을 사용하지 않는한 잘 작동합니다. 이 문제를 방지하는 방법은? 규칙 (Convention)

패키지 명명 규칙

  • 패키지 이름은 클래스 or 인터페이스 이름과 충돌하지 않도록 모두 소문자로 작성
  • 역방향 인터넷 도메인 이름을 사용 ex) example.com에서 만든 mypackage 패키지 → com.example.mypackage
  • 회사 내에서 발생하는 이름 충돌은 회사 이름 뒤에 지역 or 프로젝트 이름 (ex: com.example.region.mypackage)을 포함하여 사내 규칙에 따라 처리)
  • Java 언어 안의 패키지는 java or javax로 시작
  • 도메인 이름이 패키지 이름으로 적절하지 않거나 하이픈, 특수문자, Java 키워드, 이름규칙에 안맞는 숫자 문자가 포함될 경우 다음 예시와 같이 처리

출처: https://docs.oracle.com/javase/tutorial/java/package/createpkgs.html


import 키워드

// 정규화 이름 사용 예
graphics.Rectangle myRect = new graphics.Rectangle();
import graphics.Rectangle;
. . .
// import 사용 예
Rectangle myRectangle = new Rectangle();
. . .
import graphics.*;
. . .
// 패키지 내 모든 내용 import 사용 예
Circle myCircle = new Circle();
Rectangle myRectangle = new Rectangle();
. . .
// 패키지가 계층적으로 보이지만 사실 그렇지 않음
// 예) java.awt.* 이 java.awt 패키지의 모든 내용을 가져올 것 같지만
// java.awt.color, java.awt.font 같은 패키지 내용을 가져오지 않음
import java.awt.*;
import java.awt.color.*;

가져온 두 패키지의 맴버 이름이 동일하면 정규화 이름을 사용

java.awt.Rectangle rect1;
graphics.Rectangle rect2;

Static Import

하나 or 두 개의 클래스에서 static final fields (constants 상수)와 static 메소드에 자주 접근해야 하는 상황이 있습니다. 이러한 클래스 이름을 반복해서 붙이면 코드가 복잡해질 수 있습니다. static import 선언을 사용하면 상수와 static 메소드를 가져오는 방법을 제공하므로 해당 클래스의 접두사를 지정할 필요가 없습니다.

java.lang.Math 클래스는 PI 상수와 많은 static 메소드, 사인, 코사인, 탄젠트 등등을 포함합니다. 예를 들어

public static final double PI 
    = 3.141592653589793;
public static double cos(double a)
{
    ...
}

일반적으로 다른 클래스의 객체를 사용하려면 다음과 같이 클래스 이름을 접두사로 지정

double r = Math.cos(Math.PI * theta);

static import를 사용하면 'java.lang.Math'의 static members를 가져올 수 있으므로 Math 접두사를 붙일 필요가 없습니다. Math의 static members는 개별적으로 불러올 수 있습니다.

import static java.lang.Math.PI;
import static java.lang.Math.*;

// 불러온 static members는 제한없이 사용할 수 있다.
double r = cos(PI * theta);

// 자주 사용하는 상수와 static 메소드를 포함하는 클래스를 작성한 다음
// import 문을 사용할 수 있습니다.
import static mypackage.MyConstants.*;

'static import'는 매우 절약해서 사용하세요. 과도하게 사용하면 코드를 읽는 사람이 특정 static 객체가 어느 클래스에서 정의됐는지 알 수 없기 때문에 코드를 읽고 유지 관리하기 어려울 수 있습니다. 올바르게 사용하면 static import 사용해 클래스 이름의 반복을 제거해 코드를 더 쉽게 읽을 수 있습니다.

출처: https://docs.oracle.com/javase/tutorial/java/package/usepkgs.html


클래스패스

컴파일러와 JVM이 .class 파일을 찾을 수 있도록 설정하는 경로

<출력 파일의 상위 디렉터리 경로>\com\example\graphics\Rectangle.class
<출력 파일의 상위 디렉터리 경로>\com\example\graphics\Helper.class
<path_one>\sources\com\example\graphics\Rectangle.java
<path_two>\classes\com\example\graphics\Rectangle.class

이렇게 하면 소스를 공개하지 않고 다른 프로그래머에게 클래스 디렉토리를 제공할 수 있다. 또한 컴파일러와 JVM이 프로그램에서 사용하는 모든 타입을 찾을 수 있도록 이러한 방식으로 소스 및 클래스 파일을 관리해야 한다.

현재 CLASSPATH 값 확인
In Windows:   C:\> set CLASSPATH
In UNIX:      % echo $CLASSPATH

현재 CLASSPATH 값 삭제
In Windows:   C:\> set CLASSPATH=
In UNIX:      % unset CLASSPATH; export CLASSPATH

CLASSPATH 설정 예시
In Windows:
C:\> set CLASSPATH=C:\users\george\java\classes
In UNIX:
% CLASSPATH=/home/george/java/classes; export CLASSPATH

출처: https://docs.oracle.com/javase/tutorial/java/package/managingfiles.html


CLASSPATH 환경변수

읽어보기: https://hyoje420.tistory.com/7

https://effectivesquid.tistory.com/entry/자바-클래스패스classpath란


-classpath 옵션

클래스 경로를 지정하는 기본 방법은 -cp 명령어를 사용. 이를 통해 다른 애플리케이션에 영향을 주지 않고 각 애플리케이션에 개별적으로 CLASSPATH를 설정할 수 있다.

javac -cp C:\Java\jdk1.7.0\bin;C:\Windows\System32\
java -cp C:\Java\jdk1.7.0\bin;C:\Windows\System32\

출처: https://docs.oracle.com/javase/tutorial/essential/environment/paths.html


접근지시자

패키지 레벨과 관련있는 접근지시자

출처: https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

public: 제한없이 접근 가능

protected: 자신의 패키지 내에서만 접근 가능

default(접근 지시자가 없는 경우): 자신의 패키지 내에서만 접근 가능

private: 클래스 내에서만 접근 가능