기술 면접을 대비하여 자바 관련 예상 질문과 답변을 정리합니다.
객체
를 통해 프로그램을 동작하는 방식. 여기서 객체는 현실의 물체를 의미함. 객체지향은 사람의 사고와 가장 비슷하게 한 프로그래밍 방식이라 생각.
사람이 살아가는 세상에 물체를 모델링
해서 객체라는 구조체계로 만들고 그 객체들이 메서드(행위
)를 가지고 서로 메시지(상태
)를 전달해 협력
하는 구조로 프로그래밍하는 형식.
그걸 객체 지향의 기술인 4가지 기술(캡
.상
.추
.다
)를 그리고 SOLID 원칙
을 기준삼아 이의존성,결합도를 줄이고
응집도 있게 하여
코드의 재사용, 유지보수성을 용이하게 하게 프로그래밍함.
비즈니스적으로 구조화한 데이터들이 변고 사용자들의 요구사항이 빈번할 때인 현재상황에 유지보수성을 용이하게 하는 객체지향프로그래밍 방식이 각광받는 것.
그에 반대되는 프로그래밍 방식은 절차지향임.
절차지향은 순차적인 작업방식. C언어가 그런 방식.
한 데이터를 사용하는 프로시저(함수)가 많아질수록 그 데이터의 타입을 변경하기 어려워진다.
요구 사항 변경에 변경할 데이터를 사용하는 모든 프로시저들을 다 변경해야 해서 유지보수성이 굉장히 취약해지는 방식.
하지만 순차적인 방식으로 하는 프로그래밍 방식에는 적합하다. 머신러닝 방식이 그런 방식으로 알고 있다.
1) 추상화
: 모델링 ex. 도메인 클래스, 인터페이스
2) 캡슐화
: private 키워드로 데이터 노출을 줄이는 것, 인터페이스 사용
3) 상속
: 재사용성
4) 다형성
: 하위타입이 변경이 가능, 오버라이딩/로딩
1) SRP
: 응집도 얘기. 한 클래스에 너무 많은 기능(메서드)들을 넣지 말라.
2) OCP
: 추상화된 클래스, 인터페이스에 의존해 impl한 클래스들이 추가 확장에는 열려 있고 클라이언트 객체인 상위 클래스가 변하는 것에 하위 클래스들이 영향을 안받게 하는 것.
3) LSP
: 하위타입이 대체되더라도 문제가 없어야 한다.
4) ISP
: SRP랑 비슷. ISP에 너무 많은 기능(메서드)들을 넣지 말고 쪼개라.
5) DIP
: OCP랑 비슷. 구체 클래스에 의존하지 말고 추상화된 클래스, 인터페이스에 의존해서 의존관계를 역전시키는 것.
자바 가상머신인 JVM위에서 동작하기 때문에 언어가 OS에 독립적임. <-> C언어
아파치, 스프링과 같은 자바를 지원하는 많은 오픈소스 프로그램이 존재해 단기간에 개발시간을 단축과 더불어 안정성 좋게 개발할 수 있다.
단점인 비교적 속도 느리다는 것이 이제는 하드웨어 성능이 워낙 압도적으로 좋아져서 무색한 말인 것 같다.
따로 동적 메모리 할당시, 해제를 개발자가 하지 않고 알아서 JVM의 GC(Garbage Collector)가 해준다.
언어적 불편함 : 대부분의 클래스 선언 등을 이름을 명사형으로만 강요되며 명사 외를 지양한다. 또한 외부 범위에서 변수를 함수 내부로 바인딩하는 기술인 클로저 기능이 미 지원된다. C언어에 있는 전역 변수 개념이랑 유사하다 본다. 자바 언어의 경우 전역, 지역 변수는 모두 클래스 내부에 캡슐화로 진행되기 때문이다.
실시간 응용 시스템에 부적합 : 가비지 컬렉션
이 언제 어떻게 작동될지는 아무도 모르기 때문에 중간에 끊김 현상이 발생
할 수 있어서 실시간 응용 시스템으로는 부적합하다.
Oracle사의 Java 유료화 : 본인은 프로그래밍 업데이트 지원이 유료화되는 것이 큰 타격이라 본다. 프로그래밍 언어가 본격적으로 유료화되면서 적은 금액이라도 프로젝트에 투입되는 인력 비용 + 프로그램 인프라 비용 등등이 부담이 될 수 있다.
Oracle JDK : 폐쇄적인 상업 기반의 코드로 이루어짐, 재산권이 걸린 플러그인을 제공하고 있다 ex.Java Web Start 그리고 글꼴 라이브러리
Open JDK : 오픈소스 기반, 무료임.
옛날 자바 6에서는 Open JDK이 성능이나 안정성이 크게 떨어졌지만 현재에는 Oracle 제공하고 있는 일부 기능을 제외하고는 크게 차이가 없다.
method
영역 : static이 붙은 변수/메서드(ex.main 메서드), 전역변수, class 정보들 저장.stack
: 지역변수, 파라미터, 복귀주소값 저장heap
: new 연산자를 통한 동적 할당 된 객체 저장. 가비지 컬렉션에 의해 관리.JVM(Java virtual machine)
은 자바를 실행하기 위한 가상 기계이다. 여기서 가상 기계란 말이 어색하다면 한다디로 가상 OS
라고 해석할 수 있겠다.
JVM은 Byte Code를 OS에 맞게 해석해주는 역할을 한다. Java Compiler가 java 파일
을 컴파일을 하면 .class라는 java byte code
로 변환시켜 주며, Byte Code는 기계어가 아니기 때문에 OS에서 바로 실행이 되지 않는다.
이 때 JVM이 OS가 이해할 수 있도록 이 byteCode를 (여기서 JIT 컴파일러+인터프리터
) 기계어로 해석해준다.
자바 클래스들은 시작 시 한번에 로드되지 않고, 애플리케이션에서 필요할 때 로드된다. 클래스 로더는 JRE의 일부로써 런타임
에 클래스를 동적으로 JVM에 로드 하는 역할을 수행하는 모듈이다. 자바의 클래스들은 자바 프로세스가 새로 초기화되면 클래스로더가 차례차례 로딩되며 작동한다.
부트스트랩 클래스로더(Bootstrap classloader) : JVM 시작 시 가장 최초로 실행되는 클래스로더. 부트스트랩 클래스로더는 자바 클래스를 로드하는 것이 아닌, 자바 클래스를 로드할 수 있는 자바 자체의 클래스로더와 최소한의 자바 클래스(java.lang.Object
, Class
, Classloader
)만을 로드한다.
확장 클래스로더(Extension classloader) : 확장 클래스로더는 부트스트랩 클래스로더를 부모로 갖는 클래스로더로써, 확장 자바 클래스들을 로드한다. java.ext.dirs 환경 변수에 설정된 디렉토리의 클래스 파일을 로드하고, 이 값이 설정되어 있지 않은 경우 ${JAVA_HOME}/jre/lib/ext
에 있는 클래스 파일을 로드한다.
애플리케이션 클래스로더(Application classloader) : 자바 프로그램 실행 시 지정한 Classpath에 있는 클래스 파일 혹은 Jar에 속한 클래스들을 로드한다.
클래스
: 객체를 정의하는 틀 또는 설계도와 같은 의미로 사용객체
: 클래스 정의를 기반으로 실제로 메모리에 올라온 상태. new로 생성된 객체, 인스턴스라 불린다. 객체란?
원래 의미는 식별 가능한 개체 또는 사물이라는 뜻. 객체는 구별 가능한 식별자, 특징적인 행동, 변경 가능한 상태를 가지며 이 상태들을 관장하고 책임을 져야 한다.(객체 지향과 사실과 오해)
자바로 만든 모든 프로그램의 공통된 특징은 main문이 있으며 그곳에서 시작된다는 점.
main문을 가진 클래스는 context 즉, 객체들의 heap 정보를 가지고 있다. (by 메타코딩)
기본형 (primitive)
: stack 메모리 영역에 실제 값
을 저장하는 데이터 타입. byte, short, int, long, float, doublle, char, boolean / call by vaue 호출 방식을 사용함.
참조형 (reference)
: stack 메모리 영역에 객체가 있는 주소값
을 저장하는 데이터 타입. String, 클래스, 인터페이스 등이 그렇다. new 연산자로 정의. 실제 값은 heap에 저장되고 stack에는 메모리 주소만 저장하는 걸 잊지 말자. call by reference 호출 방식을 사용함.
사실은 자바는 엄연히 call by vaue 호출 방식으로 사용할 뿐
이다.
스택은 함수를 호출할시 사용한 파라미터, PC값(복귀주소값), 지역변수가 저장되는데 쓰레드함수가 호출하고 그역할이 끝나면 복귀하기 위해 stack을 독립적으로 가져야 한다.
장점)
단점)
이 둘의 차이는 "메모리 공유"이다.
process는 os에게 독립적인 메모리공간을 할당받는다. 반대로 Thread는 stack을 제외하고 나머지 메모리공간을 공유해서 context swiching에서 overhead가 적다
는 이점이 있다.
단점은 공유하는 영역이 많아(Code, Heap, Data 부분) 여러 스레드가 많은(멀티스레드) 환경에서는 공유변수에 스레드가 변경할 이슈가 생기는 동시성 이슈
가 생길 수 있다는 것이다.
Heap 영역에서 시스템에서 더 이상 사용하지 않는 동적 할당된 메모리 블록을 찾아 다시 사용 가능한 자원으로 회수하는 것. 자동으로 이루어지므로 따로 개발자가 메모리 영역을 관리할 필요 없음.
클래스가 메모리에 로드되어 컴파일된 것을 말함. 클래스가 실체화된 것.
Method
영역-클래스 변수) : 함수 바깥에 선언하여 클래스 전체에서 사용가능한 변수. 여러 메소드에서 공통적으로 사용 가능.heap
영역) : 객체 생성 후 메서드 내 지역변수에 들어갈 수 있는 변수 Stack
영역-메서드 안에 변수) : 함수 속에서 선언하여 해당 함수 속에서만 사용가능한 변수. 선언한 메서드가 끝나면 자동으로 소멸.포장객체라 불린다. 기본타임(byte,char,short,int,long,float,double,boolean)을 객체로 생성하는 클레스이다.
Byte, Character, String, Short, Integer, Long, Float, Double, Boolean 들이 있다.
Wrapper클래스는 불변객체
로써 값을 변경할 수가 없다.
형변환시,
int obj = 0;
Integer obj1 = (Integer) obj; // 명시적 변환, 박싱, null 초기화 가능
int value = obj1; // 묵시적 변환, 언박싱
// 박싱, 언박싱에도 자동형변환이 가능하다.
정적 멤버 (static) : 메모리 공간 할당 시 처음 설정된 메모리 공간이 변하지 않는 것. 프로그램이 끝날 때까지 계속 상주함.
객체를 생성하지 않고도 사용할 수 있는 필드와 메소드.
객체마다 가지고 있을 필요가 없는 공용적인 데이터라면 static으로 선언.
객체 생성을 하지 않고 사용 가능하기 때문에 객체에서 호출불가능.
주의할 점)
Garbage Collector의 관리 영역 밖에 존재하므로 Static을 자주 사용하면
프로그램의 종료시까지 메모리가 할당된 채로 존재하므로 자주 사용하게 되면 시스템의 퍼포먼스에 악영향(메모리 누수)을 주게 된다.
메모리 누수 또는 릭(Memory Leak)
: 프로그램이 필요하지 않은 메모리를 계속 점유하고 있는 현상자바에서 메모리 누수는 더이상 사용하지 않는 객체가 가비지 컬렉션(GC)에 의해서 회수되지 않고 계속 누적되는 현상.
Old 영역에 누적된 객체로 인해서 메이저 GC가 빈번하게 발생하게 되고, 프로그램의 응답속도가 늦어지다. 결국 OOM(OutOfMemory) 오류로 프로그램이 종료됩니다.
주로 빈번한 전역변수의 선언(static)이나, List나 HashMap 같은 콜렉션에 저장한 객체를 해제하지 않고 계속 유지하게 되면서 주로 발생합니다.
출처: https://118k.tistory.com/608 [개발자로 살아남기:티스토리]
출처: https://118k.tistory.com/608 [개발자로 살아남기:티스토리]
static은 주로 상수(final)화 시키는데 많이 쓰인다. (주의할 점은 상수화란 건 초기화가 필수이다. 그래서 static만 붙인다고 상수가 아니다!)
다른 클래스에도 사용할 수 있게 public을 붙여서 사용하는 경우가 많다!
final 키워드를 붙이면 무언가를 정의하는 것이 안된다
라는 의미를 가진다. 이 키워드는 변수, 메서드, 클래스에 final을 붙일 수 있고 각각의 의미는 다음과 같다.
수정할 수 없다
는 의미를 가집니다.(상수화)컴파일에러
가 납니다.`헷갈리는 부분
1) Enum은 상수가 아니다!
정확히는 상수의 열거타입
이다.
enum 자체로 보면 상수들의 모음이기 때문에 상수가 계속 바뀔 수 있기에 enum 자체를 상수라고 표현하면 안된다.
2) Enum 존재 자체가 Thread-safe하다는 걸로 오해하면 안된다. 나중에 바뀔 수 있는 값이 Thrad-safe 할리가 없다.
단순히 initialization에서는 Thrad-safe할 수 있겠지만, 그 뒤부턴 보장이 안된다.(상수가 아니기에!)
힙영역 내 Perm(영구적) 영역에 생성되어 자바 프로세스 종료까지 계속 유지되는 메모리영역이다.
기본적으로 JVM에서 관리하며 프로그래머가 작성한 상수에 대해 최우선적으로 찾아보고 없으면 상수풀에 추가한 이후 그 주소값을 리턴한다. 그로 인해 메모리 절약 효과가 있는 것이다.
String의 리터럴 방식을 사용하여 생성할 경우 상수풀 영역에 등록한다.
자식클래스의 생성자 안에 부모클래스 생성자를 적지않아도 자동실행이 되는 생성자가 있다.
그 자동실행되는 부모의 생성자가 super()이며, 매개변수 없는 생성자가 실행되는 것이다.(안써도 무방하다.)
그러나, 부모의 생성자 중 매개변수 있는 생성자가 실행되도록 해야 하려면 super(arg1, arg2, ...) 를 반드시 써서 매개변수를 넣어주어야 한다.
volaite : 변수에 붙이는 키워드, 이 키워드를 변수에 붙이면, 스레드가 cpu내 캐시에 있는 변수를 재사용안하고 메인메모리
에 참조하게 되고 최신화된 데이터만 접근할 수 있다.
transient 예약어를 사용하여 선언한 변수는 Serializable의 대상에서 제외된다.
그러면 뭐하러 이것을 사용하나 싶을 수 있지만, 패스워드와 같이 보안상 중요한 변수나 꼭 저장해야 할 필요가 없는 변수에 대해서는 transient를 사용할 수 있습니다.
String은 new 연산자를 통해 생성되면 인스턴스 메모리 공간이 절대 변하지 않으므로 +, concat과 같은 연산시 메모리의 내용이 변하는 것이 아니라 새로운 String 인스턴스가 생성됨. 이렇게 새로운 문자열이 만들어지면 기본의 문자열은 가비지 콜렉터에 의해 제거되야 함. 문자열 연산이 많아지는 경우 성능이 떨어짐. 하지만 불변하기 때문에 조회가 빠르고 멀티스레드 환경에서 동기화를 신경 쓸 필요 없음.
가변(mutable)객체인 StringBuffer나 StringBuilder의 값들은 변하기 때문에 append()메서드를 통해 계속 값을 갱신
할 수 있다. 내부적으로 문자열 편집을 위한 Buffer(버퍼)가 존재하기 때문이다.
즉, 한 번만 만들고 메모리의 값을 변경시켜서 문자열을 변경할 수 있다 그러므로 문자열 연산이 String을 쓸 때보다 자주 사용하면 좋다.
그러나 StringBuffer
은 멀티 스레드 환경에서 synchronize 키워드가 가능하므로 동기화가 가능, 멀티 스레드 환경에서 적합하다.
StringBuilder
는 synchronize 키워드가 없어 동기화를 지원하지 않기 때문에 단일 스레드 환경에서만 적합하다. 하지만 synchronize 키워드가 없기에 연산 속도가 가장 빠르다.
https://velog.io/@mooh2jj/String-말고-StringBuffer-StringBuilder
1) private
: 같은 클래스에서만 접근 가능
2) default
: 같은 패키지에서만 접근 가능
3) protected
: 같은 패키지 + 다른 패키지(상속받으면)
4) public
: 같은 프로젝트 어디서든 접근 가능
접근 가능 순서
private < default < protected < public
접근 제한자를 사용하는 이유는 외부에 보여주고 싶은 정보들을 선택적으로 제공하기 위함이며, 자바의 특성 중 캡슐화
와 통하는 면이 있다.
추상 메소드를 하나 이상 가진 클래스
자식 클래스에게 강제성 부여 가능. 이를 통해서 기능 확장 가능.
직접적으로 객체 생성이 불가능하고 클래스를 상속 받아서 오버라이딩하여 사용해야 함.
모든 메서드가 구현부가 없는 추상메서드로 이루어진 클래스
ex) 키보드. 키보드를 누른다 라는 행위는 같지만 엔터/스페이스바를 눌렀을 때의 결과는 다름. 메소드는 같지만 동작 방식은 다르게 다형성을 주고 싶은 경우 사용.
협업시 객체의 내부 구조(구현부)를 모르더라도 인터페이스의 메서드 명만 알면 사용 가능 = 협업에 유리.
interface, implements, abstract pubilc 메소드, public static final 상수
다중 상속(구현) 가능. ex. interface extends interface1, interface2
둘 다 서로 하위 클래스에게 공통적으로 사용하는 방식을 뽑아 전달하는 객체들인 공통점이 있다.
그러나 목적이 다르다. 추상 클래스는 추상 메서드를 자식 클레스가 구체화해서 기능 확장
에 목적이 있지만,
인터페이스는 서로 관련이 없는 클래스에게 공통적으로 사용하는 방식이 필요하지만 기능을 각각 모두 반드시 구현케 하는 경우에 사용한다.
그리고 추상 클래스는 단일 상속, 인터페이스는 다중 상속 가능하다.
그외 차이
추상클래스
: 클래스, 필드값(변수) 설정/메서드 내부 코딩 가능, 상속을 통한 재사용/기능확장 목적
이 큼인터페이스
: 클래스x
, 필드값 설정 불가(상수만 가능), 메서드(기능) 이름만 지정 가능, 기능 명세화, 강제구현
, 컴포지션 등 추상화에 의존하는 하는 방식의 대표 객체오버라이딩 : 상속에서 나온 개념. 부모 클래스의 메소드를 자식 클래스의 메소드로 재정의 하는 것. 기능의 확장을 위해서 사용함. 런타임 다형성
이라고 부름.
추가로, @Override 어노테이션은 꼭 써주면 좋다. 컴파일 타임에 오버라이딩에 대한 안정성을 부여해 주기 때문이다.
오버로딩 : 같은 클래스 내에서 같은 이름
의 메소드를 여러개 정의 하는 것(메서드 시그니쳐)으로 매개변수의 타입이 다르거나 개수가 다름. return 타입과 접근 제한자는 영향 없음. 컴파일 다형성
이라고 부름
하나의 클래스나 함수가 다양한 방식으로 동작 가능 한 것. 오버라이딩과 오버로딩을 통해서 다형성 구현 가능.
ex) 게임 > 다양한 캐릭터들에 대해서 동일한 버튼을 클릭해서 다른 스킬을 사용하게 하는 것.
데이터 보호, 불필요한 부분은 감출 수 있음.
클래스내 상태정보들은 private로 은닉하고 getter/setter 같은 메서드는 pulbic 을 붙여, 이 메서드로만 접근할 수 있게 하는 것.
부모 클래스가 가지고 있는 상수, 메소드를 자식 클래스에서 물려 받아 같이 공유하면서 확장하는 것
자식 클래스의 부모 클래스 오버라이딩
코드 중복 방지, 공통적인 코드 변경 할 때 시간 단축 가능.
상속 : 기본적으로 클래스에서 구현,
단일 상속, 다중 상속 등의 방식을 씀.
다형성 : 기본적으로 함수(메소드)에서 구현,
컴파일 타임 다형성 (오버로딩) 과 런타임 다형성 (오버라이드)의 방식을 씀.
상속은 재사용으로 확장 기능으로 유용하지만, 상위클래스가 릴리스(업데이트)때마다 내부 구현이 달라질 수 있으며, 그 여파로 하위 클래스가 오동작, 유지보수가 쉽지 않아진다.
그래서 상속으로 기존 클래스를 확장하는 대신, 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하는 컴포지션은 유지보수하기 더 좋아 더 애용된다.
클래스 안의 클래스(중첩 클래스)
종류
프로세스 내에서 실제로 작업을 수행하는 주체를 의미하며 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행함. 두개 이상 스레드를 가지는 프로세스를 멀티 스레드 프로세스라고 함.
Thread 클래스, Runnable 인터페이스를 통해 구현 가능
더 이상 싱글 코어로는 한계에 다다르게 되면서 멀티코어 CPU
가 대중화 되고 있다. Java 8 은 시대 흐름에 맞게 병렬 프로세싱
을 활용하고자 했고, 그로 인해 기존 Java 에서는 구현하기 어려웠던 부분을 Java 8 을 기점으로 함수형 프로그래밍과 비동기 논블로킹 방식(NIO)을 도입을 통해 해결할려고 했다.
그러나
자바 8버전의 병렬 프로세싱이 성능적인 퍼포먼스를 만드는 것이 힘들어(참고: https://woodcock.tistory.com/28) 현재는 많이 사용하지 않고 있다. 현재로는 멀티스레싱 환경에서 불변화
특성을 이용한 Thread-safe한 방식으로 많이 사용되고 있다.(참고: https://velog.io/@mooh2jj/왜-함수형-프로그래밍인가)
자바에서 DB의 종류에 상관 없이 데이터베이스에 더욱 쉽게 접근 할 수 있도록 하는 API. OOP의 OCP원칙의 예시이기도 함.
Connection, PreparedStatement, ResultSet 등 여러개의 클래스를 생성하고 Exception 처리도 해야 하는 번거로움이 있음 > 반복적인 코드 발생, 생산성 저하.
Spring JDBC : 기존의 JDBC 단점 극복, 반복적으로 해야 하는 많은 작업들을 대신 해줌. Connection 열기 닫기, Statement 준비, 실행
ConnectionPool : DB와 항상 연결되어 있는 객체들. DB와의 연결을 위해서 사용하는 객체
DataSource : ConnectionPool 관리 객체. 커넥션풀 이용해서 연결, 반납하는 작업 수행.
pom.xml에서 dependency 추가
하나의 클래스에 대해 하나의 인스턴스만 만들어서 사용하기 위한 패턴. 커넥션 풀과 같은 객체의 경우 인스턴스를 여러개 만들게 되면 자원 낭비가 되므로 하나만 생성하는 것이 효율적.
생성자에 private 접근 제어자를 지정해 인스턴스 생성에 제약을 걸고, 단일 객체를 반환 할 수 있도록 정적 메소드를 지원해야 함.
직렬화의 목적
은 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트 형태로 데이터 변환하는 것
이다. 바이트로 변환된 데이터를 다시 변환하는 기술을 역직렬화라고 하는 것이다.
자바에서 입출력에 사용되는 것은 스트림이라는 데이터 통로를 통해 이동한다. 하지만 객체는 바이트형이 아니라서 스트림을 통해 파일에 저장하거나 네트워크로 전송할 수 없다. 따라서 객체를 스트림을 통해 입출력하려면 바이트 배열로 변환하는 것이 필요한데, 이를 '직렬화' 라고 한다.
자바에서는 Serializable
이라는 인터페이스를 구현받아 생성하면 되는데.
반대로 스트림을 통해 받은 직렬화된 객체를 원래 객체로 만드는 과정을 역직렬화라고 한다.
자바가상기계 (JVM)은 직렬화와 역직렬화를 하는 시점의 클래스에 대한 버전 번호를 부여한다. 만약 그 시점에 클래스의 정의가 바뀌어 있다면 새로운 버전 번호를 할당한다. 그래서 직렬화할 때의 버전 번호와 역직렬화를 할 때의 버전 번호가 다르면 역직렬화가 불가능하게 될 수도 있다. 이런 문제를 해결하기 위해 SerialVerionUID를 사용한다.
간단명료하게 serialVersionUID값을 저장할 때 클래스 버전이 맞는지 확인하기 위한 용도다.
만약 직렬화할 때 사용한 serialVersionUID의 값과 역직렬화 하기 위해 사용했던 serialVersionUID값이 다르다면 InvalidClassException이 발생할 수 있다.
리플렉션은 컴파일러를 무시하고 런타임 상황에서 메모리에 올라간 클래스나 메서드등의 정의를 동적으로 찾아서 조작할 수 있는 일련의 행위를 말한다.
즉 동적인 언어의 특징이라 말 할 수 있다. 프레임워크에서 유연성이 있는 동작을 위해 자주 사용하기도 하다.
자바의 클래스 멤버 변수 초기화 순서에 대해서 설명
static 변수 선언부 : 클래스가 로드 될 때 (메모리 모델상 Methd area 에 올라감) 변수가 제일 먼저 초기화
됨
필드 변수 선언부 : 객체 생성 될 때 (메모리 모델상 Heap area에 올라감) 생성자 block 보다 앞서 초기화 함
생성자 block : 객체 생성 될 때 (메모리 모델상 Heap area에 올라감)
JVM이 내부적으로 locking (thread safe 영역임)
필드 변수 중 final 변수의 가시화는 (다른 스레드에 공개하는 시점은) 생성자 block이 끝난 다음.
필드 변수 선언부에서 이미 초기화 되었다면 그 값들은 덮어 씀
초기화 시점
초기화 순서
클래스 내부에서 사용할 데이터 Type
을 외부에서 지정하는 기법. 제네릭은 컴파일과정에서 데이터 타입을 체크
해주는 기능이다.
내가 따로 만든 객체를 지정할 수 있음.
코드의 안정성과 재사용성을 높여줌 > 객체 타입을 컴파일 시에 체크하고, 형변환을 하지 않아도 됨.
ArrayList<MemberDto> // ArrayList 객체 내부에 어떤 형태의 클래스가 있는지 확인 가능.
다수의 데이터를 다루는데 표준화된 클래스들을 자료구조를 직접 구현하지 않고 편하게 사용 가능 함.
배열과 다르게 객체를 보관하기 위한 공간을 미리 정의하지 않아도 됨 객체의 수를 동적으로 할당 가능하므로 효율성 증대.
1) List
: 순서 있는 데이터의 집합. 데이터의 중복 허용.
복사
됨으로 빈번한 경우에는 부적합.2) Set
: 순서를 유지하지 않는 데이터 집합. 데이터 중복 허용하지 않음.
3) Map
: Key와 value의 쌍으로 이루어진 데이터 집합. 키는 중복을 허용하지 않고 순서를 유지하지 않음.
컬렉션 클래스에서 제네릭을 사용하면 컴파일러는 특정 타입만 포함 될 수 있도록 컬렉션을 제한한다.
컬렉션 클래스에 저장하는 인스턴스 타입을 제한하여 런타임에 발생할 수 있는 잠재적인 모든 예외를 컴파일타임에 잡아낼 수 있도록 도와준다.
원시형(Primitive type)으로 표현할 수 있는 간단한 데이터를 객체로 만들어야 할 경우가 있는데 그러한 기능을 지원하는 클래스
묵시적 : 프로그래머가 임의로 박싱을 해주는 것이 아니라 자동으로 박싱이 되는 것을 말한다.
명시적 : 프로그래머가 코딩하여 명시적으로 wrapper로 변환하는 것을 말한다.
여기에 명시적, 묵시적으로 변하는 것을 설명
박싱과 언박싱이 이루어질 때,
저장되는 공간이 다르고 불필요한 형변환이 이루어지기 때문에 Overhead(오버헤드)가 발생할 수 있다.
== : 주소, 값 모두 비교(동일성 비교), 원시 타입 비교
에 쓰임
equals() : 값만 비교(동등성 비교), String 객체 등 레퍼런스 타입 비교
에 쓰임
equals() 메서드 재정의
기본적으로 자바에서는 Object 클래스에 정의된 equals() 메서드가 동일성(주소, 값 모두) 비교를 합니다. 따라서, 개발자는 원한다면 equals() 메서드를 오버라이딩해서 동등성(값만 비교)의 판단 기준을 정의해둘 수 있습니다.
예외(Exception) 처리라 함은 비정상적인 상황이 예측하여 처리하는 것이다. 개발자는 자신이 구현한 로직에서 예외를 예측하고 그에 따른 예외처리를 신경써야 한다. 그중에 unchecked
, RuntimeException
처리가 특히 그렇다.
그리고 자바는 예외처리방법 3가지를 쓴다.
1) try-catch (finally) : 예외처리 코드를 직접적으로 제어할 수 있다.
2) throws : throws는 자신을 호출하는 메소드에 예외처리의 책임을 넘기는 것
ex) public void throwTest(int a, int b) throws ArithmeticException{...}
3) throw : throws랑 확연히 다르다. 제일 헷갈렸던 부분이었는데,
throw는 개발자가 직접 예외를 발생시키고싶을 때 쓰는 것이다.
ex) throw new ArithmeticException();
An object that has no state is stateless. All stateless objects are immutable (because there is nothing to mutate); this is a tautological technicallity
참고: https://stackoverflow.com/questions/12980884/what-is-the-difference-between-stateless-and-immutable
: stateless한 객체들은 모두 immutable한 것이다. 반대는 성립이 안된다.
Thread-safe한 context에서 둘은 같은 의미가 될 수 있지만, 엄밀히 말하면 같은 뜻은 아니다.
쓰레드
쓰레드는 자바 프로그램의 실행흐름단위, 자바 프로그램의 컨셉? 정도로 받아들여야...
트랜잭션
DB와 연관된 개념인데, 롤백, 커밋하는 독립성을 가진 단위로 만들어져야 함. 여러 query들을 가질 수 있다.
보통 한 쓰레드 안에 여러 트랜잭션이 있는 것. 그래서 개발자는 이런 트랜잭션들이 모여서 공유하는 자원을 최대 줄이거나, 불변화 시켜 데드락같은 현상을 없애게 하는 것!
https://stackoverflow.com/questions/66045195/db-transaction-vs-java-thread
데드락에서 쓰레드 데드락, 트랜잭션 데드락은 다르다.
개념은 같다. 데드락은 '언제' 발생할지는 알 수 없다. 그저 '발생할 수 있을' 뿐이다.
이건 둘 다 공통이다.
쓰레드 데드락
다수의 쓰레드가 같은 lock 을, 동시에, 다른 명령에 의해, 획득하려 할 때 발생할 수 있다.
// 전제: addChild setParent 동기화 메서드임
Thread 1: parent.addChild(child); //locks parent
--> child.setParentOnly(parent);
Thread 2: child.setParent(parent); //locks child
--> parent.addChildOnly()
-> parent 객체는 Thread 1 에 의해 lock 이 걸린 상태이고 Thread 2 블록 상태가 된다.
-> child 객체는 Thread 2 에 의해 lock 이 걸린 상태이고,Thread 1 은 블록 상태가 된다.
서로 블록이 되어 데드락이 되어진다.
자세한 내용은 여기 블로그 참고
https://parkcheolu.tistory.com/19
트랜잭션 데드락
데드락이 발생할 수 있는 더 복잡한 상황은, 데이터베이스 트랜젝션이다. 데이터베이스 트랜젝션은 많은 SQL 업데이트 요청으로 구성되곤 한다. 한 트랜잭션에서 어떤 레코드에 대해 업데이트가 수행될 때, 이 레코드는 업데이트 수행을 위해 다른 트랜잭션의 접근을 막도록 lock 이 걸린다. 이 lock 은 업데이트를 수행하는 트랜젝션이 끝날 때까지 지속된다. 같은 트랜잭션 안에서의 각 업데이트는 데이터베이스의 레코드들에 lock 을 걸 수 있다.
다수의 트랜젝션이 동시에 같은 레코드들을 업데이트한다면, 이는 데드락에 빠질 위험성이 있다.
Transaction 1, request 1, locks record 1 for update
Transaction 2, request 1, locks record 2 for update
Transaction 1, request 2, tries to lock record 2 for update.
Transaction 2, request 2, tries to lock record 1 for update.
여기서 lock 은 서로 다른 요청에 의해 잡혀있고, 어느 쪽도 먼저 알려져 있지 않기 때문에, 데이터베이스 트랜젝션에서의 데드락은 감지하거나 방지하기가 어렵다.