열심히 끝까지

Java1 day08 본문

Java1(주말)

Java1 day08

노유림 2022. 5. 1. 15:18

[8일차 수업내용]
1. 제어자
2. 다형성
=============================================================
1. 제어자 ( modifier )
            1-1 ) 제어자란?
                        - 제어자란 클래스,  변수 또는 메소드의 선언부에 함께 사용되어
                          부가적인 의미를 부여
                        - 제어자의 종류는 크게 접근제어자그 외의 제어자로 나눌 수 있음

                       접근제어자       public, protected, (default), private
                       그 외 제어자     static, final, abstract, transient, synchronized ....

                        - 제어자는 클래스나 멤버변수와 메소드에 주로 사용하며, 
                          하나의 대상에 대해서 여러 제어자를 조합하여 사용하는 것이 가능
                        - 단, 접근제어자는 한번에 4가지 중 하나만 선택해서 사용할 수 있음

            1-2 ) static - 클래스, 공통적인
                        - static은 '클래스의' 또는 '공통적인'의 의미를 가지고 있음
                        - 인스턴스변수는 하나의 클래스로부터 생성되었더라도 
                          각기 다른 값을 유지하지만
                          클래스변수는 인스턴스에 관계없이 같은 값을 갖음
                        - static이 붙는 멤버변수와 메소드 그리고 초기화블록은 인스턴스가 아닌
                          클래스에 관계된 것이기 때문에 인스턴스를 생성하지 않고도 사용할 수 있음
                                           * static이 사용될 수 있는 곳 - 멤버변수, 메소드, 초기화블록

제어자 대상 의미
static 멤버변수모든 인스턴스에 공통적으로 사용되는 클래스 변수
- 클래스변수 사용은 인스턴스 생성이 필요 없음
- 클래스가 메모리에 로드될 때 사용

메소드 - 인스턴스를 생성하지 않고도 호출 가능한
     static 메소드가 됨
- static 메소드 내에서는 인스턴스 멤버들을
    직접 사용할 수 없음

            1-3 ) final - 마지막의, 변경될 수 없는
                        - final은 '마지막의' 또는 '변경될 수 없는'의 의미를 가지고 있으며
                          거의 모든 대상에 사용될 수 있음
                        - 변수에 사용되면 값을 변경할 수 없는 상수가 되며,
                          메소드에 사용되면 오버라이딩 할 수 없게 되고
                          클래스에 사용되면 자신을 확장하는 자손클래스를 정의하지 못함

제어자 대상 의미
final 클래스 - 변경될 수 없는 클래스, 확장될 수 없는 클래스
=> final로 지정된 클래스는 
    다른 클래스의 조상이 될 수 없음

메소드 - 변경될 수 없는 메소드
=> final로 지정된 메소드는 오버라이딩을 통해
    재정의 될 수 없음

멤버변수, 지역변수 - 변수 앞에 final이 붙으면 값을 변경할 수 없는
    상수가 됨

            1-4 ) abstract - 추상의, 미완성인
                        - abstract는 '미완성'의 의미를 가지고 있음
                        - 메소드의 선언부만 작성하고 실제 수행내용은 구현하지 않는
                          추상메소드를 선언하는데 사용
                                  * abstract가 사용될 수 있는 곳 - 클래스, 메소드

제어자 대상 의미
abstract 클래스 클래스 내에 추상메소드가 선언되어 있음을 의미
메소드 선언부만 작성하고 구현부는 작성하지 않는 추상메소드

            1-5 ) 접근제어자 ( access modifier )
                        - 접근제어자는 멤버 또는 클래스에 사용되며 해당하는  
                          멤버 또는 클래스 외부에서 접근하지 못하도록
                          제한하는 역할을 함
                        - 접근제어자가 default임을 알리기 위해 실제로는
                          default를 붙이지는 않음
                                 * 접근제어자가 사용될 수 있는 곳 - 클래스, 멤버변수, 메소드, 생성자

제어자 같은 클래스 같은 패키지 자손클래스 전체
public O O O O
protected O O O X
(default) O O X X
private O X X X

                      public > protecte > (default) > private ( 접근 범위가 넓은 쪽의 순으로 나열 )

            ① 접근제어자를 이용한 캡슐화(capsulation)
             - 클래스나 멤버, 주로 멤버에 접근제어자를 사용하는 이유는 
               클래스 내부에 선언된 데이터를 보호하기 위해서
             - 데이터가 유효한 값을 유지하도록, 또는 비밀번호와 같은 데이터를
               외부에서 함부로 변경하지 못하도록 하기 위해서
               외부로부터의 접근을 제한하는 것이 필요
             - 이를 데이터 감추기(data hiding)라고 하며, 객체지향의 캡슐화(encapsulation)
               해당하는 내용
             - 또 다른 이유는 클래스 내에서만 사용되는, 내부 작업을 위해 임시로 사용되는
               멤버변수나 부분작업을 처리하기 위한 메소드 등의 멤버들을 내부에 감추기 위함
             - 외부에서 접근할 필요가 없는 멤버들을 private으로 지정하여
               외부에 노출시키지 않음으로써 복잡성을 줄일 수 있음

            ② 생성자의 접근제어자
             - 생성자에 접근제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있음
             - 보통 생성자의 접근제어자는 클래스의 접근제어자와 같지만 다르게 지정할 수 있음
             - 생성자의 접근제어자를 private으로 지정하면 외부에서 생성자에 접근 할 수 없으므로
               인스턴스를 생성할 수 없게 됨
             - 그래도 클래스 내부에서는 인스턴스를 생성할 수 있음
             
            1-6 ) 제어자의 조합

대상 사용가능한 제어자
클래스 public, (default), final, abstract
메소드 모든 접근 제어자, final, abstract, static
멤버변수 모든 접근 제어자, final, static
지역변수 final

            ① 메소드에 static과 abstract를 함께 사용할 수 없음
                         => static 메소드는 몸통이 있는 메소드에서만 사용할 수 있기 때문
            ② 클래스에 abstract와 final을 동시에 사용할 수 없음
                         => abstract는 상속을 통해서만 완성되어야 한다는 의미이므로 모순
            ③ abstract메소드의 접근제어자가 private일 수 없음
                         => abstract메소드는 자손클래스에서 구현해줘야 하는데
                              접근제어자가 private이면 자손클래스에서 접근할 수 없기 때문
            ④ 메소드에 private과 final을 같이 사용할 필요는 없음
                         => 접근제어자가 private인 메소드는 오버라이딩 될 수 없기 때문에
                              이 둘 중하나만 사용해도 의미가 충분

======================================================================
package example01;

public class Access {

            public int publicVar;
            protected int protectedVar;
            // default int defaultVar;
            int defaultVar;
            private int privateVar;

            void info() {
                        System.out.println(publicVar);
                        System.out.println(protectedVar);
                        System.out.println(defaultVar);
                        System.out.println(privateVar);
            }
}
-------------------------
package example01;

public class AccessEx {
            public static void main(String[] args) {
                        Access ac = new Access();
                        System.out.println(ac.publicVar);
                        // public이 붙은 멤버변수는 다른 클래스에서 접근 가능

                        System.out.println(ac.protectedVar);
                        // protected가 붙은 멤버변수는 다른 클래스에서 접근 가능

                        System.out.println(ac.defaultVar);
                        // default가 붙은 멤버변수는 다른 클래스에서 접근 가능

                        // System.out.println(ac.privateVar);
                        // private이 붙은 멤버변수는 같은 클래스에서만 접근 가능
            }
}
------------------------------------------------------------
package example02;

import example01.Access;

public class AccessEx02 extends Access{
            void method() {
                        System.out.println(this.publicVar);
                        // public이 붙은 멤버변수는 다른 패키지에 있는 자손클래스에서 접근 가능

                        System.out.println(this.protectedVar);
                        // protected가 붙은 멤버변수는 다른 패키지에 있는 자손클래스에서 접근 가능

                        // System.out.println(this.defaultVar);
                        // default가 붙은 멤버변수는 다른 패키지에 있는 자손클래스에서 접근 불가능

                        // System.out.println(this.privateVar);
                        // private이 붙은 멤버변수는 같은 클래스를 벗어나는 순간 접근 불가능
            }
}

class AccessEx3{
            Access ac = new Access();
            int num = ac.publicVar;

            // int num1 = ac.protectedVar;
            // int num2 = ac.defaultVar;
            // int num3 = ac.privateVar;
            // 다른 패키지에 있는 상속관계가 없는 클래스에서는 
            // public 접근제어자가 붙어있는 멤버만 접근이 가능하다.
}
-------------------------------------------------------------------
package example02;

public class FinalTest extends Child{
            // 1. Parent클래스를 extends 키워드로 상속하려한다면 불가능하다
            /*@Override
            final void hello() {

            }
            2. Child클래스를 extends 키워드로 상속하는 것은 불가능하다.
           */

}
final class Parent{}

class Child{
             final void hello() {
                        System.out.println("hi");
            }
}
-----------------------------------------------------------------
package example02;

// private이 붙은 멤버변수 값을 초기화하는 방법 ==> getter()/ setter() 메소드 이용

public class TimeTest {
            public static void main(String[] args) {
                        Time t = new Time();
                        t.setHour(50);
                        System.out.println(t.getHour());
            }
}
class Time{
            private int hour, minute, second;
            // alt + shift + s + r ==> getter/setter 생성하는 단축키

            public int getHour() {
                        return hour;
            }

            public void setHour(int hour) {
                        if(hour < 0 || hour > 23) {
                        return;
            }
            this.hour = hour;
            }

            public int getMinute() {
                        return minute;
            }

            public void setMinute(int minute) {
                        this.minute = minute;
            }

            public int getSecond() {
                        return second;
            }

            public void setSecond(int second) {
                        this.second = second;
            }
}
----------------------------------------------------------------------------
package example02;

// 객체(인스턴스)를 하나만 만들고 싶을 때 사용하는 방법 => SingleTon Pattern
class Singleton{
            private static Singleton s = new Singleton();

            private Singleton() {}

            public static Singleton getInstance() {
                        if( s == null ) {
                                    return new Singleton();
                       }
            return s;
            }
}

public class SingletonTest {
            public static void main(String[] args) {
                        // Singleton s = new Singleton();
                        Singleton s = Singleton.getInstance();
                        Singleton s1 = Singleton.getInstance();
                        System.out.println("s : " + s);
                        System.out.println("s1 : " + s1);
                        // 객체가 단 하나만 만들어져 s 와 s1은 같은 주소값을 가진다.
            }
}
================================================================
2. 다형성(polymorphism)
            2-1 ) 다형성이란?
                        - 객체지향개념에서 다형성이란 여러가지 형태를 가질 수 있는 능력을 의미하며,
                          자바에서는 한 타입의 참조변수로 여러 타입의 객체(인스턴스)를
                          참조할 수 있또록 함으로써 다형성을 프로그램적으로 구현
                        - 좀 더 구체적으로 말하자면 조상클래스의 타입의 참조변수로
                          자손클래스의 인스턴스를 참조할 수 있도록 하였다는 것

                        class Tv{                                           class CaptionTV extend Tv{
                                  boolean power;                                    String text;
                                  int channel;                                         void caption(){}
                                  void power(){}                           }
                                  void channelUp(){}
                                  void channelDown(){}
                        }

                        Tv t;                   ==> t가 가리킬 수 있는 멤버의 개수 : 5개
                        CaptionTv ct;        ==> ct가 가리킬 수 있는 멤버의 개수 : 7개

                        - 지금까지 우리는 Tv t = new Tv(); CaptionTv c = new CaptionTv(); 형식으로만 사용
                        - 이처럼 인스턴스의 타입과 참조변수의 타입이 서로 일치하는 것이 보통이지만,
                          Tv와 CaptionTv처럼 클래스가 서로 상속관계에 있을 경우 다음과 같이
                          조상 클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조하도록 하는 것이 가능
                                     Tv t = new CaptionTv(); (가능)
                                     CaptionTv c = new Tv(); (불가능)
                       그렇다면...
                                     CaptionTv c = new CaptionTv();   
                                     Tv t = new CaptionTv();                 이 둘의 차이는 무엇일까?
                        - 둘 다 같은 타입의 인스턴스지만 타입에 따라 사용할 수 있는 멤버의 개수가 달라짐
                        - 그럼 여기서 반대로
                                     CaptionTv c = new Tv();                 는 가능할까에 대한 답변은 불가능하다는 점
                                     실제 인스턴스인 Tv멤버개수(5개)보다 참조변수 c가 사용할 수 있는 
                                     멤버 개수가 더 많기 때문
                       - CaptionTv클래스에는 text와 caption()가 정의되어 있으므로 참조변수 c로는
                         c.text, c.caption()과 같은 방식으로 c가 참조하고 있는 인스턴스에서 text와 caption()을
                         사용하려고 시도할 수 있음
                         하지만, c가 참조하고 있는 인스턴스는 Tv타입이고, Tv타입의 인스턴스에는 
                         text와 caption()이 존재하지 않기 때문에 이를 사용하려면 문제가 발생
                       - 그래서 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은

                          존재하지 않는 멤버를 사용하고자 할 가능성이 있으므로 허용하지 않는 것

                                * 조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다.
                                  (조상타입의 참조변수 <= 자손타입의 인스턴스)
                                * 반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수 없다
                                  (자손타입의 참조변수 >= 조상타입의 인스턴스)
  
                                Tv                           CaptionTv
                          [               ]                 [               ]
                          [               ]                 [               ]
                          [               ]                 [               ]
                          [               ]                 [               ]
                          [               ]                 [               ]
                                                            [               ] 
                                                            [               ]

            2-2 ) 참조변수의 형변환
                      - 기본형변수와 같이 참조변수도 형변환이 가능
                      - 단, 서로 상속관계에 있는 클래스에서만 가능하기 때문에
                        자손타입의 참조변수를 조상타입의 참조변수로,
                        조상타입의 참조변수를 자손타입의 참조변수로의 형변환이 가능

                                * 자손타입 -> 조상타입 ( Up-casting )                  : 형변환 생략가능
                                * 조상타입 -> 자손타입 ( Down-casting)               : 형변환 생략불가

                     - 형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것이 아니기 때문에
                       참조변수의 형변환은 인스턴스에 아무런 영향을 주지 못함
                     - 단지, 참조변수의 형변환을 통해서 참조하고 있는 인스턴스에서 사용할 수 있는
                       멤버의 범위(개수)를 조절하는 것 뿐

                            ==> 형변환 생략/불가가 헷갈리다면 그냥 형변환을 전부 붙이는 방법을 사용하자
            2-3 ) instanceof 연산자
                     - 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용
                     - 주로 조건문에 사용되며 instanceof의 왼쪽에는 참조변수를, 
                       오른쪽에는 타입(클래스명)이 피연산자로 위치
                     - 그리고 연산의 결과로 boolean값인 true와 false중 하나로 반환
                     - instanceof를 이용한 연산결과로 true를 얻었다는 것은 
                       참조변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻
===========================================================================
package example03;

public class Car {
            String color;
            int door;

            void drive() {
                        System.out.println("Drive, Brrr~");
            }

            void stop() {
                        System.out.println("stop!");
            }
}
class FireEngine extends Car{
            void water() {
                        System.out.println("water!");
            }
}
---------------------------------------------------------
package example03;

public class CastingEx01 {
            public static void main(String[] args) {
                        Car car = null;         // 4개 존재
                        FireEngine fe = new FireEngine();       // 상속받은것 4개 + 개인 1개 = 5개
                        FireEngine fe2 = null;                // parent <= child

                        fe.water();
                        car = (Car)fe;

                        // car.water(); Car타입의 참조변수는 water()를 호출할 수 없다.
                        System.out.println("car : " + car);  // 아래와 같은 주소
                        System.out.println("fe : " + fe);

                        fe2 = (FireEngine)car;
                        fe.water();

                        System.out.println("car : " + car); // 아래 두개도 같은 주소
                        System.out.println("fe : " + fe);
                        System.out.println("fe2 : " + fe2);
            }
}
-----------------------------
package example03;

public class CastingEx02 {
            public static void main(String[] args) {
                        Car car = new Car();   // 4개
                        FireEngine fe = null;  // 5개

                        car.drive();
                        fe = (FireEngine)car;
                        // 5개로 4개를 가리키는데 이건 크기가 부족 + water()이 없음 그렇기에 에러
                        fe.water();
            }
}

-----------------------------------------------------------------
package example03;

public class PolyArgumentTest {
            public static void main(String[] args) {
                        Buyer b = new Buyer();
                        b.buy(new TvProduct()); // void buy(TvProduct tv) 내가 부르는 참조형 메소드
                        System.out.println("현재 남은 돈 : " + b.money);
                        System.out.println("현재 적립금 : " + b.bonusPoint);
                        System.out.println();
                        b.buy(new ComputerProduct());
                        System.out.println("현재 남은 돈 : " + b.money); 
                        System.out.println("현재 적립금 : " + b.bonusPoint);
            }
}

class Product{
            int price;            // 제품의 가격
            int bonusPoint;       // 제품 당 적립금

            public Product(int price) {
                        this.price = price;
                        this.bonusPoint = price / 10;
            }
}
 class TvProduct extends Product{
             public TvProduct() { super(100); }
            @Override
            public String toString() { return "TvProduct"; }
 }
 
 class ComputerProduct extends Product{
            public ComputerProduct() { super(200); }
            @Override
            public String toString() { return "ComputerProduct"; }
 }
 
 class Buyer{              // 고객, 물건을 사는 사람
             int money = 1000;     // 보유금액
            int bonusPoint = 0;   // 적립금액 
 
             // Tv를 구매하기 위한 buy()메소드
             void buy(TvProduct tv) {
                         if(money < tv.price) {
                                     System.out.println("잔액이 부족합니다.");
                                     return;
                        }
                        money -= tv.price;
                         bonusPoint += tv.bonusPoint;
             System.out.println(tv + "을/를 구매하였습니다.");
            }
 
            // Computer를 구매하기 위한 buy()메소드
            void buy(ComputerProduct cp) {
                         if(money < cp.price) {
                         System.out.println("잔액이 부족합니다.");
                         return;
                        }
                         money -= cp.price;
                        bonusPoint += cp.bonusPoint;
                         System.out.println(cp + "을/를 구매하였습니다.");
            }
             // 만약, 노트북, 전자레인지, 세탁기, 냉장고, 오디오, 에어컨 ... 등등등 구매를 한다면?
 }
-------------------------------------------------------
package example04;

public class PolyArgumentTest02 {
            public static void main(String[] args) {
                        Buyer b = new Buyer();
                        // void buy(Product p)
                        b.buy(new TvProduct());
                        System.out.println("현재 남은 돈 : " + b.money);
                        System.out.println("현재 적립금 : " + b.bonusPoint);
                        System.out.println();

                        b.buy(new ComputerProduct());
                        System.out.println("현재 남은 돈 : " + b.money);
                        System.out.println("현재 적립금 : " + b.bonusPoint);
                        System.out.println();

                        b.summary();
            }
}
class Product{
            int price;            // 제품의 가격
            int bonusPoint;       // 제품 당 적립금

            public Product(int price) {
                        this.price = price;
                        this.bonusPoint = price / 10;
            }
}
class TvProduct extends Product{
             public TvProduct() { super(100); }
             @Override
            public String toString() { return "TvProduct"; }

class ComputerProduct extends Product{
             public ComputerProduct() { super(200); }
             @Override
             public String toString() { return "ComputerProduct"; }
}

class Buyer{
            int money = 1000;
            int bonusPoint = 0;
            Product[] item = new Product[10];
            int i = 0;

            // 제품을 구매하기 위한 buy()메소드
            // Product p = new TvProduct(); ( O )
            // product p = new ComputerProduct(); ( O )

            void buy(Product p) {
                        if(money < p.price) {
                                    System.out.println("잔액이 부족합니다.");
                                    return;
                        }
                        money -= p.price;
                        bonusPoint += p.bonusPoint;
                        // Product p = new TvProduct();
                        // iten[0] = new Product();           ( O )
                        // item[0] = new Tvproduct();         ( O )
                        // item[0] = new ComputerProduct();   ( O )

                        /*
                         * Product p = new Product();
                         * Product p1 = new Product();
                         * 
                         * Product[] item = new Product[2];
                         * item[0] = new Product();
                         * item[1] = new Product();
                         */
                        item[i++] = p;
                        System.out.println(p + "을/를 구매하였습니다.");
            }
            void summary() {
                        int sum = 0;
                        String itemList = "";

                        for(int i = 0; i < item.length; i++) {
                                    if(item[i] == null) {
                                    break;
                        }
                        sum += item[i].price;
                        itemList += item[i] + ", ";
            }
            System.out.println("구매하신 물품의 총 금액 : " + sum);
            System.out.println("구매하신 제품 목록 : " + itemList);
            }
}
---------------------------------------
package example03;

public class InstanceofTest {
            public static void main(String[] args) {
                        // instanceof를 이용한 연산결과를 true로 얻었다는 것은
                        // 참조변수가 검사한 타입으로 형변환이 가능하다는 의미이다.

                        FireEngine fe = new FireEngine();

                        if(fe instanceof FireEngine) {
                                    System.out.println("This is a FireEngine instance");
                        }

                        if(fe instanceof Car) {
                                    System.out.println("This is a Car instance");
                                    Car car = (Car)fe;
                        }

                        if(fe instanceof Object) {
                                    System.out.println("This is a Object instance");
                                    Object obj = (Object)fe;
                        }
            }
}

'Java1(주말)' 카테고리의 다른 글

Java1 day07  (0) 2022.04.30
Java1 day06  (0) 2022.04.24
Java1 day05 보충  (0) 2022.04.24
Java1 day05  (0) 2022.04.23
Java1 day04 보충  (0) 2022.04.23