목표
자바가 제공하는 다양한 연산자를 학습하세요.
학습할 것
클래스를 정의하는 방법
- 객체 만드는 방법(new 키워드 이해하기)
- 메소드 정의하는 방법
- 생성자 정의하는 방법
- this 키워드 이해하기
- ※ ♥ 스터디원 참고 및 리뷰
과제(Optional)
- int 값을 가지고 있는 이진 트리를 나타내는 Node라는 클래스를 정의하세요.
- int value, Node left, right를 가지고 있어야 합니다.
- BinaryTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs(Node node)와 dfs(Node -node) 메소드를 구현하세요.
- DFS는 왼쪽, 루트, 오른쪽 순으로 순회하세요.
클래스를 정의하는 방법
자바는 대표적인 객체지향 프로그래밍 언어 중 하나이다.
객체 지향 프로그래밍(Object-Oriented Programming)이란 컴퓨터 프로그래밍의 패러다임 중에 하나로, 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들어 자율적인 객체들끼리 상호작용하여 시스템을 분할하는 방법이다.
즉, 객체지향의 세계에서는 객체들이 서로 메시지를 주고받으며 돌아가는 시스템인데 이러한 객체를 만들어 주는게 클래스이다.
😊 클래스란 객체를 만드는데 필요한 구현 메커니즘일뿐이다.
※ 좀 더 쉽게 설명하자면
많이들 들어봤을 법한 클래스 = 붕어빵 틀, 객체 = 붕어빵의 비유를 들 수 있다.
붕어빵 틀을 통해 붕어빵을 만들 듯이 클래스를 통해 객체를 만든다.
클래스는 다음의 과 같이 정의할 수 있다.
public class Animal{
/** 필드 */
private String name;
private String type;
/** 생성자 */
public Animal() {
}
/** 메소드 */
public void eat() {
System.out.println("eat!! ");
}
public void introduce(){
System.out.println("저는 " + type + "동물이고 이름은 " + name + "입니다.");
}
}
Animal 클래스에서 상태(필드)는 name, type이 되고 행위(메소드)는 eat(), introdue()가 된다.
클래스는 크게 필드, 생성자, 메소드로 이루어져 있다. 간단하게 말하면 아래와 같다.
필드 : 객체의 데이터
생성자 : 객체 생성시 초기화 역할
메소드 : 객체의 동작
✏️ 클래스를 만들 때 주의할 점
1. 관례상 클래스는 명사이여야 하며 각 단어의 첫글자를 대문자로 하는 파스칼 표기법으로 네이밍을 한다.
2. 한 클래스파일 내에 여러 클래스들을 정의할 수 있지만 public 접근 제한자가 붙은 클래스는 딱 하나이여야 한다. 또한, 파일 이름과 동일한 이름의 클래스에만 public 접근 제한자를 붙일 수 있다.
3. 첫 번째 글자는 숫자가 올 수 없다.
4. '$', '_' 이외의 특수 문자는 사용할 수 없다.
5. 클래스명은 한글, 영어 둘다 되지만 그냥 영어로 쓰자...^^
6. 클래스 명으로 자바의 예약어를 사용할 수 없다.
※ 여담
클래스는 객체의 정적인 타입(상태, 행위)을 기술하지만 객체는 각각 동적으로 메시지를 주고받으면서 협력하는 것이 바로 객체지향이다.
여담으로 보통 자바 코딩을 할 때 클래스에 기능 구현 시 어떤 클래스를 만들고 그안의 변수, 메소드 등 클래스 관점에서 생각을 많이 한다. 하지만 이는 잘못된 생각이고 객체들간의 커뮤니케이션에 초점을 맞춰야 한다.
객체 만드는 방법(new 키워드 이해하기)
클래스로부터 객체를 만드는 것을 클래스의 인스턴스화라고 하며, 어떤 클래스로 부터 만들어진 객체를 그 클래스의 인스턴스라고 한다.
위에서 만든 Animal 클래스는 아래와 같은 방법으로 만들 수 있다.
public class Animal{
public static void main(String[] args){
Animal animal = new Animal();
animal.eat(); //메소드 호출
animla.intrduce(); //메소드 ㅗ출
}
}
new 연산자를 통해 Animal이라는 객체를 만들었다. 객체를 생성하는 부분을 뜯어보자.
Animal animal = new Anima();
1. new 연산자를 통해 객체를 만들었다.
2. 이는 힙(heal)영역에 데이터를 저장할 공간을 할당받고 할당받은 공간의 참조값(객체의 주소값)을 객체변수(animal)에게 반환한다.
3. new 오른쪽에 있는 기본 생성자를 호출한다.
4. 건네받은 주소를 참조 타입인 클래스 변수에 저장해두면, 변수를 통해 객체를 사용하여 클래스에 정의한 메소드를 호출한다.
메소드 정의하는 방법
메소드란 클래스 안에 선언하는, 입력값을 매개변수로 받아 특정 행동(로직)을 수행하고 결과를 호출자에게 리턴하는 명령문이다.
(보통 JavaScript에서는 함수(function)라곧 불린다.)
쉽게 비유하자면
믹서기에 과일(입력값)을 넣으면 믹서기 내부에서 과정을 거쳐 과일쥬스(리턴값)이 나온다는 식이다.
자바의 메소드 구조는 다음과 같다.
접근제어자 제어자 리턴타입 메서드명(매개변수타입 매개변수명 ...) {
//내부 로직
return;
}
public static void main(String[] args) {
return;
}
※리턴타입, 메서드명, 괄호는 필수로 적어주어야 한다. 리턴타입이 void가 아니라면 타입에 맞는 값을 return 하는 구문도 필수다.
1. 접근제어자
접근제어자는 총 4가지가 있는데 사용하는 종류에 따라 접근할 수 있는 제한이 생긴다.
- public : 같은 프로젝트 폴더 안에 있는 어디든 접근할 수 있다.
- protected : 같은 패키지안에 존재하면 접근가능하며, 다른 패키지더라도 상속관계면 접근이 가능하다.
- default : 접근제어자 생략시 자동으로 붙는 것으로 같은 패키지안에 존재하면 접근이 가능하다.
- privte : 같은 패키지의 같은 클래스에서만 접근이 가능하다.
2. 제어자
static이 붙은 메서드는 객체를 생성하지 않고 클래스명.메소드명으로 바로 접근이 가능하다.
static이 붙지 않았다면 메소드가 존재하는 객체를 생성한 다음 접근할 수 있다.
3. 리턴타입
메소드에서 어느타입을 반환할지 적어준다. void면 아무것도 반환하지 않아도 된다.
4. 메소드명
자바에서 메소드는 첫단어의 첫글자는 소문자 나머지 단어의 첫글자는 대문자로 이루어진 카멜 케이스 기법을 관례상 많이 사용한다.
5. 매개변수타입 매개변수명
메소드에 입력값으로 작성할 수 있다. 어느 타입을 무슨이름으로 받을것인지 상황에 따라 작성해주면 된다.
그럼 메소드는 어떨 때 작성하면 좋을까?
코딩을 하다보면 반복되는 코드를 작성하는 순간이 있다. 이 때 메서드로 분리하여 "어떤 입력값을 주었을 때 어떤 리턴값을 돌려준다."라는 식의 메서드를 작성하는 것이 유지보수하기 좋은 코드를 작성할 수 있다.
또한, 비즈니스 로직을 메소드로 분리한 뒤 적절한 메소드명을 네이밍하면 코드의 유지보수가 훨씬 수월해 진다.
public class ExampleMethod {
public static void main(String[] args) {
sum(5, 20);
}
public static void sum(int a, int b) {
int sum = 0;
for (int i = a; i <= b; i++) {
sum += i;
}
System.out.println(sum);
}
}
정수형 숫자 두개를 sum()의 메서드에 인자로 넣어준뒤 메소드의 매개변수로 받아 a부터 b까지의 숫자를 더하는 로직을 작성하였다. 약간 극단적인 예시같기도 하지만...😅 main에서 sum(5, 20)을 보면 "아 5부터 20까지 더하는 메소드인가 보구나"라는 것을 어림짐작 할수 있다.
그래서 협업시 한눈에 파악하기 쉽도록 변수명, 메소드명 등의 이름을 잘 지어야 한다.
생성자 정의하는 방법
생성자란 new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당한다.
객체안의 멤버필드의 값을 초기화 한다.
자바의 생성자 구조는 다음과 같다.
접근제한자 클래스명 (매개변수) throws 예외처리 {
멤버 필드 초기화 작업
}
생성자는 다음과 같은 규칙으로 만들어야 한다.
1. 생성자이름은 클래스의 이름과 동일해야 한다.
2. 명시된 리턴타입이 없어야 한다.
=> 생성자는 인스턴스를 생성해주는 역할을 하는 특수한 메소드이다. 그런데 반환값이 있다면 엉뚱한 객체가 생성될 수 있따. 따라서 return도 사용하지 않고 반환타입도 명시하지 않는다.
class Car {
private String color;
private String type;
private int door;
public Car(){ //기본생성자
}
public Car(String color){
this(color, "auto", 4);
door = 5;
}
public Car(String color, String type){
this.color = color;
this.type = type;
}
public Car(String color, String type, int door){
this.color = color;
this.type = type;
this.door = door;
}
}
생성자는 매개변수가 없는 생성자인 기본생성자와 매개변수가 있는 생성자 두종류가 있다.
위 코드처럼 생성자를 오버로딩할 수도 있다.
그럼 생성자를 어떨때 사용할까❓
객체 생성시 일부 필드값을 강제하고 싶을 수 있다. 그렇다면 객체생성시 생성자의 인자로 해당값을 받아야지만 객체를 생성할 수 있도록 강제할 수 있다.
생성자에서 초기화하지 않고 자바 빈 규약에 의한 setter 메서드를 통해 객체를 먼저 생성 후 값을 세팅할 수도 있다. 만약 한번 할당받고 더이상 수정이 원치않는 필드값에 대한 setter메서드가 존재하면 후에 값이 마음대로 변경될 가능성이 있기 때문에 객체 생성시 필드값을 강제로 초기화하는 방법이 더 적절하다.(final 키워드를 붙이면 된다)
class Car2{
private String color;
private String type;
private int door;
public Car2(String color, String type, int door){
this.color = color;
this.type = type;
this.door = door;
}
}
public class Main {
public static void main(){
Car car = new Car();
Car2 car2 = new Car2(); //컴파일 에러
}
}
👄자바에서는 생성자가 아무것도 없으면 컴파일러가 자동으로 기본생성자를 만들어 준다.
- 컴파일러가 만들어 주는 기본 생성자의 경우, 상위 클래스의 기본 생성자를 호출(super(); 생략)한다.
- 만약 상위 클래스에서 기본 생성자가 존재하지않고, 하위 클래스는 컴파일러에 의해 기본 생성자가 생성되는 경우에는 컴파일 에러가 발생한다.
하지만 생성자가 하나라도 존재하면 기본생성자를 자동으로 만들어주지 않기 때문에 직접 작성해주어야 한다.
this 키워드 이해하기
자바에서 this 키워드는 객체의 자기자신을 가르키는 참조이다.
즉, 자기자신의 메모리 주소를 담고 있는 키워드이다.
모든 인스턴스메서드에 지역변수로 숨겨진 채로 존재한다.
주로 다음과 같은 형태로 사용된다.
1. 객체 자신의 속성(변수)값을 가리키기 위해 사용한다.
public class Person {
int name;
int address;
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
public int getAddress() {
return address;
}
public void setAddress(int address) {
this.address = address;
}
}
위의 코드를 보면 setName(), setAddress()의 매개변수에 받는 이름이랑 Person클래스의 속성에서 받는 변수명이 같다. 이를 구분하기 위해 변수명을 달리할 수 있지만 나중에 헷갈릴 수 있고 어떻게 변수명을 지을지 고민도 해야한다. 이런 상황에서 this 키워드를 사용하여 매개변수로 받는 변수랑 클래스 안에 선언된 필드값을 구분하여 변수명을 달리하지 않아도 된다.
2. 클래스 내부에서 생성자 오버로딩된 다른 생성자 호출하기 위해 사용한다.
※ this에 괄호를 붙인 this()이다.
public class Car {
private String color;
private String type;
private int door;
Car(String color){
this(color, "auto", 4);
door = 5;
}
Car(String color, String type, int door){
this.color = color;
this.type = type;
this.door = door;
}
}
D설계시 다양한 조건을 줄때 활용하면 좋다. 예를 들면 color만 선택하는 사용자에게는 문이 4개이고 "자동"인 차만 줘야되겠다! 라고한다거나...
✏️ 주의할 점
- 생성자의 이름으로 클래스이름 대신 this를 사용한다.
=> Car(color, "auto", 4); 불가능
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.
3. 객체 자신의 참조값을 전달하고 싶을 때 사용한다.
package com.soap;
public class Car {
private String color;
private String type;
private int door;
Car(String color){
this(color, "auto", 4);
door = 5;
}
Car(String color, String type, int door){
this.color = color;
this.type = type;
this.door = door;
}
public Car getMyself(){
return this;
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car("red");
System.out.println(car); //com.soap.Car@5b480cf9
System.out.println(car.getMyself()); //com.soap.Car@5b480cf9
}
}
Car의 getMyself()는 this키워드를 이용하여 객체 자신의 주소값을 반환한다.
main메소드에서 찍어보면 주소값이 같은걸 확인할 수 있다.
♥스터디원 참고 및 리뷰
1. 인스턴스 초기화 순서
public class Init{
private int number;
{
this.number = 10;
}
public Init(){
this.number = 100;
}
public static void main(String[] args){
Init init = new Init();
System.out.println(init.number); //100
}
}
초기화 순서 : 기본값 => 인스턴스 초기화 -> 생성자
※ 생성자에서 오버라이딩 되서 number는 100이 됨
2. 생성자 != 메서드
생성자는 메서드가 아니다
public Init(int number){
Init(); //컴파일 에러
this.number = number;
}
public Init(){
this.name = "wkwc";
}
만약 메소드였다면 Init(int number)안에서 Init();를 호출할 수 있어야 한다.
3. 클래스 종류
3.1 Nested Class(중첩 클래스)
한 클래스 내부에 다른 클래스를 선언한 것이다.
종류
- static 키워드를 이용한 경우, static nested class라고 한다.
- static 키워드를 이용하지 않은 경우, inner class라고 한다.
public class OuterClass {
static class StaticNestedClass {
}
class InnerClass {
}
}
중첩 클래스는 중첩 클래스를 둘러싼 클래스의 멤버이기 때문에, 둘러싼 클래스의 필드에 얼마든지 접근이 가능하다.(private로 되어있어도 접근 가능). 하지만 static으로 선언한 중첩 클래스의 경우 필드에 접근이 불가능 하다.
중첩 클래스 또한 둘러싼 클래스의 멤버이기 때문에, 모든 접근제어 지시자를 사용할 수 있다.
중첩 클래스를 사용하는 이유
- 한 곳에서만 사용되는 클래스들을 논리적으로 그룹화 할 수 있다
- 캡슐화를 증가시킬 수 있다.
- 좀 더 읽기 쉽고 유지보수하기 쉬운 코드를 작성할 수 있다.
3.2 Static Nested Class
Static Nested Class는 바깥 클래스의 변수나 메서드에 직접 접근이 불가능하다. 따라서 바깥 클래스의 변수나 메소드에 접근하기 위해서는 그 클래스의 객체 참조를 사용해야한다.
Static Nested Class는 일반적으로 선언하는 클래스처럼 사용된다. 따라서 단순히 패키징을 보다 편하기 하기 위해 사용된 클래스라고 이해하면 된다.
Static Nested Class는 바깥 클래스의 이름을 통해 접근할 수 있다.
public class OuterClass {
public static class StaticNestedClass {
}
}
public class Main {
public static void main(String[] args) {
OuterClass.StaticNestedClass staticNestedClass = new OuterClass.StaticNestedClass();
}
}
3.3 Inner Class
Inner Class는 객체의 메서드나 필드에 바로 접근이 가능하지만, Inner Class는 인스턴스와 관련되기 때문에(정확히는 인스턴스의 멤버 변수로서 정의되기 때문에) static 멤버 변수를 가질 수 없다.
public class OuterClass {
public class InnerClass {
static int NUMBER = 10; // 컴파일 에러 발생
static final int IMMUTABLE_NUMBER = 10; // 선언 가능
}
}
💋 위의 코드를 보면 단순히 static키워드만 쓴 멤버 변수는 컴파일 에러가 발생한다. 그러한 이유는 Inner Class는 바깥 클래스의 인스턴스에 종속적인데, 바깥 클래스에 대한 인스턴스를 여러개 생성한 경우, 어떤 인스턴스의 static 멤버 변수 값이 옳은 것인지 알 수 없기 때문이다. 하지만 final 키워드를 사용하게 될 경우 값이 컴파일 시점에 결정되고 변경될 수 없기 때문에 사용이 가능하다.
Inner Class를 인스턴스화 하기 위해서는 먼저 바깥 클래스를 인스턴스화 해야 한다. 그 후 바깥 클래스의 인스턴스를 이용하여 Inner Class 객체를 만들 수 있다.
public class OuterClass {
public class InnerClass {
}
}
public class Main {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
}
}
Inner Class에 바깥 클래스의 멤버 변수와 동일한 멤버 변수가 존재할때,
{ 바깥 클래스의 이름 }.this{ 멤버 변수 }로 바깥 클래스의 멤버 변수에 접근할 수 있다.
public class OuterClass {
private int number = 20;
public class InnerClass {
private int number = 30;
public void method(int number) {
System.out.println(number); // 10이 출력됩니다.
System.out.println(this.number); // 30이 출력됩니다.
System.out.println(OuterClass.this.number); // 20이 출력됩니다.
}
}
}
public class Main {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.method(10);
}
}
3.4 Local Class
Local Class란 코드 블럭 { } 안에 정의된 클래스를 의미한다. Local Class는 특정 메서드내에서만 필요한 객체가 필요할 때 주로 사용된다.
public class Main {
public static void main(String[] args) {
boolean result = hasInvalid("V111111111", "V123456789", "E123456789");
System.out.println(result); // true 출력
boolean result2 = hasInvalid("V111111111", "V123456789");
System.out.println(result2); // false 출력
}
public static boolean hasInvalid(String... values) {
boolean result = false;
final int length = 10;
final String prefix = "V";
class Value {
private String prefix = "";
private String number = "";
Value(String value) {
if(value.length() == length) {
this.prefix = value.substring(0, 1);
this.number = value.substring(1, value.length());
}
}
public String getPrefix() {
return this.prefix;
}
public String getNumber() {
return this.number;
}
}
for(String value : values) {
Value valueObject = new Value(value);
if(!prefix.equalsIgnoreCase(valueObject.getPrefix()) || valueObject.getNumber().length() != 9) {
result = true;
}
}
return result;
}
}
3.5 Anonymous Class
Anonymous Class는 이름이 없다는 것만 제외하면 Local Class와 동일하다. 따라서 Local Class를 한번만 사용해야하는 경우에 사용할 수 있다. 또한 Anonymous Class 선언과 동시에 인스턴스화가 가능하다.
Anonymous Class는 표현식이기 때문에, 다른 표현식에서 사용이 가능하다.
public class Main {
public static abstract class MyAbstractClass {
private int number;
public MyAbstractClass(int number) {
this.number = number;
}
public void method() {}
}
interface MyInterface {
public void method();
}
public static void main(String[] args) {
// 선언과 동시에 인스턴스화를 할 수 있습니다.
MyInterface myInterface = new MyInterface() {
public void method() {
System.out.println("method 호출!");
}
};
myInterface.method(); // 'method 호출!'이 출력됩니다.
MyAbstractClass myAbstractClass = new MyAbstractClass(10) {
@Override
public void method() {
System.out.println(super.number);
}
};
myAbstractClass.method(); // 10이 출력됩니다.
}
}
Anonymous Class 표현식은 다음으로 구성
- new 연산자
- 구현하고자 하는 클래스 혹은 인터페이스의 이름
- 구현하고자하는 클래스의 생성자와 동일한 arguments를 가진 괄호, 인터페이스의 경우 빈 괄호를 사용
- 클래스를 정의한 body 부분. 메서드는 선언할 수 있지만, 명령어는 선언할 수 없다.
Anonymous Class 제약사항
- final 혹은 effectively fianl이 아닌 지역 변수에 접근할 수 없다.
※ effectively fianl : final이 붙지 않은 변수의 값이 변경되지 않는 변수
- 중첩클래스처럼 바깥 클래스의 변수와 동일한 이름으로 선언시 바깥 클래스의 변수를 가리키게 된다.
- static 초기화 블럭 및 멤버 interface를 선언할 수 없다.
- 상수 변수에 한해서만 static 멤버를 가질 수 있다.
- 생성자를 정의할 수 없다.
Anonymous Class 허용사항
- 바깥 클래스의 멤버에 접근할 수 있다.
- 필드, 추가 메서드, instance 초기화 블럭 및 Local Class를 선언할 수 있다.
※ 추가
내부클래스가 선언된 클래스 컴파일시 .class파일이 여러개 생김, 내부 클래스는 $표시
4. 컴파일된 클래스 파일이 로드되고 메모리에 적재되기 까지의 과정
4.1 클래스 로더 서브시스템 : 동적 클래스 로딩 과정
우선 클래스를 로딩하는 작업이다. 자바의 동적 클래스 로딩은 클래스 로더 서브시스템에 의해 처리된다.
👉 로딩 절차는 다음과 같이 요약할 수 있다.
1. 메서드 호출 문장을 만난다.
2. 해당 메서드를 가진 클래스 바이트 코드가 로딩 전이다.
3. 클래스 로더가 jre 라이브러리 디렉토리에서 해당 클래스의 유무를 조사한다.
4. 발견하지 못했다면, jdk 확장 디렉토리에서 해당 클래스의 유무를 조사한다.
5. 발견하지 못했다면, classpath 환경변수에 지정된 디렉토리(직접 선언한 클래스)에서 해당 클래스의 유무를 조사함.
6. 클래스를 발견한다.
7. 해당 클래스 파일이 올바른 바이트 코드인지 검증한다.
8. 올바른 바이트 코드라면 method 영역으로 파일을 로딩한다.
9. 클래스 변수를 만들라는 명령어가 있으면 method영역에 해당 변수를 준비한다.
10. 클래스 블록이 있으면 순서 대로 해당 블록을 실행한다.
11. 프로그램이 종료될 때까지 현재 상태를 유지한다.
로딩 과정은 크게 로딩 -> 연결 -> 초기화의 3단계로 이루어진다.
4.1.1. 로딩(Loading)
로딩과정은 클래스 로더에 의해 이루어진다.
클래스 로더는 Bootstrap, Extension, Application으로 총 3가지가 있다. 각 클래스 로더는 상속관계를 가지고 있으며, 부모가 수행하지 않은 로딩작업을 자식이 수행하는 방식으로 위임(delegation)하며 작업을 진행한다.
- Bootstrap Class Loader : 기본 자바 API 라이브러리 로드 담당
º jre의 lib 폴더에 있는 rt.jar파일을 뒤져 라이브러리를 로드
º 최우선적으로 로드됨
- Extension Class Loader : 확장 코어 클래스 파일 담당
º jdk 확장 디렉토리(JAVA_HOME\lib\ext 디렉토리 혹은 java.ext.dirs)에서 로드
- Application Class Loader : 시스템 클래스 로드 담당
º 사용자가 직접 정의한 클래스 파일을 로드
º classpath 환경변수에 클래스 파일이나 -cp 명령어 옵션이 있는 파일들을 로드
4.1.2. 연결(Linking)
클래스 파일의 바이트 코드를 검증하고, 정적변수의 메모리를 할당하는 과정
- Verify : 바이트 코드 검증
º 생성된 자바 바이트코드가 적절한지를 검증
º검증 실패할 경우 오류를 내보냄
- Prepare : 준비
º모든 정적변수의 메모리를 할당
ºdefault 값으로 할당하며 아직 명시된 값으로 초기화되어있진 않음
4.1.3. 초기화(Initializing)
클래스 로딩의 마지막 단계
- Initialization : 초기화
º 모든 정적 변수가 자바 코드에 명시된 값으로 초기화
º 정적블록 실행
4.2 실행 데이터 영역(Runtime Data Area)
클래스 로드 과정에서 읽힌 클래스 수준의 데이터뿐만 아니라 프로그램에서 사용되는 모든 데이터들이 저장되는 영역이다. 크게 5가지로 나뉘며, 스레드간 공유하는 영역과 스레드별로 개별 할당되는 영역이 존재한다.
4.2.1. 공유영역 - 메서드 영역
프로그램에서 사용되는 클래스 정보와 클래스 변수가 저장되는 영역
- 클래스명, 부모클래스명, 클래스 메서드, 클래스 변수 등의 데이터를 저장
- JVM당 하나밖에 없으며, 모든 스레드가 공유한다.
4.2.2 공유영역 - 힙 영역
프로그램에서 사용되는 모든 인스턴스 변수가 저장되는 영역
- new 키워드를 사용하여 인스턴스가 생성되면, 해당 인스턴스의 정보를 힙 영역에 저장
- 저장한 인스턴스 대한 참조값을 stack에 반환
º 만약 stack 값이 null 이라면, 참조할 수 있는 값이 없으므로 NPE을 발생시킨다.
- JVM당 하나밖에 없는 공유자원이기 때문에, 멀티 스레드환경에서 안전하지 않다.
4.2.3 개별영역 - 스택 영역
프로그램에서 메서드가 호출될 때, 메서드의 스택 프레임이 저장되는 영역이다.
- 각 스레드마다 개별적으로 영역을 할당받는다.
- 메서드가 호출되면 메서드 호출과 관계되는 지역변수와 매개변수를 스택영역에 저장하고, 호출이 완료되면 소멸한다.
- 이렇게 스택 영역에 저장되는 메서드의 호출 정보를 스택 프레임이라 한다.
º 하나의 스레드가 사용할 수 있는 스택 사이즈를 넘기게 되면, java.lang.StackOverFlowError를 발생시킨다.
- 스택 프레임은 3부분으로 나누어 진다.
º Local Variable Array(LVA)
▶ 말 그대로 변수를 담을 수 있는 배열이다.
▶ 메서드의 모든 파라미터와 지역변수를 담고 있다.
º Operand Stack(OS)
▶ 중간 연산의 결과가 저장되는 공간이다.
▶ push와 pop과정을 거쳐서 필요한 작업을 수행한다.
º Frame Data(FD)
▶ 특정 메서드와 관련된 모든 참조와 리턴값 등이 저장되는 공간이다.
4.2.4. 개별영역 - PC 레지스터
- 각 스레드가 시작할 때 생성된다.
- 스레드가 실행중인 명령어 주소를 담고있다.
4.2.5 개별영역 - 네이티브 메서드 스택
- bytecode가 아닌 binarycode를 실행하는 영역이다.
- JNI(Java Native interface)를 통해 호출되는 C/C++ 코드를 실행하는 영역이다.(I/O 작업을 위한 C라이브러리 함수 등)
5. 만약 런타임시 클래스의 정보를 알고 싶다면?
만약 해당 클래스의 정보를 런타임시 알고 싶을 때는 어떻게 할까? 일반적인 애플리케이션을 개발하는 개발자라면 사용할 일이 거의 없겠지만 Spring이나 Jackson같은 프레임워크, 라이브러리를 개발하는 개발자라면 사용할 일이 많을 것이다. 이때 사용 하는 것이 Reflection API이다. Reflection API를 사용하면 클래스의 정보(생성자, 필드, 메서드 등)를 확인할 수 있으며, 그 클래스의 멤버들을 호출하여 상황에 따라 인스턴스를 유동적으로 생성할 수 있다.
pocada.medium.com/java-reflection-api-1-61c3fc957e6d
※ 추가
프레임워크나 라이브러리에서는 런타임시 어떤 클래스(구체 클래스)가 올지 모르니 리플렉션을 하는것
※ 우아한 블로그 : 엑셀 기능 구현시 리플렉션 활용
https://woowabros.github.io/experience/2020/10/08/excel-download.html
6. 객체지향언어 vs 절차지향언어
7. Heap 영역
Heap 자료구조는 아래 사진과 같은 2진 트리이며 priority queue라는 이름으로도 불린다.
- 일반적으로 Heap영역이라고 이름붙은 이유는 메모리 블록에 대한 참조를 구성하는 데 사용되는 데이터 구조가 Heap이기 때문에(그중에서도 특히 minimun heap)그렇다.
- 저장하려는 데이터를 저장할 수 있는 가장 작은 메모리 블록을 빠르게 찾을 수 있는 적절한 방법인데, 여기서 우선순위는 메모리 블록의 사이즈이다.
- 메모리 블록의 크기순으로 항상 정렬되어 있는 메모리 구조이므로, Heap 영역에 메모리 할당을 요청하는 시스템 콜(new 혹은 maloc)이 들어왔을 때 가장 적당한 메모리 공간을 최대한 빠른시간에 할당해주기 위해 Heap 자료구조를 사용한다.
- 단 위의 Heap 내용은 논쟁이 있는 사항으로 그런가보다~ 하고 넘어가면 된다.
8. 자바는 항상 call by value
요약 : 자바는 객체를 가리키는 레퍼런스라는 개념이 있는 것이지, 이게 함수의 매개변수로 넘어갈때는
call by value로 넘어간다. 즉, 레퍼런스 그 자체가 deep copy되어 넘어간다.
자바는 레퍼런스 연산자(&)가 없다!!
참고 : aomee0880.tistory.com/145
9. 과제 참고
blog.naver.com/swoh1227/222175350122
github.com/jikimee64/live-study-assignment : 과제 코딩
📘 Reference
- wooaoe.tistory.com/7
- salix97.tistory.com/4
- wikidocs.net/225
- library1008.tistory.com/4
- www.opentutorials.org/module/516/5519
- github.com/ByungJun25/study/tree/main/java/whiteship-study/5week
- ahnyezi.github.io/java/javastudy-5/#4-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A1%9C%EB%94%A9%EA%B3%BC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%A0%81%EC%9E%AC%EA%B3%BC%EC%A0%95
'Language > Java' 카테고리의 다른 글
[자바 스터디 7주차] 패키지 (0) | 2021.01.03 |
---|---|
[자바 스터디 6주차] 상속 (0) | 2020.12.20 |
switch문 동작방식 (0) | 2020.12.09 |
[자바 스터디 4주차] 제어문 (0) | 2020.12.02 |
[자바 스터디 3주차] 연산자 (4) | 2020.11.24 |