- Java는 객체지향 프로그래밍 언어이다.
- 기본 자료형을 제외한 모든 요소들은 객체로 표현되고, 객체 지향 개념의 특징인 캡슐화, 상속, 다형성이 잘 적용된 언어이다.
- 장점
- JVM 위에서 동작하기 때문에 운영체제에 독립적이다.
- GabageCollector를 통한 자동적인 메모리 관리가 가능하다.
- 단점
- JVM 위에서 동작하기 때문에 실행 속도가 상대적으로 느리다.
- 다중 상속이나 타입에 엄격하며, 제약이 많다.
JVM은 스택 기반으로 동작하며, Java Byte code를 OS에 맞게 해석 해주는 역할을 하고 가비지컬렉션을 통해 메모리 관리를 해준다.
- 클래스 로더 세부 동작
1. 로드 : 클래스 파일을 가져와서 JVM 메모리에 로드한다.
2. 검증 : 자바 언어 명세(Java Language Specification) 및 JVM 명세에 명시된 대로 구성되어 있는지 검사한다.
3. 준비 : 클래스가 필요로 하는 메모리를 할당한다. (필드, 메소드, 인터페이스 등등)
4. 분석 : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다.
5. 초기화 : 클래스 변수들을 적절한 값으로 초기화한다. (static 필드)
정수형 : byte, short, int, long
실수형 : float, double
문자형 : char
논리형 : boolean
정수형 위 순서대로 1, 2, 4, 8 바이트
실수형은 4, 8 바이트
문자형은 2바이트
논리형은 1바이트
상위 클래스에 있는 메소드를 하위 클래스에서 재정의하는 것
매개변수의 개수나 타입을 다르게하여 같은 이름의 메소드를 여러 개 정의하는 것
프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체로 만들고, 객체들간의 상호작용을 통해 로직을 수성하는 프로그래밍이다.
즉, 기능이 아닌 객체가 중심이며, "누가 어떤 일을 할 것인가?"가 핵심
특징으로는 캡슐화, 상속, 다형성, 추상화 등이 있고, 모듈 재상용으로 확장 및 유지보수 용이
장점
단점
- try-catch-finally의 문제점을 보완하기 위해 나온 개념이다.
- try(...) 안에 자원 객체를 전달하면, try블록이 끝나고 자동으로 자원 해제 해주는 기능을 말한다.
- 따로 finally 구문이나 모든 catch 구문에 종료 처리를 하지 않아도 되는 장점이 있다.
- 불변 객체는 객체 생성 이후 내부의 상태가 변하지 않는 객체를 말한다.
- Java에서 필드가 원시 타입인 경우 final 키워드를 사용해 불변 객체를 만듬.
- 참조 타입일 경우에 추가 작업 필요
참조 타입은 대표적으로 (1)객체를 참조할 수도 있고, (2)배열, (3)List등을 참조할 수 있다.
- 참조 변수가 일반 객체인 경우 객체를 사용하는 필드의 참조 변수도 불변 객체로 변경
- 배열일 경우 배열을 받아 copy해서 저장하고, getter를 clone으로 반환하도록 한다.
- 배열을 그대로 참조하거나, 반환할 경우 외부에서 내부 값을 변경할 수 있음. 때문에 clone을 반환해 외부에서 값을 변경하지 못하게함
- 리스트도 배열과 마찬가지로 새로운 List를 만들어 값을 복사하도록 함
- 배열과 리스트는 내부를 복사하여 전달하는데, 이를 방어적 복사라고 한다.
- Thread-Safe하여 병렬 프로그래밍에 유용하며, 동기화를 고려하지 않아도 된다.
(공유 자원이 불변이기 때문에 항상 동일한 값을 반환하기 때문)- 실패 원자적인 메소드를 만들 수 있다.
- 어떠한 예외가 발생되더라도 메소드 호출 전의 상태를 유지할 수 있어 예외 발생 전과 똑같은 상태로 다음 로직 처리 가능
- 부수효과를 피해 오류를 최소화 할 수 있다.
- 부수효과 : 변수의 값이 바뀌거나 객체의 필드 값을 설정하거나 예외나 오류가 발생하여 실행이 중단되는 현상
- 메소드 호출 시 파라미터 값이 변하지 않는다는 것을 보장할 수 있다.
- 가비지 컬렉션 성능을 높일 수 있다.
- 가비지 컬렉터가 스캔하는 객체의 수가 줄기 때문에 GC 수행시 지연시간도 줄어든다.
클래스 내 추상 메소드가 하나 이상 포함되거나 abstract로 정의된 경우
모든 메소드가 추상 메소드로만 이루어져 있는 것
싱글톤 패턴의 대표적인 예시는 Spring Bean이다.
스프링 빈 등록 방식은 기본적으로 싱글톤 스코프이고, 스프링 컨테이너는 모든 빈들을 싱글톤으로 관리한다.
스프링은 요청할 때마다 새로운 객체를 생성해서 반환하는 기능도 제공(프로토타입 빈, @Scope("prototype")
가비지 컬렉션은 JVM의 메모리 관리 기법 중 하나로 시스템에서 동적으로 할당됐던 메모리 영역 중에서 필요없어진 메모리 영역을 회수하여 메모리를 관리해주는 기법이다.
GC의 작업을 수행하기 위해 JVM이 어플리케이션의 실행을 잠시 멈추고, GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업을 중단 후(Stop The World 과정) 사용하지 않는 메모리를 제거(Mark and Sweep 과정)하고 작업이 재개된다.
++ GC의 작업은 Young 영역에 대한 Minor GC와 Old 영역에 대한 Major GC로 구분된다.
https://gyoogle.dev/blog/computer-language/Java/Garbage%20Collection.html
자바의 메모리 공간은 크게 Method 영역, Stack 영역, Heap 영역으로 구분되고, 데이터 타입에 따라 할당된다.
전역 변수와 static 변수를 저장하며, 메소드 영역은 프로그램의 시작부터 종료까지 메모리에 남아있다.
JVM이 동작해서 클래스가 로딩될 때 생성
지역변수와 매개변수 데이터 값이 저장되는 공간이며, 메소드가 호출될 때 메모리에 할당되고 종료되면 메모리가 해제된다. LIFO구조를 갖고 변수에 새로운 데이터가 할당되면 이전 데이터는 지워진다.
컴파일 타임 시 할당
new 키워드로 생성되는 객체(인스턴스), 배열 등이 Heap영역에 저장되며, 가비지 컬렉션에 의해 메모리가 관리되어 진다.
런타임시 할당
클래스는 객체를 만들어내기 위한 설계도 혹은 틀 이라고 할 수 있고, 객체를 생성하는데 사용한다.
객체는 설계도(클래스)를 기반으로 생성되며, 자신의 고유 이름과 상태, 행동을 갖는다.
여기서 상태는 필드(fields), 행동은 메소드(Method)라고 표현한다.
객체에 메모리가 할당되어 실제로 활용되는 실체는 '인스턴스'라고 부른다.
생성자는 클래스와 같은 이름의 메소드로, 객체가 생성될 때 호출되는 메소드다.
명시적으로 생성자를 만들지 않아도 default로 만들어지며, 생성자는 파라미터를 다르게 하여 오버로딩할 수 있다.
기본 자료형(Primitive data type)에 대한 객체 표현을 Wrapper class라고 한다.
기본 자료형 -> Wrapper class로 변환하는 것을 Boxing
Wrapper class -> 기본 자료형으로 변환하는 것을 UnBoxing
new String()은 new 키워드로 새로운 객체를 생성하기 때문에 Heap 메모리 영역에 저장되고 Heap 안에 있는 String Constant Pool 영역에 저장된다.
불변의 속성을 가지며, StringBuffer와 StringBuilder는 가변의 속성을 가진다.
동기화를 지원하여 멀티 쓰레드 환경에서 주로 사용.
동기화를 지원하지 않아 싱글 쓰레드 환경에서 주로 사용한다.
변수 또는 메소드의 접근 범위를 설정해주기 위해서 사용하는 Java의 예약어
- static 키워드를 사용한 변수나 메소드는 클래스가 메모리에 올라갈 때 자동으로 생성되며 클래스 로딩이 끝나면 바로 사용할 수 있다. 즉, 인스턴스(객체) 생성없이 바로 사용 가능.
- 모든 객체가 메모리를 공유한다는 특징이 있고, GC 관리 영역 밖에 있기 때문에 프로그램이 종료될 때까지 메모리에 값이 유지된 채로 존재하게 된다.
static은 자주 변하지 않는 값이나 공통으로 사용되는 값 같은 공용자원에 대한 접근에 있어서 매번 메모리에 로딩하거나 값을 읽어들이는 것보다 일종의 '전역변수'와 같은 개념을 통해 접근하는 것이 비용도 줄이고 효율을 높일 수 있다.
인스턴스 생성 없이 바로 사용 가능하기 때문에 프로그램 내에서 공통으로 사용되는 데이터들을 관리할 때 이용.
구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API이다.
코드를 작성할 시점에는 어떤 타입의 클래스를 사용할지 모르지만, 런타임 시점에 지금 실행되고 있는 클래스를 가져와서 실행해야 하는 경우 사용된다.
프레임워크나 IDE에서 이런 동정인 바인딩을 이용한 기능을 제공한다. intelliJ의 자동완성 시능, 스프링의 어노테이션이 리플렉션을 이용한 기능이라 할 수 있다.
실행 중 일어날 수 있는 치명적 오류를 말한다. 컴파일 시점에 체크할 수 없고, 오류가 발생하면 프로그램은 비정상 종료되며 예측 불가능한 UncheckedException에 속함.
Error보다 비교적 경미한 오류이며, try-catch를 이용해 프로그램의 비정상 종료를 막을 수 있다.
실행하기 전에 예측 가능한 예외를 말하고 반드시 예외 처리를 해야한다.
실행하고 난 후에 알 수 있는 예외를 말하고 따로 예외처리를 하지 않아도 된다.
RuntimeException은 UncheckedException을 상속한 클래스이고, RuntimeException이 아닌 것은 CheckedException을 상속한 클래스이다.
개발할 때 가장 많이 발생하는 예외 중 하나가 NPE(NullPointerException)이다.
NPE를 피하려면 null 여부 검사를 필연적으로 하게 되는데 만약 null 검사를 해야하는 변수가 많은 경우 코드가 복잡해지고 번거롭다. 하지만 Java8부터 Optional<'T'>를 제공하여 null로 인한 예외가 발생하지 않도록 도와주고, Optional 클래스의 메소드를 통해 null을 컨트롤 할 수 있다.
다수의 데이터를 쉽고 효과적으로 관리할 수 있는 표준화된 방법을 제공하는 클래스 집합을 의미
자바 컬렉션에는 List, Set, Map 인터페이스를 기준으로 여러 구현체가 존재하고, 이에 더해 Stack, Queue 인터페이스도 존재한다.
순서가 있는 데이터의 집합이며, 데이터의 중복을 허용한다.
대표적인 예로 ArrayList가 있고, 이는 Vector를 개선한 것이다. 이외에도 LinkedList 등의 구현체가 있다.
순서가 없는 데이터의 집합이며, 데이터의 중복을 허용하지 않는다.
대표적인 구현체로 HashSet이 있고, 순서를 보장하기 위해서는 LinkedHashSet을 사용한다.
(Map의 key-value 구조에서 key 대신 value가 들어가 value를 key로 하는 자료구조)
키와 값이 한 쌍으로 이뤄져 있고, 키를 기준으로 중복을 허용하지 않으며, 순서가 없다. key의 순서를 보장하기 위해서는 LinkedHashMap을 사용한다.
객체는 직접 new 키워드로 사용할 수 있으며, Queue 인터페이스는 LinkedList에 new 키워드를 적용해 사용할 수 있다.
hashCode() 메소드를 오버라이딩하여 리턴된 해시코드 값이 같은지를 보고 해시코드 값이 다르다면 다른 객체로 판단하고, 해시코드 값이 같으면 equals() 메소드를 오버라이딩하여 다시 비교한다. 이 두 개가 맞으면 중복 객체다.
벡터는 데이터 삽입시 원소를 밀어내지만 리스트는 노드를 연결만 하기 때문에, 삽입, 삭제 부분에서리스트가 시간복잡도의 우위를 가진다.
벡터는 랜덤부분접근이 가능하지만 리스트는 더블링크드리스트로 되어있기 때문에 랜덤 접근이 되지 않는다. 검색적인 측면에서는 벡터가 우위에 있다.
벡터는 리스트와 달리 항상 동기화 되는 장점이자 단점을 가지고 있다. 멀티 쓰레드 환경에서 안전하게 객체를 추가하고 삭제할 수 있지만, 단일 쓰레드 환경일때도 동기화를 하기 때문에 List보다 성능이 떨어진다.
제네릭은 데이터 타입을 하나로 지정하지 않고 사용할 때마다 범용적이고 포괄적으로 지정한다는 의미이다.
제네릭 타입을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있어 에러를 사전에 방지할 수 있다.
클래스, 메소드, 변수, 인자를 선언할 때 사용할 수 있으며, 한 번만 할당하고 싶을 때 사용한다.
try-catch와 함께 사용되며, try-catch가 종료될 때 finally block이 항상 수행되기 때문에 마무리 해줘야 하는 작업이 존재하는 경우에 해당하는 코드를 작성해주는 코드 블록이다.
Object 클래스에 정의되어 있는 메소드이며, GC에 의해 호출되는 메소드로 절대 호출해서는 안되는 메소드이다. GC가 발생하는 싲점이 불분명하기 때문에 해당 메소드가 실행된다는 보장이 없고, finalize() 메소드가 오버라이딩 되어 있으면 GC가 이루어질 때 바로 Garbage Collectioning 되지 않다. GC가 지연되면서 OOME(Out of Memory Exception)이 발생할 수 있기 때문에 finalize()메소드를 오버라이딩하여 구현하는 것을 권장하지 않고 있다.
시스템 내부에서 사용되는 객체 또는 데이터를 외부의 시스템에서도 사용할 수 있도록 바이트 형태로 데이터 변환하는 기술, 반대로 직렬화된 바이트 형태의 데이터를 다시 객체로 변환하는 과정을 '역직렬화'라고 한다.
JVM의 메모리에 상주(힙 or 스택)되어 있는 객체 데이터를 바이트 형태로 변환하는 기술
JVM은 직렬화와 역직렬화를 하는 시점의 클래스에 대한 버전 번호를 부여하는데, 만약 그 시점에 클래스의 정의가 바뀌어 있다면 새로운 버전을 할당한다. 그래서 직렬화할 때의 버전 번호와 역직렬화를 할 때의 버전 번호가 다르면 역직렬화가 불가능하게 될 수 있기 때문에 이런 문제를 해결하기 위해 SerialVersionUID를 사용
만약 직렬화할 때 사용한 SerialVersionUID의 값과 역직렬화 하기 위해 사용했던 SVUID가 다르다면 InvalidClassException이 발생할 수 있다.