자바가 제공하는 제어문을 학습하세요.

  • 선택문
  • 반복문

선택문

if/else 문

  • if 문에 들어가는 조건식이 참인 경우 if 내 블록 코드 실행
int a = 1;
if(a>0) {
  a += 5;
}
  • if문에 들어가는 조건식이 참이 아닌 경우 else if 문의 조건을 비교하고 else if 문의 조건이 참이 아닌 경우 else문 내 블록 코드 실행
int a = 1;
if(a>0) {
  a += 5;
}else if(a>1) {
  a += 6;
}else if(a>2) {
  a += 7;
}else {
  a += 10;
}
  • 중첩 if문 사용 가능
int a = 1;
if(a>0) {
  a += 5;
  if(a==0) {
    return a;
  }
}

switch/case 문

  • switch의 매개변수에 맞는 조건에 따라 case문을 실행하여 다중 if문의 단점을 개선
  • 각각의 case문에 break 키워드를 사용하지 않으면 switch문을 탈출하지 않고 그 다음 case문을 실행하기 때문에 꼭 break 키워드를 써주어야 한다.
  • 자바 13부터 switch/case 문과 유사한 switch 연산자가 만들어졌다.
public static void main(String[] args) {
    String day = "FRIDAY";

    switch (day) {
        case "MONDAY":
        case "TuDAY":
        case "WENDAY":
            System.out.println("WENDAY");
            break;
        case "NOT":
            System.out.println("not");
        default:
            System.out.println("default");
    }
}

반복문

for문

  • 초기화 ; 조건문 ; 증감식
  • 초기화한 값을 가지고 조건문을 검사해 초기화한 값을 증감식의 조건에 따라 증감해가며 내부 코드를 반복하는 구문
for(int i = 0 ; i < array.length() ; i++) {
// 0 ~ array.length()만큼 반복 수행
}

while문

  • 조건이 참인 경우 계속하여 반복하는 구문
  • 무한루프 주의
  • 유한적인 조건을 주거나 while문 내부에 탈출 조건을 명시해야 한다.
int i = 0;
while(i<10){
  i++; // i가 10 이상일 경우 반복 해제됨
}

do-while문 

  • 조건이 하단에 있는 구문
  • 구문을 실행한 후 마지막에 조건을 확인함으로 구문을 반드시 한번 이상 실행
  • 하단 조건 이후 세미콜론을 써야 한다.
int i = 0;
do{
  i++; // i가 10 이상일 경우 반복 해제됨
}while(i<10);

for-each문

  • for문과 동일하지만 for문보다 직관적이며, 반복할 객체를 하나씩 차례대로 가져와 사용
public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    for (int i = 0; i < list.size(); i++) {
        list.add("Hello");
    }

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

Iterator

  • iterator는 java의 collection에 저장되어 있는 데이터를 읽어오는 방법을 표준화한 기술 중 하나이다.
  • hasNext(), next(), remove() 메소드를 이용해 데이터를 사용할 수 있다.
public static void main(String[] args) {
    Set<String> set = new HashSet<>();
    set.add("Hello1");
    set.add("Hello2");
    set.add("Hello3");

    Iterator<String> it = set.iterator();
    while (it.hasNext()) {
        System.out.println(it.next());
        it.remove();
    }
}

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

  • 인텔리J, 이클립스, VS Code에서 JUnit 5로 테스트 코드 작성하는 방법에 익숙해 질 것.
  • 이미 JUnit 알고 계신분들은 다른 것 아무거나!
  • 더 자바, 테스트 강의도 있으니 참고하세요~

Maven Dependencies

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>

Junit 5 가 4 버전과 달라진 점

접근 제어자 변경

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

확장성

  • Extension 하나로 여러 규칙을 통합, 조합 가능하다.
  • -> @ExtendWith, @RegisterExtension, Java ServiceLoader

분리된 jar(모듈)

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

다양해진 assert 방식

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

한번에 여러 테스트 실행 가능

  • 기존에 하나의 테스트가 실패하면 더 이상 진행되지 않는 Junit 4의 문제점을 해결
assertAll( 
    () -> assertNotNull(), 
    () -> assertEquals(), 
    () -> assertTrue(), 
    () -> assertTimeout(), 
);

 

Dynamic Test

  • 테스트가 런타임 환경으로 생성되고 수행이 가능해졌다.
@TestFactory 
Stream<DynamicNode> dynamicTests() { 
   return Stream.of("AAA","BBB","CCC","DDD") 
        .map(text -> dynamicTest("AAAEEETTT", () -> assertTrue(text.contains("AAA"))); 
}

 

Test 순서 설정

  • @Order(정수 값) 을 이용하여 우선순위를 정한다.

 

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

 

  • @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에서 실행

기본 Annotation

@BeforeAll

  • 해당 annotation이 달린 메서드가 현재 클래스의 모든 테스트 메서드보다 먼저 실행된다.
  • 해당 메서드는 static 이어야 한다.
  • 이전의 @BeforeClass와 동일

@BeforeEach

  • 해당 annotation이 달린 메서드가 각 테스트 메서드 전에 실행된다.
  • 이전의 @Before와 동일

@DisplayName

  • 테스트 클래스 또는 테스트 메서드의 이름을 정의할 수 있다.

@Disable

  • 테스트 클래스 또는 메서드를 비활성화 할 수 있다.
  • 예전의 @Ignore와 동일

@AfterAll

  • 해당 annotation이 달린 메서드가 현재 클래스의 모든 테스트 메소드보다 이후에 실행된다.
  • 해당 메서드는 static 이어야 한다.
  • 이전의 @AfterClass와 동일

@AfterEach

  • 해당 annotation이 달린 메서드가 각 테스트 메서드 이후에 실행된다.
  • 이전의 @After와 동일


 

Assertions

  • org.junit.jupiter.api.Assertions로 이동


assertAll, assertTrue

 

  • assertAll()을 사용하여 assertions을 그룹화하여 그룹 내에서 실패한 assertions를 MultipleFailuresError와 함께 기록할 수 있다.
  • 즉 실패의 정확한 위치를 파악할 수 있기 때문에 복잡한 assertions를 만들어도 안전하게 사용할 수 있다.

assertThrows

 

예외 테스트 성공 (비정상 값)
예외 테스트 실패 (정상 값)


assertTimeout

@Test
static void assertTimeoutTest() {
	assertTimeout(ofSeconds(1), () -> {
	  // 1초 이내에 수행해야함
  });
}

Assumptions

  • 특정 조건이 충족되는 경우에만 테스트를 실행하는데 사용된다.
  • 일반적으로 테스트가 제대로 실행되기 위해 필요한 외부 조건에 사용된다.
  • 테스트와 직접적인 관련은 없다.
  • assumptions가 실패하면 TestAbortedException이 발생하고 테스트는 수행되지 않는다.

assumeTrue(), assumeFalse(), assumingThat()

 


Dynamic Test

@TestFactory

  • 해당 annotation이 달린 메서드는 동적 테스트를 위한 testfactory 메서드이다.
  • 런타임에 생성된 테스트 케이스를 선언하고 실행할 수 있다.
  • e.g. 각각 in, out 이라는 두 개의 ArrayList 를 사용하여 단어를 번역한다
  @TestFactory
  public Stream<DynamicTest> translateDynamicTestsFromStream() {
      return in.stream()
        .map(word ->
            DynamicTest.dynamicTest("Test translate " + word, () -> {
              int id = in.indexOf(word);
              assertEquals(out.get(id), translate(word));
            })
      );
  }
  • @TestFactory 메서드는 private 또는 static 이면 안된다.
  • 테스트 수는 동적이며, ArrayList 크기에 따라 달라진다.
  • @TestFactory 메서드는 Stream, Collection, Iterable 또는 Iterator를 리턴해야 한다.

ETC

@Nested

  • 중첩된 비정적 테스트 클래스임을 나타낸다.
  • 테스트 클래스 안에서 내부 클래스를 정의해 테스트를 계층화할 수 있다.
  • 내부 클래스로 정의하기 때문에 부모 클래스의 멤버 필드에 접근할 수 있고,  Before / After와 같은 테스트 생명주기에 관계된 메소드들도 계층에 맞게 동작한다.

@Tag

  • 테스트 필터링을 위한 태그를 선언할 수 있다.

@ExtendWith

  • 사용자 정의 확장명을 등록하는데 사용된다.

@RepeatedTest

  • @RepeatedTest(반복횟수)를 이용하면 해당 테스트를 반복 실행할 수 있다.

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

  • 깃헙 이슈 1번부터 18번까지 댓글을 순회하며 댓글을 남긴 사용자를 체크 할 것.
  • 참여율을 계산하세요. 총 18회에 중에 몇 %를 참여했는지 소숫점 두자리가지 보여줄 것.
  • Github 자바 라이브러리를 사용하면 편리합니다.
  • 깃헙 API를 익명으로 호출하는데 제한이 있기 때문에 본인의 깃헙 프로젝트에 이슈를 만들고 테스트를 하시면 더 자주 테스트할 수 있습니다.
package gitlivestudy;

import org.kohsuke.github.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LiveStudyExample {
    public static void main(String[] args) throws IOException {
        String myToken = "";
        GitHub github = new GitHubBuilder().withOAuthToken(myToken).build();
        GHRepository ghRepository = github.getRepository("whiteship/live-study");

        Map<String,Double> userMap = new HashMap<>();

        List<GHIssue> issues = ghRepository.getIssues(GHIssueState.ALL);

        for (GHIssue issue : issues) {
            List<GHIssueComment> ghIssueComments = issue.getComments();
            for (GHIssueComment ghIssueComment : ghIssueComments) {
                String name = ghIssueComment.getUser().getName();
                userMap.put(name,userMap.getOrDefault(name,0.0) + 1);
            }
        }

        for (String s : userMap.keySet()) {
            double v = (userMap.get(s) / 18) * 100;
            System.out.println(s + " : " + String.format("%.2f",v));
        }
    }

}


과제 2. LinkedList를 구현하세요

  • LinkedList에 대해 공부하세요.
  • 정수를 저장하는 ListNode 클래스를 구현하세요.
  • ListNode add(ListNode head, ListNode nodeToAdd, int position)를 구현하세요.
  • ListNode remove(ListNode head, int positionToRemove)를 구현하세요.
  • boolean contains(ListNode head, ListNode nodeTocheck)를 구현하세요.

ListNode.java

package linkedlistex;


public class ListNode {

    int data;
    ListNode link;

    public ListNode() {
    }

    public ListNode(int data) {
        this.data = data;
    }

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

        for(int i = 0; i < position -1 ; i++) {
            node = node.link;
        }

        nodeToAdd.link = node.link;
        node.link = nodeToAdd;
        return head;
    }

    ListNode remove(ListNode head, int positionToRemove) {
        ListNode node = head;

        if(positionToRemove == 0) {
            ListNode deleteNode = node;
            head = node.link;
            deleteNode.link = null;
            return deleteNode;
        }

        for(int i = 0; i < positionToRemove -1 ; i++) {
            node = node.link;
        }
        ListNode deleteNode = node.link;
        node.link = deleteNode.link;
        deleteNode.link = null;
        return deleteNode;
    }

    boolean contains(ListNode head, ListNode nodeTocheck) {
        while(head.link != null){
            if(head.link == nodeTocheck) {
                return true;
            }
            head = head.link;
        }
        return false;
    }

}

ListNodeTest.java

package linkedlistex;

import org.junit.jupiter.api.*;

import java.util.ArrayList;
import java.util.List;

class ListNodeTest {
    private List<Integer> array;
    private ListNode listNode;
    private static final int[] ADD_DATA = {1,2,4,3};

    @BeforeEach
    void init() {
        array = new ArrayList<>();

        listNode = new ListNode();
        ListNode first = new ListNode(1);
        ListNode second = new ListNode(2);
        ListNode third = new ListNode(3);

        this.listNode = first;
        first.link = second;
        second.link = third;
    }
    @Test
    void add() {
        listNode = listNode.add(listNode, new ListNode(4), 2);

        while (listNode != null) {
            array.add(listNode.data);
            listNode = listNode.link;
        }

        for (int i = 0 ; i < array.size() ; i++) {
            Assertions.assertEquals(ADD_DATA[i], array.get(i));
        }
    }

    @Test
    void delete() {
        ListNode removeNode = listNode.remove(this.listNode,0);
        Assertions.assertEquals(1,removeNode.data);
    }

    @Test
    void contains() {
        boolean contains = listNode.contains(listNode, new ListNode(2));
        Assertions.assertEquals(contains,true);
    }

}

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

  • int 배열을 사용해서 정수를 저장하는 Stack을 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

Stack.java

package stackex;

import java.util.ArrayList;
import java.util.List;

public class Stack {
    public List<Integer> array = new ArrayList<>();

    public void push(int data) {
        array.add(data);
    }

    public int pop() {
        if(array.size() == 0) {
            return 0;
        } else {
            return array.remove(array.size()-1);
        }
    }

}

StackTest.java

package stackex;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class StackTest {
    private Stack stack;
    private static final int[] PUSH_DATA = {5,4,3,2,1};
    
    @Test
    void push() {
        Stack stack = new Stack();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        stack.push(4);
        stack.push(5);

        for (int i=0;i<PUSH_DATA.length;i++) {
            Assertions.assertEquals(PUSH_DATA[i],stack.pop());
        }
    }

}

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

  • ListNode head를 가지고 있는 ListNodeStack 클래스를 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

ListNodeStack.java

package linkedliststackex;

import linkedlistex.ListNode;

import java.util.ArrayList;
import java.util.List;

public class ListNodeStack {
    static int top;
    ListNode node;

    public ListNodeStack() {
        this.node = null;
        this.top = -1;
    }

    public void push(int data) {
        ListNode pushNode = new ListNode(data);

        if(node == null) {
            node = new ListNode(data);
            top++;
        } else {
            node = node.add(node,pushNode,++top);
        }
    }

    public int pop() {
        if(top == -1) {
            return top;
        } else {
            return node.remove(node,top--).getData();
        }
    }

}

ListNodeStackTest.java

package linkedliststackex;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class ListNodeStackTest {
    ListNodeStack listNodeStack;
    private static final int[] ADD_DATA = {5,4,3,2,1};

    @Test
    void Test() {
        listNodeStack = new ListNodeStack();
        listNodeStack.push(1);
        listNodeStack.push(2);
        listNodeStack.push(3);
        listNodeStack.push(4);
        listNodeStack.push(5);

        for(int i=0; i<ADD_DATA.length; i++) {
            Assertions.assertEquals(ADD_DATA[i],listNodeStack.pop());
        }
    }
}

과제 5. Queue를 구현하세요.

  • 배열을 사용해서 한번
  • ListNode를 사용해서 한번.

QueueExample.java

package queueex;

import java.util.NoSuchElementException;

public class QueueExample {
    private int[] array;
    private int maxSize;
    private int front;
    private int rear;

    QueueExample() {
        final int DEFAULT_QUEUE_SIZE = 11;
        this.array = new int[DEFAULT_QUEUE_SIZE];
        this.maxSize = DEFAULT_QUEUE_SIZE;
        this.front = 0;
        this.rear = 0;
    }

    public QueueExample(int maxSize) {
        this.array = new int [maxSize + 1];
        this.maxSize = maxSize + 1;
        this.front = 0;
        this.rear = 0;
    }

    public boolean enQueue(int data) {
        if ((this.rear + 1) % this.maxSize == this.front)
            throw new IllegalStateException("full");

        this.rear = (this.rear + 1) % this.maxSize;
        this.array[this.rear] = data;

        return true;
    }

    public Integer deQueue() {
        if(this.front == this.rear){
            throw new NoSuchElementException();
        }
        int removed = element();
        this.front = (this.front + 1) % this.maxSize;
        return removed;
    }

    public Integer element() {
        if(this.front == this.rear) throw new NoSuchElementException("Queue is empty...");
        return this.array[(this.front + 1) % this.maxSize];
    }
}

QueueExampleTest.java

package queueex;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class QueueExampleTest {
    final int FIRST_DATA_VALUE = 1;
    final int SECOND_DATA_VALUE = 2;
    final int THIRD_DATA_VALUE = 3;

    @Test
    void enqueue() {
        QueueExample queue = new QueueExample();
        Assertions.assertAll(
                () -> Assertions.assertTrue(queue.enQueue(FIRST_DATA_VALUE)),
                () -> Assertions.assertTrue(queue.enQueue(SECOND_DATA_VALUE)),
                () -> Assertions.assertTrue(queue.enQueue(THIRD_DATA_VALUE))
        );
    }

    @Test
    void dequeue() {
        QueueExample queue = new QueueExample();

        Assertions.assertAll(
                () -> Assertions.assertTrue(queue.enQueue(FIRST_DATA_VALUE)),
                () -> Assertions.assertTrue(queue.enQueue(SECOND_DATA_VALUE)),
                () -> Assertions.assertTrue(queue.enQueue(THIRD_DATA_VALUE)),

                () -> {
                    int removed = queue.deQueue();
                    Assertions.assertEquals(removed, FIRST_DATA_VALUE);
                },
                () -> {
                    int removed = queue.deQueue();
                    Assertions.assertEquals(removed, SECOND_DATA_VALUE);
                },
                () -> {
                    int removed = queue.deQueue();
                    Assertions.assertEquals(removed, THIRD_DATA_VALUE);
                }
        );
    }
}
Queue<Integer> queue = new LinkedList<>();
queue.offer(1); // null 리턴
queue.add(1); // 예외 발생

queue.poll(); // null 리턴
queue.remove(); // 예외 발생

queue.peek(); // null 리턴
queue.element(); // 예외 발생

+ Recent posts