본문 바로가기
Study/JAVA

[JAVA] 7주차_추상 클래스와 인터페이스 (접근지정자, final과 객체의 타입 변환)

by 8희 2022. 4. 14.

1차시 접근지정자

 

 


접근 지정자의 접근 범위

 

접근지정자의 종류: public protected default(없음) private

 

1. public (공개)

- 동일 패키지의 클래스, 자식 클래스 접근 가능

- 다른 패키지의 클래스, 자식 클래스 접근 가능

- 그냥 public은 공개된 접근 지정자라서 다 접근 가능

 

2. protected (상속 관계에서 사용)

- 상속 관계라면 패키지가 다르더라도 접근 가능

- 동일 패키지의 클래스, 자식 클래스 접근 가능

- 다른 클래스의 자식 클래스 접근 가능 (상속된 거니까!)

- 다른 패키지의 클래스 접근 불가능

 

3. default (없음) (접근지정자 생략!)

- 같은 패키지에선 접근 가능

- 동일 패키지의 클래스, 동일 패키지의 자식 클래서 접근 가능

- 다른 패키지의 클래스, 자식 클래스 접근 불가능

 

4. private

- 자기자신의 클래스에서만 접근 가능

- 동일 패키지의 클래스, 자식 클래스, 다른 패키지의 클래스, 자식 클래스 모두 접근 불가능

- private 접근지정자로 선언된 멤버변수와 메서드는 설정자와 접근자를 통해서 접근

 

접근 지정자 사용 시 주의 사항

 

1. private 멤버는 자식 클래스에 상속 X

2. 클래스 멤버는 어떤 접근 지정자로도 지정할 수 있지만, 클래스는 protected와 private으로 지정 불가능

3. 메서드를 오버라이딩할 때 부모 클래스의 메서드보다 가시성을 더 좁게 할 수는 없다.

= 접근지정자를 축소할 수 없다! = 접근지정자를 동일하게 하거나 확장하는 것은 가능하다.


접근 지정자의 접근 범위 예시

 

 

/* 각종 접근 지정자를 사용하는 클래스 (기준) */

package sec06;

public class One {
	private int secret = 1; //private 접근 지정자
	int roommate = 2; //default 접근 지정자
	protected int child = 3; //protected 접근 지정자
	public int anybody = 4; //public 접근 지정자

	public void show() { //멤버 메서드
	}
}
/* 동일 패키지 (sec06)에 있는 자식 클래스 */

package sec06;

//같은 패키지기 때문에 import문 쓸 필요 x
public class One1 extends One { 
	//상속 관계니까 객체 만들 필요 x
	void print() { //default 접근지정자
		// System.out.println(secret); //private이니까 상속 자체도 안 됨
		System.out.println(roommate); //실행 가능 (default 접근 지정자)
		System.out.println(child); //실행 가능 (protected 접근 지정자)
		System.out.println(anybody); //실행 가능 (public 접근 지정자)
	}

	// void show() { //에러남. 가시성이 달라진 것!
	// } 
    
   	 //부모는 public인데 default로 가시성을 좁힌 거임 그래서 오류남
}	//오버라이딩 할 거면 public해야 됨
	//확장도 가능하긴 한데 public은 어차피 최상위니까 더이상 확장 X
    
    public void show() { //부모 메서드와 같은 접근 지정자를 사용해서 오류 X
    }
    
	//예를 들어 부모에서 접근 지정을 default로 하면 여기서는 default public protected 가능
	//private만 안 됨
/* 동일 패키지에 있는 클래스 (상속 관계는 X) */

package sec06;

public class Two { //상속 관계 없음
	//상속 관계가 없으므로 객체를 이용해서 접근해야 함
	void print() { //One 클래스에 접근
		One o = new One(); //클래스가 public이니까 클래스를 이용해 객체 생성 접근 가능
        
		// System.out.println(o.secret); //private라서 접근 불가능 애초에 상속 관계도 안 됨
		System.out.println(o.roommate);
		System.out.println(o.child);
		System.out.println(o.anybody);
	}
}
/* 다른 패키지 (sec06.other)에 있는 자식 클래스 */

package sec06.other;

import sec06.One; //다른 패키지에 있는 클래스 상속하기 위해서 import 문 꼭 필요!

public class One2 extends One { //상속 관계
	void print() {
		// System.out.println(secret); //private이니까 상속 자체도 안 됨
		// System.out.println(roommate); //default는 패키지가 다르면 안 됨
		System.out.println(child); //protected니까 상속 관계면 가능
		System.out.println(anybody); //public은 그냥 다 가능
	}
}
/* 다른 패키지 (sec06.other)에 있는 클래스 (상속 관계는 X) */

package sec06.other;

//import 문 생략 불가능, import 문 생략하면 sec06에 있는 클래스 사용 X
//클래스 사용 X인 경우 객체 생성 X -> 11번 라인에서 오류 발생
import sec06.One;

public class Three { //독립된 클래스에선 반드시 객체를 통해서 접근해야 함
	void print() {
		One o = new One();
		// System.out.println(o.secret);  //private이니까 x
		// System.out.println(o.roomate); //같은 패키지 x
		// System.out.println(o.child); //상속 관계 x
		System.out.println(o.anybody);
	}
}

2차시 final과 객체의 타입 변환

 


final 클래스

- 더이상 상속될 수 없는 클래스

- 대표적인 final 클래스로는 String 클래스

 

//class ChildString extends String {} //String은 final 클래스로 부모 클래스가 될 수 없음

class Good { //부모 클래스
}

class Better extends Good { //자식 클래스
}

final class Best extends Better { //자식 클래스 //final 클래스로 지정되어 더이상 상속 X
}
// class NumberOne extends Best {} //클래스 선언 불가능

public class FinalClassDemo {
	public static void main(String[] args) {
		// new NumberOne();
		new Best();
	}

 

final 메서드

- final 클래스는 클래스 내부의 모든 메서드 오버라이딩 불가능

- 오버라이딩은 상속 관계에서만 가능하므로 메서드 앞에 final이 붙으면 오버라이딩 불가능!

 

final 필드

- 상수: 변경할 수 없는 데이터를 담는 변수

- 상수 PI를 선언하고 싶으면 final double PI = 3.14;

 

class Chess { //부모 클래스
	enum ChessPlayer { //enum 열거형
		WHITE, BLACK
	}

	final ChessPlayer getFirstPlayer() { //final 메서드
		return ChessPlayer.WHITE; //WHITE 반환
	}
}

class WorldChess extends Chess { //Chess 클래스를 상속한 자식 클래스
	// ChessPlayer getFirstPlayer() {} //ChessPlayer()는 final 메서드이기 때문에
    								   //오버라이딩 불가능! 
}

public class FinalMethodDemo { //메인 메서드를 갖는 public 클래스
	public static void main(String[] args) { //메인 메서드
		WorldChess w = new WorldChess(); //w 객체 생성
		//w.getFirstPlayer();
		System.out.println(w.getFirstPlayer()); //WHITE 출력
	}
}

 


객체의 타입 변환

- 참조 타입인 객체도 상속 관계일 경우, 기초 타입 데이터처럼 타입 변환 가능 

- 객체 타입 변환도 자동 타입 변환과 강제 타입 변환이 있다.

 

자동 타입 변환 - Upcasting (업 캐스팅)

- 업캐스팅하는 이유: 공유하기 위해서!

- 업캐스팅을 하는 경우, 부모 타입의 멤버는 부모, 자식 모두가 볼 수 있으나 

  자식 타입의 멤버는 자식에서만 볼 수 있음

- 즉 부모가 바라보는 영역은 자기자신으로 한정, 자식이 바라보는 영역은 부모에서 자식까지

/* 업 캐스팅 예시 */

public class UpcastDemo {
	public static void main(String[] args) {
		Person p; //객체 p 생성 - new 안 했으니까 아직 공간은 확보 X 
		Student s = new Student(); //객체 s 생성 - new 해서 메모리 할당 받음
								   //오른쪽에서 생성된 Student() 크기 만큼이 s가 바라보는 크기
		
		p = s; //타입(형)이 맞지 않음. 왼쪽은 부모, 오른쪽은 자식
			   //자동으로 타입 변환, 업캐스팅 발생!
			   //p=(Person)s와 동일
		
		// p.number = 1; //number와 work()는 부모 타입에 없는 멤버이므로
		// p.work(); //부모 타입 변수에서 볼 수 없음
		
		p.whoami();
	}
}

 

강제 타입 변환 - DownCasting (다운 캐스팅)

- 적어도 한 번의 업캐스팅이 되어야 다운캐스팅이 가능!

Person p = new Person(); //객체 p 생성
Student s = (Student) p; //전자는 자식, 후자는 부모로 타입이 다름
			//강제로 타입 변환을 하면 오류 발생 = 업캐스팅을 안 해서 오류 발생
                         //오류 발생하지 않으려면
                         //1번 라인을 Person p = new Student();로 수정
/* 다운 캐스팅 예시 */

public class DowncastDemo {
	public static void main(String[] args) {
		Student s1 = new Student(); //객체 생성 (타입 같음)
		Person p = s1; //업캐스팅 //전자는 부모, 후자는 자식
		//p가 자식의 공간을 바라보진 않지만 기억은 하고 있기 때문에 다운 캐스팅 가능
		Student s2 = (Student) p; //다운캐스팅 //강제 타입 변환
	}
}

 

타입 변환된 객체의 구별

 

(참조) 변수 instanceof(boolean 값 반환) 타입(클래스 이름 혹은 인터페이스 이름)

public class InstanceofDemo {
	public static void main(String[] args) {
		Student s = new Student(); //객체 생성
		Person p = new Person(); //객체 생성

		System.out.println(s instanceof Person); //s에서 Person 클래스가 보이는지 //true

		System.out.println(s instanceof Student); //true
 
		System.out.println(p instanceof Student); //false

		// System.out.println(s instanceof String); //상속 관계에서만 타입 이야기 가능

		downcast(s);
	}

	static void downcast(Person p) { //업캐스팅
		if (p instanceof Student) {
			Student s = (Student) p; //다운캐스팅
			System.out.println("ok, 하향 타입 변환");
		}
	}
}

 

타입 변환을 이용한 다형성

/* 오버라이딩 여부에 따른 타입 변환의 영향 */

class Vehicle { //부모 클래스
	String name = "탈 것";

	void whoami() {
		System.out.println("나는 탈 것이다.");
	}
	
	//static 키워드가 붙으면 업캐스팅 발생 시, 부모 것을 실행
	static void move() { //static 키워드가 붙은 메서드
		System.out.println("이동하다.");
	}
}

class Car extends Vehicle { //Vehicle 클래스르 상속한 자식 클래스
	String name = "자동차";

	void whoami() { //메서드 오버라이딩
		System.out.println("나는 자동차이다.");
	}

	static void move() {
		System.out.println("달리다.");
	}
}

public class OverTypeDemo {
	public static void main(String[] args) {
		Vehicle v = new Car(); //업캐스팅
		System.out.println(v.name); //Vehicle의 name 필드 접근 //필드는 오버라이딩 X
		v.whoami(); //메서드 오버라이딩했으므로 자식 메서드 실행 //나는 자동차이다.
		v.move(); //정적 메서드이므로 부모 객체의 메서드 호출 //이동하다.
	}
}