4주 차 시작합니다!

 

선택문 (switch)?

주어진 조건 값의 결과에 따라 프로그램이 다른 명령을 수행하도록 하는 일종의 조건문이다.

  • 모든 값, 범위를 기반으로 판단하는 if 문과 달리 정수 값이나 열거된 값 또는 문자, 문자열만을 사용할 수 있다.

  • 컴파일러를 통해 실행 경로를 설정하는 점프 테이블이라는 것이 만들어지게 되어서 많은 조건을 비교하여야 할 때, if else 보다 더 빠른 성능을 보이게 된다.

    → case의 수가 5개 이상이라면, 성능 차이가 보이기 시작한다.

  • if else에 비하여서 좋은 가독성을 가지고 있다.

switch 문

public static String monthCheck(int num){
        int days = 0;
        switch (num) {
            case 1 :
            case 3 :
            case 5 :
            case 7 :
            case 8 :
            case 10 :
            case 12 :
                days = 31;
                break;
            case 4 :
            case 6 :
            case 9 :
            case 11 :
                days = 30;
                break;
            case 2 :
                days = 28;
                break;
            default:
                days = -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

 

if else 문 (비교 용)

public static String monthCheck(int num){
        int days = 0;
        if (num == 1 || num == 3 || num == 5 || num == 7 || num == 8 || num == 10 || num == 12){
            days = 31;
        } else if (num == 4 || num == 6 || num == 9 || num == 11){
            days = 30;
        } else if (num == 2){
            days = 28;
        } else {
            days = -1;
        }
        return "입력하신 달은 "+days+"일 입니다.";
    }

 

반복문?

어떠한 명령을 일정한 횟수만큼 반복하여 수행하도록 하는 명령문이다.

1부터 100까지를 더하는 코드를 여러 방법으로 작성해보았다.

 

 

while 문

  • 조건 식이 맞는 경우 실행되는 반복문이다. (bool 값을 통해 반복한다.)

구조

while(조건식){
        //do something
}

// 무한 루프 : 사용시 조건 검증과 break; 을 통해 탈출해야한다.
while(true){
        //do something
}
public static void whiles(){
    int total = 0;
    int loopCount = 1;
    while(loopCount <= 100){
        total += loopCount;
        ++loopCount;
    }
    System.out.println(total);
}

 

 

do / while 문

  • while 문과 비슷하지만 조건 식이 만족되지 않더라도 무조건 한 번은 실행되는 반복문이다.

구조

do{
        //do something
}while(조건식);

// 무한 루프 : 사용시 조건 검증과 break; 을 통해 탈출해야한다.
do{
        //do something
}while(true);

코드

public static void doWhiles(){
    int total = 0;
    int loopCount = 1;
    do {
        total += loopCount;
        ++loopCount;
    }while(loopCount <= 100);
    System.out.println(total);
}

 

 

for 문

  • 반복된 횟수가 고정된 경우 사용하거나, index 위치나 값이 필요한 경우 사용한다.

구조

for(조건식){ 
        //do something
}

// 무한 루프 : 사용시 조건 검증과 break; 을 통해 탈출해야한다.
// 해당 구문은 디컴파일 시 While(true)로 변경되어 있다.
for(;;){ 
        //do something
}

코드

public static void fors(){
    int total = 0;
    for (int loopCount = 0; loopCount <= 100; loopCount++) {
        total += loopCount;
    }
    System.out.println(total);
}

 

 

labeled for 문

  • 각각의 for 문에 대하여서 라벨을 부여함으로써 중첩 for문에서 조건에 따라 중단하거나 계속하게끔 사용하기 좋다.
  • 내부에 중첩된 for 문에 대해서도 라벨 부여가 가능하다.

구조

label:
        for(조건식){
                //do something
        }

혹은

label:
        for(조건식){
                //do something
                for(조건식){
                        //do something
                        break label;
                        // 혹은 continue;
                }
        }

코드

public static void labeledFors(){
    int total = 0;

        // 무한 루프
    loop1: for (;;){
        for (int inLoop=0;inLoop<=1000;inLoop++){
            total += inLoop;

                        // 100번을 반복하게 되면 loop1을 탈출한다. 
            if(inLoop==100) {
                break loop1;  
            }
        }
    }
    System.out.println(total);
}

 

 

Enhanced for 문 (for each)

  • index 값이 아닌 요소를 통한 값의 순회를 진행한다.
  • 가변적인 컬렉션의 값을 처리하기에 좋다.

구조

for(Type variable : collection<Type>, Type[] array){
        //do something
}

코드

public static void enhancedFors(){
    int total = 0;
    int[] numArr = {10,20,30,40,50,60,70,80,90,100};
    for (int num : numArr){
        total += num;
    }
    System.out.println(total);
}

 

 

HashTable, Vector, Stack의 Enumeration

  • Collection Framework가 만들어지기 전에 사용되던 인터페이스이다.

  • 순차 접근 시 컬랙션 객체의 내부가 수정되더라도 이를 무시하고, 끝까지 동작한다

    → Enumeration을 이용해서는 내부 요소를 수정할 방법이 없다. (읽기만 가능하다.)

    → 해당 컬랙션 내부의 수정 메서드를 이용하여야 한다.

Enumeration<String> enumeration = vector.elements();

// 읽어올 요소가 존재한다면, True 요소가 없다면 false를 반환한다.
enumeration.hasMoreElements()

// 다음 요소를 읽어올 때 사용한다.
enumeration.nextElement()

 

 

Collection의 Iterator()

Collection Framework 에서 사용되는 Enumeration을 보완한 인터페이스이다.

→ 요소를 제거하는 기능을 포함하였다. (remove)

  • Fail Fast Iterator (ArrayList, HashMap)

    • 순차적 접근이 끝나기 전에 객체에 변경이 일어날 경우 예외를 반환한다.

      → 동일한 자원의 수정 발생 시 데이터의 일관성을 보장하기 위함이다.

    • 내부적으로 변경, 수정된 횟수를 나타내는 modCount 필드가 있다.

      • 하나 이상의 요소가 제거된 경우

      • 하나 이상의 요소가 추가된 경우

      • 컬렉션이 다른 컬렉션으로 대체되었을 때

      • 컬렉션이 정렬될 때 등

        → 구조가 변경될 때마다 카운터가 증가하게 된다..

        → 데이터를 순회하는 동안 expectedModCount에 modCount 값을 저장하고 비교하며,

        → 두 개의 값이 달라질 경우 ConcurrentModificationException()를 발생시킨다.

        → next(), remove(), add(), remove(), forEachRemaining()에서 사용된다.

        → HashTable에도 expectedModCount, modCount를 사용한다.

  • Fail Safe Iterator (CopyOnWriteArrayList, ConcurrentHashMap)

    • Enumeration와 같이 내부가 수정되더라도 예외가 발생하지 않는다.

    • 사실은 컬렉션의 원본이 아닌 복제본을 순회하기 때문이다.

      → 복제된 컬렉션을 관리하기 위해 추가적인 메모리를 사용한다.

 

과제 0. JUnit 5 학습하세요.

 

JUnit 5를 만들게 된 이유? (JUnit 4의 단점)

  • IDE에 대한 강한 결합도를 해결하기 위하여

  • 부족했던 확장성의 해결

    → 하나의 @Runwith에 여러 Rule을 사용하여 확장하고, Runwith들이 결합되지 못했다.

  • 하나의 Jar에 포함되어 있던 코드를 분리하였다. (하나의 Jar의 큰 책임)

이전 버전의 문제점을 해결하며 좀 더 단순하고, 확장성 있게 만들기 위해 그런 선택을 하였다고 한다.

그 외에도 외부 라이브러리를 사용해야 했던 Parameter Test 등의 부가 기능을 공식적으로 지원한다.

//넘겨지는 Source의 수 만큼 테스트를 반복한다.

@ParameterizedTest(name = "{index} {displayName} SourceParam = {0}")
@ValueSource(strings = {"1", "2", "3"})
@NullAndEmptySource // CsvSource 등..
void repeatParameterTest(String Param) {
    System.out.println("Param = " + Param);
}

// 넘겨지는 정수 값 만큼 테스트 반복.
@RepeatedTest(10)
void repeatTest(String Param) {
    System.out.println("test");
}

혹시 Junit 4를 사용하고 있다면, Parameter Test가 필요한 경우

Pragmatists/JUnitParams

해당 라이브러리를 사용하면 된다.

 

그 외에도

 

접근 제어자 변경

  • public 접근 제어자가 필요했던 4 버전과 달리 5 버전은 패키지 범위에서 실행이 가능하다.

 

좋아진 확장성

  • Extension 하나로 여러 규칙을 통합, 조합 가능하다.

    → @ExtendWith, @RegisterExtension, Java ServiceLoader

 

분리된 Jar (모듈)

  • Junit 4의 Jar→Vintage(4 버전과의 호환성), Jupiter(5 버전 모듈), Platform(Extension, 실행, 관리 등)

 

다양해진 assert 방식

  • assertThat → assertThrows, assertEquals, assertTimeout, assertNotNull 등..

 

한 번에 여러 test를 실행 가능

  • 기존에 하나의 테스트가 실패하면 더 이상 진행되지 않는 Junit 4의 문제점을 해결

      assertAll(
              () -> assertNotNull(),
              () -> assertEquals(),
              () -> assertTrue(),
              () -> assertTimeout(),
      );

등이 변경되었다.

 

 

Junit 5의 Test Cycle

        // 모든 테스트가 실행되기 이전에 1번 호출된다.
    @BeforeAll
    public static void beforeAll() {
        System.out.println("AppTest.beforeAll");
    }

    // 모든 테스트가 실행된 이후에 1번 호출된다.
    @AfterAll
    public static void afterAll() {
        System.out.println("AppTest.afterAll");
    }

    // 각각의 테스트가 실행되기 이전에 호출된다.
    @BeforeEach
    public void beforeEach() {
        System.out.println("AppTest.beforeEach");
    }

    // 각각의 테스트가 실행된 이후에 호출된다.
    @AfterEach
    public void afterEach() {
        System.out.println  ("AppTest.afterEach");
    }

 

 

JUnit 5의 Dynamic Test

  • Junit 5부터는 테스트가 런타임 환경으로 생성되고 수행이 가능하게 되었다.
  • 이를 통해 외부 자원을 활용하고, 랜덤 데이터를 생성하여 활용할 수 있다.
@TestFactory
Stream<DynamicNode> dynamicTests() {
        return Stream.of("AAA","BBB","CCC","DDD")
                        .map(text -> dynamicTest("AAAEEETTT", () -> assertTrue(text.contains("AAA")));
}

 

 

Junit 5의 Tagging, Filtering

  • @Tag 주석을 통해 테스트 클래스 및 메서드에 태그를 지정할 수 있다.
@Tag("fast")
@Tag("User Test")...

 

 

Junit 5의 Test 순서 설정

  • @Order(정수 값) : 넘겨진 정수 값을 우선순위로 설정한다.

    → test_1 → test_2 → test_3...

@TestMethodOrder(OrderAnnotation.class)
class OrderedTest {

----------------------------

@Test
@Order(1)
void test_1() {
    // do someThing
}

@Test
@Order(2)
void test_2() {
    // do someThing
}

@Test
@Order(3)
void test_3() {
    // do someThing
}

 

 

JUnit 5의 Test 이름 표시

  • @DisplayName("test name")

    • DisplayNameGenerator 보다 우선적으로 적용된다.

    • method 위에 작성한다.

      @Test
      @DisplayName("AAA Test")
      void Arrange_Act_Assert() {
            // Arrange : 생성
            // Act : 조작
            // Assert : 결과 비교
      }
  • @DisplayNameGenerator

    • Class 내부의 모든 메서드에 적용된다.

    • Class 위에 작성한다.

      // 모든 밑줄 문자를 공백으로 바꾸는 static class
      @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
      
      // 표준 표시 생성 static class
      @DisplayNameGeneration(DisplayNameGenerator.Standard.class)

 

Junit 5의 테스트에 대한 환경 변수 설정

  • @EnabledIfEnvironmentVariable : 지정된 환경 변수 값이 지정된 정규식과 일치하는 경우 활성화
  • @EnabledIfSystemProperty : JVM 시스템에 따라서 테스트를 활성화
  • @EnabledOnJre : 지정된 JRE (Java Runtime Environment) 버전에서만 활성화
  • @EnabledForJreRange : min으로 지정된 jre와 max로 지정된 jre 버전 사이에서 활성화
  • @EnabledOnOs : 지정된 운영 체제에서만 활성화
@EnabledIfEnvironmentVariable(named = "GDMSESSION", matches = "ubuntu") // ununtu 서버에서 실행
@EnabledIfSystemProperty(named = "java.vm.vendor", matches = "Oracle.*") // Oracle jvm에서 실행
@EnabledForJreRange(min = JRE.JAVA_8, max = JRE.JAVA_13) // 8 ~ 13 버전의 자바에서 실행
@EnabledOnJre({JRE.JAVA_8, JRE.JAVA_9}) // 8, 9 버전의 자바에서만 실행
@EnabledOnOs({OS.WINDOWS, OS.LINUX}) // windows와 linux에서 실행

 

 

Junit5 Assertions

// assertEquals(A, B);
assertEquals("user", user.getUserName());

// assertTrue(A, B);
assertTrue("L", firstName.startsWith(user.getFirstName().charAt(0));

Exception exception = assertThrows(ArithmeticException.class, () -> calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());

// 시간 제한
assertTimeout(Duration.ofSeconds(1), () -> new user("Lob"));

// 시간이 초과되면 테스트 실패
assertTimeoutPreemptively(ofSeconds(1), () -> { Thread.sleep(5000); });

 

과제 1. live-study 대시 보드를 만드는 코드를 작성하세요.

package assignment.gitapi;

import org.kohsuke.github.*;

import java.io.IOException;
import java.util.*;

public class Application {

    private final String token = "tokenString";
    private GitHub github;

    public static void main(String[] args){

        Application app = new Application();

        try {
            app.run();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void run() throws IOException {

        // 연결
        connectGitApi();

        GHRepository ghRepository = github.getRepository("whiteship/live-study");
        // 참여자 이름, 참여 횟수
        Map<String, Integer> participant = new HashMap<>();
        // 모든 이슈를 가져온다.
        List<GHIssue> issues = ghRepository.getIssues(GHIssueState.ALL);

        //Find Issue
        for (GHIssue issue : issues) {
            // 각각의 Issue 에 존재하는 comment 들을 저장.
            List<GHIssueComment> commentList = issue.getComments();

            // 혹시 모를 유저 중복을 제거하기 위한 Set
            Set<String> nameList = new HashSet<>();

            addParticipantInSet(commentList, nameList);

            // 참여자 명단에서 비교한다.
            for (String s : nameList) {
                hasParticipantInSet(participant, s);
            }
        }
        printParticipantRate(participant);
    }

    private void hasParticipantInSet(Map<String, Integer> participant, String s) {
        if (!participant.containsKey(s)){
            participant.put(s, 1);
        } else {
            Integer integer = participant.get(s);
            participant.put(s, ++integer);
        }
    }

    private void addParticipantInSet(List<GHIssueComment> commentList, Set<String> name) throws IOException {
        for (GHIssueComment ghIssueComment : commentList) {
            name.add(ghIssueComment.getUser().getLogin());
        }
    }

    private void printParticipantRate(Map<String, Integer> participant) {
        participant.forEach((key, value)-> {
            double percent = (double) (value * 100) / 18;
            System.out.println(key+"  :  "+String.format("%.2f", percent)+"%");
        });
    }

    private void connectGitApi() throws IOException {
        github = new GitHubBuilder().withOAuthToken(token).build();
    }
}

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

 

과제 2. LinkedList를 구현하세요.

package assignment.linkedlist;

public class ListNode {

    private int elements;

    private ListNode next = null;

    public ListNode(int data) {
        elements = data;
    }

    public ListNode add(ListNode newElement){
        if (this.next == null){
            this.next = newElement;
            return this;
        }

        ListNode nextNode = this.next;
        while (nextNode.next != null){
            nextNode = nextNode.next;
        }

        nextNode.next = newElement;

        return this;
    }

    public ListNode add(ListNode head, ListNode nodeToAdd, int position){
        ListNode nextNode = head;

        for (int loop = 0; loop < position-1; loop++) {
            if (nextNode.next == null){ break; }
            nextNode = nextNode.next;
        }

        ListNode tmp = nextNode.next;
        nextNode.next = nodeToAdd;
        nodeToAdd.next = tmp;

        return this;
    }

    public ListNode remove(ListNode head, int positionToRemove){
        ListNode nextNode = head;

        for (int loop = 0; loop < positionToRemove-1; loop++) {
            nextNode = nextNode.next;
        }
        // 현재 시점의 nextNode 에서 next 가 지워져야할 node
        ListNode tmp = nextNode.next;
        nextNode.next = tmp.next;
        tmp = null;

        return this;
    }

    public boolean contains(ListNode head, ListNode nodeToCheck){
        ListNode nextNode = head;

        while (nextNode.next != null){
            if (nextNode.elements == nodeToCheck.elements){
                return true;
            }
            nextNode = nextNode.next;
        }

        return false;
    }

    public void printForEach(){
        ListNode nextNode = this;

        while (nextNode != null){
            System.out.println(nextNode.elements);
            nextNode = nextNode.next;
        }
    }

    public int size(){
        ListNode nextNode = this;

        int size = 0;
        while (nextNode != null){
            ++size;
            nextNode = nextNode.next;
        }

        return size;
    }
}

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

테스트

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

 

과제 3. Stack을 구현하세요.

package assignment.stack;

public class StackNode {

    private int[] elements;
    private int head = 0;
    private int size = 16;
    private int modifyCount = 0;

    public StackNode() {
        elements = new int[size];
    }

    public StackNode(int size) {
        elements = new int[this.size = size];
        System.out.println(this.size);
    }

    public boolean push(int data){
        if (modifyCount >= size){ return false; }
        elements[modifyCount] = data;
        head = modifyCount;
        ++modifyCount;
        return true;
    }

    public int pop(){
        if (head < 0) { return -1; }
        int res = elements[head];
        elements[head] = -1;
        head--;
        return res;
    }

    public void print(){
        for (int index : elements){
            if (index == 0 || index == -1){
                System.out.println("is Empty");
                break;
            }
            System.out.println(index);
        }
    }
}

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

테스트

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

 

과제 4. 앞서 만든 ListNode를 사용해서 Stack을 구현하세요.

package assignment.stack;

import assignment.linkedlist.ListNode;

import java.util.List;

public class ListStack {

    private ListNode node = null;
    private ListNode head;

    public void push(int data){
        if (node == null){
            node = new ListNode(data);
            head = node;
        } else {
            ListNode nextNode = node.next;
            while (nextNode.next != null){
                nextNode = nextNode.next;
            }
            nextNode.next = new ListNode(data);
            head = nextNode.next;
        }
    }

    public int pop(){
        ListNode nextNode = node;
        ListNode preNode = head;

        if (node.next == null){ node = null; }

        while (nextNode.next != null){
            preNode = nextNode;
            nextNode = nextNode.next;
        }

        int result = head.elements;
        head = preNode;
        preNode.next = null;

        return result;
    }

    public void print(){
        if (node == null){
            System.out.println("is empty");
        } else if (node.next == null){
            System.out.println(node.elements);
        } else{
            while (node.next != null){
                System.out.println(node.elements);
            }
        }

    }

    public static class ListNode{
        private int elements;
        private ListNode next = null;

        public ListNode(int data){
            elements = data;
        }
    }

}

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

테스트

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

 

(optional) 과제 5. Queue를 구현하세요.

배열 사용

package assignment.queue;

public class ArrayQueue {

    private int[] elements;
    private int size = 16;
    private final int head = 0;
    private int modifyCount = 0;

    public ArrayQueue() {
        elements = new int[size];
    }

    public ArrayQueue(int size) {
        elements = new int[size];
    }

    public boolean offer(int data){
        if (modifyCount >= size){ return false; }
        if (data < 0){ return false; }

        elements[modifyCount] = data;
        ++modifyCount;
        return true;
    }

    public int poll(){
        int res = elements[head];
        int modify = 0;
        for (int loop = 1; loop < modifyCount; loop++) {
            elements[loop-1] = elements[loop];
            modify = loop;
        }
        elements[modify] = -1;
        modifyCount = modify;
        return res;
    }

    public int size() {
        int size = 0;
        for (int index : elements){
            if (index == -1){ break; }
            if (index == 0){ break; }
            ++size;
        }
        return size;
    }

    public void print() {
        for (int index : elements){
            if (index == -1){ break; }
            if (index == 0){ break; }
            System.out.println(index);
        }
    }
}

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

테스트

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

 

ListNode 사용

package assignment.queue;

import assignment.stack.ListStack;

public class ListQueue{

    private ListNode node = null;

    private ListNode head;

    public ListQueue() {
    }

    public ListQueue(int element) {
        node = new ListNode(element);
        head = node;
    }

    public void offer(int data){
        if (node == null){
            node = new ListNode(data);
            head = node;
        } else {
            ListNode nextNode = node;
            while (nextNode.next != null){
                nextNode = nextNode.next;
            }
            nextNode.next = new ListNode(data);
        }
    }

    public int poll(){
        int result = head.elements;

        ListNode nextNode = head.next;
        head = null;
        head = nextNode;

        return result;
    }

    public int size() {
        int size = 0;
        ListNode nextNode = head;
        while (nextNode != null){
            ++size;
            nextNode = nextNode.next;
        }
        return size;
    }

    public void print() {
        if (head == null){
            System.out.println("is empty");
        } else if (head.next == null){
            System.out.println(node.elements);
        } else {
            ListNode nextNode = head;
            while (nextNode != null){
                System.out.println(nextNode.elements);
                nextNode = nextNode.next;
            }
        }

    }

    public static class ListNode{
        private int elements;
        private ListNode next = null;

        public ListNode(int data){
            elements = data;
        }
    }
}

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

테스트

Lob-dev/DashBoardDemo

 

Lob-dev/DashBoardDemo

Java Live Study assignment test(week 4). Contribute to Lob-dev/DashBoardDemo development by creating an account on GitHub.

github.com

 

참고 자료

Fail Fast and Fail Safe Iterators in Java - GeeksforGeeks

 

Fail Fast and Fail Safe Iterators in Java - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

Difference between Iterator and Enumeration in Java

 

Difference between Iterator and Enumeration in Java

Difference between Iterator and Enumeration in Java Iterator and Enumeration both are the cursors to traverse and access an element from the collection. They both belong to the collection framework. Enumeration was added in JDK1.0 and Iterator in the JDK.1

www.tutorialspoint.com

Loops in Java | Java For Loop - Javatpoint

 

Loops in Java | Java For Loop - Javatpoint

Loops in Java | Java for loop with java while loop, java for loop, java do-while loop, java for loop example, java for loop programs, labeled for loop, for each loop or advanced for loop, java infinite for loop example, java simple for loop, nested for loo

www.javatpoint.com

JUnit 5 User Guide

 

JUnit 5 User Guide

Although the JUnit Jupiter programming model and extension model will not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and cus

junit.org

if(kakao)2020

 

if(kakao)2020

오늘도 카카오는 일상을 바꾸는 중

if.kakao.com

 

3주 차 시작합니다!

연산이란?

주어진 정보를 통해 일정한 규칙에 따라 어떤 값이나 결과를 구하는 과정을 의미한다.

 

연산자란?

연산을 진행하는 동안 사용되는 기호를 말한다.

연산 대상의 수나 연산 방식에 따라서 명칭이 나뉘게 된다.

 

피연산자란?

연산될 대상을 말하며. 변수, 상수, 리터럴 등을 의미한다.

 

단항 연산자 (Unary Operator)

연산이 수행될 피연산자가 1개인 경우 해당 연산자를 단항 연산자라고 한다.

  • 전위 증감, 후위 증감 연산자
  • 단항으로써 사용하는 +, - 연산자 (부호 연산자)
  • 비트 반전 ~ 연산자

이항 연산자 (Binary Operator)

연산이 수행될 피연산자가 2개인 경우 해당 연산자를 이항 연산자라고 한다.

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 대입 연산자
  • 논리 연산자

삼항 연산자 (Ternary Operator)

연산이 수행될 피연산자가 3개인 경우 해당 연산자를 삼항 연산자라고 한다.

  • 조건 연산자 ( 조건식? True의 경우 반환될 식 : False의 경우 일시 반환될 식 )

 

산술 연산자 (Arithmetic Operator)

산술 연산자는 수치 계산을 실시하는 연산자를 말한다.

  • A + B : 덧셈 연산자 (정수형, 실수형 리터럴뿐만 아니라 문자열 연산에도 사용 가능하다.)

      // 기본적인 사용법
          int numA = 10;
          int numB = 10;
    
          System.out.println(numA + numB); // 20
    
          int i = numA + numB;
        System.out.println(i);
    • 문자열 더하기 연산

      String strA = "hello";
      strA += " World";
      System.out.println(strA); // hello world
    • 값의 범위가 다른 Type 간의 연산 (공통)

      // long과 int를 더하는 경우에는?
      long longA = 10l;
      int numC = 10;
      
      System.out.println(longA+numC);
      
      // 실제로는 int 가 long으로 Type Conversion이 된 것을 알 수 있다. - 2주차 내용
      System.out.println(longA + (long)numC);
      
      // long과 double를 더하는 경우에는?
      long longA = 10;
      double doubleA = 10.0;
      
      System.out.println(longA+doubleA);
      
      // 실제로는 long 이 double로 Numeric Promotion이 된 것을 알 수 있다.
      System.out.println((double)longA + doubleA);
      
  • A - B : 뺄셈 연산자

      int numA = 10;
      int numB = 10;
    
      System.out.println(numA - numB); 
    
      int j = numA - numB; 
      System.out.println(j); // 0 출력
  • A / B : 나눗셈 연산자

    나눗셈 연산과 나머지 연산의 경우 피연산자로 0을 사용하면 Exception이 발생한다.

      int numA = 10;
      int numB = 10;
    
      System.out.println(numA / numB); 
    
      int k = numA / numB;
      System.out.println("k = " + k); // 1 출력 
  • A % B : 나머지 연산자

      int numA = 11;
      int numB = 2;
    
      System.out.println(numA % numB); 
    
      int l = numA % numB;
      System.out.println("l = " + l); // 1 출력
  • A * B : 곱하기 연산자

      int numA = 11;
      int numB = 2;
    
      System.out.println(numA * numB); 
    
      int h = numA % numB;
      System.out.println("h = " + h); // 22 출력
  • ++N, --N : 전위 증감 연산자

      int numC = 10;
      numC = --numC;
      System.out.println("numC = " + numC);
    
      int numD = 10;
      numD = ++numD;
      System.out.println("numD = " + numD);
    
      // Decompile
      // 전위 증감 연산자는 내부적으로 A = A +- 1; 의 형식을 취하는 것을 알 수 있다.
    
      int numC = 10;
      int numC = numC - 1;
      System.out.println("numC = " + numC);
    
      int numD = 10;
      int numD = numD + 1;
      System.out.println("numD = " + numD);
    
  • N++, N-- : 후위 증감 연산자

      int numE = 10;
      System.out.println(numE--);
      System.out.println("numE = " + numE);
    
      int numF = 10;
      System.out.println(numF--);
      System.out.println("numF = " + numF);
    
      // Decompile
      // 우선 임시변수를 만들어서 해당 값을 복사한뒤 전달하고 반영된 값을 그 이후에 제공한다.
    
      int numE = 10;
      byte var10001 = numE; // 임시변수에 값 복사
      int numE = numE - 1; // 변수 연산
      System.out.println(var10001); // 임시변수 출력
      System.out.println("numE = " + numE); // 연산된 변수를 제공
    
      int numF = 10;
      var10001 = numF; 
      int numF = numF - 1;
      System.out.println(var10001);
      System.out.println("numF = " + numF);
    • Wrapper와 Primitive 간의 산술 연산

    • Wrapper Class* (Integer, Long, Double)와 Primitive Type 연산 시에는 Wrapper Class 피연산자가 Unboxing 되어 연산이 진행되고 다시 Boxing이 되는 등에 성능상 오버헤드가 발생한다.

      int numB = 10;       
      
      Integer numA = numB; //autoboxing
      
      numA = numA + numB; //unboxing 후 연산 -> 결과 반영 전 다시 autoboxing 진행

      그리고 Operand Stack에서 관리되는 Primitive TypeHeap에서 참조되는 Wrapper Type은 값에 접근하는 시간에 차이가 존재한다. (일반적으로 Wrapper의 Access 시간이 길다.)

    • Primitive 도 일정 값 이상의 경우, Constant Pool이라는 Stack 외부에 존재하는 공간에 저장된다.*

    • JVM 설정 중에 Primitive 값의 크기에 대한 설정이 있다고 한다.

    • 즉 프로그램의 환경적인 부분에 따라서 수치나 연산 시간 등이 달라질 수 있다.

 

비트 연산자?

  • A & B : AND 연산 ( A와 B의 비트에 대해서 AND 연산을 수행한다.)

      //양쪽의 비트가 1이면 1, 0이면 0, 0 과 1 인 경우에는 0을 나타낸다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 0110 : AND 연산
    
      int i = num & nums;
      System.out.println("i = " + i); // 6
  • A | B : OR 연산 ( A와 B의 비트에 대해서 OR 연산을 수행한다.)

      //하나의 비트가 1이면 1, 양쪽의 비트가 0이면 0이다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 1111 : OR 연산
    
      int i = num | nums;
      System.out.println("i = " + i); // 15
  • A ^ B : XOR 연산 ( A와 B의 비트에 대해서 XOR 연산을 수행한다.)

      //하나의 비트가 1이면 1, 양쪽의 비트가 0이면 0, 양쪽의 비트가 1이면 0이다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 1001 : XOR 연산
    
      int i = num ^ nums;
      System.out.println("i = " + i); // 9
  • A << N : 시프트 연산자 ( A의 비트를 좌측으로 N만큼 이동한다.)

      //비트를 좌측으로 N만큼 이동하고 우측에서 N만큼의 영역을 0으로 채우게된다.
    
      int num = 15; // 1111
      int nums = 2;
    
      int i = num << nums; // 111100 = 좌측으로 비트를 2칸씩 이동.
      System.out.println("i = " + i);
  • A >> N : 시프트 연산자 ( A의 비트를 우측으로 N만큼 이동한다.)

      //비트를 우측으로 N만큼 이동하고 좌측에서 N만큼의 영역을 0으로 채우게된다.
    
      int num = 15; // 1111
      int nums = 2;
    
      int i = num >> nums; // 0011 = 우측으로 비트를 2칸씩 이동.
      System.out.println("i = " + i);
  • A >>> N : 부호가 없는 시프트 연산자 ( A의 2진수를 우측으로 비트를 N만큼 이동한다.)

      int num = -15; // 11111111111111111111111111110001
      int nums = 2;
    
      // 부호를 신경쓰지 않고 모든 비트 값들을 오른쪽으로 이동시킨 후에 왼쪽의 빈 공간은 모두 
      // 0 으로 채운다.
    
      int i = num >>> nums; 
      System.out.println("i = " + i); // 1073741820 = 00111111111111111111111111111100
    
      int j = -15 >> nums;         
      System.out.println("j = " + j); // -4 : 11111111111111111111111111111100 
  • ~A : 반전 연산자 ( A 내부의 비트 값을 반전시킨다.)

      int num = -15; // 11111111111111111111111111110001
    
      System.out.println(~num);   // 14 : 00000000000000000000000000001110

 

관계 연산자?

  • A == B : 동일성 비교 ( A와 B가 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA == numB; // boolean 값을 반환.
    
      if (isSame){
          System.out.println(isSame); // true
      }
    
      혹은
    
      if (numA == numB){
          System.out.println(isSame); // true
      }
  • A != B : 동일하지 않은 경우 ( A와 B가 같지 않다면?)

      int numA = 10;
      int numB = 9;
    
      boolean isSame = numA != numB;
    
      if (isSame){
          System.out.println(isSame); //true
      }
    
      또는
    
      if (numA != numB){
          System.out.println(isSame); //true
      }
    
      또는
    
      boolean isSame = numA == numB;
    
      if (!isSame){
          System.out.println(isSame); //false 
      }
  • A > B : 대소 관계 비교 ( A가 B보다 크다면?)

      int numA = 10;
      int numB = 9;
    
      boolean isSame = numA > numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA > numB){
         System.out.println(isSame); //true
      }
  • A >= B : 대소관계 비교 ( A가 B보다 크거나 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA >= numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA >= numB){
         System.out.println(isSame); //true
      }
  • A < B : 대소관계 비교 ( A가 B보다 작다면?)

      int numA = 9;
      int numB = 10;
    
      boolean isSame = numA < numB;
    
      if (isSame){ 
         System.out.println(isSame); //true
      }
    
      if (numA < numB){
         System.out.println(isSame); //true
      }
  • A <= B : 대소관계 비교 ( A가 B보다 작거나 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA <= numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA <= numB){
         System.out.println(isSame); //true
      }

 

논리 연산자?

  • 조건식 1 && 조건식2 : 조건식1과 조건식 2를 모두 만족한다면?

      int numA = 10;
      int numB = 10;
      int numC = 10;
      int numD = 10;
    
      boolean bool = numA == numB && numC < numD; //false
    
      if (bool){
         System.out.println(bool);
      }else {
           **System.out.println(bool);
      }
    
      if (numA == numB && numC == numD){
         System.out.println(numA == numB && numC == numD); //true
      }
  • 조건식1 || 조건식2 : 조건식1 혹은 조건식2를 만족한다면?

      int numA = 10;
      int numB = 10;
      int numC = 10;
      int numD = 10;
    
      boolean bool = numA == numB || numC < numD; //true
    
      if (bool){
         System.out.println(bool); 
      }else {
         System.out.println(bool);
      }
    
      if (numA == numB || numC == numD){
         System.out.println(numA == numB && numC == numD); //true
      }
  • ! : 논리 반전 연산자 : != 같지않다면?, !< 작지않다면?

      int num = 10;
      String conn = null;
    
      boolean bool = num != 9;
    
      if (bool){
         System.out.println(bool);
      }else {
         System.out.println(bool);
      }
    
      if (conn != null){
         System.out.println("true");
      }else {
         System.out.println("false");
      }
  • JDK Assertion (JDK 1.4+)

    이전에 작성된 코드에서 사용된 assert라는 변수들과의 하위 호환성을 위해 기본적으로 비활성화되어 있다.

    ```java
    assert service != null; // true -> 아무 일도 발생하지 않는다.
                                                // false -> AssertionError (Unchecked Exception) 발생
    
    혹은
    
    assert service != null : "Service is Null"; // false 시 추가적으로 메세지를 제공한다.

 

instanceof

  • A instanceof B : (공변) 타입 검증 연산자, A가 B의 타입 혹은 하위 구현체인지 판단한다.
    • Reference Type 만 사용 가능하다.
public static void main(String[] args) {
    InstanceofExample example = new InstanceofExample();
    example.run();
}

public void run() {
    SomeClass someClass = new SomeClass();
    SomeClasz someClasz = new SomeClasz();
    SomeClassChild someClassChild = new SomeClassChild();

    typeCheck(someClass);      // is SomeClass
    typeCheck(someClasz);      // is SomeClasz
    typeCheck(someClassChild); // is SomeClass
}

public void typeCheck(Object obj){
    if (obj instanceof SomeClass){
        System.out.println("is SomeClass");
    }else if (obj instanceof SomeClasz){
        System.out.println("is SomeClasz");
    }
}

class SomeClass {

}
class SomeClassChild extends SomeClass{

}
class SomeClasz {

}

 

대입 연산자? (assignment(=) operator)

Primitive 변수에 값을 할당하기 위해 사용하거나 Reference의 참조 값을 할당하기 위해 사용한다.

  • 그 외에도 이항 연산자와 결합하여 사용할 수 있다.
int num = 4;
int variable;

variable = num; // variable = 4;
System.out.println("= 연산 : " + variable); // = 연산 : 4

variable += num; // variable = 4 + 4
System.out.println("+= 연산 : " + variable); // += 연산 : 8

variable -= num; // variable = 8 - 4
System.out.println("-= 연산 : " + variable); // -= 연산 : 4

variable *= num; // variable = 4 * 4
System.out.println("*= 연산 : " + variable); // *= 연산 : 16

variable /= num; // variable = 16 / 4
System.out.println("/= 연산 : " + variable); // /= 연산 : 4

variable %= num; // variable = 4 % 4 
System.out.println("%= 연산 : " + variable); // %= 연산 : 0

variable = 10;
variable &= 2; // variable = 10 & 2
System.out.println("&= 연산 : " + variable); // &= 연산 : 2

variable = 10;
variable |= 2; // variable = 10 | 2
System.out.println("|= 연산 : " + variable); // |= 연산 : 10

variable = 10;
variable ^= 2; // variable = 10 ^ 2
System.out.println("^= 연산 : " + variable); // ^= 연산 : 8

variable = 10;
variable <<= 2; // variable = 10 << 2
System.out.println("<<= 연산 : " + variable); // <<= 연산 : 40

variable = 10;
variable >>= 2; // variable = 10 >> 2
System.out.println(">>= 연산 : " + variable); // >>= 연산 : 2

variable = 10;
variable >>>= 2; // variable = 10 >>> 2
System.out.println(">>>= 연산 : " + variable); // >>>= 연산 : 2

 

Arrow operator? ( (parameter list) -> lambda body )

Java 8에 추가된 lambda 식을 지원하기 위해 사용되는 연산자이다.

// 이러했던 구문을
Runnable runnable = new Runnable() {
   @Override
   public void run() {
       System.out.println("Hello!");
   }
};

// 이렇게 축약할 수 있다.

// 하나의 식, 로직을 가지고 있다면 { } block 과 return도 생략 가능하다.
Runnable runnable = () -> System.out.println("Hello!");

// 그렇지 않다면 이런식으로 작성해야한다.
Runnable runnable = () -> {
    System.out.println("Hello!");
    helloLogic();
**};**

 

3항 연산자? ( Ternary Operator : 변수 = (조건)? true 인 경우 : false 인 경우)

삼항 연산자는 피연산자 3개를 사용하며, if-then-else 명령문의 축약형이라고 할 수 있다.

int num = 10;
String result;

if(num == 10){
        result = "true";
        System.out.println("result = " + result);
}else {
        result = "false;
        System.out.println("result = " + result);
}

result = (num == 10) ? "true" : "false";
System.out.println("result = " + result);

 

연산자 우선순위?

수식 내에 여러 연산자가 함께 등장할 때, 어느 연산자가 먼저 처리될 것인가를 결정합니다.

( ) 등으로 우선순위, 결합 방향에 따른 연산자의 처리 순서를 변경할 수도 있습니다.

 

연산자의 결합 규칙?

수식 내에 우선순위가 같은 연산자가 둘 이상 있을 때, 먼저 어느 연산을 수행할 것인가를 결정합니다.

int numA = 5;
int numB = 2;
int numC = 2;

// +, - 의 우선 순위가 동일하다.
// 결합 방향에 따라서 왼쪽부터 오른쪽으로 진행하게 된다.
int res = numA + numB - numC;

 

Java 13의 switch 연산자?

기존 switch 문법으로 만든 요일 반환식 (윤년을 앞선 로직에서 체크했다고 가정.)

public static String monthCheck(int num){
        int days = 0;
        switch (num) {
            case 1 :
            case 3 :
            case 5 :
            case 7 :
            case 8 :
            case 10 :
            case 12 :
                days = 31;
                break;
            case 4 :
            case 6 :
            case 9 :
            case 11 :
                days = 30;
                break;
            case 2 :
                days = 28;
                break;
            default:
                days = -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

Java 12에서 switch는

  • case 라벨을 쉼표로 구분하여 사용하게끔 문법을 지원하게 되었다.
  • break을 통해 값을 반환할 수 있게끔 되었다.
public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 :
                break 31;
            case 4, 6, 9, 11 :
                break 30;
            case 2 :
                break 28;
            default:
                break -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

혹은 이와 같이 람다 식을 사용할 수 있게 되었다.

public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> 28;
            default -> -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

Java 13에서 switch는

  • 기존에 사용하던 break이라는 키워드를 yield로 대체(확장) 하게 되었다.
public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 :
                yield 31;
            case 4, 6, 9, 11 :
                yield 30;
            case 2 :
                yield 28;
            default:
                yield -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

또는

public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> 28;
            default -> {
                                System.out.println("잘못 입력했습니다.");
                                yield -1;
                        }
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

이렇게 표현할 수 있다.

 

Java 12 에서는 해당 기능을 미리보기 기능으로 제공하였고, (활성화 필요)

Java 13 에서는 해당 식을 확장하였으며,

Java 14 에서는 해당 기능을 표준으로 제공하게 되었다.

 

 

참고 자료

Primitive Type 종류와 값의 범위 그리고 기본 값

Primitive Variable 이란?

어떠한 Instance의 참조 값을 가지지 않고, 지정된 Type에 맞는 리터럴을 저장한 변수를 말한다.

객체를 다루지 않기 때문에 Null을 저장할 수 없으며, 같은 타입의 변수들이 동일한 값을 다룰 때에는 Operand Stack이나 Constant Pool에 리터럴을 저장해두었다가 꺼내어 사용한다.

**// 정수 타입
// JDK 7 까지는 int와 long에 대해서 Unsigned 를 지원하지 않았지만,
// JDK 8 부터는 int와 long에 대하여서 지원하기 시작하였다.**

// 1 byte (8 bit)
// -128 (-2^7)  ~  127 (2^7–1).
// 기본 값은 0. (공통적으로 Method, Loop Scope 등에 선언된 Primitive 변수는 초기화 해야한다.)
byte variableB;

// 2 byte (16 bit)
// -32,768(-2^15)  ~  32,767(2^15–1).
// 기본 값은 0.
short variableS;

// 4 byte (32 bit) 
// 2,147,483,648 (-2^31)  ~  2,147,483,647 (2^31-1)\
// int 를 통해 8, 16 진수를 표현 가능하다. 0 시작하면 8진수, 0x 로 시작하면 16진수 이다.
// (JDK 8+) 부호 없는 int를 만들기 위해서는 Integer.parseUnsignedInt 를 사용해야 한다.
// 기본 값은 0. 
int variableI;

// 8 byte (64 bit)
// -9,223,372,036,854,775,808 (-2^63)  ~  9,223,372,036,854,775,807 (2^63–1)
// (JDK 8+) 부호 없는 long를 만들기 위해서는 Long.parseUnsignedLong 를 사용해야 한다.
// 기본 값은 0.
long variableL;


**// 실수 (부동소수점 방식) 타입**

// 4 byte (32 bit)
// 1.40239846 x 10^-45  ~  3.40282347 x 10^38
// 기본 값은 0.0f
float variableF;

// 8 byte (64 bit)
// 4.9406564584124654 x 10-324  ~  1.7976931348623157 x 10308
// 기본 값은 0.0D
double variableD;


**// 논리 타입**

// 표시 용량은 1 bit (내부적으로는 1 byte 내부 7 비트에 값을 채우고 하나의 bit 만 사용한다.) 
// true , false
// 기본 값은 false
boolean bool;


**// 문자 타입**

// 2 Byte (16 bit)
// 유니코드를 표현하는 변수이며, 값의 범위는 0  ~  65,535 까지이다.
// 기본 값은 /u0000 (Unicode Character 'NULL')
char variableC

 

Primitive Type과 Reference Type

Reference Type?

Reference Type 은 Primitive Type을 제외한 모든 타입을 포함한다.

참조 값을 통해 관리하기 때문에 Reference Type이라고 한다. ( Null을 사용할 수 있다. )

Wrapper Class?

Primitive Type을 인스턴스로 다룰 수 있게끔 도와주는 것들을 말한다.

값을 조작하기 위해 작성된 Util Method를 사용하기 위해서 이용하는 경우도 많다.

  • Min, Max_value 상수, valueOf, parsexxx, toString 등..

Boxing과 Unboxing을 통해 Primitive Type을 감싸거나 다시 Primitive로 변환할 수 있다.

//대응 클래스
byte Byte      char Character
short Short    float Float
int Integer    double Double
long Long      boolean Boolean


//Wrapper Class 선언 예시
Integer num = new Integer(10);
            = Integer.valueOf(10);//혹은 primitive 변수



// Integer의 valueOf 메서드는 Boxing 할 값이-127~128 범위 값인 경우 기존에 있는 객체를 
// 재사용할 수 있게끔 작성되어 있다. 
// IntegerCache 클래스에 -127~128 만큼의 범위를 가지는 별도의 배열을 만들고 
// 해당 valueOf 로직에서 검증하여 중복된 경우 기존에 선언된 객체를 반환한다.
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

Primitive Type과 Reference Type의 차이점?

  • Reference 변수를 통해서 생성된 객체에 접근하고 조작하고 값을 받아올 수 있다.

  • Null을 다룰 수 있다. (NPE 가 발생될 수 있다.)

    • Null 을 가지는 Wrapper와 Primitive Type 연산 시 NPE 가 발생한다.
  • Generic을 사용할 수 있다.

  • 별도의 식별 값을 가진다. (Hashcode)

  • 성능 상의 차이가 있을 수도 있다.

 

그러면 Wrapper Class 를 사용하지 말라는 걸까?

  • Front 단의 데이터를 검증해야 한다거나, DB Table에서 Null을 다루고 있다면, 해당 데이터를 받아오는 객체에서 검증을 진행해야 하는 것이 맞다.
    • DB Table에서 Null 을 다룰 때 변수를 Primitive로 선언하여 사용한다면 0이 저장될 것이고 의도치 않은 결과가 발생할 수 있다고 생각한다.
  • Collection이나 Generic 은 Primitive를 지원하지 않는다. (결국 Wrapper를 사용해야 한다.)

 

리터럴

리터럴이란? wiki

어떤 변수에 의해 저장되는, 값 자체로써 변화하지 않는 것들을 의미한다.

// 변수를 초기화할 때 사용되는 값들도 모두 리터럴이다.
int numberA = 10;
double numberB = 10.11D;
String str = "문자열 리터럴";

변수 선언 및 초기화하는 방법

흐름

  1. 타입에 맞는 메모리 공간을 확보하고 (byte b;)
  2. 타입에 맞는 리터럴을 저장한다. (byte b = 100;)
// 정수 리터럴의 _ 은 숫자를 구별하기 위해 사용할 수 있게 지원해준다. 
// 100_000 = 100,000
// 맨 앞이나 뒤에 _ 를 작성할 경우 컴파일 에러가 발생한다.

byte variableB; 
variableB = 100;

short variableS;
variableS = 30_000;

// 10진수
int variableI = 100_000;
// 8진수
int variableI = 010; // 8
// 16진수
int variableI = 0xa; // 10
// 2진수
int variableI = 0b11; // 3

long variableL = 100_000_000_000; // 혹은 100_000_000_000l;

float variableF = 3.14f;

double variableD = 3.14; // 3.14D; 기본적으로 Double 로 판단하여 선언해준다.

char variableC = 'c';

boolean bool = true;

 

Variable Scope와 Life Cycle?

Class Scope

public class someClass {
        // 해당 Class 의 Bracket 내부에서만 사용이 가능하다.
        // 외부에서는 해당 Class 의 getter, setter 를 통해서 해당 변수를 접근할 수 있다.
        private int age = 32;
        -------------------------------------------
}

Method Scope

public class someClass {

        public static void main(String[] args) {
        VariableScope variableScope = new VariableScope();
        variableScope.someMethodA();
        System.out.println(variableScope.someMethodB());
    }        

        public void someMethodA() {
                // 해당 Method 의 Bracket 내부에서만 사용이 가능하다.
                int count = 0;
                System.out.println("count = " + count);
        }

        public int someMethodB() {
                //Compile Error 발생
                //cannot find symbol 
                    //symbol:   variable count
                    //location: class noteThree.VariableScope
                count += 1;
                return count;
        }
}

Loop Scope

public class someClass {

        public static void main(String[] args) {
        VariableScope variableScope = new VariableScope();
        variableScope.min(new int[]{10,20,30,40,50});
    }

    public void min(int[] args) {
        int min = 21700000;

        for (int arg : args) {
            // int arg 는 해당 Loop 내부에서만 사용이 가능하다.
            if (min > arg) { min = arg; }
            System.out.println("arg = " + arg);
        }

        //cannot find symbol.. symbol: variable arg...
        System.out.println("arg = " + arg);

        System.out.println("min = " + min);
    }
}

Bracket Scope

public class someClass {

        public static void main(String[] args) {
        VariableScope variableScope = new VariableScope();
        variableScope.someMethod();

    }

        public void someMethod() {
        int numberA = 0;

        {
                        // 해당 변수는 자신을 감싸는 Bracket 영역에서만 사용 가능하다.
            int numberB = 1;
            numberA += numberB;
        }

        //cannot find symbol.. symbol: variable arg...
                System.out.println(numberB);

        System.out.println("numberA = " + numberA); // 1
    }
}

Variable Shadowing

public class someClass {

        public static void main(String[] args) {
        VariableScope variableScope = new VariableScope();
        variableScope.someMethodA();
        variableScope.someMethodB();

    }
        //Class Scope Variable
    private int count = 10;

    public void someMethodA(){
                // Instance Variable 인 count 를 접근하여 사용한다.
        System.out.println("count = " + count);
    }

        // Class Scope > Method Scope
    public void someMethodB(){
                // Method 내부에 Instance Variable 인 count와 동일한 이름을 가진 변수가 존재한다.
                // 동일한 이름의 변수가 하위 Scope에서 존재한다면 하위 Scope의 변수가 상위 Scope의
                // 변수를 가리게(Shadowing) 된다. 
        int count = 1;
        System.out.println("count = " + count);
    }

}

Variable Life Cycle

  • Static Variable (Class Member Variable)

    static 키워드가 붙은 필드 변수를 말하며, 전역적으로 접근이 가능한 녀석이다.

    인스턴스 생성 이전에 미리 로드되어 생성되고 Heap 영역에서 관리된다. (JDK 8+)

    더 이상 해당 Class (Static Obejct)가 참조되지 않는 경우 GC 된다.

    (JDK 7 이전에선 Application 의 Life Cycle이 종료될 때까지 유지된다.)

  • Instance Member

    class scope 에서 작성된 변수를 말한다. 인스턴스의 참조 값을 통해서 접근과 조작이 가능하다.

    인스턴스 생성시에 메모리 공간을 가지고 기본 값으로 초기화되거나 생성자를 통해 주입된다.

    더 이상 해당 Instance가 참조되지 않는 경우 GC 된다.

  • Local Variable

    Method, Loop Scope 등에서 생성되어 사용되는 지역 변수들을 말한다.

    지역 변수가 포함된 Scope가 값을 반환하는 등, 로직이 끝난다면 사라진다.

 

Type Conversions, Casting, Type Promotion

Type Conversions

  • Widening Conversion

    Type 간의 호환성이 존재하고 변환될 타입이 이전에 지니고 있던 타입보다 큰 경우 자동으로 변환되는 것을 말한다.

    (int → long , float → double...)

  • Narrowing Conversion

    대상 Type들이 같은 리터럴을 다루지만, 변환될 타입이 이전에 지니고 있던 타입보다 작은 경우 Casting을 통해 변환할 수 있는 것을 말한다. 내부의 값에 따라 OverFlow가 발생할 수 있다.

    (int → short, double → float)

  • String Conversion

    Wrapper Class의. toString()을 통해 변환되는 것을 말하거나 혹은 parse를 통한 경우를 말한다.

      Integer numA = 100;
    
      String str = num.toString();
    
      int nunB = integer.parseInt(str);
  • Boxing/Unboxing Conversion (JDK 5+)

    Wrapper Class 가 Primitive Type 과의 연산이나 값 저장이 이루어지는 경우에 컴파일러에서 자동적으로 변환해주는 것을 말한다.

      int numA = 10;
      Integer numB = 10; // Integer.valueOf(10) _ 오토박싱
    
      int result = numA + numB; // numB.intValue() - int 10 _ 언박싱
  • Numeric Promotions

    이진 연산 시에 변수들의 크기가 호환되지 않는 경우, 호환성을 위해 지원하는 규칙들을 말한다.

    • 하나의 변수가 Double이면, 다른 변수도 Double로 변경된다.
    • 하나의 변수가 float이면, 다른 변수도 float으로 변경된다.
    • 하나의 변수가 long이면, 다른 변수도 long으로 변경된다.
    • 위의 상황을 만족시키지 않는 경우 Int로 간주한다.

Casting

어떠한 타입을 다른 타입으로 명시적인 변환을 하는 것을 Casting이라고 한다.

  • Primitive Type 간의 Type 변환
  • 구현, 상속 관계의 Class 간의 Type 변환

구현, 상속 관계의 Class 간의 Type 변환

public class Casting {
    public static void main(String[] args) {

        // Up Casting
        // 상위 클래스의 공통 메서드, 상태는 하위 클래스에서 사용 가능하다.
        // 하위 클래스의 재정의, 메서드, 상태는 상위 클래스에서 사용이 불가능하다.
        Parent child = new Child();
        child.name = "name";
        child.getDegree(); // Child의 메서드 접근 - 컴파일 에러

        // Down Casting
        Child childTwo = (Parent) childOne; 
        childTwo.getDegree(); // 사용 가능
    }
}

 

1차 및 2차 배열 선언하기

// 1차원 Int 배열 생성. 
// 정수 값을 넘김으로써 그 길이 만큼의 배열을 생성할 수 있다.

int[] numArrA = new int[**length**];

//혹은

int[] numArrA = {10,20,30,40,50};

int[][] numArrB = new int[length][length];

//혹은

int[][] numArrB = {{10,20}, {30,40}};

// for을 통한 초기화 방식
int[][] numArrB = new int[10][10];
for (int loop = 0; loop < numArrB.length; loop++) {
     for (int innerLoop = 0; innerLoop < numArrB[loop].length; innerLoop++) {
         numArrB[loop][innerLoop] = 10;
     }
}

//혹은

int[][] numArrC = new int[10][10];
for (int loop = 0; loop < numArrB.length; loop++) {
     Arrays.fill(numArrC[loop], 10);
}

// 출력 방식
for (int[] ints : numArrB) {
    System.out.println("ints = " + ints);
    for (int anInt : ints) {
        System.out.println("anInt = " + anInt);
    }
}

// 혹은

System.out.println("numArrB = " + Arrays.deepToString(numArrB));

 

Type Inference (타입 추론) , var

Type Inference 이란 정적인 언어에서 컴파일러가 컴파일 시점에서 변수를 초기화하는 리터럴을 판단하여 해당 타입으로 변환하는 것을 말한다.

자바는 JDK 10부터 Local Scope 내에서 var이라는 새로운 Type을 지원하게 되었다.

사용 시 주의점

  • 초깃값 (리터럴)이 없다면 컴파일 에러가 발생한다. var num;
  • null로 초기화하면 작동하지 않는다. var num = null;
  • local 변수가 아니라면 작동하지 않는다. public num = "String";
  • 람다 표현식에는 var 타입의 변수를 사용할 수 없다. var foo = (String s) → s.length() > 10;
  • 타입 없이 배열 초깃값을 넘겨도 작동하지 않는다. var arr = { 1, 2, 3 };
public void someMethod() {
    var str = "Hello world";
    assertTrue(str instanceof String);

        var num = 10;
    var numB = 10.10D;
    System.out.println(numB+num); // 20.1
}

 

참고한 자료.

Java Primitives versus Objects (https://www.baeldung.com/java-primitives-vs-objects)

Introduction to Java Primitives (https://www.baeldung.com/java-primitives#:~:text=The eight primitives defined in, objects and represent raw values.)

Java Primitive Conversions (https://www.baeldung.com/java-primitive-conversions)

Variable Scope in Java (https://www.baeldung.com/java-variable-scope)

Java 10 LocalVariable Type-Inference (https://www.baeldung.com/java-10-local-variable-type-inference)

자바의 신

1주 차 과제 : JVM 은 무엇이며, 자바 코드는 어떻게 실행하는 것인가.

JVM 이란 무엇인가

JVM (자바 가상 머신) 이란 Java와 Kotlin, Scala 등이 컴파일된 바이트코드를 실행하는 Virtual machine이다.

컴파일하는 방법 Java Compiler : wikipedia

Java Compiler 명령어를 사용하고 Source File의 Path를 넘겨주면 된다.

Directory\Study>Javac source.java  // File Path를 이용하여 컴파일한다. -> Directory\Study\source.java

바이트코드란 무엇인가? Bytecode : wikipedia

Java Bytecode 란 Java의 Execution Engine를 통해 수행될 수 있는 상태의 코드를 말한다. (기계어보다 추상적이다.)

1번의 컴파일 단계를 더 거침으로써, 작성한 코드를 JVM이 설치된 모든 기기에서 실행 가능하다. (JVM에 종속적)

실행하는 방법

>Java source  // source.class

자바 코드를 실행하기 위해선 .java 파일 (즉 SourceFile)를 컴파일하여 그 기기에 맞는 .class (Bytecode)로 변환해주어야 하며, 해당 파일을 인터프리터와 JIT 컴파일러를 통해 해석하여 프로그램을 실행하게 된다.

자바 프로그램의 기본적인 실행 절차.

  1. 해당 Java 파일을 Java Compiler가 Class 파일로 (Bytecode) 변환
  2. 클래스 로더가 JRE 환경을 조성한 후 Application 클래스 로더는 해당 클래스를 PATH를 이용해서 로드 (JVM 실행)
  3. 로드된 바이트 코드를 실행 엔진으로 넘기게 되고, 실행 엔진 내부의 인터프리터에서 내부 클래스를 행단위로 읽어 들인다.
  4. (Static Obj의 경우에는 실행 엔진 이전에 클래스 로더의 초기화 단계에서 생성, 그 외의 정보는 PERM으로)
  5. 인터프리터 내부의 Counter가 만족되면 JIT 컴파일러를 호출하여 실행하게 된다. (중복되는 코드는 캐싱된 코드를 사용한다.)
  6. Class 파일을 기계어로 컴파일하고 런타임 메모리에 올리게 된다.
  7. Main Method에 일정한(기본적으로 빈 배열 등) 인자 값을 넘기고 실행한다.
  8. Main Method 내부에서 필요한 객체가 있다면 해당 객체를 생성하기 위해 PERM Gen의 Class 정보를 읽는다.
  9. 리플렉션을 통해 클래스 이름을 가지고 타입을 특정하고 해당 클래스의 메서드, 변수, 생성자 등을 읽어서 생성한 뒤에 Heap 영역에 띄운다.
  10. 해당 객체를 지역 변수를 통해 참조하거나 다른 객체의 인스턴스 변수가 참조하여 사용한다.

JIT 컴파일러란 무엇이며, 어떻게 동작하는지

JIT 방식은 프로그램을 실행하는 동안 필요한 바이트코드를 기계어로 번역하는 기법을 말하며, JVM에선 해당 기법을 구현한 녀석을 JIT Compiler라고 한다.

JVM 구성 요소

Class Loader

  • Bootstrap Class Loader
    첫 번째로 동작하는 Class Loader이며, jre 실행 환경을 조성하는 등의 Java Library Class들을 로드하게 된다.
    Runtime, i18n Class... (Nativecode로 구성되어 있다.)

  • Extention Class Loader
    jdk/ jre 패키지 내부의 ext 디렉터리에 있는 Class들을 로드하게 된다.

  • Application Class Loader (System Class Loader)
    Class Path를 기반으로 실행할 Class를 로드하게 된다.

로드된 Class 들은 로딩 -> 링크 -> 초기화의 절차를 진행하게 된다.

링크, 초기화 이후에는 참조로 연결된 Class 들을 로드하는 절차가 추가될 수 있다.

 

Execution Engine

  • interpreter
    프로그램 실행을 위하여 Bytecode를 Nativecode로 변환하기 위해 행별로 해석하는 구현체.
    내부적으로 Counter를 지니고 있으며, 해당 행을 해석할 때마다 Counter의 수치가 증가한다.
    일정 수치를 만족하게 되면 JIT Compiler를 호출하여 Dynamic Loading, Compile을 수행하게끔 한다.

  • JIT Compiler
    interpreter의 요청에 따라 컴파일을 수행하는 구현체이다.
    성능 최적화를 위하여 코드를 캐싱하다가 중복 코드가 발견된다면 캐시 된 코드를 재사용한다

Runtime Data Areas

  • Permanent Generation
    Class, Interface, Package 등의 Meta 정보를 저장하고 String Pool 제외한 Constant Pool을 관리하는 공간이다.
    JDK 7 에선 String Pool 이 Heap으로 이동하였으며,
    JDK 8 에선 Static Object 이 Heap 으로 이동하고, OS Memory 상에서 위치하게 변경되었다.

  • Heap Area
    New로 생성되는 Instance 들과, Static Object, String Pool 이 관리되는 영역이다.
    GC를 통하여서 메모리 관리가 이루어지고, 다른 모든 영역에서 공유가 되는 영역이다.
    Young 영역, 2개의 Survivor 영역, Old 영역으로 이루어져 있다. (Hotspot JVM. JDK 8 기준)
    Young과 Survivor 영역에서 이루어지는 GC를 Minor GC라고 하고, Old 영역에서 이루어지는 것을 Major GC 라고 한다.
    JDK 9부터는 G1 GC라는 모듈이 Default로 설정되어 있으며, 영역별로 나누어져있지 않고 메모리 영역을 2천 개 이상의 작은 영역으로 분할하게 되어있다.

  • Stack Area
    Scope에 따른 크고 작은 Stack Frame과 지역 변수, 스레드 들의 Life Cycle이 진행되는 영역이다.
    Application이 실행되는 동안 Main Method Stack Frame 위에서 다른 Frame 이 생성되고 사라진다.
    스레드들은 각각의 Stack 공간(Runtime Stack)을 가지게 되고, 여러 자원을 공유할 수 있다. (가시성 문제가 발생할 수 있다.)

  • PC Register
    Thread 가 생성될 때 같이 생성되는 공간이다. Thread가 실행할 명령의 위치를 기록한다.

  • Native Method Stack
    자바 이외의 언어에서 제공되는 Method의 정보가 저장되는 공간이다.
    JNI(Native Method Interface)와 Library를 통해 표준에 가까운 방식으로 구현된다. (Thread Class 등)

JDK와 JRE의 차이

JRE : Java Runtime Environment
Java Application을 실행하기 위한 최소의 실행 환경을 제공하는 것.
JVM + Java API (핵심 라이브러리)가 포함되어 있다.

JDK : Java Development Kit
JRE에서 제공하는 실행 환경과 개발에 필요한 API, 명령어(메모리 확인, 배포 등), Compiler를 포함하고 있는 것.
JVM, Java API, Java Tool, Java Compiler 가 포함되어 있다.

< Reference >

https://ko.wikipedia.org/wiki/%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC
[JVM Performance Optimizing 및 성능 분석 사례]

+ Recent posts