다형성과 캐스팅

  • 다운캐스팅
    • 부모타입의 형태를 자식타입의 형태로 강제로 형변환을 시켜주는것
// 다움캐스팅 (부모타입 -> 자식타입)
Child child = (Child) poly; 
// <- 이렇게 사용하면 강제로 부모티입을 자식타입으로 변환 시킨다.
child.childMethod();

→ 이렇게 사용하면 자식 타입에서 호출한 주소를 사용하는 부모가 자식 타입의 기능을 찾게 해준다.

  • 실행 순서 🔽
Child child = (Child) poly // 다운 캐스팅을 통해 부모 타입을 자식 타입으로 변환한 다음 대입 시도
Child child = (Child) x001 // 참조 값을 읽은 다음 자식 타입으로 지정
Child child = x001 // 최종 결과

→ 주소 값을 복사하여 읽어온다는걸 기억하자

  • 업 캐스팅
    • 부모 타입으로 변경

캐스팅의 종류

  • 일시적 다운 캐스팅
  • → 자식 캐스팅을 일일히 선언하고 사용 하기 번거로워 해당 메서드를 호출하는 순간에만 다운 캐스팅을 하는것을 말한다.
// 일시적 다운캐스팅 - 해당 메서드를 호출하는 순간만 다운캐스팅
((Child) poly).childMethod();

→ Parent 타입의 poly 를 호출 할 때에 기존 참조에서는 Parent 에서 찾았지만 일시적 다운 캐스팅을 하면 Child 에 바로 찾으러 간다.

  • 업 캐스팅
  • → 다운 캐스팅과 반대로 자식 타입을 부모 티입으로 바꾸는것을 말한다.
package poly.basic;

// upCasting cs downCasting
public class CastingMain3 {

    public static void main(String[] args) {
        Child child = new Child();
        Parent parent1 = (Parent) child; // 업 캐스팅은 생략 가능, 생략 권장
        Parent parent2 = child; // 업 캐스팅 생략 -> 이렇게 사용해야 한다.

        parent1.parentMethod();
        parent2.parentMethod();
    }

}

→ 부모 타입은 자식 타입을 담을 수 있기 때문에 업 캐스팅 시에는 부모타입 형변환을 생략할 수 있다.


다운 캐스팅과 주의점

  • 다운 캐스팅을 잘못하면 심각한 런타임 오류가 발생할 수 있다.
package poly.basic;

// 다운 캐스팅을 자동으로 하지 않는 이유
public class CastingMain4 {
    public static void main(String[] args) {
        //---------------------------------------------------
        Parent parent1 = new Child();
        Child child1 = (Child) parent1;
        // 부모의 크기로 자식을 담은 모양은 자식에 바로 못 들어 간다.
        child1.childMethod(); // 문제 없이 실행 됨
        
        //---------------------------------------------------

        Parent parent2 = new Parent();
        Child child2 = (Child) parent2;
        // 부모 자체를 자식에 담게 되면  런타임 오류 생성
        child2.childMethod(); // 실행 불가
        //---------------------------------------------------
    }
}

→ 문제없이 실행된 경우 🔽

→ 자식 객체로 생성한 부모타입 변수에는 자식과 부모의 영역이 모두 생성 되어있기 때문에 다운 캐스팅 하여 호출이 가능하능하다.

 

 

→ 다운 캐스팅중 문제가 발생한 경우 🔽

→ 부모 객체로 생성한 부모 타입 변수 내부에는 자식 영역이 존재 하지 않기 때문에 런타임 오류가 발생 하게 된다.

다운 캐스팅 결론

  • 자바 에서는 사용할 수 없는 타입으로 다운캐스팅 하는 경우에 예외를 발생시키고 프로그램을 종료 시키기 때문에 다운 캐스팅 사용 할때는 주의를 기울여야 한다.

업캐스팅이 안전하고 다운 캐스팅이 위험한 이유

  • 업캐스팅의 경우는 다운캐스팅 처럼 호출 대상이 존재 하지 않는 경우가 없다. → 이유는 해당 타입의 상위 부모 타입은 모두 함깨 생성 되기 때문에 업 캐스팅의 경우 문제가 없다.
  • 하지만 다운 캐스팅의 경우 부모 타입의 객체를 생성 할때에 자식 타입은 생성 하지 않기 때문에 → 개발자 다운 캐스팅 사용시 항상 주의를 하여 사용해야 한다.

컴파일 오류 VS 런타임 오류

  • 컴파일 오류 : 변수명 오타, 잘못된 클래스등 자바 실행전 발생하는 오류로, IDE 에서 즉시 확인 하는 안전한 오류다.
  • 런타임 오류 : 말 그대로 프로글매 실행중 발생하는 오류, 고객이 프로그램 실행도중 발생하기 때문에 매우 안좋은 오류다.

다형성

  • 객체 지향 프로그래밍의 특징 중 하나다.
  • 객체 지향 프로그래밍의 꽃
  • 이름 그대로 다양한 형태를 뜻한다.
  • 한 객체가 여러 타입의 객체로 취급될 수 있는 능력을 뜻하고, 하나의 객체는 하나의 타입으로 고정되어 있지만 다형성을 사용하면 하나의 객체가 다른 타입으로 사용될 수 있다는 뜻이다.

다형성의 핵심 이론

  • 다형적 참조
  • 메서드 오버라이딩

다형적 참조

  • 부모 타입의 변수가 자식 인스턴스 참조

→ 부모 class 🔽

package poly.basic;

public class Parent {
    public void parentMethod() {
        System.out.println("Parent.parentMethod");
    }
}

→ 상속 받은 자식 class 🔽

package poly.basic;

public class Child extends Parent{
    public void childMethod() {
        System.out.println("Child.childMethod");
    }
}

→ 부모 타입 변수 선언 하고 자식 타입으로 객체 생성 🔽

package poly.basic;

public class PolyMain {
    public static void main(String[] args) {
        // 부모 변수가 자식 인스턴스 참조(다형적 참조)
        System.out.println("Parent -> Child");
        Parent poly = new Child();
        poly.parentMethod();
    }
}

⇒ 위와 같이 부모 타입 변수가 자식 타입 객체를 담을 수 있다.

Parent poly = new Child();

코드 실행 의 로직을 보면

  1. Child 타입으로 객체가 생성된다
  2. Child 타입으로 생성된 객체 내부에는 Parent 영역과 Child 영역이 생성 된다.
  3. 하지만 poly 변수는 Parent 타입으로 생성 했기 때문에 생성된 객체 내에 Parent 영역만 끌어다 쓰게 된다.

다형적 참조 결론

  • 부모 타입은 → 자식을 담을 수 있다.
    • 자식 뿐 아니라 자손들의 타입도 담을 수 있다.
    • 하지만 부모는 자식의 기능을 호출할 수 없다.
  • 자식 타입은 → 부모를 담을 수 없다.

다형적 참조에서 부모 타입이 자식 타입기능을 사용하지 못하는 이유

  • 상속 관계로 객체를 생성 했을 때는 자식이 누가 부모 인지 찾을 수 있지만 부모는 누가 자식인지 찾을 수 없다.( 마치 미래에 내가 누구를 낳을지 모르는 것처럼? )
  • Parent 클래스의 정보에 누가 자식인지 정보도 없기 때문이기도 해서 못 찾는다.
  • 하지만 자식을 찾고 싶을 때 캐스팅 이란 기능을 사용하면 된다.

클래스와 메서드에 사용되는 final

  • class 에 final
    • 상속 끝!
    • fianl 선언된 class 는 확장할 수 없다.
  • 메서드에 final
    • 오버라이딩 끝!
    • final 선언된 메서드는 상속된 곳에서 오버라이드할 수 없다.

'자바 공부 > [ 完 ] 자바 실전 - 기본편' 카테고리의 다른 글

10-2. 다형성 - 캐스팅  (0) 2024.03.19
10-1. 다형성 - 이론과 다형적 참조  (0) 2024.03.19
9-3 상속과 접근제어  (0) 2024.03.14
9-2 상속과 메서드 오버라이딩  (0) 2024.03.14
9-1 상속  (0) 2024.03.14

상속과 접근 제어

  • 기존에 배웠던 접근제어자에서 protected 인 같은 패키지만 허용 하고, 다른 패키지 일지라도 상속된 관계의 호출 허용

→ 부모 class 와 자식 class 를 다른 class 에 생성 하고, 부모 class 에 접근 제한자를 적용 하여 만들어보자 🔽

// 부모 class
package extends1.access.parent;

public class Parent {
    public int publicValue;
    protected int protectedValue;
    int defaultValue;
    private int privateValue;

    public void publicMethod(){
        System.out.println("Parent.publicMethod");
    }

    protected void protectedMethod() {
        System.out.println("Parent.protectedMethod");
    }

    void defaultMethod() {
        System.out.println("Parent.defaultMethod");
    }

    private void privateMethod() {
        System.out.println("Parent.privateMethod");
    }

    public void printParent() {
        System.out.println("==Parent 메서드 안==");
        System.out.println("publicValue = " + publicValue);
        System.out.println("protectedValue = " + protectedValue);
        System.out.println("defaultValue = " + defaultValue);
        System.out.println("privateValue = " + privateValue);

        // 부모 메서드 안에서 모두 접근 가능
        defaultMethod();
        privateMethod();
    }
}

→ 자식 class 가 상속 받은 후 부모 class 의 변수와 메소드를 호출 해보면 🔽

// 자식 class 
package extends1.access.child;

import extends1.access.parent.Parent;

public class Child extends Parent {
    public void call() {
        publicValue = 1;
        protectedValue = 1; // 상속 관계 or 같은 패키지
        // defaultValue = 1; // 다른 패키지 접근 불가, 컴파일 오류
        // privateValue = 1; // 접근 불가, 컴파일 오류

        publicMethod();
        protectedMethod(); // 상속 관계 or 같은 패키지
        // defaultMethod(); // 다른 패키지 접근 불가, 컴파일 오류
        // privateMethod(); // 접근 불가, 컴파일 오류

        printParent(); // 부모의 public 메서드 내부에 있는 default, private 접근 제한자는
        // 상관없이 호출 가능하다
    }
}

→ 자식 class 객체를 생성 하여 작동 시켜 보면

package extends1.access;

import extends1.access.child.Child;

public class ExtendsAccessMain {
    public static void main(String[] args) {
        Child child = new Child();
        child.call();
    }
}

Parent.publicMethod
Parent.protectedMethod
==Parent 메서드 안==
publicValue = 1
protectedValue = 1
defaultValue = 0
privateValue = 0
Parent.defaultMethod
Parent.privateMethod

→ 이런 결과를 볼 수 있다.

→ 자식 class 의 객체를 생성 하면 위와 같이 생성되고 자식에서 없는 기능을 부모에서 찾으려 할 때 접근 제한자가 영향을 끼친다.


 

상속과 메서드 오버라이딩

  • 부모 타입의 기능을 자식에서 다르게 재정의 하고 싶을 때 메서드 오버라이딩을 사용해 재정의 할 수 있다.

메서드 오버라이딩

→ 사용 방법 🔽

package extends1.overriding;

public class Car {
    public void move() {
        System.out.println("차를 이동합니다");
    }

    // 추가
    public void openDoor() {
        System.out.println("문을 엽니다.");
    }
}
// 부모 class

→ 부모 class 를 상속 받아 move 메서드를 재정의 🔽

package extends1.overriding;

public class ElectricCar extends Car {
    @Override // <- 오버라이딩 했다는 표시 필수
    public void move() {
        System.out.println("전기차를 빠르게 이동합니다");
    }
    
    public void charge() {
        System.out.println("충전 합니다.");
    }
}
// 자식 class

→ 이런 식으로 자식 class 에서 재정의 하여 사용 가능하다.

메서드 오버라이딩시 구조

→ 자식class 에서 부모 class 의 기능을 overriding 하면 다음과 같은 구조를 갖는다.🔽

→ ElectricCar 객체를 생성시켜보면 🔽

ElectritcCar electricCar = new ElectricCar();
electricCar.move();

→ 다음과 같은 객체 메모리 영역에 부모 class 와 자식 class 구역이 생성 되고 move() 메서드도 동일 하게 생성 된다.🔽

→ 자식 class 를 호출 했기 때문에 자식 move() 메서드를 먼저 탐색하고 존재 한다면 자식의 기능을 사용하고 부모 타입은 찾지 않는다.

추가 - 오버로딩, 오버라이딩의 차이

  • 오버로딩 : 메서드의 이름은 갖지만 매개변수의 타입, 개수에 따라 다른 기능을 호출 하여 사용 하는 것을 오버로딩 이라고 한다.
  • 오버라이딩 : 자식 class 가 부모 class 의 기능을 재정의 하여 자식의 기능으로 덮어 씌우는 것을 말한다.

메서드 오버라이딩 조건

  • 메서드 이름이 동일
  • 메서드 의 파라미터 타입, 순서, 개수가 동일
  • 반환 타입이 동일
  • 상위 메서드 보다 하위 메서드보다 더 제한되면 안된다. ex) 상위 [ protected ] , 하위 [ public, protected ] O 상위 [ protected ] , 하위 [ private , default ] X
  • static, final, private 가 붙은 메서드는 오버라이딩 될 수 없다.
  • 생성자는 오버라이딩 할 수 없다.

'자바 공부 > [ 完 ] 자바 실전 - 기본편' 카테고리의 다른 글

8-2. 클래스와 메서드에 사용되는 final (상속 이해 필요)  (0) 2024.03.14
9-3 상속과 접근제어  (0) 2024.03.14
9-1 상속  (0) 2024.03.14
8-1. final  (0) 2024.03.12
7-4 static 메서드  (0) 2024.03.09

상속 - 시작

  • 공통되는 것을 묶어 부모를 만들고 그 하위에 공통 사항을 각각사용하며 다른 기능을 만드는 것을 제공 하기 위해 상속을 사용한다.

상속 관계

  • 상속은 객체 지향 프로그래밍의 핵심 요소 중 하나로 기존 클래스의 필드와 메서드를 새로운 클래스에 재사용하게 해준다.
  • 이름 그대로 기존 클래스의 모든것을 그대로 물려받는 것이다.
  • 사용하는 방법은 extend 키워드를 사용하면 된다.
  • 자식 클래스는 부모클래스를 하나만 상속 받을 수 있다.

상속 관계

  • 부모 클래스 (슈퍼 클래스) : 상속을 통해 자신의 필드와 메서드를 다른 클래스에 제공하는 클래스
  • 자식 클래스 (서브 클래스) : 부모 클래스로부터 필드와 메서드를 상속받는 클래스

상속 사용

  1. 자동차 속성을 갖는 부모 클래스 🔽
package extends1.ex2;

public class Car {
    public void move() {
        System.out.println("차를 이동합니다");
    }
}

  1. 부모 클래스로부터 상속 받는 전기차 클래스 🔽
package extends1.ex2;

public class ElectricCar extends Car{
    public void charge() {
        System.out.println("충전 합니다.");
    }
}

  1. 부모 클래스로부터 상속 받는 가솔린차 클래스 🔽
package extends1.ex2;

public class GasCar extends Car{
    public void fillUp(){
        System.out.println("기름을 주유 합니다.");
    }
}

  1. 전기 차 가스 차 의 기능을 구현하는 클래스
package extends1.ex2;

public class CarMain {
    public static void main(String[] args) {
        ElectricCar electricCar = new ElectricCar();
        electricCar.move();
        electricCar.charge();

        GasCar gasCar = new GasCar();
        gasCar.move();
        gasCar.fillUp();
    }
}

→ 전기 차 와 가스 차 에 move 메소드가 없지만 상속받으면 마치 자식의 메소드인것 처럼 사용 가능해진다.


상속과 메모리 구조

→ 다음과 같은 상속 구조도를 갖은 class 를 보자🔽

ElectricCar electricCar = new ElectricCar();

→ ElectritcCar 의 객체를 생성할때의 메모리 구조 상황을 보면 🔽

  • 객체를 생성 할 때에 상속 받은 부모의 기능도 ElectricCar 의 참조 값 안에 생성하게 된다.
  • 외부에서 볼때 하나의 인스턴스를 생성 하는거 같지만 내부에는 부모와 자식이 모두 생성 되고 공간이 구분 된다.
  • 실제로는 참조 값 안에 Car(부모), ElectricCar(자식) 이라는 두 가지 클래스 정보가 공존하게 된다.

→ 그럼 ElectricCar.charge() 를 호출 한 상황에서는

  • 본인의 타입에서 찾아보고 해당 기능이나 변수가 존재하면 바로 사용.

→ ElectircCar.move() 인 부모 기능을 호출

  • 우선 본인의 타입에 존재하는지 찾아보고 없으면 부모타입에서 찾아 기능을 꺼내 사용한다.
  • 상속 결론
    • 상속 받은 객체를 생성하면 그 내부에는 부모와 자식이 모두 생성 된다.
    • 상속 관계의 객체를 호출 할 때, 대상 타입을 정하고 이때 호출자의 타입을 통해 찾는다.
    • 현재 타입에서 기능을 찾지 못하면 상위 부모 타입의 기능을 찾아서 실행 한다.

'자바 공부 > [ 完 ] 자바 실전 - 기본편' 카테고리의 다른 글

9-3 상속과 접근제어  (0) 2024.03.14
9-2 상속과 메서드 오버라이딩  (0) 2024.03.14
8-1. final  (0) 2024.03.12
7-4 static 메서드  (0) 2024.03.09
7-3 static 변수  (0) 2024.03.09

fianl

  • final 키워드는 class, methid 를 포함한 여러 곳에 붙을 수 있다.
  • 하지만 지금은 변수에 붙는 final 키워드만 배우고 나머지는 상속을 배운 다음에 들을 예정

fianl 변수

  • final 키워드는 이름 그래도 끝! 이라는 뜻
  • 변수에 final 키워드가 붙으면 한번 값이 들어간 후에는 더는 값을 변경할 수 없다.

→ 지역변수에 fianl 을 사용한 경우 🔽

package final1;

public class FinalLocalMain {
    public static void main(String[] args) {
        // final 지역 변수
        final int data1;
        data1 = 10; // 초최 한번만 할당 가능
        // data1 = 20; // 컴파일 오류 발생

        // final 지역 변수2
        final int data2 = 10;
        // data2 = 20; // 컴파일오류
    }
}

→ 메서드의 매개변수에 final 을 사용한 경우 🔽

package final1;

public class FinalLocalMain {
    public static void main(String[] args) {
		    // 매개변수 주기
        method(10);
    }

    // final 을 배개변수로 사용할때
    static void method(final int parameter) { 
    // 인자로 전해 받기 때문에 무조건 초기값이 있다.
        // parameter = 20; // 컴파일 오류
    }
}

→ 클래스의 초기 값이 없는 멤버 변수에 final 을 사용한 경우 꼭 생성자로 초기 값을 지정해주어야 한다.🔽

package final1;

public class ConstructInit {

    final int value; // <- 초기 값이 없는 멤버변수 선언

    public ConstructInit(int value) {
        this.value = value; // <- 생성자 호출시 인자 값으로 초기화
    }
}
// 메서드 final 호출이랑 비슷해 보이네?? 생성자도 메서드여서 그런가??

(생성자 초기화 방식)

→ 클래스의 초기 값이 선언된 멤버 변수의 final을 사용한 경우 생성자로 값 설정을 못받는다.🔽

package final1;

public class FieldInit {

    final int value = 10; //<-이미 초기 값이 주어진 경우

// 생성자로 초기값 선언시 컴파일 오류를 발생 시킨다🔽
	//  public FieldInit(int value) {
	//      this.value = value;
	//  }
}

(필드 초기화 방식)

→ 이런식으로 필드 초기화를 하게 되면 동일한 값을 객체 생성시 마다 불러와 메모리를 낭비하게 된다.

이렇게 중복인 메모리 낭비를 막기위해 사용하면 좋은 것이 static 을 사용해 메서드 영역에서 관리 시키는 것이 좋다. 이런 static 과 fianl을 같이 사용한 방식을 사용한 변수를 상수 라고 한다.

상수

  • 변하지 않는 값
  • 변수 앞에 static final 이 같이 붙은 변수를 상수라고 한다.
  • 상수 변수 설정 규칙으로는 모두 대문자로 작성해주고 단어마다 언더라인 으로 구분해준다. EX) static final int *CONST_VALUE* = 10; 이런식으로 사용한다.

static - fianl

  • 변수를 상수로 만들어주기 위해 변수 앞에 두 개를 같이 사용한다
  • 이렇게 사용하면 공용 변수 이면서 바뀌지 않는 변수가 된다.
package final1;

public class FieldInit {

    static final int CONST_VALUE = 10;
	  // 상수 사용
}

package final1;

public class FinalFieldMain {
    public static void main(String[] args) {
        // 상수
        System.out.println("상수");
        System.out.println(FieldInit.CONST_VALUE);
    }
}

// 상수는 static (정적)변수 이므로 호출 시에도 class 명으로 바로 호출 한다.

  • 자바가 끝날때까지 상수는 변함없이 값을 유지하고
  • 메서드 영역에서 관리 하기 때문에 메모리 낭비를 막을 수 있다.

fianl 상수

  • 상수는 변하지 않고 일정한 값을 갖는 수를 말한다.
  • static final 두 키워드를 같이 붙여 사용한다.
  • 대문자를 사용하고 구분은 언더바로 구분 한다.
  • 상수는 기능이 아니기 때문에 필드를 직접 접근하여 사용한다.

→ 고정 된 값을 사용하는 예 🔽 (PI, 하루 총 시간 등등)

package final1;

public class Constant {
    // 수학 상수
    public static final double PI = 3.14;

    // 시간 상수
    public static final int HOURS_IN_DAY = 24;
    public static final int MINUTES_IN_HOURS = 60;
    public static final int SECONDS_IN_MINUTE = 60;

    // 애플리케이션 설정 상수
    public static final int MAX_USERS = 1000;
}

// 이런식으로 사용할 수 있다.
  • 상수들은 거의 애플리케이션전반에 사용 되기 때문에 public 을 주로 접근 제한자로 사용한다.
  • 상수를 변경하려면 프로그램을 종료하고 → 코드를 변경 해야 한다.

final 변수와 참조

  • final 은 변수의 밧을 변경하지 못하게 막는다.
  • 변수는 기본형과 참조형으로 나뉘는데
    • final 기본형 변수는 → 값을 변경할 수 없다.
    • final 참조형 변수는 → 참조값을 변경할 수 없다.

→ final 참조형 변수의 특징 🔽

package final1;

public class Data {
    public int value;
}
// final 이 아닌 멤버 변수를 갖는 class 를 만들고
package final1;
// 해당 클래스를 사용하는 class 를 보자
public class FinalRefMain {
    public static void main(String[] args) {
        final Data data = new Data(); // 참조형
        //data = new Data();
        // 참조형 을 이미 final 을 설정 했기 때문에 새로운 객체를 참조할 수없다.

        // 그러나, 참조 대상의 값은 final 이 아니기때문에 변경 가능하다.
        data.value = 10;
        System.out.println(data.value);
        data.value = 20;
        System.out.println(data.value);
    }
}

→ 결론 참조하는 대상 자체를 다른 대상으로 변경 못 할 뿐 해당 대상의 값은 변경 가능하다.


'자바 공부 > [ 完 ] 자바 실전 - 기본편' 카테고리의 다른 글

9-2 상속과 메서드 오버라이딩  (0) 2024.03.14
9-1 상속  (0) 2024.03.14
7-4 static 메서드  (0) 2024.03.09
7-3 static 변수  (0) 2024.03.09
7-2 스텍과 큐 자료구조  (0) 2024.03.09

static 메서드

  • static 메서드가 필요한 이유 예를 보자🔽
package static2;

public class DecoUtil1 {
    public String deco(String str) {
        String result = "*" + str + "*";
        return result;
    }
}

→ DecoUtil1 이라는 클래스에 deco 메서드만 있다고 가정 하고 이 기능만 사용하는 클래서가 있다고 보면.

package static2;

public class DecoMain1 {
    public static void main(String[] args) {
        String s = "hello Java";
        DecoUtil1 utils = new DecoUtil1();
        String deco = utils.deco(s);

        System.out.println("before: " + s );
        System.out.println("after: " + deco );
        
        String s2 = "hello world";
        DecoUtil1 utils2 = new DecoUtil1(); // <- 또 동일한 기능을 하는 객체를 생성 해야 한다.
        String deco2 = utils2.deco(s);

        System.out.println("before: " + s2 );
        System.out.println("after: " + deco2 );
    }
}

→ 동일한 기능을 사용하기 위해 매번 객체를 새로 생성해주는 번거로움이 발생한다.

→ 이렇게 매번 단순한 동일 작업(메서드)을 생략하기 위해 static 을 메서드에 붙여준다.

  • static 메서드를 사용한 예 🔽
package static2;

public class DecoUtil2 {
    public static String deco(String str) { // <- 메서드에 static을 붙여준다.
        String result = "*" + str + "*";
        return result;
    }
}

package static2;

public class DecoMain2 {
    public static void main(String[] args) {
        String s = "hello Java";
        String deco = DecoUtil2.deco(s);

        System.out.println("before: " + s );
        System.out.println("after: " + deco );

        String b = "static Method";
        String deco2 = DecoUtil2.deco(b); 
        // 2번째 객체 생성없이 바로 클래스 명에 메서드 호출이 가능해진다.
        System.out.println("before: " + b);
        System.out.println("after: " + deco2);
    }
}

→ 이렇게 사용하면 불필요한 객체 생성 없이 편리하게 메서드 사용이 가능해진다.

메서드의 종류

  • 클래스 메서드 : 메서드 앞에 staitc 이 붙은 형태로, 정적 메서드, 클래스 메서드라고 부른다. 인스턴스 생성 없이 마치 클래스에 있는 메서드를 바로 호출 하는 것 처럼 느껴진다.
  • 인스턴스 메서드 : static이 붙지 않은 메서드로 항상 인스턴스(객체)를 생성해야 호출할 수 있다.

static 메서드 사용 시 주의 사항

  • static 메서드는 static 이 선언된 것만 사용 가는하다
  • staitc 메서드가 class 내부의 기능을 사용할때에 → static 메서드는 static 이붙은 메서드나, 변수 만 사용 가능하다.
  • static 메서드는 인스턴스 변수, 인스턴스 메서드를 사용할 수 없다.
  • 접근 제어자가 허용하는 범위 내에서 클래스를 통해 staic변수,메서드를 호출할 수 있다.

static 메서드 결론

  • static 붙은 것만 staitc메서드에서 사용 가능 하고 stiatc 메서드 자신은 접근제한자 범위 내에서 마음껏 사용될 수 있다.

static 메서드 사용 범위 예 🔽

package static2;

public class DecoData {
    private int instanceValue;
    private static int staticValue;

    public static void staticCall() { // <- 정적 메서드

        // instanceValue++; // 인스턴스 변수 접근, compile error
        // instanceMethod(); // 인스턴스 메서드 접근, compile error

        staticValue++; // 정적 변수 접근 가능
        staticMethod(); // 정적 메서드 접근 가능
    }

    public void instanceCall() { // <- 인스턴스 메서드

        instanceValue++; // 인스턴스 변수 접근 가능
        instanceMethod(); // 인스턴스 메서드 접근 가능

        staticValue++; // 정적 변수 접근 가능
        staticMethod(); // 정적 메서드 접근 가능
    }

    private void instanceMethod() { // <- private 인스턴스 메서드
        System.out.println("instanceValue = " + instanceValue);
    }

    private static void staticMethod() { // private 정적 메서드
        System.out.println("staticValue = " + staticValue);
    }
}

→ 해당 코드를 작동 시켜 보면

package static2;

public class DecoDataMain {
    public static void main(String[] args) {
        System.out.println("1. 정적 호출");
        DecoData.staticCall();

        System.out.println("2. 인스턴스 호출1");
        DecoData data1 = new DecoData();
        data1.instanceCall();

        System.out.println("3. 인스턴스 호출2");
        DecoData data2 = new DecoData();
        data2.instanceCall();
    }
}

staticValue = 1 // <- static 변수는 메서드 영역에서 관리되므로 호출 하면 계속 증가 될것
2. 인스턴스 호출1
instanceValue = 1 // <- instance 변수는 힙 영역에 각각 생성 되므로 호출해도 계속 1 유지
staticValue = 2
3. 인스턴스 호출2
instanceValue = 1
staticValue = 3

// 다음과 같이 출력된다.

→ 그림으로 보면 다음과 같다 🔽


static 메서드의 용어 정리

  • 클래스에 정의한 메서드 (맴버 메서드)의 종류
    1. 인스턴스 메서드 : static이 안 붙은 것 → 객체(인스턴스)를 생성해야 사용할 수 있다.
    2. class 메서드(static 메서드) : static이 붙은 것 → 객체생성하지 않고 바로 class 에 접근 해 사용 가능하다.

static 메서드의 main() 메서드

  • main 메서드는 프로그램의 시작점이다.
  • main 메서드는 static 메서드 이므로 객체 생성없이 바로 실행이 된다.
  • 그래서 main 메서드에서 호출할 때는 static 메서드만 호출 했다.

'자바 공부 > [ 完 ] 자바 실전 - 기본편' 카테고리의 다른 글

9-1 상속  (0) 2024.03.14
8-1. final  (0) 2024.03.12
7-3 static 변수  (0) 2024.03.09
7-2 스텍과 큐 자료구조  (0) 2024.03.09
7-1 자바 메모리 구조  (0) 2024.03.09

static 키워드

  • static 키워드는 주로 멤버 변수와 메서드에 사용된다.

static 변수

  • 클래스의 인스턴스 변수와 다르게 여러곳에서 공유하는 목적으로 사용된다.
  • 특정 class 에 공용으로 함께 사용할 수 있는 변수를 만들기 위해 사용된다.
  • static변수라 할지라도 class의 멤버 변수임은 변함이 없다.
package static1;

public class Data3 {
    public String name;
    public static int count; // <- 이런식으로 맴버 변수에 붙여 사용한다.

    public Data3(String name) {
        this.name = name;
        count++;
    }
}

  • 멤버 변수에 staitc 을 붙이면 static변수, 정적 변수, 클래스 변수라 부른다.
  • static 변수에 접근하는 방식은 객체를 저장한 변수가 아닌 클래스 명을 호출하여 사용한다.
package static1;

public class DataCountMain3 {
    public static void main(String[] args) {
        Data3 data1 = new Data3("A");
        System.out.println("A count = " + Data3.count); // <- class 명 호출

        Data3 data2 = new Data3("B");
        System.out.println("B count = " + Data3.count); // <- class 명 호출

        Data3 data3 = new Data3("C");
        System.out.println("C count = " + Data3.count); // <- class 명 호출
    }
}

A count = 1
B count = 2
C count = 3
// 실행결과는 다음과 같다.
  • static 이 붙은 변수는 힙 영역이 아닌 메서드 영역에서 메서드와 같이 관리된다.

  • 최종적으로 위 코드가 작동 되면 다음과 같은 상태가 된다.

  • 그래서 static 변수에 접근하려면 메서드 영역에 접근 해야 하기 때문에 Data3.count 로 클래스에 직접 들어가야하는것 이다.

staitc 변수 사용 장점

  • static 변수를 사용함으로써 공용 변수를 사용해 편리하게 문제를 해결 할 수 있다.

static 변수 정리

  • static 변수는 클래스인 붕어빵 틀이 특별히 관리하는 변수인 붕어빵이다.
  • 붕어빵 틀(클래스)은 1개 이므로 붕어빵(static 변수)도 1개만 존재한다.
  • 반면 객체(인스턴스)로 생성한 변수들 (맴버 변수)은 객체를 생성한 만큼 변수가 존재하게 된다.


변수와 생명주기

  • 지역변수(매개변수 포함) : 스택 영역안에서만 활동 하고, 메서드가 종료되면 메서드 스택 프레임이 제거되고, 변수도 함께 제거 된다. → 가장 짧은 생명 주기
  • 인스턴스 변수 : 인스턴스에 있는 멤버 변수를 인스턴스라 하고, 인스턴스 변수는 힙 영역을 사용한다. CG가 발생하기 전까지 생존해 있다. → 중간 생명 주기
  • 클래스 변수 : 클래스 변수는 메서드 영역의 static 영역에 보관된다, 해당 class 가 JVM에 로딩 되는 순간 생성 되고, JVM이 종료되면 제거된다. → 가장 긴 생명 주기

static 변수가 정정 변수라고 하는 이유 ?

  • 힙 영역에 생성되는 인스턴스 변수는 동적으로 생성되고, 제거된다 반면 staitc 변수는 거의 프로그램 실행 시점에 딱 만들어지고, 프로그램 종료 시점에 제거된다. → 이런 특성 때문에 정적 변수라고 부른다.

static 변수 추가 사항

  • static 변수를 인스턴스 접근이 가능하나 권장하진 않는다.
// 추가
// 인스턴스를 통한 접근 (비 권장)
Data3 data4 = new Data3("D");
System.out.println("D count = " + data4.count);
  • 이유는 가져다 쓰는 사람 입장에서 힙 영역의 인스턴스 변수라 착각 할 수도 있기 때문이다.

'자바 공부 > [ 完 ] 자바 실전 - 기본편' 카테고리의 다른 글

8-1. final  (0) 2024.03.12
7-4 static 메서드  (0) 2024.03.09
7-2 스텍과 큐 자료구조  (0) 2024.03.09
7-1 자바 메모리 구조  (0) 2024.03.09
6. 접근 제어자  (0) 2024.03.07

+ Recent posts