티스토리 뷰
1. Final 클래스와 final 메소드
- 상속할 수 없는 final 클래스 : 자식 클래스를 만들지 못하도록 final 클래스로 생성한다.
- 오버 라이딩 불가능한 final 메소드 : 자식클래스가 재정의 할 수 없도록 부모 클래스의 메소드를 final로 생성한다.
- Final 키워드의 용도 :
- 1 ) final 필드 : 수정이 불가능한 필드
- 2 ) final 클래스 : 부모로 사용이 불가능한 클래스
- 3 ) final 메소드 : 자식이 재정의할 수 없는 메소드
2. Protected 접근제한자
- Protected 접근제한자 = default 접근제한자 + alpha
- 같은 패키지 내에서는 default 접근제한자와 같이 허용되지만
- 다른 패키지에서는 default와 다르게 자식 클래스에서는! 접근이 허용된다.
- 클래스 생성시 superclass를 활용하여 부모를 선택할 수 있다.
3. 요약
- 1 ) 부모객체의 필드와 메소드 중에 상속이 불가능한 조건 2가지!
- ( 1 ) private 접급제한자를 가지는 필드와 메소드는 상속이 불가능 하다.
- ( 2 ) 다른 패키지에 자식 클래스가 있을 때, default 접근제한을 가지는 부모객체의 필드. 메소드 역시 접근이 불가능하다.
- 2 ) 다형성 ( OOP의 가장 핵심 개념 )
- ( 1 ) 목적 : 부품관계의 필드에 실제 부품객체를 조립할 수 있는 수단이다.
- ( 2 ) 전재 조건 : 부모 / 자식 관계의 상속관계를 요구한다.
- ( 3 ) 위 2개의 전재조건이 이루어진다면, 부모타입 참조변수 = 자식객체;가 성립된다.
- ( 4 ) 또한 상속받은 부모객체의 메소드를 재정의했다면, 부모객체.메소드( )를 통해 자식객체에서 재정의된 메소드가 호출된다!
- + 부모객체가 자식객체보다 먼저 생성되어야 한다.
4. 프로모션 ( 자동형변환 )
- 프로그램 실행 도중에 자동 타입 변환이 일어나는 것을 말한다.
- 바로 위의 부모가 아니더라도 상속 계층의 상위이면, 자동타입변환 가능하다.
- 변환 후에는 부모 클래스 멤버에만 접근이 가능하다.
01. final 클래스
package Practice;
public final class Member { // final 클래스 - 자식 생성이 불가능 하다.
;;
} // end class
package Practice;
//public class VeryVeryImportantPerson extends Member { // Member가 final 클래스이기에 상속이 불가능하다.
public class VeryVeryImportantPerson {
;;
} // end class
02. final 메소드
package PCar;
public class Car { // final 메소드 - 오버라이딩 X
// 1. 상태 필드
public int speed;
// 2. 인그턴스 메소드
public void speedUp() {
speed += 1;
} // speedUP
// 3. final 메소드
public final void stop() {
System.out.println("차를 멈춤");
speed = 0;
} // stop
} // end class
package PCar;
public class SportsCar extends Car {
@Override // 자바컴파일러가 컴파일 시에, 이 메소드가 재정의 규칙에 맞게 되었는지 강하게 검사
public void speedUp() {
speed += 10;
} // speedUp
// final 메소드는 오버라이딩 할 수 없음
// @Override
// public void stop () {
// System.out.println("스포츠카를 멈춤");
// speed = 0;
// } // stop
} // end class
03. protected 접근제한자
package org.zerock.myapp.p1;
public class A { // protected 접근제한자
protected String field;
protected A () {
;;
} // default constructor
protected void method () {
System.out.println("A::method() invokes");
} // method
} // end class
package org.zerock.myapp.p1;
public class B { // + 상속관계는 아니다!!
public void method () {
A a = new A(); // protected method를 가져오기 위한 객체 생성
a.field = "뭐야"; // 불러오기 성공!
a.method(); // 불러오기 성공!
} // method
} // end class
package org.zerock.myapp.p2;
import org.zerock.myapp.p1.A;
public class C {
public void method () {
// A a = new A ( );
// 클래스 자체는 public이어서 접근은 가능하지만, 객체를 생성하기 위한 생성자가 A클래스에서 protected로 막혀 있다.
// a.field = "뭐야";
// a.method();
// 동일한 이유로 필드나 메소드 불러오기도 불가능 하다.
} // method
} // end class
package org.zerock.myapp.p2;
import org.zerock.myapp.p1.A; // import문 까먹지 말자!!!
public class D extends A { // D가 A를 extends한다.
public D () { // protected 불러오기 (****)
// protected는 자식 생성자 내부에서 super을 통한 부모를 생성한 후에 사용할 수 있다!!(*****)
super (); // 부모 생성자 생성
// 부모 객체 생성은 생성자(constructor) 내에서 생성이 가능하지만!
// 1번 부모객체를 생성했다면, 밑의 메소드에서도 사용이 가능하다.
// 단, 부모가 가지고 있는 모든 필드와 메소드를 가지고 오는 것이 아니라,
// 다른 패키지나 다른 클래스에서 접근이 불가능한 private와 default는 상속 대상에서 제외된다. (***)
// ============================================================================
// protected의 진짜 alpha의 의미!!! (*****)
this.field = "Yoseph"; // OK!
this.method(); // OK!
} //default constructor
void method2() {
// A a = new A(); // protected 접근제한자의 경우, 생성자에서만!! 부모객체 생성이 가능하다!!
// 이때는 부모자식관계로 생성한 것이 아니라, 단순히 A클래스 객체를 생성한 것이다.
this.field = "머"; // OK!
this.method(); // OK!
// 출력 : D::method3() invoked
// ============================================================================
super.field = "뭐인가여"; // 부모 객체의 필드를 불러오는 것!! " super. " 기억하기!! (***)
super.method(); // 부모 객체의 메소드를 불러오는 것
// 출력 : A::method() invokes
// ============================================================================
// 자식 클래스가 부모 클래스로부터 필드와 메소드를 상속받게 될 시, (****)
// 부모 객체의 필드와 메소드가 사라지는 것은 아니다.
// this. 은 상속받은 필드와 메소드를 사용하겟다는 의미이고,
// super. 은 부모 객체에 있는 필드와 메소드를 그대로 쓰겠다는 의미이다.
// 이는 오버 라이딩할 때 주의해서 사용해야 한다.
// 재정의를 할때, this.을 사용하면 재정의한 것이 super.을 활용하면 부모객체의 오리지날이 나타난다.
} // method2
@Override // 오버 라이딩
protected void method() {
System.out.println("D::method3() invoked");
} // method3
public static void main (String[] args) {
D d = new D ();
d.method2();
} // main
} // end class
04. protected 접근제한자의 응용
package org.zerock.myapp.p1;
public class Parent {
protected String name = "Cho";
protected Parent() {
System.out.println("Parent::default constructor invoked.");
} // default constructor
protected void parentmethod() {
System.out.println("Parent::parentmethod invoked.");
} // parentmethod
} // end class
package org.zerock.myapp.p1;
public class child extends Parent {
child() {
super(); // OK!
System.out.println("Child::default constructor invoked.");
System.out.println("\t + name : " + this.name);
this.parentmethod();
super.parentmethod();
} // default constructor
void method () {
System.out.println("Child::method invoked.");
Parent parent = new Parent();
System.out.println(parent);
System.out.println("\t + name : " + super.name);
this.parentmethod();
super.parentmethod();
} // method
{
Parent parent = new Parent();
System.out.println(parent.name);
parent.parentmethod();
} // User - defined block
// =======================================================================
public static void main (String[] args) {
child child1 = new child ();
child1.method();
// =====================================================================
// 출력 결과 :
// Parent::default constructor invoked.
// Parent::default constructor invoked.
// Cho
// Parent::parentmethod invoked.
// Child::default constructor invoked.
// + name : Cho
// Parent::parentmethod invoked.
// Parent::parentmethod invoked.
// Child::method invoked.
// Parent::default constructor invoked.
// org.zerock.myapp.p1.Parent@372f7a8d
// + name : Cho
// Parent::parentmethod invoked.
// Parent::parentmethod invoked.
} // main
} // end class
package org.zerock.myapp.p1;
public class Other {
Other() {
// 사용관계
Parent parent = new Parent();
System.out.println(parent.name);
parent.parentmethod();
// Other은 부모자식관계가 아니어서 super을 사용할 수는 없지만
// 같은 클래스 내에 있기 때문에 사용관계로 사용할 수 있다.
// this.parentmethod();
// super.parentmethod();
// 컴파일 오류 발생 : 부모자식관계이가 아니기에 this.이 불가능하다. 호출메소드는 상속된 것이 아니다.
} // default constructor
void OtherMethod () {
// 사용관계
Parent parent = new Parent();
System.out.println(parent.name);
parent.parentmethod();
// 호출메소드는 상속된 것이 아니다!!
// this.parentmethod();
// super.parentmethod();
} // OtherMethod
} // end class
05. protected 접근제한자의 응용 - 상속관계에 따른
package org.zerock.myapp.p2;
import org.zerock.myapp.p1.Parent;
public class Child2 { // 상속관계가 없을 때의 protected
Child2 () {
// Parent parent = new Parent (); // 오류발생 : protected이기는 하지만 부모자식관계가 형성되지 않았기에 오류가 발생한다.
// parent.name
// parent.parentmethod();
} // default constructor
void Child2Method () {
// Parent parent = new Parent (); // 오류발생
;;
} // Child2Method
{
// Parent parent = new Parent (); // 오류발생
} // User - defined block
} // end class
package org.zerock.myapp.p2;
import org.zerock.myapp.p1.Parent;
// protected = default + alpha 공식에서
// 이미 (1) default 접근제한은 통과하지 못하고
// (2) alpha 예외 조건으로 접근이 가능한가?
public class Child3 extends Parent { // 상속관계가 있을때의 protected
Child3( ) {
super(); // OK!
// Parent parent = new Parent(); // 오류 : 부모 클래스의 객체가 아직 초기화되지 않았다.
// 다른 패키지에서 Parent를 생성하기 위해서는 먼저, 부모 객체를 호출한 후에 사용이 가능하다.
// protected 생성자의 의미 :
// 이 클래스에서는 자식 객체의 생성자가 호출이 될 때, 부모객체의 초기화가 먼저 수행되어야 하는데
// 이때 부모클래스의 생성자가 protected일때, 과연 부모생성자 호출이 가능한가?
// 결과 : protected = default ( X ) + alpha ( O )
// 즉, Parent parent = new Parent(); 이렇게 호출하는 것이 아니라, super();로 불러오라는 이야기이다.
} // default constructor
void Child3Method() {
// Parent parent = new Parent(); // 오류 : 부모 클래스의 객체가 아직 초기화되지 않았다.
super.parentmethod(); // OK!
this.parentmethod(); // OK!
} // Child3Method
} // end class
06. 다형성 ( polymorphism ) - 타이어 교체
package PracticePolymorphism;
public class Tire {
// 1. 고유필드
public int maxRotation; // 최대 회전수 ( 최대 수명 )
public String location; // 타이어의 위치
// 2. 상태필드
public int accumulatedRotation; // 누적 회전수
//3. 생성자
public Tire (String location, int maxRotation) {
this.location = location;
this.maxRotation = maxRotation;
} // Constructor
// 4. 메소드
public boolean roll() {
++ accumulatedRotation;
if(accumulatedRotation < maxRotation) {
System.out.println(location + " Tire 수명 : " + ( maxRotation - accumulatedRotation ) + "회");
return true;
} else {
System.out.println("***" + location + "Tire 펑크 ***");
return false;
}// if - else
} // roll method
} // end class
package PracticePolymorphism;
public class KumhoTire extends Tire {
// 1. 필드
// 2. 생성자
public KumhoTire ( String location, int maxRotation) {
super(location, maxRotation);
} // constructor
// 3. 메소드
@Override
public boolean roll () {
++ accumulatedRotation;
if ( accumulatedRotation < maxRotation) {
System.out.println(location + " Kumho Tire 수명 : " + ( maxRotation - accumulatedRotation ) + "회");
return true;
} else {
System.out.println("***" + location + "Tire 펑크 ***");
return false;
}// if - else
} // roll
} // end class
package PracticePolymorphism;
public class HankookTire extends Tire {
// 1. 필드
// 2. 생성자
public HankookTire ( String location, int maxRotation) {
super(location, maxRotation);
} // constructor
// 3. 메소드
@Override
public boolean roll () {
++ accumulatedRotation;
if ( accumulatedRotation < maxRotation) {
System.out.println(location + " Hankook Tire 수명 : "
+ ( maxRotation - accumulatedRotation ) + "회");
return true;
} else {
System.out.println("***" + location + "Tire 펑크 ***");
return false;
}// if - else
} // roll
} // end class
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class Car {
// 1. 부품 필드 ( 집합 관계 )
Tire frontLeftTire = new Tire ( "앞 왼쪽", 6);
Tire frontRightTire = new Tire ( "앞 오른쪽", 2);
Tire backLeftTire = new Tire ( "뒤 왼쪽", 3);
Tire backRightTire = new Tire ( "뒤 오른쪽", 4);
// 2. 생성자
// 3. 메소드
int run ( ) { // 다형성 2 - 오버 라이드
System.out.println(" [ 자동차가 달립니다! ] ");
if ( frontLeftTire.roll() == false ) { stop(); return 1; };
if ( frontRightTire.roll() == false ) { stop(); return 2; };
if ( backLeftTire.roll() == false ) { stop(); return 3; };
if ( backRightTire.roll() == false ) { stop(); return 4; };
return 0;
} // run
void stop () {
System.out.println(" [ 자동차가 멈춥니다... ] ");
} // stop
} // end class
package PracticePolymorphism;
public class CarExample {
public static void main (String [] args) {
Car car = new Car ( );
for ( int i = 1; i <= 5; i++ ) {
int problemLocation = car.run();
switch(problemLocation) {
case 1 : // 다형성 1 - car.frontLeftTire은 Tire타입이다.
System.out.println("앞 왼쪽 HankookTire로 교체");
car.frontLeftTire = new HankookTire("앞 왼쪽",15);
break;
case 2 : // 다형성 1
System.out.println("앞 왼쪽 HankookTire로 교체");
car.frontRightTire = new HankookTire("앞 오른쪽",13);
break;
case 3 : // 다형성 1
System.out.println("뒤 왼쪽 HankookTire로 교체");
car.backLeftTire = new HankookTire("뒤 왼쪽",14);
break;
case 4 : // 다형성 1
System.out.println("뒤 오른쪽 HankookTire로 교체");
car.backRightTire = new HankookTire("뒤 오른쪽",17);
break;
} // switch
System.out.println("==========================================================");
} // for
} // main
} // end class
07. 프로모션 01 - 프로모션( 강제형변환 )의 필요성
package PracticePolymorphism02;
// 다형성 1에 의해서 부모타입의 참조변수에 들어가 있는,
// 자식객체의 고유한 필드와 메소드를 어떻게 사용하는가?
public class Parent {
public String field1;
public void method1() {
System.out.println("Parent:method1() invoked.");
} // method1
public void method2() {
System.out.println("Parent:method2() invoked.");
} // method
} // end class
package PracticePolymorphism02;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access=AccessLevel.PUBLIC)
public class Child extends Parent {
public String field2;
public void method3() {
System.out.println("Child::method3() invoked.");
} // method3
} // end class
package PracticePolymorphism02;
public class ChildExample { // 강제형변환의 필요성 (***)
public static void main ( String[] args) {
Parent parent = new Child (); // 다형성 1
// 다형성 1에 의해 부모타입에 선언된 필드와 메소드만 사용가능
// 물론, 부모 메소드 중에 자식객체에서 재정의된 메소드가 있다면
// 다형성 2에 의해 자식객체의 재정의된 메소드가 호출됨
parent.field1 = "data 1";
System.out.println(parent.field1);
parent.method1();
parent.method2();
// 출력 :
// data 1
// Parent:method1() invoked.
// Parent:method2() invoked.
// =======================================================================
// child.field2 = "data 2"; // 불가능!
// child.method3(); // 불가능!
// =======================================================================
// 다형성1에 의해 부모 타입의 참조변수에 저장된 자식객체를 다시 끄집어 낸다!
// 강제형변환 필요!!! (****)
// 이때에는 주소를 가지고 있기 때문에 강제형변환의 부작용인 데이터 유실이 없다!!
Child child = ( Child ) parent;
child.field2 = " yyy "; // 가능!
System.out.println(child.field2);
child.method3(); // 가능!
// 출력 :
// yyy
// Child::method3() invoked.
} // main
} // end class
08. 프로모션 02
package PracticePromotion;
public class PromotionExample { // 프로모션 (****)
// 다형성 1 - 실험 :
// 부모타입 참조변수 = 자식객체 ; 의 대입이 가능하다!!
// 이것이 가능한 이유는 상속관계를 통해 대/소비교가 가능하고
// 이를 통해서 자동형변환이 발생하게 된다!
public static void main (String [] args ) {
B b = new B ();
C c = new C ();
D d = new D ();
E e = new E ();
// =========================
// Lvalue( 할아버지 참조변수 ) = Rvalue( 자식 또는 후손 객체 )
A a1 = b;
System.out.println("1. a1 : " + a1);
// 출력 : 1. a1 : PracticePromotion.B@1f32e575
// b가 A타입의 a1에 들어갔기는 했지만, 실제로는 B타입이 출력되는 것을 볼 수 있다.
// 이는 프로모션(자동형변환)된다하더라도 b가 A타입으로 변하는 것이 아니라 B타입 그대로 있는 것을 알 수 있다.
A a2 = c;
A a3 = d;
A a4 = e;
// 자식객체가 맞다면 후손도 들어갈 수 있다.
// ====================
B b1 = d;
System.out.println("2. b1 : " + b1);
// 출력 : 2. b1 : PracticePromotion.D@279f2327
C c1 = e;
// =====================
// 오류가 발생하게 된다!
// B b3 = e;
// C c3 = d;
// 모두 같은 조상객체인 A를 가지고는 있으나 서로가 서로의 부모자식관계가 아니기에 오류가 발생한다.
} // main
} // end class
09. Instance of
package Instanceof;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access=AccessLevel.PUBLIC)
public class parent {
;;
} // end class
package Instanceof;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class Child extends parent {
;;
} // end class
package Instanceof;
public class InstanceofExample { // instanceof (***)
// 다형성 1에 의해 부모타입의 참조변수에 저장되어 있는 자식객체를 다시 빼낼 때 ( 강제형변환 )
// 자식타입을 잘못알고 하면, classCastException 예외가 발생할 가능성이 있기 때문이다.
// 먼저, 자식타입을 검사해서 맞으면, 그때 안전하게 강제형변환을 통해 빼내고자
// ** instanceof ** 라는 연산자가 필요하다!!
//즉! instanceOf를 사용하면 이 객체가 child인지, parent로부터 상속받은 클래스의 객체인지 확인할 수 있다.
// instanceOf는 주로 상속 관계에서 부모객체인지 자식객체인지 확인하는데 사용된다.
public static void method1 ( parent Parent) { // 매개변수의 다형성 1
System.out.println("InstanceofExample::method1(parent) invoked");
if ( Parent instanceof Child ) { // 매개변수를 활용하여 매개변수에 따라서 강제형변환을 진행할지 말지를 결정하도록 하였다.
Child child = (Child) Parent; // 안전한 강제형변환 (***)
System.out.println( "method1 - child로 변환성공 ");
} else {
System.out.println( "method1 - child로 변환되지 않음 ");
} // if - else
} // method1
public static void method2 ( parent Parent) { // 매개변수의 다형성 1
System.out.println("InstanceofExample::method2(parent) invoked");
Child child = (Child) Parent; // 안전하지 않은 강제형변환 (***) - 오류가 발생할 수 있다.
System.out.println( "method2 - child로 변환성공 ");
} // method1
public static void main ( String [] args ) {
parent parentA = new Child(); // 다형성 1 발생
InstanceofExample.method1(parentA); // 매개변수의 다형성 OK
InstanceofExample.method2(parentA); // 매개변수의 다형성 OK
// ==============================================================
parent parentB = new Child();
InstanceofExample.method1(parentB); // 매개변수의 다형성 NO!
InstanceofExample.method2(parentB); // 매개변수의 다형성 NO!
// ==============================================================
parent parentC = new parent();
InstanceofExample.method1(parentC); // 매개변수의 다형성 NO!
InstanceofExample.method2(parentC); // 매개변수의 다형성 NO!
// parentC의 경우 실행하면 InstanceofExample.method2에서 오류가 발생하게 되는데
// 그 이유는 InstanceofExample.method2의 경우 무조건 child가 들어온다고 생각하고 있는데 parent가 들어왔기에 오류가 발생했다.
} // main
} // end class
'KH 정보교육원 [ Java ]' 카테고리의 다른 글
KH 17일차 - 인터페이스와 예외처리 (0) | 2022.03.22 |
---|---|
KH 16일차 - 추상 클래스 및 인터페이스 (0) | 2022.03.21 |
KH 14일차 - 상속 (0) | 2022.03.17 |
KH 13일차 - 어노테이션 (0) | 2022.03.16 |
KH 12일차 - 접근제한자 및 어노테이션 [ + 1차 시험 후기 ] (0) | 2022.03.14 |