코드가 실행되는 과정
개발자가 작성한 .java파일 -> 자바 컴파일러 -> 바이트코드 .class파일 -> JVM(Java Virtual Machine)
JDK : Java Development ToolKits +JRE 로 구성
JavaC : 자바 컴파일러
JRE : Java Runtime Environment + JVM 로 구성 ( 자바에서 제공되는 기본 라이브러리들이 포함되어있다)
/ Library : java.lang , java.util 등
inteliJ 컴파일해준다
다음과 같이 자바 코드가 실행된다
공식문서의 JVM구조
아래자료로 이해해보자
.class에서 바이트 코드를 읽고 메모리에 저장한다
1. 메소드 영역(Method Area) : 모든 클래스 레벨의 데이터 저장 + static 메서드나 블록
2. 스택영역 (=스레드) : 스레드마다의 런타임 스택을 만든다 / 스택 프레임 블록을 쌓음 / 모든 지역변수 저장
3. 힙 영역 : 인스턴스 저장
1. JIT컴파일러(just-in-time compiler) : 캐싱기능으로 반복되는 코드를 재사용해 실행속도를 높인다
2. GC(=garbage collector) : 더이상 사용하지 않는 인스턴스를 정리해 메모리효율을 증가시킨다
1. Loading : 클래스로더가 .class파일을 읽고 데이터를 "메소드영역"에 저장
저장하는 데이터 :
1 FQCN(Fully Qualified Class Name) 패키지 포함 클래스이름 ex) java.lang.String
2 클래스, 인터페이스, 이늄(열거체 클래스?)
3 메소드와 변수들
2. Linking : 로드된 클래스나 인터페이스, 그 직계 부모클래스나 인터페이스,
필요한 경우 요소 타입(배열 타입인 경우)을 검증하고, 준비하고, 해석하는 과정
3. Initialization : static 붙은 애들(메소드 블럭) 초기화
1. 메소드 영역(Method Area) (=클래스 영역 =스태틱 영역) :
모든 클래스 레벨의 데이터 저장 + static 메서드나 블록
2. 스택영역 (=스레드 영역) :
매서드(함수)들이 저장된다 ( 자바는 단독함수 사용안된다 클래스안의 함수를 "메서드"라한다 )
스레드마다의 런타임 스택을 만든다 / 스택 프레임 블록을 쌓음 / 모든 지역변수 저장
3. 힙 영역 :
객체(Instance) 저장
main(String[] args) 을 통한 예시 ( =PSVM 메서드)
*PSVM 메서드
→ Java 프로그램을 실행하기 위한 진입점. 프로그램에 이미 세팅되어있음.
(이말은, 조작이 가능하다는 뜻)
process의 thred
JVM의 PSVM메서드->여러개 가질수 있다 (멀티스레드)
실행순서
JRE는 PSVM 메서드 찾는다
->
존재하다면? JRE는 JVM을 부팅한다
->
JVM이 바이트파일(.class) 실행
( JVM 전처리 java.lang 패키지 로드 : 데이터 저장 영역의 Static 영역에 올린다 → System.out.println() 같은 메서드를 쓸 수 있게 되는 것 )
->
개발자가 작성한 클래스 와 import package를 데이터 저장영역의 "Static 영역" 에 올린다
->
데이터 저장영역의 "Stack 영역"에 main()메서드 스택프레임이 쌓인다
변수 크게나누면 전역변수 지역변수로 나뉜다
자바의 변수범위 크게 지역변수, 인스턴스변수, static변수 세가지로 나뉜다
/자바의 필드초기화 방법 세가지/
명시적 초기화 : 지역변수 초기화와 같음 , 필드 선언과 동시에 초기화하는 방법
생성자를 이용한 초기화 : 객체를 생성과 동시에 필드를 초기화 하는 방법
초기화 블록을 이용한 초기화 : 인스턴스초기화블록/클래스초기화블록 아래설명
인스턴스 초기화 블록 ({...} 이용한 초기화) :
객체를 생성과 동시에 필드를 초기화한다(생성자와 동일) ->
생성자보다 먼저 실행되지만, 생성자와 인스턴스 초기화 블록의 차이는 거의 없으므로 인스턴스 초기화 블록은 잘 사용되지 않습니다
/ 실행순서 : 인스턴스 생성 -> 인스턴스 초기화 블록 실행 -> 생성자 호출
클래스 초기화 블록(인스턴스 초기화에 static키워드추가 -> static{...})
클래스가 로딩되고 클래스 변수가 준비된 후 자동으로 실행되는 블록!
용도 : 생성자나 인스턴스 초기화 블록으로 초기화 할 수 없는 |클래스 변수의 초기화|를 수행할때 사용한다
//
-> 여러번의 초기화를 거치면, 가장 마지막으로 초기화한 값이 남게된다
//
primitive type -> wrapper class ,기본 타입의 데이터를 객체로 취급해야 하는 경우
8개의 기본 타입에 해당하는 데이터를 객체로 포장해 주는 클래스를 래퍼 클래스(Wrapper class)라고 합니다
래퍼 클래스는 모두 java.lang 패키지에 포함되어 제공
WrapperClass와 primitive data type 사이에 이루어지는 자동변환기능을 일컫는다
Autoboxing : primitive data type -> WrapperClass
원시타입이 wrapper클래스 타입의 파라미터를 받는 메서드를 통과할때
원시타입이 wrapper클래스의 변수로 할당될때
ex) int text가 integer클래스인 getText() 의 변수로 할당될 때
public class Test {
private int text;
public Integer getText() {
return text; // 이부분에 생략된것 return (Integer.valueOf(text);)
}
위처럼 원시타입을 wrapper클래스 타입으로 변환해주지 않아도 컴파일오류 없이 실행된다
Unboxing : WrapperClass -> primitive data type
Wrapper 클래스 타입이 원시 타입의 파라미터를 받는 메서드를 통과할때
Wrapper 클래스 타입이 원시 타입의 변수로 할당될 때
ex)
import java.io.*;
class GFG
{
public static void main (String[] args)
{
Integer i = new Integer(10); // wrapper class 타입의 변수 i
int i1 = i; // *언박싱* Integer i -> int i1
System.out.println("Value of i: " + i); // Value of i: 10
System.out.println("Value of i1: " + i1); // Value of i1: 10
//Autoboxing of char
Character gfg = 'a'; // 'a' : char? -> char의 wrapper class Character
// Auto-unboxing of Character
char ch = gfg; // char의 wrapper class Character -> char 언박싱
System.out.println("Value of ch: " + ch); // Value of ch: a
System.out.println("Value of gfg: " + gfg); // Value of gfg: a
}
}
즉 박싱된 타입의 두 인스턴스는 값이 같아도 다르다고 식별될 수 있다.
기본 타입의 값은 NULL을 허용하지만 박싱된 타입은 허용하지 않는다.
기본 타입이 박싱된 타입보다 시간과 메모리 사용면에서 효율적이다.
// 주의할점 : wrapper 클래스간의 연산은 wrapper클래스는 객체이기 때문에
== 동등 연산자 사용할 경우 두 인스턴스의 값을 비교하는게 아니라 두 인스턴스의 주소값을 비교하게된다
따라서 항상 false 나오게된다, .equals() 사용한다
public class Wrapper03 {
public static void main(String[] args) {
Integer num1 = new Integer(10);
Integer num2 = new Integer(20);
Integer num3 = new Integer(10);
System.out.println(num1 < num2); // true
System.out.println(num1 == num3); // false
System.out.println(num1.equals(num3)); // true
}
}
박싱된 기본타입(Wrapper Class) 은 언제 사용할까?
기본 타입의 데이터를 객체로 취급해야 하는 경우
컬렉션의 원소, 키, 값으로 쓴다. - 컬렉션은 기본 타입을 담을 수 없으므로
메서드를 호출할 때 파라미터를 전달하는 방법에는 두 가지 ( 값넘겨주나? / 참조를 넘겨주나?)
자바는 "Call by Value" 사용한다
Call by Value : Call by Value 는 메서드를 호출할 때 값을 넘겨주기 때문에 Pass by Value 라고도 부릅니다
'값을 전달하는 방식'이며 다르게 말하면 '값만 전달하는 방식'
메서드를 호출하는 호출자 (Caller) 의 변수와 호출 당하는 수신자 (Callee) 의 파라미터는
**복사된 서로 다른 변수**입니다
값만을 전달하기 때문에 수신자의 파라미터를 수정해도 호출자의 변수에는 **아무런 영향이 없습니다**
Call by Reference : 참조 (주소) 를 직접 전달하며 Pass By Reference 라고도 부릅니다
참조를 직접 넘기기 때문에 호출자의 변수와 수신자의 파라미터는 완전히 동일한 변수입니다
메서드 내에서 파라미터를 수정하면 그대로 **원본 변수에도 반영됩니다**
자바의 객체타입에는 Immutable 타입과 mutable타입이 있다
객체들은 기본적으로 heap영역에 할당되고 stack영역에 래퍼런스 값을 갖는 참조 변수들로 접근 가능하다
Immutable :
변경불가능한, 바뀌지 않는 객체이다
immutable 객체는 대표적으로 String, Boolean, Integer, Float, Long등이 있다
(String 제외하면 primitive 타입의 wrapper class 객체이다)
new연산자로 객체를 생성하면 heap영역에 객체가 생성되고 레퍼런스값을 가지는 변수가 stack영역에 생성된다
immutable 객체는 객체의 값을 heap영역에서 바꿀수 없다는 의미이다
(새로운 객체를 만들어 레퍼런스값을 주는 재할당 만 가능하다)
Mutable :
mutable객체는 불변객체와는 다르게 heap영역에 생성된 객체를 변경할 수 있다
대표적인 가변 객체는 List, ArrayList, HashMap, StringBuilder, StringBuffer등이 있다
가변객체를 multi-thread 환경에서 사용하려면 별도의 동기화 처리를 해줘야한다
아렇게 동기화 처리된 객체중 하나가 StringBuffer이다
String 과 StringBuilder 비교 ( imutable / mutable )
String result = "Hello";
result = result.concat("World");
String 문자열을 이어붙일 때 concat(+=)을 쓰면 새 주소값이 생기는 것을 확인
이유는 concat할때마다 new String()으로 새로운 객체를 만들어 할당받기 때문이다
StringBuilder result = new StringBuilder();
result.append("Hello");
result.append("World");
반면에 StringBuilder는 append로 객체를 이어붙여 주소값이 변경되지 않는다
1 방어적 복사(defensive copy) : 모든 field 변수가 final이 아닐때 즉 가변객체타입의 field 변수가 있을 경우 그 가변객체 타입의 field변수에 대해 직접적으로 접근하지 못하도록 copy 객체를 생성하여 새로운 인스턴스를 반환하도록 방어적 복사본 전략을 사용
2 Setter 메소드 제거
3 멤버변수를 final로 설정
4 class를 상속하지 못하도록 선언 (class를 final로 선언하거나 생성자를 private로 선언)