본문 바로가기
포스코x코딩온

[포스코x코딩온] 풀스택 부트캠프 18주차 정리 Java(Class)

by 김선지 2024. 2. 19.

Class

객체지향 프로그래밍으로 유명한 언어답게 뭔가 strict하다.

정말 간단하게 말하자면 field (js의 property), method(js의 method)로 이루어져 있다고 말할 수 있을 것 같다.

(constructor는 method니까)

public class ClassName {
    // 필드 (변수)
    private String name;
    private int number;

    // 메소드 (함수)
    // 생성자 constructer-> 클래스명과 동일한 이름
    // 기본생성자(defualt)는 구현하지 않더라도 자동으로 생성되서 객체를 만들어준다. => 생략 가능.
    public ClassName(String name, int number) {
        this.name = name;
        this.number = number;
    }

    public void testMethod() {
        System.out.println("I'm felling pretty good!");
    }

    @Override
    // class의 최상위 클래스인 Object 클래스에 있는 toString 오버라이딩
    // 원래는 클래스 이름 + hashcode로 이루어져있다.
    public String toString() {
        return "마이 네임: " + name + "/ 마이 넘버" + number;
    }

    public static void main(String[] args) {
        ClassName newClass = new ClassName("지훈", 56);
        System.out.println(newClass);
        newClass.testMethod();
        // 현재 클래스 안의 메인메소드이므로 private 접근 가능.
        System.out.println(newClass.name);
    }
}

 

constructor에 대한 부가 이해

  - constructor는 그냥 함수다. 함순데 말 그대로 새로운 인스턴스를 생성해주고 그에 대한 객체 주소 (toString)을 반환해준다.

ClassName newClass = new ClassName("지훈", 56);

해당 코드의 경우에는 ClassName이라는 type의 newClass라는 변수에다가 new를 통해서 새롭게 만든 ClassName이라는 인스턴스의 주소값을 넣겠다는 뜻이다.

 


접근 제어자

public - 같은 프로젝트 안이면 접근 가능

protected - 같은 패키지 안이면 접근 가능, 다른 패키지라도 해당 패키지를 extends하면 사용 가능.

default - 안쓰면 이걸로 적용, 동일 패키지라면 접근 가능.

private- 클래스 내부에서만 사용 가능. (필드의 경우 private로 하고 setter와 getter를 public으로 이용하는 게 일반적)

 

 

 

여기서 protected가 좀 궁금해서 실험좀 해봤다.

 

pack3 패키지에 있는 class A

package _05_class._02_access_modifier._pack3;

public class A {

    protected void method4() {}

}

 

pack4 패키지에 있는 class C (A클래스의 method 4 호출 가능)

package _05_class._02_access_modifier._pack4;

// A와 C는패키지가 다르므로 import 해야함.
import _05_class._02_access_modifier._pack3.A;

public class C extends A {

    public static void main(String[] args) {
        C c = new C();
        c.method4(); //  protected기 때문에 extends A이므로 호출 가능,
    }
}

 

pack4 패키지에 있는 class D (C instance를 만들고 method4를 호출) (A 클래스의 method 4호출 불가능)

package _05_class._02_access_modifier._pack4;

public class D {
    public static void main(String[] args) {
            C c = new C();
            c.method4(); // comfile Error

    }
}

 

이렇게 된다.

protected는 다른 패키지라도 해당 패키지를 extends하면 사용 가능하다고 되어있고, 이는 사실이다.

다만 그 Class 안에서만 사용 가능하고 만약에 extends한 클래스를 다른 클래스에서 불러온다면 사용할 수 없는 것 같다.

(자신을 이해시키자면 extends한 클래스에서는 protected가 아닌 private이 된다고 생각하면 될 것 같다.)

그래서 조금 꼬아서 생각해봤는데 이건 된다.

 

pack4 패키지에 있는 class C

package _05_class._02_access_modifier._pack4;

// A와 C는패키지가 다르므로 import 해야함.
import _05_class._02_access_modifier._pack3.A;

public class C extends A {
    @Override
    public void method4() {
        super.method4();
    }

    public static void main(String[] args) {
        C c = new C();
        c.method4(); // 당연히 호출 가능
    }
}

 

pack4 패키지에 있는 class D

package _05_class._02_access_modifier._pack4;

public class D {
    public static void main(String[] args) {
            C c = new C();
            c.method4(); // 호출 가능

    }
}

 


Static (field, method)

 

정적 (static) 멤버

모든 인스턴스가 하나의 static 값을 공유한다고 생각하면 된다.

일반 non static의 경우 instance별로 각각의 field나 method를 가진다.


- 객체를 생성할 필요 없이 클래스 통해서 바로 접근 가능
- 클래스 메모리에 로딩되면 정적 멤버 바로 사용 가능
- 클래스 이름과 함께 dot(.) 접근법으로 사용 가능
- 정적 메소드 정적 블록은 객체가 없어도 사용 가능하므로 내부 필드와 메소드 사용 불가.
- 고로 this 키워드도 사용 불가
- but, non static method는 static method에 접근 가능함.

- 참고)
정적 필드는 객체 생성 없이도 사용 가능하므로 생성자에서 초기화 작업을 하지 않음
 => 생성자는 객체 생성 후에 실행되기 때문

정적 메소드
plus, minus 외부에서 주어진 값으로 처리하므로 정적 메서드로 처리하는 것이 유리
: 메소드에서 외부 인자를 받아서 실행하므로

 

Final

js의 const 와 똑같다고 생각하면 된다. 주소값이 바뀌지 않는 내에서 객체 값이 바뀌는 건 상관 없다.

다만 거기서 더 나아가서

Final method의 경우 오버라이딩을 할 수 없고

Final class의 경우 extends를 할 수 없다.

 


abstract Class

extends를 염두에 둔 클래스 틀이라고 생각하면 될 것 같다.

abstract를 붙이면 자식 클래스에서 따로 오버라이딩을 하거나, 그 자식 클래스에게 다시 abstract로 넘길 수 있다.

다만 abstract class의 경우 new연산자를 통해 만들 수 없다. 그냥 extends용 클래스다.

package _05_class._07_interface;

public abstract class Vehicle {
    private String name;
    private int maxSpeed;

    public Vehicle(String name, int maxSpeed) {
        this.name = name;
        this.maxSpeed = maxSpeed;
    }

// move abstract method로 선언
    public abstract void move();
}

 

 

package _05_class._07_interface;

public class Car extends Vehicle{
    public Car(String name, int maxSpeed) {
        super(name, maxSpeed);
    }

    @Override
    public void move() {
        System.out.println(getName() +" 도로를 따라 이동중");
    }
}

Interface

package _05_class._07_interface;

// interface 선언
public interface RemoteControl {
    // 상수 필드
    // - 인터페이스에 선언된 필드는 모두 ublic static final 특성을 갖는다.
    // 생략하더라도 컴파일 과정에서 자동으로 붙는다.
    // - 상수명은 대문자로 작성, 언더바로 연결
    int MAX_VOLUME = 10;
    int MIN_VOLUME = 0;

    // public abstract를 안적어도 알아서 적용됨
    public abstract void turnOn();
    void turnOff();

    // static method, field도 사용 가능.
    static void kimchi() {
        System.out.println("kimchi");
    }

    void setVolume(int volume);
}

package _05_class._07_interface;

public class RemoteControlRunner {
    // interface도 하나의 타입이므로 변수의 타입으로 사용 가능
    // - interface는 참조 타입 -> null 대입 가능
    public static void main(String[] args) {

    RemoteControl rc; // null
        // rc 변수에 Tv 객체를 대입
        rc = new Tv();
        rc.turnOn();
        RemoteControl.kimchi();
        rc.setVolume(37);
        rc.turnOff();

        rc = new Audio();
        rc.turnOn();
        RemoteControl.kimchi();
        rc.setVolume(-37);
        rc.setVolume(3);
        rc.turnOff();
    }

}

 

abstract class VS interface

abstract class는 메소드를 바로 implement 할건지 다음 자식에게 넘겨줄건지 (또 abstract method로 할건지) 선택할 수 있지만,

interface는 전부 바로 구현해야한다. 또한 interface에서 field는 무조건 상수만 들어올 수 있다. (public final)