: 소스 코드가 컴파일되거나 실행될 때 컴파일러 및 다른 프로그램에게 필요한 정보를 전달해주는 문법 요소
표준 애너테이션: JDK에 내장된 일반적인 애너테이션
애너테이션 | 설명 |
---|---|
@Override | 메서드 앞에 붙임으로써 선언한 메서드가 상위 클래스의 메서드를 오버라이딩하거나 추상 메서드를 구현한다고 컴파일러에게 알려주는 역할 |
@Deprecated | 기존에 사용하던 기술이 대체되어 기존 코드를 더이상 사용하지 않도록 유도하는 역할 |
@SuppressWarnings("경고 메시지") | 컴파일 경고 메시지가 나타나지 않도록 하는 역할 |
@FunctionalInterface | 함수형 인터페이스를 선언할 때, 컴파일러가 함수형 인터페이스의 선언이 바르게 되었는지 확인하는 역할 |
메타 애너테이션: 다른 애너테이션을 정의할 때 사용하는 애너테이션(@interface 키워드를 사용해 애너테이션을 정의하고, @Targer, @Retention을 사용해 애너테이션의 적용 대상과 유지 기간을 지정)
@Target
: 애너테이션을 적용할 대상 지정하는 역할
대상 타입 | 적용 범위 |
---|---|
ANNOTATION_TYPE | 애너테이션 |
CONSTRUCTOR | 생성자 |
FIELD | 필드(멤버변수, 열거형 상수) |
LOCAL_VARIABLE | 지역변수 |
METHOD | 메서드 |
PACKAGE | 패키지 |
PARAMETER | 매개변수 |
TYPE | 타입(클래스, 인터페이스, 열거형) |
TYPE_PARAMETER | 타입 매개변수 |
TYPE_USE | 타입이 사용되는 모든 대상 |
@Documented
: javadoc으로 작성한 문서에 포함되도록 하는 역할
@Inherited
: 하위 클래스가 애너테이션을 상속받도록 하는 역할
@Rentention
: 애너테이션의 지속 시간을 결정하는 데 사용
유지 정책 | 설명 |
---|---|
SOURCE | 소스파일에 존재, 클래스파일에는 존재 X |
CLASS | 클래스 파일에 존재, 실행 시 사용 불가 |
RUNTIME | 클래스 파일에 존재, 실행 시 사용 가능 |
@Repeatable
: 애너테이션을 여러 번 붙일 수 있도록 허용하는 역할
: 메서드를 하나의 식으로 표현하는 것으로 함수형 프로그래밍 기법을 지원하는 자바 문법요소
void sayHello() {
System.out.println("Hello");
}
// 람다식으로 표현할 때 반환타입과 이름을 생략가능
() -> System.out.println("Hello");
: 기존의 인터페이스 문법을 활용해 람다식을 다루기 위해 사용하며, 1개의 추상 메서드를 갖는 인터페이스
@FunctionalInterface
interface ExampleFuction {
int sum(int num1, int num2);
}
public static void main(String[] args) {
ExampleFuction exampleFuction = (num1, num2) -> num1 + num2;
}
: 람다식에서 불필요한 매개변수를 제거할 때 사용
public class Calculator {
public static int staticMethod(int x, int y) {
return x + y;
}
public int instanceMethod(int x, int y) {
return x + y;
}
}
IntBinaryOperator operator;
// 정적메서드
// 클래스이름::메서드이름
operator = Calculator::staticMethod;
operator.applyAsInt(3, 5);
// 인스턴스 메서드
// 인스턴스이름::메서드이름
Calculator calculator = new Calculator();
operator = calculator::instanceMethod;
operator.applyAsInt(3, 5);
: 배열, 컬렉션의 요소를 하나씩 참조해 람다식으로 처리할 수 있도록 하는 반복자
String[] arr = new String[]{"AAA", "BBB", "CCC"};
// Arrays.stream() 사용
Stream<String> stream = Arrays.stream(arr);
// Stream.of() 사용
Stream<String> stream = Stream.of(arr);
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// list.stream() 사용
Stream<Integer> stream = list.stream();
// 임의의 수 무한 생성
IntStream ints = new Random().ints();
// 스트림을 5개까지 생성
IntStream ints = new Random().ints(5);
IntStream ints = new Random().ints().limit(5);
// 1~10까지 특정 범위의 수 생성
IntStream ints = new Random().rangeClosed(1, 10);
distinct() : 요소들의 중복 제거
filter() : 조건에 따라 요소들을 필터링
List<String> list = Arrays.asList("AAA", "BBB","CCC", "AAA", "EEE");
// distinct() 중복 제거
list.stream().distinct().forEach(e -> System.out.println(e));
// filter() C로 시작하는 요소만 필터링
list.stream().filter(e -> e.startsWith("C")).forEach(e -> System.out.println(e));
map() : 요소들을 특정 형태로 변환하거나 원하는 필드만 추출할 때 사용
flatMap() : 중첩 구조를 제거하고 단일 컬렉션으로 만들어 주는 역할
List<String> list = Arrays.asList("aaa", "bbb","CCC", "ddd", "EEE");
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
String[][] stringArr = new String[][]{{"AA", "BB"}, {"CC", "DD"}};
// 요소들을 하나씩 대문자로 변환
list.stream().map(e -> e.toUpperCase());
// 각 요소에 3을 곱한 값으로 변환
nums.stream().map(e -> e * 3);
// flatMap()을 사용해 중첩 구조를 제거
Arrays.stream(stringArr).flatMap(Arrays::stream).forEach(System.out::println);
sorted() : 요소 정렬
List<String> animals = Arrays.asList("Tiger", "Lion", "Monkey", "Duck", "Horse", "Cow");
// sorted() 내에 아무런 입력이 없으면 기본적으로 오름차순 정렬
animals.stream().sorted();
// 내림차순 정렬
animals.stream().sorted(Comparator.reverseOrder());
int[] arr = {1, 2, 3, 4, 5};
// 요소 개수 카운팅
long count = Arrays.stream(arr).count();
// 합계
long sum = Arrays.stream(arr).sum();
// 평균
double average = Arrays.stream(arr).average().getAsDouble();
// 최대값
int max = Arrays.stream(arr).max().getAsInt();
// 최소값
int min = Arrays.stream(arr).min().getAsInt();
// 첫 요소
int first = Arrays.stream(arr).findFirst().getAsInt();
int[] arr = {1, 2, 3, 4, 5};
// 모든 요소가 짝수라면 true
Arrays.stream(arr).allMatch(e -> e % 2 == 0);
// 요소중 하나라도 짝수라면 true
Arrays.stream(arr).anyMatch(e -> e % 2 == 0);
// 모든 요소가 짝수가 아니라면 true
Arrays.stream(arr).noneMatch(e -> e % 2 == 0);
T reduce(T identity, BinaryOperator accumulator) : 스트림의 요소를 줄여나가면서 연산 수행(identity는 연산 초기값, accumulator는 각 연산의 조건식)
int[] arr = {1, 2, 3, 4, 5};
int sum = Arrays.stream(arr).map(e -> e * 2).reduce((a, b) -> a + b).getAsInt();
// 연산 과정
// (1, 2) -> 누적값 a = 3, 다음 연산값 b = 3
// (3, 3) -> 누적값 a = 6, 다음 연산값 b = 4
// (6, 4) -> 누적값 a = 10, 다음 연산값 b = 5
// (10, 5) -> 최종 결과 15
class Student {
public enum Gender {Male, Female};
private String name;
private int score;
private Gender gender;
public Student(String name, int score, Gender gender) {
this.name = name;
this.score = score;
this.gender = gender;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
public Gender getGender() {
return gender;
}
}
List<Student> totalList = Arrays.asList(
new Student("AAA", 100, Student.Gender.Male),
new Student("BBB", 80, Student.Gender.Male),
new Student("CCC", 90, Student.Gender.Female),
new Student("DDD", 60, Student.Gender.Female)
);
// 남자인 요소들을 모아 Map으로 반환
Map<String, Integer> maleMap = totalList.stream()
.filter(s -> s.getGender() == Student.Gender.Male)
.collect(Collectors.toMap(
student -> student.getName(), // Key
student -> student.getScore() // Value
));
프로세스 : 실행 중인 애플리케이션
스레드 : 데이터와 애플리케이션이 확보한 자원을 활용해 소스 코드를 실행하는 실행 흐름
Runnable 인터페이스 구현한 객체에서 run()을 구현
Thread 클래스를 상속받은 하위 클래스에서 run()을 구현
class ThreadTask1 implements Runnable {
public void run() {
for (int i = 0; i< 100; i++) System.out.print("#");
}
}
// Runnable 인터페이스를 구현한 ThreadTask1 객체 생성해 실행
Thread thread1 = new Thread(new ThreadTask1());
thread1.start();
-------------------------------------------------
class ThreadTask2 extends Thread {
public void run() {
for (int i = 0; i< 100; i++) System.out.print("#");
}
}
// Thread 클래스를 상속받은 ThreadTask2 객체 생성해 실행
Thread thread2 = new ThreadTask2();
thread2.start();
: 메인 스레드는 "main" 이름을 가지고, 그 외 추가로 생성한 스레드는 0번부터 "Thread-n"의 이름을 가진다.
Thread thread3 = new Thread(new Runnable() {
public void run() {
System.out.println("Get Thread Name");
}
});
thread3.start();
// getName()으로 이름 조회
System.out.println(thread3.getName()); // Thread-0 출력
// setName()으로 이름 변경
thread3.setName("AAA");
//Thread.currentThread()를 사용해 현재 실행 중인 스레드의 참조 가능
Thread.currentThread().getName(); // main 출력
: 멀티 스레드의 경우 여러 스레드가 같은 데이터를 공유하게 되어 문제 발생, 따라서 임계 영역과 락을 사용해 스레드 동기화를 적용한다.
임계영역(Critical Section) : 오직 하나의 스레드만 코드를 실행할 수 있는 코드 영역
락(Lock) : 임계 영역을 포함하고 있는 객체에 접근할 수 있는 권한
class Account {
// synchronized 키워드를 사용해 메서드 전체를 임계 영역으로 지정
public synchronized boolean withdraw(int money) {
if (balance >= money) {
try {Thread.sleep(1000);} catch (Exception error) {}
balance -= money;
return true;
}
return false;
}
// synchronized 키워드를 사용해 특정 영역을 임계 영역으로 지정
// synchronized () 괄호 안에 객체의 참조를 넣고, 해당 코드를 실행하는 스레드가 this에 해당하는 락을 얻는다.
public boolean withdraw(int money) {
synchronized (this) {
if (balance >= money) {
try {Thread.sleep(1000);} catch (Exception error) {}
balance -= money;
return true;
}
return false;
}
}
}
: 자바 가상 머신(Java Virtual Machine)은 자바로 작성한 소스 코드를 해석해 실행하는 별도 프로그램이다. JVM이 자바 프로그램과 운영체제 사이 통역가 역할을 하면서 자바는 다른 프로그래밍 언어와 달리 운영체제에 독립적이다.
: 메모리를 자동으로 관리하는 프로세스이고, 프로그램에서 더 이상 사용하지 않는 객체를 찾아 삭제하거나 제거하여 메모리를 확보한다.