1. 오늘 학습 키워드 : Java 프로그래밍 ( 람다식, 스트림 ,쓰레드 ) , 계산기 과제 1단계
2. TMI
오늘부로 이제 매일아침 부터 sql, 알고리즘 문제를 한두문제씩 풀고 제출해야한다....
해야할게 하나 늘었지만, 코딩 실력을 올리는데 있어서 매우 중요한 시간이 될것같다.
3. 학습내용
[3-5 람다식]
익명 클래스 - 이름이 없는 클래스, 별도의 클래스 파일을 만들지 않고 코드 내에서 1회성으로 정의해서 사용
☞ 인터페이스, 클래스(일반, 추상) 의 구현과 상속을 활용해 구현 가능하다. → 람다에서는 인터페이스를 사용한 익명 클래스가 활용됨.

람다(Lambda) - 람다 는 익명클래스를 더 간결하게 표현하는 문법
함수형 인터페이스를 통해서 구현하는 것을 권장 ( @FuntionalInterface 어노테이션 선언 ! ! )
→ 하나의 추상 메소드만 가져야하기 때문
→ 하지만, 하나의 추상 메소드를 가진 일반 인터페이스를 통해 사용가능함.
★ 함수형 인터페이스에 추상 메소드가 하나뿐 이기 때문에 컴파일러는 람다표현식 그 해당 메소드라고 추론 가능
람다 사용시 주의사항
- 함수형 인터페이스는 단 하나의 추상 메소드만 가지도록 강제하는 어노테이션
- 람다식에서 함수형 인터페이스 활용 가능
- 인터페이스에 2개 이상의 추상 메소드가 존재하면, 컴파일러가 어떤 메소드를 구현하는지 모호해짐.
- 오버로딩 기능을 통해 동일 명의 메서드를 여러 형태로 정의한다면, 람다 표현식이 어떤 메소드를 구현하는 것이 명확하지 않아 모호해짐
★ 오버로딩 - 같은 클래스나 인터페이스 내 동일한 메소드 이름을 사용해 선언하는 기능.
→ 메소드명은 동일하며, 매개변수의 타입이나 개수, 순서는 다르게 선언 ( 생성자에서 주로 쓰임 )
public class Person {
private String name;
private int age;
// 생성자 오버로딩
public Person() {}
public Person(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void run() {
System.out.println("달린다.");
}
}
★ 오버라이딩 - 부모 클래스에 정의된 메소드를 자식 클래스에서 재정의 하는 것 ( 주로 상속관계에서 다룸 )
public class Hero extends Person {
// 메소드 오버라이딩 ( 부모 메소드를 재정의 함 )
public void run() {
System.out.println("히어로는 멋지게 달린다");
}
}

[3-6 스트림]
스트림 - 데이터를 효율적으로 처리할수 있는 흐름 ( 스트림은 컬렉션과 함께 자주 활용됨 )
데이터 준비 → 중간 연산 → 최종 연산 순으로 처리

실무에서 자주 쓰이는 map(), filter()
ArrayList를 List로 받는 이유
다형성을 활용해 List 인터페이스로 ArrayList 구현체를 받으면, 나중에 다른 구현체 ( LinkedList, Vector )로 변경 할때 코드 수정을 최소화 할 수 있다. → 실무에서 리스트로 선언시, List타입으로 받는것을 권장
ArrayList<> 다음 list.of()함수는 선언과 동시에 값을 저장한다는 개념으로 보면 편하다.
데이터 준비
stream() - 데이터를 스트림으로 변환해 연산흐름을 만들 준비함.
중간 연산
map() - 각 요소를 주어진 함수에 적용해 변환
filter() - 각 요소를 주어진 조건에 따라 추출해냄
최종 연산
collect() - 결과를 원하는 형태(List, set)으로 수집함.
*실습문제*
💬 Q1. filter를 람다식으로 활용해 보세요.
💡요구사항
- 익명 클래스 활용
- 람다식 만들기
- 직접 람다식 대입

Predicate 인터페이스는 한 개의 제네릭 타입을 사용하며,
내부에는 한 개의 추상 메서드, 세 개의 디폴트 메서드, 한 개의 정적 메서드가 존재합니다
[3-7 쓰레드]
쓰레드 - 프로그램 내에서 독립적으로 실행되는 하나의 작업단위
싱글 쓰레드 (1명의 일꾼이 작업 처리) - 일꾼이 1명이기 때문에 여러개의 작업이 있으면 순차적으로 처리
main() 메소드는 프로그램과 시작 동시에 생성되는 하나의 쓰레드 임.
멀티 쓰레드 (작업을 처리할수 있는 여러명의 일꾼) - 여러 작업을 병렬(동시) 처리할 수 있다. ( Thread 클래스를 상속받아 쓰레드를 구현 할수 있음. )
Thread.run() 메소드를 오버라이딩 해서 쓰레드가 수행할 작업을 정의함.
Start() 메소드를 호출하면 새로운 쓰레드가 생성되고, run()의 작업 내용이 실행된다.

총 3개의 쓰레드 ( main, thread0, thread1 ) 병렬로 실행.
★ 쓰레드를 실행시킬때 꼭 start()로 실행하기
☞ start()는 새로운 쓰레드에서 run()을 실행
☞ run()을 직접 호출하면, 현재 쓰레드에서 실행됨.
join() [ 특정 쓰레드가 끝날때 가지 기다리기 ] - main() 쓰레드가 다른 쓰레드가 종료될때 까지 기다리게 하는 메서드
Runnable 인터페이스 활용 - Runnable 인터페이스를 활용해 쓰레드를 구현하는 것을 권장함.
- 유지 보수성과 재사용성 향상
- 확장 가능성
■ 계산기 과제 1단계 ■
| 클래스 없이 기본적인 연산을 수행할 수 있는 계산기 만들기 |
package com.example.calculator;
import java.util.Scanner;
public class Calculator {
public static void main(String[] args) {
// 1단계 클래스 없이 기본적인 연산을 수행할 수 있는 계산기 만들기
Scanner sc = new Scanner(System.in);
while (true){
System.out.print("첫번째 정수를 입력하세요: ");
int num1 = sc.nextInt();
// 양의 정수(0포함) 만 입력받아야 하는 조건
if (num1 < 0) {
System.out.println("음수는 입력할수 없습니다.");
continue;
}
System.out.print("두번째 정수를 입력하세요: ");
int num2 = sc.nextInt();
if (num2 < 0) {
System.out.println("음수는 입력할수 없습니다.");
continue;
}
System.out.print("사칙연산(+, -, x, /) 기호를 입력하세요: ");
char operator = sc.next().charAt(0); // 문자열에서 첫 번째 문자만 추출
int result = 0;
switch (operator) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 == 0) {
System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
break;
} else {
result = num1 / num2;
break;
}
default:
System.out.println("부호를 잘못 입력하였습니다.");
}
System.out.println("결과 = " + result);
sc.nextLine(); // 버퍼 비우기 (중요) 엔터값이 남아있어서 초기화 필요함!!
System.out.print("더 계산하시겠습니까? (exit 입력 시 종료): ");
String answer = sc.nextLine();
if (answer.equals("exit")) {
System.out.println("계산기 시스템을 종료합니다.");
break;
}
}
}
}
[트러블 슈팅]
- 배경 : 사용자가 입력한 후 바로 다음 입력창이 넘어가는 입력 누락 현상을 발견했다. 이는 Scanner를 사용할 때, 버퍼(buffer) 문제가 발생했기 때문으로 추측된다.
- 발단 :처음에는 nextInt()로 정수를 입력받고, nextLine()을 사용하여 추가 입력을 받으려 했지만,
예상과 달리 nextLine()이 입력을 받지 않고 건너뛰는 현상이 발생했다.
이 때문에 "더 계산하시겠습니까?"라는 질문이 사용자의 입력 없이 바로 넘어갔다. - 전개 : nextInt()는 정수만 읽고 개행 문자(\n)는 버퍼에 남겨둔다. 이후 nextLine()이 실행되면 남아 있는 \n을 읽어들여, 사용자가 입력한 것처럼 처리한다. ( 개행이 버퍼에 남아있기 때문 )
- 위기 : 처음에는 nextLine()을 바로 호출하는 것이 문제라고 생각해 입력 방식을 변경해보았지만, 문제는 여전히 해결되지 않았다. 이를 해결하기 위해 사용자의 입력 흐름을 고려한 근본적인 해결책이 필요했다.
- 절정 : 문제를 해결하기 위해 버퍼를 비우는 작업을 추가했다.
즉, nextInt()로 숫자를 입력받은 후, nextLine()을 한 번 실행하여 남아 있는 개행 문자(\n)를 제거하는 방법을 적용했다 - 결말 : 이제 프로그램이 정상적으로 동작하며, 사용자가 원하는 입력을 정상적으로 받을 수 있게 되었다.
앞으로도 입력을 받을 때, Scanner의 버퍼 문제를 고려하여 nextLine()을 적절히 활용하는 습관을 들이면,
유사한 문제 발생을 방지할 수 있다. ( 항상 스캐너 입력 받을때 nextLine() 을 활용해 버퍼 지우는 습관 가지기 )
버퍼는 입력 데이터를 임시적으로 저장하는 공간이라고 생각하면 좋다.
→ 입력이 끝난후 남아있는 불필요한 데이터를 처리하지 안흥면 예상치 못한 동작을 할 수 있다. 이를 방지하려면 버퍼에 남은 개행 문자를 처리하기 위해 nextLine()을 한 번 호출해서 버퍼를 비워줘야 한다.
4. 내일 학습 할 것: 주말동안 자바,git 복습
'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글
| TIL 12 [계산기 과제 3단계(enum, 제네릭, 람다, 스트림)] (0) | 2025.03.05 |
|---|---|
| TIL 11 [Java(접근제어자, 클래스란?, 객체지향?) + 계산기 과제 2단계 ] (0) | 2025.03.04 |
| TIL 9 [ Java(예외처리, Optional, 제네릭, 컬렉션) ] (0) | 2025.02.27 |
| TIL 8 [ Java(final, static, 인터페이스, 객체지향) ] (0) | 2025.02.26 |
| TIL 7 [ Java(조건/반복문, 배열 , 메소드) ] (0) | 2025.02.25 |