6장 - 객체지향 프로그래밍 I 2탄..

·2022년 12월 12일
0

자바의 정석

목록 보기
9/12
post-thumbnail

변수와 메서드

1. 선언위치에 따른 변수의 종류

변수 - 클래스변수, 인스턴스변수, 지역변수

class Variables { // 클래스 영역
	int iv; // instance valuable
    static int cv; // class valuable, static valuable
    
    void method() { // 메서드 영역
    	int lv = 0; // local valuable
    }
}

+) cv는 클래스가 메모리에 로딩될 때 생성되어 종료될 때까지 유지, public을 앞에 붙이면 같은 프로그램 내에서 어디서나 접근할 수 있는 전역변수의 성격을 가진다.

2. 클래스변수, 인스턴스변수

인스턴스 변수는 인스턴스가 생성될 때마다 생성되므로
인스턴스마다 각기 다른 값을 유지할 수 있다.
클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.

3. 메서드

특정 작업을 수행하는 일련의 문장들을 작업단위로 묶은 것.

  1. 높은 재사용성
  2. 중복 코드 제거
  3. 프로그램 구조화

이후의 세세한건 알고 잇어서... 궁금하시면 자바의 정석을 드셔보세요

4. JVM의 메모리 구조

  1. 메서드 영역

    • 클래스에 대한 정보(클래스 데이터)를 여기다 저장. 이 때 cv도 이 영역에 함께 저장된다.
    • 인스턴스가 생성되는 공간. iv들이 생성되는 공간.
  2. 호출스택

    • 메서드 작업에 필요한 메모리 공간 제공.
      메서드 호출 시 메서드를 위한 메모리 할당, 메서드가 작업을 수행하는 동안 lv들과 연산 중간결과 등을 저장. 작업을 마치면 반환되어 비어진다.
    • 호출스택의 제일 상위에 있는 메서드가 현재 실행중인 메서드, 나머지는 대기상태
  • 메서드 호출 -> 필요한만큼 메모리를 스택에 할당
  • 수행을 마치면 사용한 메모리 반환 -> 스택에서 제거
  • 맨 위의 메서드 = 현재 실행중인 메서드
  • 그 아래에 있는 메서드 = 바로 위의 메서드 호출한 메서드

5. 기본형 매개변수, 참조형 매개변수

메서드 호출 시 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨준다.
기본형일 때는 기본형 값 자체가 복사
참조형이면 인스턴스의 주소가 복사

기본형 매개변수 - 변수의 값을 읽기만
참조형 매개변수 - 변수의 값을 읽고, 쓰기(변경)

외요?
인텔리제이 꺼라. 그림판 켜라,..

class Data {
	int x;
}

class PrimitiveParamEx {
	public static void main(String[] args) {
    	Data d = new Data();
        d.x = 10;
        System.out.println("main() : x = " d.x);
        
        change(d.x);
        System.out.println("After cahnge(d.x)");
        System.out.println("main() : x = " d.x);
    }
    
    static void change(int x) {
    	x = 1000;
        System.out.pringln("change() : x = " + x);
    }
}

change 메서드는 수행하고나서 stack에서 제거되고, main이 바라보고 있는 변수는 여전히 10으로 남아있기 때문이다.

이번엔 참조변수를 보자

class Data {
	int x;
}

class PrimitiveParamEx {
	public static void main(String[] args) {
    	Data d = new Data();
        d.x = 10;
        System.out.println("main() : x = " d.x);
        
        change(d);
        System.out.println("After cahnge(d.x)");
        System.out.println("main() : x = " d.x);
    }
    
    static void change(Data d) {
    	d.x = 1000;
        System.out.pringln("change() : x = " + d.x);
    }
}

이번엔 주소를 넘겨줬다!!
그러니 객체를 다룰 수 있도록 리모콘을 넘겨준거니 값을 조작할수 있게 되었다.

int 배열도 있는데 얘도 참조변수니까 똑같이 돌아간다고 생각하면 된다~~~ 어우 힘들어

class ReferenceParamEx2{
	int[] x = {10};
    System.out.println("main() : x = " + x[0]);
    
    change(x);
    System.out.println("After change(x)");
    System.out.println("main() : x = " + x[0]);
    
    static void change(int[] x) {
    	x[0] = 1000;
        System.out.println("change() : x = " + x[0]);
    }
}

실행결과
main() : x = 10
change() : x = 1000
After change(x)
main() : x = 1000

그림판 꺼라...

6. 클래스 메서드(static 메서드), 인스턴스 메서드

메서드 앞에 static 붙으면 클래스메서드~
이렇게 클래스 변수를 생성하면, 객체를 생성하지 않고 클래스이름.메서드이름 요런 형식으로 호출이 가능하다. 인스턴스 메서드는 당연히 객체를 생성해야 호출이 가능하고여

그럼 언제 static을 붙여야할까?

인스턴스와 관계없는 메서드를 정의할 때!

  1. 멤버변수 중 모든 인스턴스에 공통으로 사용하는 것

    • 인스턴스는 서로 독립적이라 iv들은 서로 다른 값을 유지할 수 있따.
      그러니 모든 인스턴스에서 같은 값이 유지되어야하는 변수는 static을 붙여 클래스변수로 정의해주자.
  2. 클래스 변수(static 변수)는 인스턴스 생성안해도 사용 가능

    • 클래스가 메모리에 올라갈 때 이미 자동생성
  3. 클래스 변수는 인스턴스 변수를 사용할 수 없다.

    • 클래스 메서드가 호출되었을 때, 인스턴스의 유무에 대한 불확실성 때문에 클래스 메서드에서 인스턴스변수의 사용을 금지한다.
  4. 메서드 내에서 iv를 사용하지 않는다면, static을 붙이는걸 고려하자

    • iv가 필요하면, 당연히 static을 붙일 수 없다. 메서드 호출시간이 짧아져, 성능 향상. 안붙이면 실행 시 호출되어야할 메서드를 찾는 과정이 추가적으로 필요하기 때문이다.

클래스 멤버변수 중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있다면, static을 붙여보자.
메서드 중 인스턴스 변수나 인스턴스 메서드를 사용하지 않는다면, 메서드에 static을 붙일 것을 고려해보자~

Q. static 메서드는 static 메서드 호출 가능?
A. 예. 언제 어디서든 가넝

Q. static메서드는 인스턴스 변수, 메서드 사용 가능?
A. 둘다 안돼 숭이야

Q. 왜여?
A. static메서드 호출 시 객체(iv묶음)가 없을 수도 없어서요

오버로딩

와 드디어 다음챕터다 쒸엣~~~ 쏴리질러

한 클래스 내에 같은 이름의 메서드를 여러 개 정의한다.

엥? 구분은 어덕하라고요
오버로딩의 조건은 다음과 같다.

메서드 이름이 같다.
근데 여기서? 매개변수 개수 혹은 타입이 달라야됨

반환값은 아무런 영향을 못준다,,

생성자

어? 왜 벌써.. ㅋㅋ

1. 생성자?

인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드

iv 초기화 + 객체 생성으로 이해하면 된다.
클래스 내에 선언되어 구조도 메서드와 유사한데, 리턴 값이 없다

조건은

  1. 생성자 이름 == 클래스 이름
  2. return값이 없다.
  3. 오버로딩 가능. 하나의 클래스에 여러개 생성자 존재 가능
class Card {// class 이름
	Card() {} // 기본생성자
    
    Card(String k, int num) {
    }
}
오버로딩한 생성자 이름들이 다 class 이름과 같다~

연산자 new가 인스턴스를 생성한 것. 생성자는 인스턴스를 생성하는게 아니다!!

Card c = new Card();

  1. 연산자 new -> 메모리(heap)에 Card클래스의 인스턴스(객체) 생성
  2. 생성자 Card()가 호출, 수행 -> 만든적 없음? 기본생성자 생김
  3. 연산자 new -> 생성된 Card인스턴스 주소 반환, 참조변수 c에 저장

2. 기본 생성자

백준에서도, 연습문제에서도 부루룩박박 대충 코드 짰는데, 사실 인스턴스를 생성하기 위해서는 생성자는 꼭 클래스당 하나 이상 있어야한다
저는 그딴거 만든적 없는데요? -> 컴파일러가 개짱천재라 기본생성자를 자동으로 넣어준거다.

클래스명() {}의 구조를 갖고 있다. 매개변수도 없고, 내용도 없다.

// ConstructorTest.java

class Data1 {
	int value; // 생성자가 없다.
}

class Data2 {
	int value;
    
    Data2(int x) { // 매개변수를 지닌 생성자.
    	value = x;
    }
}

class ConstructorTest {
	public static void main(String[] args) {
    	Data d1 = new Data1(); // 얜 돼요
        Data d2 = new Data2(); // 얘 컴파일 오류나요
    }
}

왜 Data1은 인스턴스가 생성되고, Data2는 컴파일 오류가 날까?
말했듯, 클래스 안에 생성자가 하나도 없으면, 기본생성자가 자동으로 생성된다
Data1에는 생성자가 하나도 없고, Data2에는 매개변수를 지닌 생성자가 하나 있다.
Data1 -> 없으니까 기본생성자 자동생성
Data2 -> 매개변수 지닌 생성자가 있어서 기본생성자 안생김.
엥? 매개변수를 지닌 생성자가 있다며!! 그럼 하나 있는거 아님?
그럴려면 Data2()로 매개변수가 없게 넘기는게 아니라, Data2(int변수)를 넣어 생성자를 사용해주면 된다
매개변수가 있는 생성자에 대해 더 알아보자

3. 매개변수가 있는 생성자

인스턴스 초기화 작업 시, 인스턴스마다 각 다른 값으로 초기화해야하는 경우가 있다. 매개변수를 사용해 지리게 초기화해주자.

class Car {
	String color;
    String gearType;
    int door;
    
    Car() {}
    Car(String c, String g, int d) {
    	color = c;
        gearType = g;
        door = d;
    }
}

Car라는 클래스에서 iv가 세 개 선언되고, 기본생성자 Car()과 매개변수가 있는 생성자 Car(String c, String g, int d)가 선언되었다
매개변가 있는 생성자는 iv를 이용해 인스턴스를 초기화하며, 기본생성자만 있었다면 생성 후에 따로 초기화를 해줘야한다.

Car c = new Car();
c.color = "white";
c.gearType = "auto";
c.door = 4;

위의 코드를 이렇게 간결하게 초기화 쌉가넝~
Car c = new Car("white", "auto", 4);
class Car {
	String color;
    String gearType;
    int door;
    
    Car() {} // 기본생성자
    Car(String c, String g, int d) { // 매개변수가 있는 생성자
    	color = c;
        gearType = g;
        door = d;
    }
}

class CarTest{
	public static void main(String[] args) {
    	Car c1 = new Car(); // 기본생성자를 이용해 인스턴스 생성
        c1.color = "white";
        c1.gearType = "auto";
        c1.door = 4;
        
        Car c2 = new Car("white", "auto", 4); // 매개변수가 있는 생성자로 인스턴스 생성 -> 바로 초기화댐 굿
    }
}

c1, c2의 요소를 출력해보면 둘 다 같은 결과를 뱉는다.

4. 생성자에서 다른 생성자 호출 - this(), this

🚨🚨 this()와 this는 완전 딴얘기입니당 관련 없음

생성자 이름으로 클래스 대신 this 사용
한 생성자 내부에서 다른 생성자 호출 시, 첫 줄에서만 호출 가능

아래의 코드는 오류가 있따

Car(String color) {
	door = 5;
    Car(color, "auto", 4);
}
  1. 생성자 안에서 다른 생성자 부를거면, 첫줄에서 불러야됨
  2. 다른 생성자 부를거면, 클래스명 대신 this 써야됨

예시를 좀 더 들어보자

class Car {
	String color;
    String gearType;
    int door;
    
    Car() {
    	this("white", "auto", 4); // 첫줄에서 Car(String color, String gearType, int door) 호출
    }
    Car(String color) { // 첫줄에서 Car(String color, String gearType, int door) 호출
    	this(color, "auto", 4);
    }
    Car(String color, String gearType, int door) {
    	this.color = "white";
        this.gearType = "auto";
        this.door = 4;
    }
}

class CarTest {
	public static void main(String[] args) {
    	Car c1 = new Car();
        Car c2 = new Car("blue");
    }
}

출력 해보면
c1은 흰, 오토, 4
c2는 파랑, 오토, 4

Car(String color, String gearType, int door) 생성자 클래스를 호출해 초기화한 코드인데 엥, 위에선 this.color 아니고 걍 color = 이런식으로 했잔아요
원래는 this.color가 맞는데, 같은 클래스 안이라 this.를 생략한거당~~

그래서 생성자 this() 말고 this는 머임??

this는 참조변수. 인스턴스 자신을 가리킨다. 인스턴스 메서드(생성자 포함)에서 사용 가능하며, lv와 iv를 구별하기 위해 사용한다.

즈엉리

this : 선언하지 않아도 사용 가능하며, 인스턴스 자신을 가리키는 참조변수. 인스턴스의 주소가 저장되어 있고, 모든 인스턴스메서드에 lv로 숨겨진 채로 존재.

this(), this(매개변수) : 생성자. 같은 클래스 안에서 다른 생성자를 호출할 때 사용한다.

5. 생성자를 이용한 인스턴스의 복사

Car(Car c) {
	color = c.color;
    gearType = c.gearType;
    door = c.door;
}

Car 클래스의 참조변수 (c)를 매개변수로 선언한 생성자
Car 인스턴스 변수인 color, gearType, door의 값을 인스턴스 자신으로 복사! 종합된 예시를 보자

class Car{
	String color;
    String gearType;
    int door;
    
    Car() {
    	this("white", "auto", 4);
    }
    
    Car(Car c) { // 인스턴스 복사를 위한 생성자
    	color = c.color;
        gearType = c.gearType;
        door = c.door;
    }
    /*
    위의 코드는 이렇게 고쳐 재사용하는게 더 바람직..
    Car(Car c) {
    	this(c.color, c.gearType, c.door);
    }
    */
    
    Car(String color, String gearType, int door) {
    	this.color = color;
        this.gearType = geraType;
        this.door = door;
    }
}

class CarTest3 {
	public static void main(Strint[] args) {
    	Car c1 = new Car();
        Car c2 = new Car(c1) // c1의 복사본 c2 생성 Car(Car c) 생성자로 들어감
        System.out.println("c1의 요소들 " + c1.color + " " + c1.gearType + " " + c1.door); // c1의 요소들 white, auto, 4
        System.out.println("c2의 요소들 " + c2.color + " " + c2.gearType + " " + c2.door); // c2의 요소들 white, auto, 4
        
        c1.door = 100;
        System.out.println("c1.door=100 수행 후");
        System.out.println("c1의 요소들 " + c1.color + " " + c1.gearType + " " + c1.door); // c1의 요소들 white, auto, 100
        System.out.println("c2의 요소들 " + c2.color + " " + c2.gearType + " " + c2.door); // c2의 요소들 white, auto, 4
    }
}

인스턴스 c2는 c1을 복사해 생성한 것이라 같은 상태를 갖는다.
하지만 서로 독립적으로 메모리 공간에 존재하는 별도의 인스턴스이므로, c1의 값이 변경되어도 c2는 영향을 받지 않는다.

인스턴스 생성 시 유의할 점
1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 - 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?

profile
어?머지?

0개의 댓글