자바는 JVM 위에서 동작하는데 이 JVM이 OS에 독립적이다.
코드를 javac라는 명령어를 통해 컴파일을 한다. 이를 완료하면 .class라는 바이트코드가 생성되는데 이 파일은 java라는 명령어를 통해 실행할 수 있다. .class를 실행하면 JVM이 클래스 파일을 읽어 프로그램을 실행하게 된다.
JVM은 자바 클래스 파일을 실행하는 역할, JRE는 JVM이 실행될 수 있는 환경을 만들어줌, JDK는 개발할 수 있는 여러 툴들을 제공
JVM은 컴파일 된 클래스 파일을 구동하는 역할을 한다.
JRE는 클래스 파일을 읽는 class loader, 로딩된 클래스 파일을 검증하는 bytecode verifier, JDK를 포함한다.
JDK는 컴파일러, 데이터베이스 등 개발에 필요한 툴을 제공한다.
자바에는 primitive 데이터 타입과 object 데이터 타입이 있다.
Primitive 데이터 타입은 stack메모리에 머물고, object 데이터 타입은 heap에 저장되며 이 데이터를 포인팅하고 있는 레퍼런스는 stack에 저장된다.
이 두 타입을 서로 호환시키기 위해 wrapper 클래스라는 것을 만들어 primitive 데이터를 object화 시켰다.
Wrapper class에서 primitive로 변환하는 것을 언박싱, primitive 데이터 타입을 wrapper 클래스로 변환하는 것을 오토박싱이라고 한다.
자바의 메모리는 primitive, 레퍼런스 등 가벼운 데이터를 저장하는 스택과 무거운 데이터(object)를 저장하는 힙이 있다. 참고로 힙 메모리가 가득차면 메모리 부족으로 out of memory가 발생할 수 있고 힙에 저장된 데이터를 사용하기 위해서는 이를 포인팅하고 있는 스택에 저장된 레퍼런스를 통해 접근해야 한다.
String은 immutable, StringBuffer은 mutable하다. 즉 String 연산에서 + 연산을 하게 되면 메모리에 새로운 객체를 만들어 연산하게 된다. 이것은 힙 메모리 낭비로 이어질 수 있다. 그러나 StringBuffer는 뮤터블하므로 append 메서드를 통해 더하는 연산을 할 때 새로운 객체를 생성하는 것이 아닌 기존 객체에 데이터를 더하는 방식을 취한다. 때문에 힙 메모리의 영역 낭비를 줄일 수 있다.
StringBuffer는 동기화를 지원해 성능이 비교적 낮고 StringBuilder는 동기화 지원을 하지 않기 때문에 성능이 좋다. 때문에 평소에는 StringBuilder를 사용하고 만약 멀티스레드 환경이라면 StringBuffer를 사용해야 한다.
string은 자바에서 유일하게 직접 문자열 생성이 가능한 유일한 클래스이다.
자바에서는 jdk 1.7 이후부터 힙 메모리에 Spring pool이라는 특별한 공간을 제공한다. string을 new로 생성하면 일반적인 객체처럼 힙에 저장이 되지만 literal로 직접 생성하면 string pool에 저장이 된다. 이 기존에 같은 내용의 문자열이 이 공간에 저장되면 새로운 객체를 만들지 않고 생성된 객체를 재사용한다.
Garbage collector의 약자로 메모리상에서 유효하지 않은 메모리를 제거해주는 역할을 한다. stack에 저장된 데이터 중 사용하지 않는 데이터나 힙에 저장된 데이터 중 레퍼런스가 끊긴 객체를 제거해준다.
동작 방식은 GC를 동작하는 스레드 이외에 모든 스레드를 중단시킨 후 사용되지 않는 메모리를 식별해 메모리를 해제하는 순으로 동작한다.
오버라이딩은 상속관계에서 부모 메소드의 내용을 재정의 하는 것이고 오버로딩은 한 클래스 내에서 파라미터를 다르게 하여 같은 이름이지만 독립적인 각각의 메소드를 만드는 것이다.
자바는 객체를 필요할 때마다 메모리에 올려 사용해야 한다. 그러나 static은 jvm에 의해 클래스가 로드되는 순간 자동으로 메모리에 올라가도록 하는 키워드이다. 객체가 생성되기 이전에 메모리에 로드 되므로 객체가 접근이 불가능하다. 프로그램 로딩시에 필요하거나 자주 실행되는 메서드일때 static으로 구성한다. Static 메서드는 클래스 레벨에서 호출이 가능하고 레퍼런스를 이용한 호출은 불가능하다.
추상 클래스는 상위 클래스의 구현이 어느정도 되어 있지만 하위 클래스에 완성된 형태를 요구하는 클래스이다. 멤버 변수나 구현된 메서드가 있을 수 있고 abstract 메서드가 있다. 구현이 아닌 상속의 개념으로 abstract는 단일 상속만 가능하다.
인터페이스는 구현이 전혀 되지 않고 메소드의 규약만 정한 것으로 하위 클래스에 완성을 요구한다. 다중 구현이 가능하며 변수를 선언하면 자동으로 public final static이 붙어 상수로 변환된다. Public 만 지원한다.
exception은 실행 중 예기치 않은 상황을 말한다. 이 exception을 미리 처리해 계속 프로그램 실행을 유지하도록 하는 것을 보고 exception handling이라고 한다. 반면 에러는 프로그램에서 핸들링이 불가능해 회복 불가능한 경우를 말한다. Out of memory가 대표적이다.
에러는 핸들링이 불가능한 것으로 힙 메모리 사이즈의 부족으로 발생할 수 있는 oom이 있다. Checked exception은 컴파일 시점에서 체크 가능하며 unchecked exception은 런타임시에 발생할 수 있는 것으로 널포인터 익셉션 등이 있다.
throw는 예외 상황을 발생시킬 때 사용한다. 개발자가 원하지 않는 상황이 계속해서 발생하게 되는 경우 throw를 통해 예외상황을 일으킬 수 있다. throw new Exception()
처럼 객체를 생성해서 던지는 형태로 메소드 내부에 작성한다. 객체를 만들어 던지므로 하나의 exception에 대해서만 사용할 수 있다.
throws는 익셉션 핸들링의 한가지 방식이다. 메소드 선언부에 사용되며 메소드 내부에서 일어날 수 있는 exception에 대해 throws NullPointException
과 같이 클래스 자체를 이용해 작성한다. 여러 exception에 대해 다중으로 선언이 가능하다.
자바에서는 1byte로 구성된 byte stream과 2byte로 구성된 character stream이 있다. 자바의 기본 캐릭터는 유니코드인데 이 유니코드가 2byte로 구성되어 있어 character가 만들어지게 되었다. 때문에 자바에서 텍스트와 같은 캐릭터 데이터를 다룰 경우 character stream을 이용한다.
try
~ catch
finally
에서 system.exit()
을 호출하는 것과 return
을 호출하는 것의 차이system.exit()
은 시스템 자체를 종료하는 것이므로 finally
블록이 실행되지 않는다.
하지만 return
이 실행되면 선언된 해당 블록에서 빠져나오게 되는데 try
나 catch
에서 빠져나온 것이므로 finally는 실행된다.
vector는 사이즈를 넓힐 때 2배씩 넓히며 동기화를 지원한다.
Array list는 사이즈를 넓힐 때 1.5배씩 넓히며 동기화를 지원하지 않으므로 성능이 좋다.
Array List는 엘리먼트에 인덱스를 부여해 엑세스 속도가 빠르지만 추가, 삭제 시에는 전체를 정렬해야 하기 때문에 느리다.
Linked List는 각 엘리먼트를 노드라는 객체로 감싸 앞뒤에 있는 노드와 연결시킨 구조이다. 때문에 중간에 추가나 삭제할 때 빠르게 수행 가능하다. 하지만 엑세스의 경우 인덱스가 없으므로 순차 탐색을 하기때문에 속도가 느리다.
Enumeration은 vector와 hash table과 같은 초기의 collection에서만 지원하며 스냅샷을 이용하기 때문에 사용 중 다른 곳에서 추가, 삭제할 경우 올바르지 않은 결과를 조회하게 될 수 있다.
Literal의 경우 모든 collection 객체를 지원하고 직접 객체에 접근하기 때문에 만약 다른 곳에서 추가, 삭제할 경우 에러를 발생시켜 방지할 수 있는데 이를 fail-fast라고 한다. 직접 접근하기 때문에 제거하는 메소드도 지원한다.
Run 메소드를 오버라이딩해 해당 객체를 thread construct에 넣은 후 start메소드를 통해 시작한다.
자바는 상속을 하나만 지원하기 때문에 2번째 방식이 선호된다.
스레드는 생성을 한 후 start() 메소드를 반드시 실행시켜야 실행이 된다. 멀티 스레딩의 문제점은 공유 자원의 동기화나 데드락 발생이 있다.
두 개 이상의 스레드들이 진행을 하지 못하고 서로 기다리고 있는 상황이다. 락 선점 순서가 서로 엇갈려 어떤 락도 풀리지 못해 데드락이 일어난다.
해결 방법은 다음과 같다.
프로그램이 실행되지 않는다. static을 이용해 클래스 로드 시점에 메인 메서드가 메모리에 로드되어 프로그램이 실행되는데 static을 제거하면 메모리에 처음 로딩되는 영역이 없어지므로 실행되지 않는다.