Java의 작동 방식
-> .java (소스코드) -> java compiler -> 바이트 코드 -> 자바 가상머신 (Java Virtual Machine) -> 바이트 코드 분석 -> 주기억 장치 (메모리)에 올려줌
주기억장치(메모리)를 관리하는 운영체제(OS)한테 JVM 이 메모리(Runtime Data Area) 를 할당 받아
1. Class Area, 2. Stack Area, 3. Heap Area 4. Native Method Stack Area, 5. PC Register 이 5개로 쪼개서 관리
- Class Area : 클래스 파일의 바이트 코드가 로드 되는 곳 (메인 메서드에서 사용하는 클래스와 static 변수)
- Stack Area : 지역 변수, 매개 변수 , 임시로 할당 됨 프로그램 실행이 끝나면 사라짐
- Heap Area : new 명령어를 통해 생성된 인스턴스 변수, Heap Area에 보관되는 메모리는 메서드 호출이 끝나도 사라지지 않고 유지됨
객체지향 프로그래밍 => 문제와 관련된 다양항 객체들을 중점적으로 보고 각자 가진 데이터가 무엇이며 해당 데이터가 할 수 있는 것은 무엇이 있을까?
객체가 내포한 데이터는 객체의 '상태', 객체에 행할 수 있는 행동은 '동작'
절차나 메서드의 관점으로 생각
Class => 템플릿 (객체가 갖고 있는 데이터 + 객체에 행할 수 있는 동작)
ex)
사람이 클래스면 객체는 클래스의 인스턴스
객체의 상태 = 필드 or 멤버 변수
객체에서 호출할 수 있는 동작 = 메서드
보통 클래스는 대문자로 시작, 메소드나 필드는 소문자로 시작 (이후 Camel 표기법 사용)
클래스 내부에 직접적으로 접근을 하면 안됨 => 캡슐화 파괴 ( so 필드 앞에 private를 붙여서 바로 접근을 못하게 함)
캡슐화의 주요 기능
- 메서드를 통해 검증 가능 ( ex 속도에 음수 값이 못들어오게 막을 수 있음 )
자바코드 -> 컴파일 -> 바이트 코드 생성 -> 자바 가상 머신(JVM) 실행
캡슐화 => 특정 개체에 속하는 데이터를 숨겨 접근을 차단하고 연산만 허용
추상화 => 캡슐화보다 더 포괄적인 개념 ( 모든 복잡한 내용을 숨기고 응용프로그램 인터페이스 유저는 복잡한 세부 내용을 전부 알 필요 X)
생성자는 return 타입이 필요 없고 메서드 명칭이 클래스 이름과 같다!
=> 객체를 만들 때 해당 객체의 최초 상태를 설정 가능
변수를 가지는 생성자를 생성시 디폴트 생성자도 만들어줘야 함 (만약 필요 하다면)
디폴트 생성자에서 초기 상태 값을 주고 싶으면 this(speed) 이런식
Integer => byte, short, int, long
size => 1, 2, 4, 8
리터럴의 경우 뒤에 l 을 붙여여함
ex)
long size=10000000l;
작은 값을 큰 값에 담을 수는 있어도 (implicit 유형)
큰 값을 작은 값에 담을 수는 없음 (만약 하고 싶으면 cast 함수를 통한 형변환 필요
형변환 => cast 함수
ex)
int size1;
long size = 10000000l;
int size1 = (int) size; -> 작은 값에 큰 값을 담기 때문에 casting (명시적 형변환이 필요)
앞에 0을 붙이면 8진수 , 0x를 붙이면 16진수
ex)
int eight=010 => eight=8, int sixteen=0x8 => sixteen=16
Floating Point (부동 소수점)
double, float
float => 부동 소수 리터럴 (정수 뒤에 f를 붙여야함)
ex)
float f= 34.5f
부동 소수 데이터 유형은 정확하지 않음
=> 부동 소숫값이 표현되는 방식 때문에
해결책 : BigDecimal 사용
BigDecimal 객체는 한 번 만든 이상 값을 변경할 수 없음 (불변성) => 새 변수만 생성해야함
BigDecimal 객체를 계산할 때는 double x , string o
ex)
BigDecimal number = new BigDecimal("11.5")
int i=5;
number.add(new BigDecimal(i)) => 16.5
&& 연산자와 & 연산자 차이
ex)
int i=10;
int j=15;
j>15 && i++>5 => && 수식 자체가 거짓이면 평가 자체를 하지 않음
=> i,j는 변하지 않음
j>15 & i++>5 => & 연산자 하나가 거짓임에도 다 실행
=> i=11,j=15
switch 문에서는 long, double, float type X
=> char, int, byte, short, string, enum o
참조타입 (클래스) -> 참조변수 (인스턴스, 객체)
새로운 객체가 생성될 때마다 Heap에 저장 (가장 기본적인 저장소)
기본 변수들은 Stack에 저장
실제 객체 정보는 Heap에 저장 -> Stack에는 실제 객체가 저장된 메모리의 경로
String 클래스
length()
charAt()
substring()
indexOf() => 찾으려는 스트링의 첫 글자의 첫번째 인덱스를 출력
lastIndexOf() => 찾으려는 스트링의 첫 글자의 마지막번째 인덱스를 출력
contain() => 찾으려는 스트링이 있으면 true
startsWith() => 찾으려는 스트링이 맨 앞에 있으면 true
endsWith() => 찾으려는 스트링이 맨 뒤에 있으면 true
empty()
equals()
equalsIgnoreCase() => 대소문자 관계없이
concat() => 스트링을 다른 스트링에 연결
trim() => 빈칸을 없애줌
join() => 같은 스트링에서 값을 구분하고 싶을 때
replace() => 특정 문자를 특정 문자로 대체
String 클래스
모든 스트링 리터럴은 클래스 인스턴스
특정 클래스의 인스턴스가 되는 유일한 리터럴 타입
스트링은 특별한 타입 => 생성자 없이도 새로운 인스턴스를 생성 가능 // 자바에서 자동으로 객체 생성
ex) String s="awdsa";
스트링은 값 변경 불가능 => 새로운 변수 생성해서 바꿔야함 (불변성)
ex) String str="abc";
str="xyz"
의미 : 문자열 데이터는 Heap 영역 중에서 String constant pool 이라는 곳에서 메모리를 할당 받아 그 곳에 값을 저장하고
Stack 에서 str 은 바로 그 주소값을 참조함 str="xyz" 으로 바꾸게 되면 Heap에서 "xyz"에 해당하는 새로 메모리를 할당하고
Stack 에서 str 이 그 참조값으로 변경이 됨
스트링 + 정수 => 스트링
ex) 1 + "123" -> "1123"
StringBuffer 의 경우 불변성이 없다. (값을 변경 가능)
=> 결합을 많이 수행해야 하는 경우 StringBuffer 사용 (메모리적으로 절약)
StringBuilder => 동기화된 클래스, 멀티 스레딩이 가능한 클래스
Wrapper Class => 추가적인 옵션을 다양하게 제공, 기본값에 객체를 제공
래퍼 클래스는 스태틱 메서드(생성자), valueOf를 톨해 생성
래퍼 클래스는 스트링 값을 넣을 수도 있음 but 불변성
valueOf => 존재하는 객체 중 같은 값을 가지는게 있는지 확인하고 있으면 참조값을 가르킴 (재사용)
Java5 에서는 Auto Boxing 을 통해 valueOf를 자동으로 실행
SIZE (bit) , BYTES (byte)
LocalDate, LocalDateTime, LocalTime class
LocalDate - 날짜만 보여줌
LocalDate today=LocalDate.now();
- getYear()
- getDayOfWeek()
- getDayOfMonth()
- getDayOfYear()
- isLeapYear() => 윤년 확인 가능
- plusDays()
- of() => 특정 연, 월, 일 설정 가능
- WithYear (WithDayOfMonth, WithMonth, WithDayOfYear 등) 설정가능
- isBefore, isAfter => 날짜 비교 가능
LocalDateTime - 날짜와 시간을 모두 보여줌
LocalTime - 시간만 보여줌
배열 선언
ex)
int[] marks={1,2,3};
int[] marks=new int[3];
Arrays.toString(marks) => 배열의 콘텐츠 출력
Arrays.fill(marks,100) => 모두 100으로 채움
Arrays.equals(array1,array2) => 길이가 같고 각각의 요소가 같아야함
Arrays.sort => 정렬
Integer.MAX_VALUE
Integer.MIN_VALUE
BigDecimal 불변성
가변 인수 => 인수에 갯수 제한없이 여러개를 받을 수 있음, 무조건 마지막 인수여야함!
ex) void print(int... values){
System.out.println(Arrays.toString(values));
}
배열 만드는 방법
ex)
Student[] student=new Student[5];
student[1]=new Student();
Student[] student2={new Student(),new Student()};
String[] string={"apple","banana","kiwi"}
Array(배열) 를 한번 만들면 크기 수정 안됨
=> 새로운 Array 만들어서 거기다 추가하거나 삭제 해야함
-> ArrayList 를 사용하면 해결 가능
ArrayList
String 타입의 ArrayList 를 생성시
=> ArrayList items=new ArrayList();
ArrayList 는 Collection 의 일부이기 때문에 Collection 메소드 사용 가능
ex) Collections.max(marks)
toString() => 객체명 출력시 해시 값이 나오는데, toString 메서드가 있다면 그 객체의 값(정보)가 나옴
public String toString(){
return
}
class => 단순한 템플릿
템플릿을 바탕으로 객체(인스턴스) 생성
객체 => 상태(변수) + 행동(메서드)
class 를 만들 때 고려해야 하는 것
- 상태
- 생성자
- 행동
=> 사용자의 입장에서 생각
객체 구성 관계
상속 (IS A 관계)=> 다른 클래스에서 이미 사용 중인 코드를 재사용할 때 유용함
ex) Person (수퍼 클래스) - Student (서브 클래스) => Student IS A Person
서브 클래스가 슈퍼 클래스를 확장하며 서브 클래스는 슈퍼 클래스의 모든 기능을 가지게 됨
기본적으로 클래스가 다른 클래스에서 아무것도 상속 받지 않는다면 Object 클래스(디폴트 값) 를 상속받음
하위클래스에서 상위클래스에 있는 메서드를 오버라이딩을 통해 재정의 가능하다.
하위클래스에서 생성자를 생성시 자동으로 상위클래스 생성자 호출
java 에서는 다중 상속이 되지 않음 (C++에선 가능) but 상속 계층은 가능
하위 클래스 변수를 담을 수 있는 상위 클래스 참조 변수를 생성 가능
ex) Pet pet = new Dog(); ( public class Dog extends Pet )
상위 클래스 변수는 하위 클래스 변수를 담을 수 있다.
=> 상위 클래스에서 하위 클래스 변수를 참조 가능
instanceof => 현재 객체가 특정 클래스의 인스턴스인지 알 수 있게 해줌
ex) Animal - Pet - Dog
(하위클래스의 객체) instanceof (상위클래스 or 해당 type) => true -> 현재 객체가 상위 클래스의 인스턴스인지 확인 가능
pet instanceof Animal => true
dog instanceof Pet => true
pet instanceof Dog => false
but
Pet pet=new Dog(); // 만약 pet이 Dog 인스턴스로 생성했다면
pet instanceof Dog => true 가 될 수 있음!!
추상 클래스 => abstract class Animal(){}
추상 메서드는 추상 클래스에서만 생성 가능, 비추상적 메서드는 추상 클래스에서 생성 가능
모든 추상 클래스들은 높은 단계의 알고리즘을 적용하고 자세한 세부정보들은 적용 클래스들에게 맡김
ex)
public abstract class AbstractRecipe {
public void execute(){
getReady();
doTheDish();
cleanup();
}
abstract void getReady();
abstract void doTheDish();
abstract void cleanup();
}
인터페이스와 추상 클래스의 차이 (활용도는 같을 수 있지만 사고 방식 면에서 완전히 다름)
인터페이스
=> 두 시스템 사이에 소통하길 원할 때, 두 클래스 아니면 두 부속이 서로 소통하길 원할 때 (Dummy 알고리즘으로 대체해서 사용하다가 real 알고리즘으로 변경하면 됨)
-> 프로젝트는 계속 진행 가능
추상클래스
=> 높은 단계의 구조를 제공하고 싶어할 때, 구현의 세세한 부분들을 하위 클래스에 맡기고 싶을 때
다형성 => 같은 것에 여러가지 구현을 부여
인터페이스가 공통 행동을 클래스에게 제공
인터페이스는 외부 인터페이스가 실제로 사용 가능하지 않아도 작업을 계속할 수 있게 해줌 (교체 하면 그만!!) => 통신 계약
인터페이스는 또 다른 인터페이스를 연장 가능
인터페이스 안의 메서드들은 무조건 개방형 (private 사용 X)
자신이 연장하는 인터페이스에 대한 구현이 없는 그저 인터페이스만 구현하는 추상 클래스를 생성 가능
=> 인터페이스에 있는 메서드를 다 구현하고 싶지 않다면 추상 클래스를 통해 implements 해줘야 함
인터페이스 안에서 변수가 아닌 상수를 만들 수 있음
(자동으로 public static final 이 붙음)
ex)
interface Runnable{
int i=10;
}
class Student {
public void print(){
System.out.println(Runnable.i);
}
}
return 값 앞에 default 키워드를 붙이면 구현 가능 => adapter 패턴의 단점을 보완해줌 (이미 상속 중일 땐 adpater 패턴 적용 못하는 단점)
adapter 패턴 => 인터페이스의 강제성을 덜어줌 (필터링)
ex)
interface Runnable{
default void run(){
System.out.println("run");
}
}
인터페이스는 시행될 수 있는 공통된 행동들을 대표함
인터페이스 종류의 참조 변수는 인터페이스 구현을 가질 수 있음
Collections
List words = List.of("Apple","Bat","Banana"); => 한번에 리스트 구성 (하나씩 add 할 필요없이)
words.get(0) => "Apple"
words.contain("Bat") => true
words.indexOf("Banana") => 2
words.indexOf("A") => -1
불변성 => 특정 클래스의 인스턴스를 만든 순간부터 이 값을 변경 X
ex) String , BigDecimal, Wrapper, List
List (불변성) <-> ArrayList, LinkedList, Vector (가변성)
ex)
List<String> words = List.of("Apple","Bat","Banana") => 불변성 (List.of로 구성)
List<String> words=new ArrayList<String>() => 가변성
List<String> words=new LinkedList<String>()
List<String> words=new Vector<String>()
배열의 경우 삽입과 삭제에 비효율 but 접근성은 좋음
LinkedList 의 경우 삽입과 삭제에 효과적 but 접근성은 비효율
Vector 는 Thread Safe (Synchronized Methods) => 성능 구림
안전이 필요하지 않으면 ArrayList 쓰자
ArrayList.add(index, n) => index 에 n 삽입
ArrayList.addAll()
ArrayList.set(index, n) => index 의 값을 n으로 변경
ArrayList.remove(index) => index 의 값을 제거
리스트 iterator 이용해서 출력 ex)
Iterator wordsIterator=words.iterator();
while(wordsIterator.hasNext()){
System.out.println(wordsIterator.next());
}
특정 원소를 제거시 ex)
List<String> words=List.of("Apple","Cat","Bat");
List<String> wordAl=new ArrayList<>(words);
Iterator<String> iterator=wordsAl.iterator();
while(iterator.hasNext()){
if(iterator.next().endsWith("at")){
iterator.remove();
}
}
wordAl=> [Apple]
List value=List.of("A",'A',1,1.0)
=> List 에서 type 에 상관없이 담을 수 있음 -> 특정한 type 만 담고 싶다면 Generation (일반화)
Collections.sort(객체 이름) => 정렬
ArrayList.sort()
Set 인터페이스 => 중복 X
Set, HashSet 은 요소의 친구 상관 안함 (순서대로 저장 X, 정렬 X)
<-> LinkedHashSet 은 순서대로 저장
<-> TreeSet 은 정렬됨
Set 특정 위치에 요소를 추가하거나 제거 X
Hashing 함수의 경우 13을 나눈 나머지 값을 index 로 가짐
=> 해시코드라는 것을 이용해 구현
Tree(red-black tree) => 검색, 제거, 삽입의 소모성을 줄여주는데 효과적
TreeSet.floor(n) => n보다 작거나 같은 것
TreeSet.lower(n) => n보다 작은 것
TreeSet.ceiling(n) => n보다 크거나 같은 것
TreeSet.subSet(a,b) => a보다 크거나 같고 b보단 작다
TreeSet.subSet(a,True,b,True) => a보다 크거나 같고 b보단 작거나 같다
TreeSet.headSet(n) => n 이전의 모든 숫자 출력
TreeSet.tailSet(n) => n 부터 모든 숫자 출력
queue
Queue.poll() => 맨 앞에 요소를 꺼내고 출력
Queue.offer() => 요소 추가
Queue.addAll(List.of()) => 여러 요소 추가
Comparator 를 이용해서 커스텀 정렬 가능
ex)
class StringLengthComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return Integer.compare(o2.length(), o1.length());
}
}
HashMap => key 값을 null 값과 저장 가능
HashTable => 해싱함수 사용, 동기화 (스레드 안전 => 스레드가 작업을 하고 있으면 그 작업이 끝날 때까지 기다림 so 성능은 안좋음)
HashMap, HashTable <-> LinkedHashMap <-> TreeMap
.of() => 이용해서 만들면 불변성을 지님 (추가 안됨)
Map.containsKey()
Map.containsValue()
Map.keySet()
Map.values()
TreeMap - NavigableMap
TreeMap.higherKey(n) => n보다 큰 key 값
TreeMap.lowerKey(n) => n보다 작은 key 값
TreeMap.ceilingKey(n) => n보다 같거나 큰 key 값
TreeMap.floorKey(n) => n보다 같거나 작은 key 값
TreeMap.firstEntry() => 처음 값
TreeMap.lastEntry() => 마지막 값
TreeMap.subMap(a,b) => a보다 같거나 크고, b보다 작은 key,value 값
특정 타입이 아닌 여러 타입을 받고 싶을때 Generics 이용
클래스에 제네릭 생성
ex)
public class MyCustomList<T>{
ArrayList<T> list=new ArrayList<>();
}
메서드에 제네릭 생성
ex)
static <X> X doubleValue(X value){ // 어떤 값도 출력 가능
return value;
}
static <X extends List> void duplicate(X list){ // List 계열만 ArrayList, Vector, LinkedList
list.addAll(list);
}
Number -> Integer, Double, Float, Byte, Int, Long, Short
상한 경계 와일드 카드 => Number 클래스를 연장하는 Number 클래스의 하위 클래스인 모든 리스트들에 대해 공통된 논리를 구현하는데 도움
ex)
static Double sumOfNumberList(List<? extends Number> list){ => ? : 와일드 카드
double sum=0.0;
for (Number number : list) {
sum += number.doubleValue();
}
return sum;
}
하한 경계 와일드 카드 => 숫자의 하위 타입들의 여러 다른 값들을 많이 추가 가능하게 해줌
ex)
static void addCoupleOfValues(List<? super Number> list){
// 숫자 클래스의 하위 클래스인 이상 아무 종류나 list 에 추가 가능
list.add(1);
list.add(1.0);
list.add(2L);
}
함수형 프로그래밍
stream().forEach(action) => 흐름의 각 요소마다 함수를 보냄
각 요소가 흐름되고 각 요소들에 대해 코드가 실행
filter(logic) => if문
reduce(초기값, (함수 정의))
ex)
List<Integer> list = List.of(1, 2, 3, 4, 5);
list.stream().reduce(0,(num1,num2) -> num1+num2);
num1=0, num2=1
-> num1=1, num2=2
-> num1=3, num2=3
-> num1=6, num2=4
-> num1=10, num2=5
-> num1=15
함수형 프로그래밍과 일반 절차적 프로그래밍의 차이점
=> 변수 변이를 피함
element -> element %2==0 => 여기서 -> 람다 표현식
- 중간 연산 : 또 다른 스트림
ex) filter, sorted, distinct, map - 종단 연산 : 요소를 처리하고 소모 ( 스트림을 입력값으로 받아 하나의 결과로 줄이는 것)
무조건 하나 => 스트림이 모두 소모돼 하나의 결과로 줄여주기 때문에 하나일 수 밖에 없다
ex)
forEach, reduce, max((num1,num2) -> Integer.compare(num1,num2))
IntStream=> Stream 이 아님 (IntPipeline)
(So, boxed()를 통해서 스트림의 정수형 배열로 변환)
Optional => isPresent(), orElse()
자바 컴파일러가 함수형 인터페이스 실행
- filter -> Predicate (test 메서드)
- forEach -> Consumer (accept 메서드)
- map -> Function (apply 메서드 )
메서드 참조 => 축약 가능 ( 코드의 가독성을 높이기 위함 )
클래스 이름 :: 메서드 이름
ex)
filter(n -> n%2==0) => filter(클래스 이름::isEven)
public static boolean isEven(Integer number){
return number%2==0;
}
Thread 는 유사성을 가진 모든 수행문을 동시에 실행하게 해줌
=> 자원 낭비를 줄여줌 (CPU 효율성 향상)
=> 병렬적으로 함께 실행 가능
- Thread 확장 (Thread 상속)
ex)
class Task1 extends Thread{
public void run(){
}
}
=> Task1 task1=new Task1();
task1.start();
- Runnable 인터페이스 실행
ex)
class Task1 implements Runnable{
public void run(){
}
}
=> Task2 task2=new Task2();
Thread task2Thread= new Thread(task2);
Thread 상태
- NEW : Thread 가 처음 생성될 때의 상태
- RUNNABLE : 일 혹은 작업을 처리하고 싶기는 하지만, 다른 Thread 가 우선적으로 실행되고 있는 상태
- RUNNING : Thread 가 실행 중인 상태
- BLOCKED/WAITING :
외부 인터페이스나 데이터베이스 등으로부터 어떤 입력을 위해 대기하고 있거나, 실행이 완료되지 않은 다른 Thread 로부터 데이터를 입력받아야 하는 상황 - TERMINATED/DEAD : 실행이 완료되었을 경우 의미
Thread 우선순위
setPriority (1~10)
=> 추천일 뿐 강제가 아님
Threads 사이의 소통
join 메서드 => 특정 Thread 의 실행이 끝날 때까지 대기한다는 의미 ( task 가 완료된 후에나 다음 줄의 코드 실행 )
Thread.sleep(1000) => 1초 동안 대기
Thread.yield() => CPU 를 포기하도록 요청
synchronized ( 동기화 ) => 하나의 쓰레드에 동기화된 코드의 작업 가능 so 오버헤드
Executor Service ( 실행기 ) => 다수의 쓰레드를 한 번에 실행할 수 있도록 도와줌, 쓰레드의 상태를 알게 해줌
newSingleThreadExecutor (한 개의 쓰레드) <-> newFixedThreadPool (여러 개의 쓰레드)
Thread 에서 결과를 얻는 지점 또한 설정 가능
Callable => Callable 인터페이스를 이용해서 후처리 스레드로 돌아오기
Future (결과가 나올 것이라고 예측)
ex)
Future<String> welcomeFuture
= executorService.submit(new CallableTask("Paul"));
submit() 메서드를 통해 callable 실행 가능
String welcomeMessage = welcomeFuture.get(); => task 가 실행될 때까지 기다림, task 가 실행을 끝낸 뒤에나 이 다음 코드 실행
get() 메서드를 쓰는 시점 이후부터 후처리
invokeAll : 모든 작업이 완료된 후에 한번에 다 출력 (모든 task 의 처리가 끝날 때까지 기다림)
invokeAny : 하나의 작업이 먼저 끝나면 먼저 결과를 출력 (한개의 task 가 실행이 완료되는 것을 기다린 후, 결과를 얻음)
Exception Handling ( 예외 처리 ) 2 가지 키
- 제가 이용하려는 시스템의 최종 사용자에게 친절하게 메시지를 남기는 것 (어떤 작업 또는 단계를 거쳐야 하는지 알림)
- 오류를 제거하기 위한 충분한 정보를 제공
call chain
try ~catch, throws
예외에서 서열 존재
Exception > RuntimeException > NullPointException
finally
=> 예외가 생기든 안생기든 finally 블럭 코드 실행 (무조건 절대적 return 값이 try 문에 있어도 실행됨)
but System.exit 의 경우에는 실행 X
try 는 단독으로 쓰일 수 없다. (무조건 catch 나 finally 와 같이 쓰여야함)
점검 예외 처리 vs 비점검 예외 처리
class Error extends Throwable => Error 는 프로그래머가 처리할 수 없는 오류 (메모리를 모두 사용한 경우, JVM 메모리, stack overflow 에러등)
class Exception extends Throwable => Exception 는 프로그래머가 처리 할 수 있는 오류
Exception 항목 아래 RuntimeException 이 아닌 것들이 checked exceptions ( 처리하기 까다로운 것들)
=> calling 메서드가 이를 처리하거나 또 다시 떠넘기는 방법 사용
=> 예외를 처리해줘야함
=> throws 를 앞에 붙여줘야함
RuntimeException => unchecked exception ( calling 메서드가 아무 처리 안해도 됨 => 예외를 밖으로 던지는 것을 걱정할 필요 없다)
if) 소비자가 예외를 처리할 수 있다면 (예외가 생겼다는 것을 알리길 바란다면) checked exception => 사용자에게 압박을 줌
소비자가 대응할 수 없는 일이라면 RuntimeException 을 사용
try-with-resources => try( ) 기능을 자료들에 사용 (구체적인 상황이라서 catch 나 finally 불필요 )
ex)
public class TryWithResourcesRunner {
public static void main(String[] args) {
try(Scanner scanner = new Scanner(System.in)) { // 오류가 생기면 알아서 close 해줌
int[] arr = {12, 3, 4, 5};
int a = arr[3];
}
}
}
두개 이상의 예외를 한번에 다룰 수도 있음
catch ( IOException | SQLException ex) {
ex.printStackTrace();
}
예외를 다루는데 있어서 최고의 방법들
- 예외를 숨기지 말자 => stack trace 의 전부를 log 에 넣음
- flow control 사용 X => if-else 사용 X
- 사용자를 생각하라 => 최종 사용자에게 어떻게 설명 할지를 생각하라
- calling 메서드에 대해 생각하라 => RuntimeException 으로 만들 필요가 없는지 생각
- global exception handling => 모든 것을 관장하는 무언가를 설정 (사용자에게 예외가 직접 전달되는 일이 없도록) -> 예외에 대한 올바른 메시지가 출력되어야 함
자바의 파일과 디렉토리
파일들을 목록으로 정리하는 메서드
=> Files.list(경로)
ex)
public class DirectoryScanRunner {
public static void main(String[] args) throws IOException {
Files.list(Paths.get(".")).forEach(System.out::println);
}
}
전체 디렉토리를 가져오고 싶을 때 (재귀적인 방법)
=> Files.walk(경로)
원하는 파일을 찾는 메서드
=> Files.find(경로,깊이,람다식)
파일의 attributes 들을 기준으로 필터링하여 디렉토리가 맞는지, 크기가 얼마만한지등 여러가지 알 수 있음
파일의 이름이나 path, 파일의 특성에 따라 필터링 하는 것이 가능
파일에서 내용 불러오는 메서드
=> Files.readAllLines(경로)
모든 줄의 내용을 리스트 형식으로 한번에 출력 but 큰 용량의 파일을 다룰 땐 좋은 방법이 아님
So Files.lines(경로) => Stream 이용 (filter, map 같은 다양한 연산 사용 가능)
파일에 내용 담기
=> Files.write(경로,담을 내용)
동시 컬렉션의 동시성과 활용한 원자 연산
concurrency
counter
automic operation
thread-safety => 임의의 메소드가 다양한 thread 에 의해 한 번에 안전하게 실행되는 것
not thread-safe -> synchronized 를 붙이면 됨
-> 문제점 : 적은 병행성을 가짐 ( 동기화(synchronized)된 메서드들이 여러개일 경우 각 메서드가 독립성을 가져도 여러 thread 들이 대기하는 상황이 생길 수 있음)
해결책 : Lock 를 사용시 코드를 synchronized 없이도 동기화해주거나 thread-safe 하게 해줌
( locks => 동기화된 코드를 여러 조각의 코드로 쪼갤 수 있게 해줌 )
atomic class
=> AtomicInteger
AtomicInteger 에 의존하여 thread-safe 한지를 확인하게 됨 ( locks 에 의무를 가져와서 AtomicInteger 에 부여)
아무곳이나 사용 X => 다양한 연산의 과정들이 필요할 경우 AtomicInteger 보단 lock 사용
ConcurrentHashMap => 해당 인터페이스에 computeIfAbsent() 메서드가 있는데 이 메서드가 atomic 연산을 보장해줌
so ConcurrentHashMap 을 불러내는 순간 thread-safe 에 해당
ConcurrentHashMap 은 지역을 나눠서 각 지역마다 다른 lock 를 사용해서 자원을 조금 더 효율적으로 사용 가능하게 함</br
>
CopyOnWrite
=> write 가 매우 적고, read 가 매우 많은 경우에 효율적
=> 수정시 array 전체를 복사 (복사하는 동안 그 전의 array 를 이용함)
=> 동시성 (Concurrency) 확보
ex) import static java.lang.System.out; => System.out.println() -> out.println() 가능 import static java.util.Collections.*; => Collections.sort() -> sort() 가능
=> if 문에서만 사용되는 것이 아님, block 을 지정하고 그 안에 변수를 선언하면 그 block 안에서만 유효 ex) { int i; }
=> 객체가 같을 때만 true, 안에 정보가 같다고 같은게 아님 (서로 다른 메모리에 위치)
=> hashcode 를 기준으로 bucket 에 배정
=> equals() 메서드를 실행할 때 hashCode() 메서드도 실행해야함
public - 클래스가 public 에 해당한다면 다른 패키지의 클래스에서도 사용 가능
default - 클래스가 (default) 의 경우 해당 패키지 안에서만 사용 가능
protected, private 은 클래스에 사용 불가능
public - 오로지 public 만 다른 패키지에서도 접근 가능
default - 같은 패키지이거나 자식 클래스(상속)에서 접근 가능
private - 같은 패키지 안에서 접근 불가능 (오로지 같은 클래스 안에서만 접근 가능)
protected - 같은 패키지이거나 해당 클래스에서만 접근 가능
final 클래스 - 상속 불가능 (extends X), 특정 클래스에 대한 확장을 허용하고 싶지 않을 때 사용
final 메서드 - 코드 오버라이딩 불가능
final 변수 - 불변성을 가짐 (값이 고정되길 원할 때), 단 한번만 값 부여 가능 (but, 생성자를 통해 인스턴스 마다 값을 달리 할 수 있다.)
final 전달인자 - final 을 이용해서 값을 받을 때 전달인자 수정 불가능
static 변수 - 다수의 인스턴스 사이에서 공유 가능 (각 인스턴스에 대해 별도의 인스턴스를 갖는 상황이 아닌 하나의 인스턴스만 갖길 원할 때)
ex) Player 클래스에서 인원수를 카운팅 할 때 static 이용하면 인스턴스를 생성할 때마다 카운팅이 됨
=> 인스턴스 없이 클래스 이름에서 static 메서드 바로 사용 가능
static 메서드 안에서는 인스턴스 데이터에 접근 불가능 (static 인 데이터만 접근 가능)
<-> 반대는 가능
public static final 변수 - 상수 (정의)
=> 작업을 좀 더 분명하고 쉽게 전달
public final 변수와 차이점 : 그저 final 만 선언한 변수의 경우 생성자를 통해서 인스턴스마다 값을 변경할 수 있다.
but public static final 은 가능하지 않다.
내부 클래스들(nested classes) 은 다른 클래스 안에 들어가 있는 class 를 말함
전체 클래스의 선언이 다른 클래스 안에서 이루어짐
내부 클래스는 외부 클래스의 인스턴스가 필요 하지만 정적 중첩 클래스는 외부 클래스의 인스턴스가 필요 없다
but 내부 클래스는 외부 클래스의 멤버 변수에 접근 할 수 있지만, 정적 중첩 클래스는 불가능
ex)
public class NestedClassRunner {
class InnerClass{
}
static class StaticNestedClass{
}
public static void main(String[] args) {
StaticNestedClass staticNestedClass = new StaticNestedClass();
NestedClassRunner nestedClassRunner = new NestedClassRunner();
InnerClass innerClass = nestedClassRunner.new InnerClass();
}
}
ex)
Collections.sort(animals,
new Comparator<String>() { // 익명 클래스
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}
});
열거형 , 인스턴스 변수에 저장 가능
ex)
public enum Season{
WINTER(4), SPRING(1),SUMMER(2), FALL(3);
private int value;
private Season(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}