-
[JAVA] 객체지향 2편 제어자 다형성JAVA 2022. 8. 2. 10:17728x90반응형
1. 제어자
- 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미를 부여한다.
- 제어자는 크게 접근 제어자와 그 외 제어자로 나뉜다.
- 접근 제어자 : public, protected, default, private
- 그 외 제어자 : static, final, abstract, native, transient, synchronized, volatile, strictfp
- 하나의 대상에 여러 개의 제어자를 조합해서 사용할 수 있지만 접근 제어자는 단 하나만 사용이 가능하다.
1-1. 그 외 제어자에 대해 알아보기
static - 공통적인
static이 사용될 수 있는 곳은 멤버 변수, 메서드, 초기화 블록이다.
- 멤버 변수
- 모든 인스턴스에 공통적으로 사용되는 클래스 변수가 된다.
- 클래스 변수는 인스턴스를 생성하지 않고도 사용이 가능하다.
- 클래스가 메모리에 로드될 때 생성된다.
- 메서드
- 인스턴스를 생성하지 않고도 호출이 가능한 static 메서드가 된다.
- static 메서드 내에서는 인스턴스 변수를 사용할 수 없다.
class T{ // 클래스 변수 static int width = 200; static int height = 100; // 클래스 변수 초기화 블럭(복잡) static {} // static 메서드 static int max(int a, int b){ // 인스턴스 변수 사용 x return a > b ? a : b; } }
final - 변경될 수 없는
final이 사용될 수 있는 곳은 클래스, 메서드, 멤버 변수, 지역변수이다.
- 클래스
- 다른 클래스의 부모가 될 수 없다. (확장이 불가능하다.)
- 메서드
- 오버 라이딩을 통해 재 정의될 수 없다.
- 멤버 변수, 지역 변수
- 값을 변경할 수 없는 상수가 된다.
// 상속 불가 메서드 final class Test{ // 상수 final int iv = 10; // 오버라이딩 불가 메서드 final void get(){ final lv = iv; } } class T extends Test{ // 💥Error > final class는 상속할 수 없다. void get(){}; // 💥 Error > final 메서드는 오버라이딩 할 수 없다. }
final 멤버 변수 초기화
final이 붙은 변수는 상수이므로 보통 선언과 초기화를 동시에 하지만 인스턴스 변수의 경우 생성자를 통해 단 한 번만 초기화할 수 있다.
class card{ final int num; final String kind; static int width = 300; // 생성자를 통해 final 멤버 변수 초기화 (단 한번) card(int num, String kind){ this.num = num; this.kind = kind; } card(){ this("HEART",1); } } public static void main(String[] args){ card a = new card("HEART", 10); // 💥 Error : 상수는 선언과 동시에 초기화 하던지 생성자를 통해 단한번 초기화 해야한다. a.num = 4; }
abstract - 미완성의
abstract가 사용될 수 있는 곳은 클래스, 메서드이다.
- 클래스
- 클래스 내에 추상 메서드가 선언되어 있음을 의미한다.
- 메서드
- 선언 부만 작성하고 구현부가 없는 추상 메서드임을 알린다.
abstract class T{ // 추상 클래스 abstract void method(); // 추상 메서드 }
1-2. 접근 제어자에 대해 알아보기
접근 제어자는 외부로부터 접근을 제한하여 데이터를 보호하며 내부적으로만 사용되는 부분을 감추기 위해 사용된다.
- public : 접근 제한이 없다.
- 클래스, 멤버 변수, 메서드에서 사용이 가능하다.
- protected : 같은 패키지 및 다른 패키지의 자식 클래스에서만 접근이 가능하다.
- 클래스에 사용이 불가능하다.
- 멤버 변수, 메서드에서 사용이 가능하다.
- default : 같은 패키지에서만 접근이 가능하다.
- 클래스, 멤버 변수, 메서드에서 사용이 가능하다.
- default의 경우 생략이 가능하다.
- private : 같은 클래스 내에서만 접근이 가능하다.
- 클래스에 사용이 불가능하다.
- 멤버 변수, 메서드에서 사용이 가능하다.
(default)class Time{ private int hour; Time(int hour, int minute, int second){ //super(); this.hour = hour; } public int getHour(){return hour;} public void setHour(int hour){ if (hour < 0 || hour > 23){ return; } this.hour = hour; } } public static void main(String[] args){ Time test = new Time(10); // 💥 Error : private 변수는 외부 클래스에서 접근이 불가능하다. System.out.println(test.hour); test.hour = 3; // private 변수를 사용하고 싶다면 메서드를 통해 접근해야한다. test.setHour(22); // private 변수 hour의 값을 변경한다. System.out.println(test.getHour()); // private 변수 hour을 보여준다. }
제어자 총 종리
- 클래스
- public, default, final, abstract
- 메서드
- 모든 접근 제어자, final, abstract, static
- 멤버 변수
- 모든 접근 제어자, final, static
- 지역 변수
- final
Q. 메서드에 static과 abstract를 같이 사용할 수 없는 이유는?
A. static 메서드는 구현부가 있는 메서드만 사용할 수 있기 때문이다.
Q. 클래스에 abstract와 final을 같이 사용할 수 없는 이유는?
A. abstract는 상속을 통해 완성돼야 하기 때문에 상속을 하지 못하는 final을 같이 사용할 수 없다.
Q. abstract 메서드의 접근제어자로 private을 사용할 수 없는 이유는?
A. abstract 메서드는 상속을 통해 완성돼야 하는데 private일 경우 자식 클래스에서 접근할 수 없기 때문이다.
Q. 메서드에 private과 final을 같이 사용할 수 없는 이유는?
A. private 메서드는 외부에서 접근을 못하기 때문에 오버 라이딩을 할 수 없고 final 메서드는 오버 라이딩을 하지 못하게 만들기 때문에 의미가 중복된다.
2. 다형성
부모 타입의 참조 변수로 자식 타입의 객체를 다룰 수 있는 것을 다형성이라고 부른다.
다형성의 장점
- 하나의 참조 변수로 여러 타입의 객체를 참조할 수 있다.
- 여러 가지 형태를 가질 수 있다.
다형성을 사용할 때 기억해두어야 할 점은 부모 타입의 참조 변수는 자식 타입의 인스턴스를 참조할 수 있지만 반대의 경우는 성립되지 않는다.
(default)class Tv{ int channel; Tv(){ this(1); } Tv(int x){ //super(); this.channel = x; } void channelUp(){ ++channel; } } class SmartTv extends Tv{ String text; SmartTv(){ this(1,"Hello Java"); } SmartTv(int x, String text){ super(x); this.text = text; } void caption(){ System.out.println(text); } } public static void main(String[] args){ SmartTv S = new SmartTv(); // 총 멤버 : 4 | 사용 가능 멤버 : 4 Tv T = new SmartTv(); // 총 멤버 : 4 | 사용 가능 멤버 : 2 // 💥 Error : Tv 타입의 참조변수 T는 caption() 메서드를 가지고 있지 않다. T.caption(); // 💥 Error : Tv 타입의 참조변수 T는 String text 인스턴스 변수를 가지고 있지 않다. T.text = "hi~"; }
2-1. 참조 변수의 형 변환
서로 상속 관계에 있는 타입 간의 형 변환만 가능하다.
class car{ String color; void drive(){ System.out.println("drive~~"); } void stop(){ System.out.println("stop"); } } class fireEngine extends car{ void water(){ System.out.println("water~"); } } public static void main(String[] args){ fireEngine f = new fireEngine(); // 참조변수 f에 fireEngine 인스턴스의 주소 저장 car c = null; // 참조변수 c에 null을 저장 c = (car)f; // car타입의 참조변수 c에 fireEngine 인스턴스 주소를 저장 // 두개의 참조변수 타입이 같아야 하므로 참조변수 f의 타입을 car타입으로 형변환 시켜준다. // car타입에는 변수 color, 메소드 drive, stop 있다. // 생성된 인스턴스에는 메서드 water가 있지만 사용할 수 없다. c.water(); // 💥 Error fireEngine f2 = null; // 참조변수 f2에 null을 저장 f2 = (fireEngine)c; // fireEngine타입의 참조변수 f2에 fireEngine 인스턴스 주소를 저장 // 참조변수 c의 타입은 car이기 때문에 fireEngine으로 형변환 시켜준다. // fireEngine 타입에는 모든 멤버가 있기 때문에 모두 사용 가능하다. f2.water(); // ⭐ 정상작동 }
2-2. instanceof 연산자
- 참조 변수가 참조하는 인스턴스의 실제 타입을 체크하는 데 사용한다.
- 이항 연산자이며 피연산자는 참조형 변수와 타입 연산 결과는 true, false로 반환한다.
- instanceof 연산 결과가 true이면 해당 타입으로 형 변환이 가능하다.
A instanceof B 연산자를 읽는 방법은 A의 부모 타입에 B가 있다면 true 아니라면 false를 반환합니다.
class car { void drive(){ System.out.println("drive"); } } class fire extends car{ void water(){ System.out.println("watre~"); } } public static void main(String args[]) { fire a = new fire(); car c = new car(); if(a instanceof fire){ System.out.println("This is a fire instance"); } if(a instanceof car){ System.out.println("This is a car instance"); } if(a instanceof Object){ System.out.println("This is a Object instance"); } if(c instanceof fire){ System.out.println("car의 부모타입에는 fire가 없으므로 실행 x"); } } // 결과 // This is a fire instance // This is a car instance // This is a Object instance
2-3. 참조 변수와 인스턴스 변수의 연결
- 멤버 변수가 중복 정의된 경우 참조 변수의 타입에 따라 연결되는 멤버 변수가 달라진다.
- 참조 변수 타입에 영향을 받는다.
- 메서드가 중복 정의된 경우 참조 변수의 타입에 관계없이 항상 실제 인스턴스의 타입에 정의된 메서드가 호출된다.
- 참조 변수 타입에 영향을 받지 않는다.
public static void main(String args[]) { Parent p = new Child(); Child c = new Child(); System.out.println("p.x = " + p.x); // p.x = 10 p.method(); // Child method System.out.println("c.x = " + c.x); // c.x = 20 c.method(); // Child method Parent2 p2 = new Child2(); Child2 c2 = new Child2(); System.out.println("p2.x = " + p2.x); // p2.x = 30 p2.method(); // Parent2 method System.out.println("c2.x = " + c2.x); // c2.x = 30 c2.method(); // Parent2 method } class Parent{ int x = 10; void method(){ System.out.println("Parent method"); } } class Child extends Parent{ int x = 20; void method(){ // 오버라이딩 System.out.println("Child method"); } } class Parent2{ int x = 30; void method(){ System.out.println("Parent2 method"); } } class Child2 extends Parent2{ }
2-4. 매개변수의 다형성
참조형 매개변수는 메서드 호출 시 자신과 같은 타입 또는 자식 타입의 인스턴스를 넘겨줄 수 있다.
public class MyClass { public static void main(String args[]) { Product tv = new Tv(10); Product audio = new Audio(5); Buyer buyer = new Buyer(); buyer.buy(tv); System.out.println(buyer.money); } } class Product { int price; Product(int x){ price = x; } } class Tv extends Product{ Tv(int x){ super(x); } } class Audio extends Product{ Audio(int x){ super(x); } } class Buyer{ int money; Buyer(){this(1000);} Buyer(int x){ money = x; } // 매개변수의 다형성 사용 O // 물건이 추가되더라도 이 코드는 수정하지 않아도 된다. void buy(Product p){ money = money - p.price; } // 매개변수의 다형성 사용 x // 물건이 추가되면 이 코드를 수정해줘야 한다. void buy2(Tv t){ money = money - t.price; } void buy2(Audio a){ money = money - a.price; } }
2-5. 여러 종류의 객체를 하나의 배열로 다루기
원래 배열은 같은 타입만 가능하지만 다형성을 이용하면 부모 타입의 배열에 각기 다른 자식 타입들의 인스턴스를 담을 수 있습니다.
import java.util.*; public class MyClass { public static void main(String args[]) { // 다형성을 이용해서 자식 타입의 객체를 생성후 부모 타입의 참조변수에 주소를 저장시킨다. Product tv = new Tv(10); Product audio = new Audio(5); Buyer buyer = new Buyer(); buyer.buy(tv); // tv 구매 buyer.buy(tv); // tv 구매 buyer.buy(audio); // audio 구매 System.out.println(buyer.money); // buyer 남은 돈 확인 System.out.println(Arrays.toString(buyer.cart)); // 구매 물품 배열로 확인 System.out.println(buyer.cart[0].price); // 배열의 0번째에 들어있는 물건 가격확인 System.out.println(buyer.sum); // 배열에 있는 모든 가격 합산 } } class Product { int price; Product(int x){ price = x; } } class Tv extends Product{ Tv(int x){ super(x); } } class Audio extends Product{ Audio(int x){ super(x); } } class Buyer{ int money; Product[] cart = new Product[10]; // 부모타입에 배열 생성 int i = 0; int sum = 0; Buyer(){this(1000);} Buyer(int x){ money = x; } void buy(Product p){ money = money - p.price; sum = sum + p.price; cart[i++] = p; // 배열에 하나씩 차곡차곡 자식들의 객체를 담는다. } }
728x90반응형'JAVA' 카테고리의 다른 글
[JAVA] 예외처리 (0) 2022.08.03 [JAVA] 객체지향 추상클래스 인터페이스 내부클래스 (0) 2022.08.03 [JAVA] 객체지향 2편 (1) 2022.08.01 [JAVA 공부 6일차] static, final, abstract, 접근 제어자 알아보기 (0) 2022.07.27 [JAVA 공부 6일차] 객체지향 Part 4 (0) 2022.07.27