언어/Java

[Java] 접근 제어자, 상속, is-a관계

차가운오미자 2021. 6. 25. 17:08

Access Modifier (접근 제어자)

- package 와 관련

- 총 4종류의 access modifier 존재

1. public (keyword로 존재)

접근에 제한이 없다, 패키지와 상관없이

class가 public이더라도, constructor가 public이 아니면, class에만 액세스 가능하고 다른 패키지에서 인스턴스 생성 불가(생성자 호출이 불가능해서)

따라서 기본 constructor(constructor를 코드에 명시하지 않은 경우 자동 삽입되는)는 public형임. 

2. protected (keyword로 존재)

3. default (keyword로 사용x, 다른 키워드가 없으면 기본 적용)

같은 패키지 내에서는 자유롭게 access 가능, 다른 패키지에서는 access 제한

public access modifier 아니면 (default이면) import 써도 다른 패키지에서 사용 불가

4. private (keyword)

그 클래스 안에서만 사용할 수 있음

사용:

- class 앞에 (단, public, default만 가능)

- field, method, constructor 앞에 등장 (네가지 다 가능)

- 필드 앞에 access modifier를 public으로 잡으면 안 좋다. 왜냐면, 인스턴스에게 필드의 데이터관리는 중요한데, 외부에서 액세스가 쉬우면 좋지 않기 때문이다.

- 따라서, private을 쓰는게 좋고, public, default 사용할 때는 이유가 있어야 한다.

- 즉, 다이렉트로 필드에 접근하게 하는게 아니라, method를 통해서 바꾸게 해야 좋음

 

상속 (inheritance)

자식 클래스가 부모 클래스의 속성을 물려받는다. (메소드와 변수들)

위: 상위 클래스, 부모 클래스, upper class, Super class(가장 일반적)

아래: 하위 클래스, 자식 클래스, child class, Sub class (가장 일반적)

사용

extends + 확장할 클래스

이를 통해 상위 클래스의 성질을 받을 수 있다.

package myCar;

public class Car {

  //field (private)
  int cc;
  double currentSpeed;
  String owner;

  //constructor
  public Car(){
  // class내부에 default constructor 만들어놓는 연습하기
  }

  //method (일반적으로 public, field값을 제어하는 역할이 일반적, 외부에서 인스턴스 활용하기 위해 method 호출)
  public void go(){

  }
  
   //getter, setter 메소드을 사용해서 class내 변수에 접근하는 경우가 많음.
}

class Sonata extends Car {
	
    int engineNumber;
    
    public Sonata(){
		// 상위 클래스의 instance를 생성 --> 상위 생성자의 생성자를 호출
		super(); // 상위 클래스의 생성자를 호출 (인자를 가지지 않는 생성자) 
        	// 이게 존재해야 한다. 만약 없으면 컴파일러가 자동 삽입한다.
	}
}


- 상속받은 cc, currentSpeed를 사용할 수 있다. (코드가 박히는 건 아니고 사용만 가능한 것)
- method도 사용할 수 있다고 생각하면 됨 (실제로 코드가 있는건 아님)
생성자는 상속이 되지 않음 (반환형 없고, 이름도 클래스랑 다르고, 상속되면 의미없는거만 됨)
private 이 붙은 애들도 상속되지 않음 (private은 그 클래스 안에서만 사용할 수 있는것)
- 한 번에 하나의 클래스로부터면 상속이 가능함
- 상속의 단점: 클래스 간의 관계가 밀접해져서 하나 바꾸면 다른 애도 받는 영향이 커짐 - 유지보수할 때 좀 그럼 (프로그래밍 할 때 클래스 간에 직접적인 연관성이 별로 없게 해야함)

 

is-a 관계

- 상속 관계에서 존재하는 관계이다. 상속이 이뤄지면 is-a관계도 새김

- 정의: subclass is a superclass (단, 역은 성립하지 않음)

- 의미: subclass의 class type을 써야하는 곳에 superclass의 class type을 쓸 수 있음
          물론 class type을 상위 클래스로 하는거랑 하위 클래스로 하는거랑 다르긴 함

Java에서 나오는 모든 class는 상속관계를 가지고 있다.
자바의 최상위 클래스는 java.lang.Object class이다. 

// --------------- Main.java ----------------- //
package myCar;

public class Main {

  public static void main(String[] args){

  	Sonata s = new Sonata();

  }
}

 

1. Main 이라는 class의 정보가 method area에 담김 (기본 생성자, main(){})
2. main method를 찾음 > method 호출 > stack에 main()을 위한 공간이 생김
3. Sonata라는 클래스명을 발견

  • Sonata라는 class 정보를 method area에 넣기 위해 Sonata class를 찾아간다.
  • Sonata가 Car의 상속을 받고 있음을 발견한다 > Car라는 class의 info가 필요하다 > Car 는 java.lang.Object의 상속을 받고 있다
  • Object class의 정보가 method area에 올라간다 > Car라는 클래스의 정보가 method area에 올라간다 > Sonata 클래스의 정보가 method area에 올라간다. (사실 Main 클래스가 Object의 상속을 받으므로 그 전에 method area에 들어가긴 하겠지만, 단지 상속의 관계를 표현 위해 이렇게 말함)

4. s는 stack 의main() 공간 안에 생긴다, s는 인스턴스를 가리킴킨다.
5. (사실 new Sonata()로 인스턴스 먼저 생기고, s의 공간이 생기고, 그 공간을 s가 가리키는 순서지만) 인스턴스를 만들기 위해 Sonata()라는 생성자를 찾으러 간다

6. Sonata안에 생성자를 찾는다(기본 생성자)

7. 인스턴스를 만들기 전에 상위 클래스가 있는지 확인한다.

8. 있으면 상위 클래스에 맞춰서 인스턴스를 먼저 만든다. 

 

이 과정에서 상위 클래스의 생성자가 필요한데, 생성자는 상속이 안됐기 때문에 호출이 불가능하다. 이런 상황을 위해서 super이라는 키워드가 존재한다. - super를 넣지 않으면 컴파일러가 자동으로 default형으로 넣어줌,


그리고 여기서 s라는 인스턴스를 만들기 전에 Car의 인스턴스를 만들어야 해서 super가 있기 때문에, super 위에는 뭐가 올 수 없다. 사실 인스턴스는 최상위 클래스까지 올라가서 그 인스턴스부터 차례대로 만드는 것이다. 


9. Heap에 Object의 인스턴스 생성된다.

10. Car에 대한 인스턴스가 만들어진다. (Object 인스턴스를 포함하고, cc, currentSpeed, owner을 위한 공간이 생긴다. go() 메소드의 실행포인트도 생긴다)

11. Sonata의 인스턴스가 생성(Car 인스턴스 포함, engineNumber공간 생긴다) 그리고 이 sonata 인스턴스를 Stack에 있는 s가 가리킴

근데 만약

 

// ---------------- Main.java ----------------- //
package myCar;

public class Main {

  public static void main(String[] args){

    Car s = new Sonata();

  }
 }


이면, s가 가리키는게 Car instance이다. 이렇게 선언하게 되면 engineNumber공간을 사용할 수 없다. 즉, 전체 Sonata인스턴스가 아닌 Car인스턴스만 사용할 수 있는 것이다. 즉, 인스턴스를 여러 형태로 사용할 수 있다.

이런 특성을 다형성이라고 한다. 


후에 s가 가리키는 걸 (type을 바꾸어서) 바꿀 수 있다.