final이란?

  final 변수는 값이 변경될 수 없는 상수이다.

  public static final double PI = 3.14;

  이러한 형태로 변수를 final로 선언하며 오직 한번만 값을 할당할 수 있다.

 

  final키워드는 변수, 메서드, 클래스에 적용할 수 있다.

 

 

  final 변수

 

  final 변수는 원시타입, 객체타입, 메서드인자, 멤버변수 이렇게 세부적으로 나눌 수 있다.

 

  원시타입의 경우 로컬 원시 변수에 final로 선언하면 한번 초기화된 변수는 변경할 수 없는 상수값이 된다.

final int x = 1;

x = 3;//오류발생

  이렇게 한번 assign되면 값을 변경할 수 없게 된다.

 

 

  객체타입은 객체변수에 final로 선언하면 그 변수에 다른 참조값을 지정할 수 없다.

  단, 객체 자체가 immutable 하다는 의미는 아니다. 객체의 속성은 변경이 가능하다.

final Pet pet = new Pet();

pet = new Pet(); //다른 객체로 변경할 수 없다.

pet.setWeight(3); //객체필드는 변경이 가능하다.

 

 

  메서드 인자에 final을 붙이면 메서드 안에서 변수값을 변경할 수 없다.

public class Pet {
  
  int weight;
  
  public void setWeight(final int weight) {
    weight = 1; //오류발생
  }
}

 

 

  멤버변수에 final로 선언하면 생성자 호출시 한번만 초기화 할 수 있다는 의미로 사용된다.

  초기화 된 이후에는 읽기만 허용되면 값을 다시 설정할 수 없다.

public class Employer {
  
  protected final int salary;
  
  public Employer() {
    this.salary = 2400;
  }
}

  

  salary 멤버변수에 final 키워드를 명시했기 때문에 생성자에서 한번만 초기화할 수 있고 이후 다른 메서드에서는

  읽기만 할 수 있다.

public class Employer {
  
  protected final int salary;
  
  public Employer() {
    this.salary = 2400;
  }
  
  public int setSalary(int newSalary) {
    this.salary = newSalary;//오류발생
  }
}

public class Employer {
  
  protected final int salary = 1000;
  
  public Employer() {
    this.salary = 2400;//오류발생
  }
}

  이렇게 final 멤버변수로 설정되어있는 salary를 setSalary 메서드로 수정하려고 하면 오류가 발생한다.

  그리고 아래에 있는 클래스처럼 이미 멤버변수에 값이 있는 상태라면 생성자에서도 초기화 할 수 없다.

  이렇게 인스턴스 생성시에 설정되고 바뀌지 않을 값에 final키워드를 붙여주면 의도하지 않은 멤버변수 변경을

  막을 수 있다.

  또한, 변하지 않은 멤버변수를 final로 선언해두면 자바 컴파일러가 해당 멤버변수를 Read-Only로 인식하여

  레지스터 같은 곳에 값을 캐싱 할수도 있어 성능향상을 노려볼 수도 있다.

 

 

  final 메서드

  메서드를 final로 선언하면 상속받은 클래스에서 오버라이드가 불가능해진다.

  구현한 코드를 하위클래스에서 변경하지 않도록 할 때 사용한다.

public Class Pet {
  public final void makeSound() {
    System.out.println("bark!");
  }
  
  public void walk() {
    System.out.println("walk");
  }
}


//subClass
public Class Dog extends Pet {
  
  @Override
  public void makeSound() {
    System.out.println("bark!!");
  } //오류발생
  
  @Override
  public void walk() {
    System.out.println("run!");
  }
}

 

  위와같이 final로 선언된 makeSound는 재정의 할 수없다.

  이렇게 재정의하면 안되는 메서드인 경우는 final로 막아줄 수 있다.

 

 

  final 클래스

  클래스에 final을 선언하면 상속자체가 안된다. 클래스 그대로 사용해야 하고 Util형식의 클래스나 여러 상수값을

  모아둔 Constants 클래스를 final로 사용한다.

public class Pet{

}


//Sub class
public class Dog extends Pet{

} //오류발생

 

  final 클래스 상수클래스

  상수값을 모아준 클래스는 굳이 상속할 이유가 없다.

public final class Constants {
  public static final int SIZE = 10;
}

 

  final 클래스 Util 형식 클래스

  JDK에서 String도 fianl클래스로 선언되어 있다. 자바의 코어 라이브러리이기 때문에 side-effect가 있으면 안되기

  때문이다. 다른 개발자가 상속해서 새로운 SubString을 만들어 다른곳에서 사용하게 되면 유지보수나 정상실행이

  어려워 질수 있다.

public final class String
  implements java.io.Serializable, Comparable<String>, CharSequence {
  
}

 

 

 

 

레퍼런스

 패스트캠퍼스 올인원 패키지 - 자바 객체지향프로그래밍

soft.plusblog.co.kr/m/2

advenoh.tistory.com/13

'JAVA' 카테고리의 다른 글

Class 클래스  (0) 2021.02.04
Object 클래스  (0) 2021.02.03
템플릿 메서드(Template Method)  (0) 2021.02.02
싱글톤 패턴(Singleton Pattern)  (0) 2021.02.01
인터페이스(Interface)  (0) 2021.01.31

템플릿 메서드(Template Method)

  템플릿은 틀이나 견본을 의미한다.

  템플릿메서드는 추상메서드나 구현된 메서드를 활용하여 전체의 흐름(시나리오)을 정의해놓은 메서드다.

  즉, 구현별로 달라질 수 있는 행동(메서드)들은 구현 클래스에서 선언 후 호출하는 방식으로 사용한다.

  final로 선언하여 재정의(Override)할 수 없도록 한다.

 

  어떤 소스코드상의 알고리즘에서 특정 환경 또는 상황에 맞게 확장 또는 변경을 해야 할 경우 매우 유용한

  패턴이다.

  

  이러한 템플릿 메서드를 이용한 템플릿메서드 패턴은 abstract 클래스에서 abstract 메서드를 이용해서

  전체 흐름을 정의하고 구체적인 각 메서드의 구현은 하위 클래스에 위임하는 것이다.

  하위 클래스마다 세부 구현 내용이 달라져도 로직의 흐름은 템플릿 메서드에 정의된 흐름대로 수행된다.

  

  final 이란?

 

final 키워드

final이란? final 변수는 값이 변경될 수 없는 상수이다. public static final double PI = 3.14; 이러한 형태로 변수를 final로 선언하며 오직 한번만 값을 할당할 수 있다. final키워드는 변수, 메서드, 클래스..

myyoun.tistory.com

  

 

예제코드

 

//Car class
public abstract class Car {

  public abstract void drive();
  public abstract void stop();
  
  public void startCar() {
    System.out.println("시동을 켭니다.");
  }
  
  public void turnOff() {
    System.out.println("시동을 끕니다.");
  }
  
  public void washCar() { }
  
  final public void run() {
    startCar();
    drive();
    stop();
    turnOff();
    washCar();
    /*
      구현된 메소드들 중에서는 하위클래스에서 재정의할 수 있지만
      시나리오가 틀이 되는 메소드들은 재정의하면 안되기 때문에
      final을 사용해서 재정의할 수 없도록 한다.
      
      즉, final로 선언한 run은 시나리오가 되는것이고
      순서가 변경되어서는 안되기때문에 재정의 할 수 없도록
      final로 선언해서 보호하는 것이다.
      만약 재정의하려고 한다면 override method is final 이라는 메세지를 볼 수 있다.
    */
  }
} //Car class End


//Sub class AICar
public class AICar extends Car {
  
  @Override
  public void drive() {
    System.out.println("자율 주행합니다.");
    System.out.println("자동차가 스스로 방향을 바꿉니다.");
  }
  
  @Override
  public void stop() {
    System.out.println("스스로 멈춥니다.");
  }
} //AICar class End


//Sub class ManualCar
public class ManualCar extends Car {
  
  @Override
  public void drive() {
    System.out.println("사람이 운전합니다.");
    System.out.println("사람이 핸들을 조작합니다.");
  }
  
  @Override
  public void stop() {
    System.out.println("브레이크를 밟아서 정지합니다.");
  }
  
  @Override
  public void washCar() {
    System.out.println("손세차");
  }
} //ManualCar class End


//Test Class
public class CarTest {
  
  public static void main(String[] args) {
    Car aiCar = new AICar();
    aiCar.run();
    System.out.println("=========================");
    Car manualCar = new ManualCar(0;
    manualCar.run();
  }
}//Test Class End

  결과값은

  시동을 켭니다.
  자율 주행합니다.
  자동차가 스스로 방향을 바꿉니다.
  스스로 멈춥니다.
  시동을 끕니다.
  ==================
  시동을 켭니다.
  사람이 운전합니다.
  사람이 핸들을 조작합니다.
  브레이크를 밟아서 정지합니다.
  시동을 끕니다.
  손세차

  이렇게 출력된다.

  

  Car 클래스의 경우 abstract으로 선언했는데 drive와 stop메서드를 abstract 이기 때문에 class에서도 써야 한다.

  drive와 stop메서드를 abstract으로 했다는 것은 하위클래스에서 이 두 메서드는 무조건 재정의를 하라는 것이다.

  그리고 재정의를 해도되지만 안해도 되는 메서드는 abstarct을 선언하지 말고 구현부(body)를 비워두면 된다.

  wash가 재정의를 해도되고 안해도 되는 일반 메서드다.

  그래서 결과값을 보면 wash는 재정의하지 않은 AICar에서는 출력되지 않고 재정의를 해준 ManualCar에서는

  출력되는 것을 확인할 수 있다.

 

  Car클래스의 run 메서드가 템플릿 메서드다.

  템플릿 메서드는 추상메서드나 구현된 메서드를 활용하여 시나리오를 정의해 놓은 메서드이며 fianl로 재정의 할 수

  없도록 한다고 했는데 run 메서드를 보면 그 조건을 만족하여 하위클래스에서 구현한 것을 받아와

  정의한 흐름대로 처리되는 것을 확인할 수 있다.

 

 

 

레퍼런스

 패스트캠퍼스 올인원 패키지 - 자바 객체지향프로그래밍

niceman.tistory.com/142

'JAVA' 카테고리의 다른 글

Object 클래스  (0) 2021.02.03
final 키워드  (0) 2021.02.02
싱글톤 패턴(Singleton Pattern)  (0) 2021.02.01
인터페이스(Interface)  (0) 2021.01.31
생성자(Constructor)  (0) 2021.01.30

싱글톤 패턴(Singleton Pattern)이란?

  싱글톤은 자바에서 많이 상용한다.

  싱글톤이란 어떤 클래스가 최초 한번만 메모리를 할당하고(static) 그 메모리에 객체를 만들어 사용하는 디자인패턴을

  의미한다.

  생성자가 여러차례 호출되더라도 실제로 생성되는 객체는 하나고 최초 생성이후에 호출된 생성자는 최초에 생성한

  객체를 반환한다.(자바에서는 생성자를 private으로 선언해서 생성 불가하게 하고 getInstance()로 받아쓰기도 한다)

  싱글톤 패턴은 단 하나의 인스턴스를 생성해 사용하는 디자인 패턴이다.

  인스턴스가 필요할 때 똑같은 인스턴스를 만들어 내는 것이 아니라 동일(기존) 인스턴스를 사용하게 한다.

  싱글톤은 주로 공통된 객체를 여러개 생성해서 사용하는 DBCP(DataBase Connection Pool)와 같은 상황에서 많이

  사용된다.

 

 

싱글톤 패턴을 쓰는 이유

  고정된 메모리 영역을 얻으면서 한번의 new로 인스턴스를 사용하기 때문에 메모리 낭비를 방지할 수 있다.

  또한 싱글톤으로 만들어진 클래스의 인스턴스는 전역 인스턴스이기 때문에 다른 클래스의 인스턴스들이 데이터를

  공유하기 쉽다.

  안드로이드 앱의 경우 각 액티비티나 클래스별로 주요 클래스들을 일일이 전달하기가 번거롭기 때문에

  싱글톤 클래스를 만들어 어디서나 접근하도록 설계하는것이 편하다.

 

 

싱글톤 패턴의 문제점

  싱글톤 인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유시킬 경우 다른 클래스의 인스턴스들 간에 결합도가

  높아져 객체지향 설계 원칙에 어긋나게 된다.

  따라서 수정이 어려워지고 테스트하기 어려워지는 것이다.

  또한 멀티쓰레드 환경에서 동기화처리를 안하면 인스턴스가 두개 생성된다거나 하는 경우가 발생할 수 있다.

 

 

예제코드

 

  멀티쓰레드에서 안전한 싱클톤 클래스, 인스턴스 예제

  1. Thread safe Lazy initialization

public class Company {
  
  private static Company instance = new Company();
  
  private Company(){}
  
  public static synchronized Company getInstance() {
    //외부에서 인스턴스와 상관없이 호출할 수 있도록 하기 위해 static으로 만든다.
    if(instance == null) {
      instance = new Company();
    }
    
    return instance;
  }
} //Company class End

//Test Class
public class CompanyTest {
  
  public static void main(String[] args) {
    Company company = Company.getInstance();
    Company company1 = Company.getInstance();
    
    System.out.println(company);
    System.out.println(company1);
  }
} //TestClass End

  위 예제코드를 실행하면 같은 값을 출력하는 것을 확인할 수 있다

  private static으로 인스턴스 변수를 만들고 private 생성자로 외부에서 생성을 막았으며 synchronized 키워드를

  사용해서 thread-safe하게 작성되어있다.

  하지만 synchronized 특성상 비교적 큰 성능저하가 발생하므로 권장하지 않는 방법이다.

 

  2. Thread safe lazy initialization + Double-checked locking

  lazy initialization 의 성능저하를 완화시키는 방법

public class Company {
  
  private volatile static Company Instance;
  
  private Company() { }
  
  public static Company getInstance() {
    if(instance == null) {
      synchronized(Company.class) {
        if(instance == null)
          instance = new Company();
      }
    }
    
    return instance;
  }
}

  getInstance에 synchronized를 사용하는 것이 아닌 첫번재 if문으로 인스턴스의 존재여부를 체크하고 두번째 if 문에서

  다시 한번 체크하면서 동기화 시켜 인스턴스를 생성하므로 thread-safe 하면서도 처음 생성이후에 synchronized

  블럭을 타지 않기 때문에 성능저하가 완화된다.

  그러나 완벽한 방법은 아니라고 한다.

 

  3.Initialization on demand holder idiom(holder에 의한 초기화)

  클래스 안에 클래스(Holder)를 두어 JVM의 Class loader 매커니즘과 class가 로드되는 시점을 이용하는 방법

public class Company {
  private Company() { }
  
  private static class LazyHolder {
    public static final Company INSTANCE = new Company();
  }
  
  public static Company getInstance() {
    return LazyHolder.INSTANCE;
  }
}

  개발자가 직접 동기화 문제에 대해 코드를 작성하고 문제를 회피하려 한다면 프로그램 구조가 그만큼 복잡해지고

  비용문제가 발생할 수 있는데다 특히 정확하지 못한 경우가 많다고 한다.

  그런데 이 방법은 JVM의 클래스 초기화 과정에서 보장되는 원자적 특성을 이용하여 싱글턴의 초기화 문제에 대한

  책임을 JVM에게 넘긴다.

  holder안에 선언된 instance가 static이기 때문에 클래스 로딩시점에 한번만 호출되게 되며 final을 사용해 다시 값이

  할당되지 않도록 만드는 방법이다.

  가장 많이 사용하고 일반적인 singleton클래스 사용방법이라고 한다.

 

 

레퍼런스

 패스트캠퍼스 올인원 패키지 - 자바 객체지향프로그래밍

jeong-pro.tistory.com/86

velog.io/@kyle/%EC%9E%90%EB%B0%94-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4-Singleton-Pattern

'JAVA' 카테고리의 다른 글

final 키워드  (0) 2021.02.02
템플릿 메서드(Template Method)  (0) 2021.02.02
인터페이스(Interface)  (0) 2021.01.31
생성자(Constructor)  (0) 2021.01.30
다형성(polymophism)  (0) 2021.01.29

인터페이스(Interface)란?

  극단적으로 동일한 목적하에 동일한 기능을 수행하게끔 강제하는 것이 인터페이스의 역할이자 개념이다.

  자바의 다형성을 극대화하여 개발코드 수정을 줄이고 프로그램 유지보수성을 높이기 위해 인터페이스를 사용한다.

  

  인터페이스의 요소로는 추상메서드, 상수, 디폴트메서드, 정적메서드, private메서드가 있다.

 

  인터페이스가 클래스와 다른점은 추상메서드로만 이루어져 있다는 점이다.

  인터페이스 안에 변수를 선언하게 되면 상수가 된다.

  

  default method, static method, private method는 java8부터 사용할 수 있게 되었다.

  기본적으로 인터페이스가 구현을 갖지 못하니까 중복 구현의 오류가 발생할 수 있고 이것을 방지하기 위해 기본적으로

  제공되는 메서드이다.

 

public interface inTest {
  
  //상수
  public int Num = 0;
  
  //추상메서드
  void absMethod(int a, int b);
  
  //디폴트 메서드
  default void deMethod(int a, int b){
    //구현부
  }
  
  //정적 메서드
  static void stMethod(int a, int b){
    //구현부
  }
}

 

  상수는 인터페이스에서 값을 정해줄것이니 함부로 바꾸지 말고 제공하는 값만 참조하라는 것이다.

  추상메서드는 가이드만 해줄테니 오버라이딩해서 재 구현하라는 것이고

  디폴트메서드는 기본적으로 제공은 해주지만 구현부를 바꾸고 싶다면 재정의하라는 것이다.

  정적메서드는 인스턴스 생성과 상관없이 인터페이스 타입으로 호출하는 메서드이며

  인터페이스에서 제공해주는것으로 무조건 사용해야 한다.

  private메서드는 인터페이스 내에서 사용하기 위해 구현한 메서드이고 구현하는 클래스에서 재정의 할 수 없으며

  java9부터 사용이 가능하다.

 

 

 

인터페이스 문법과 다형성 이해

  인터페이스는 interface 키워드를 통해 선언할 수 있으며 implements 키워드를 통해 일반클래스에서 인터페이스를

  구현할 수 있다.

 

  인터페이스에서는 추상메서드가 사용된다고 했는데 클래스에서와는 다르게 abstract 키워드가 없어도 

  추상메서드가 된다.

  

  인터페이스에 선언했다는 것은 인터페이스에 있는 작업들을 가져다 작업하라고 설계하는 것이라고 볼 수 있다.

 

  클래스를 만들고 인터페이스를 implements 해주면 오버라이드를 해야한다.

  만약 인터페이스에 선언한 추상메서드가 전부 다 필요한것이 아니라면 필요한 메서드만 남겨두고 지운 뒤

  클래스를 abstract으로 만들어주면 된다.

  

  인터페이스를 구현한 클래스는 인터페이스 타입으로 변수를 선언하여 인스턴스를 생성할 수 있다.

  인터페이스는 구현 코드가 없기 때문에 타입상속이라고도 한다.

 

  예제코드

 

/*
  고객센터에서는 전화가 오면 일단 대기열에 저장된다.
  상담원이 지정되기 전까지 대기상태가 되며 각 전화가
  상담원에게 배분되는 정책은 여러 방식으로 구현될 수 있다.
  - 상담원 순서대로 배분
  - 대기가 짧은 상담원 먼저 배분
  - 우선순위가 높은(숙련도가 높은) 상담원에게 먼저 배분
*/

//interface
public interface Scheduler {
  public void getNextCall();
  public void sendCallToAgent();
} //interface End

//impl RoundRobin class
public class RoundRobin implements Scheduler {
  
  @Override
  public void getNextCall(){
    System.out.println("상담전화를 순서대로 대기열에서 가져옵니다.");
  }
  
  @Override
  public void sendCallToAgent(){
    System.out.println("다음 순서의 상담원에게 배분합니다.");
  }
} //RoundRobin End

//impl Priority
public class PriorityAllocation implements Scheduler {
  
  @Override
  public void getNextCall() {
    System.out.println("고객 등급이 높은 고객의 Call을 먼저 가져옵니다.");
  }
  
  @Override
  public void sendCallToAgent() {
    System.out.println("업무 숙련도가 높은 상담원에게 먼저 배분합니다.");
  }
}//Priority End

//impl Least
public class LeastJob implements Scheduler {
  
  @Override
  public void getNextCall() {
    System.out.println("상담전화를 순서대로 대기열에서 가져옵니다.");
  }
  
  @Override
  public void sendCallToAgent() {
    System.out.println("현재 상담업무가 없거나 상담대기가 가장 적은 상담원에게 먼저 배분합니다.");
  }
} //Least End

//Test Class

import java.io.IOException

public class SchedulerTest{
  
  public static void main(String[] args) throws IOException {
    System.out.println("전화 상담원 할당방식을 선택하세요");
    System.out.println("R : 한명씩 차례대로");
    System.out.println("L : 대기가 적은 상담원 우선");
    System.out.println("P : 우선순위가 높은 고객 우선 숙련도 높은 상담원");
    
    int ch = System.in.read();
    Scheduler scheduler = null;
    
    if(ch == 'R' || ch == 'r') {
      scheduler = new RoundRobin();
    }else if(ch == 'L' || ch == 'l') {
      scheduler = new LeastJob();
    }else if(ch == 'p' || ch == 'p') {
      scheduler = new PriorityAllocation();
    }else {
      System.out.println("지원되지 않는 기능입니다");
      return;
    }
    
    scheduler.getNextCall();
    scheculer.sendCallToAgent();
  }//main End
}//Test End

 

 

  하나의 클래스는 여러개의 인터페이스를 상속 받을 수 있다.

  단, 이때 default method의 이름이 중복되는 경우에는 재정의 해야 한다.

 

  인터페이스 간에도 상속이 가능한데 구현이 없으므로 extends 뒤에 여러 인터페이스를 상속 받을 수 있다.

  구현 내용이 없기 때문에 타입상속(Type Inheritance)라고 한다.

 

  예제코드

 

//interface Buy
public interface Buy {
  void buy();
  
  default void order() {
    System.out.println("구매 주문");
  }
} //buy End

//interface Sell
public interface Sell{
  void sell();
  
  default void order(){
    System.out.println("판매 주문");
  }
} //sell End

//impl class
public class Customer implements Buy, Sell {
  
  @Override
  public void buy() {
    System.out.println("customer buy");
  }
  
  @Override
  public void sell() {
    System.out.println("customer sell");
  }
  
  @Override
  public void order() {
    System.out.println("customer order");
  }
  
  public void sayHello() {
    System.out.println("Hello");
  }
} //impl class End

//Test Class
public class CustomerTest {
  public static void main(String[] args) {
    
    Customer customer = new Customer();
    customer.buy();
    customer.sell();
    customer.order();
    customer.sayHello();
    
    Buy buyer = customer;
    buyer.buy();
    buyer.order();
    
    Sell seller = customer;
    seller.sell();
    seller.order();
  }
} //Test class End

  위 예제와 같이 여러 인터페이스를 상속 받을 수 있다.

 

  Buy와 Sell 인터페이스에 order라는 default method 가 동일한 이름을 갖고 있다.

  그래서 Customer클래스에서 둘을 오버라이드 할때 buy와 sell은 문제없이 오버라이드 되지만

  order에 대해서는 Buy인터페이스에 있는 order를 사용할지 sell에 있는 order를 사용할지 정해줘야 한다.

  여기서 재정의하지 않으면 두 인터페이스에서 동일한 디폴트메서드를 갖고 있으므로 어느것을 사용해야 할지 몰라서

  오류가 발생한다.

 

  이클립스와 인텔리제이 두가지에서 오버라이드 했을 때 차이가 발생하는데 이클립스에서는 오버라이드 하도록 하면

  order를 제외한 두 메서드만 오버라이드 한뒤 다시 Buy와 Sell중에서 선택하도록 한다.

  Buy를 선택한다면

  order안에 Buy.super.order(); 가 들어가있는 상태로 생성된다. Sell을 선택하면 당연히 Sell.super.order();가 될것이고.

  보이는 그대로 Buy인터페이스에 있는 order를 쓴다는 것이고 Sell에 있는 order를 쓴다는 것이다.

  인텔리제이는 좀 다른게 그냥 구현부가 하나도 없이 생성된다.

  @Override

  public void order() {  } 이렇게 생성되기 때문에 그냥 넘어가면 안되고 재정의를 해줘야 한다.

 

  예제에서 결과값은

  customer buy

  customer sell

  customer order

  Hello

  customer buy

  customer order

  customer sell

  customer order

  가 출력된다.

 

  결과값을 보면 디폴트 메서드인 order를 구현된 인스턴스의 메서드로 호출되는 것을 확인할 수 있다.

  여기서 Buy의 order를 출력하고 싶다거나 Sell의 order를 출력하고 싶다면 위에 설명했던 것 처럼

  order 재정의를 할 때 Buy.super.order(); 혹은 Sell.super.order(); 로 정의해주면 된다.

 

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 자바 객체지향프로그래밍

limkydev.tistory.com/197

'JAVA' 카테고리의 다른 글

템플릿 메서드(Template Method)  (0) 2021.02.02
싱글톤 패턴(Singleton Pattern)  (0) 2021.02.01
생성자(Constructor)  (0) 2021.01.30
다형성(polymophism)  (0) 2021.01.29
상속(inheritance)  (0) 2021.01.28

생성자(Constructor)

  객체를 생성할 때 new 키워드와 함께 호출한다.

  객체생성 외에는 호출할 수 없으며 인스턴스를 초기화 하는 코드가 구현된다.

  반환값이 없고 상속되지 않으며 생성자는 클래스 이름과 항상 동일해야 한다.

  클래스에는 반드시 생성자가 존재해야 하고 인스턴스 변수의 초기화가 목적이다.

 

 

기본생성자(default Constructor)

  하나의 클래스에는 반드시 생성자가 존재해야 한다고 했다.

  생성자를 구현하지 않으면 컴파일러가 생성자를 만들어주는데 이게 기본 생성자다.

  기본생성자는 매개변수가 없고 구현부가 없으며 만약 클래스에 다른 생성자가 있는 경우

  기본생성자는 제공되지 않는다.

//Customer Class
public class Customer {
  
  int customerId;
  String customerName;
  int price;
  
  /*public Customer(int id, String name) {
     이 생성자를 만들면 컴파일러가 기본생성자를
     만들어주지 않는다.
  }*/
  
  void show(){
    System.out.println(customerId + ", " + customerName + ", " + price);
  }
}

//CustomerTest Class
public class CustomerTest {
  
  public static void main(String[] args) {
    Customer customer = new Customer();
    
    customer.show();
    
    customer.customerId = 123;
    customer.customerName = "A";
    customer.price = 10000;
    
    customer.show();
  }
}

  위 처럼 생성자를 별도로 생성하지 않은 경우는 컴파일 할 때 컴파일러가 알아서 제공해준다.

  실행하게 되면 

  0, null, 0

  123, A, 10000

  이렇게 출력된다. Customer 클래스에서 생성자를 따로 만들지 않았지만 컴파일러가 기본생성자를 만들어서

  처리해줌으로써 정상적으로 처리할 수 있는 것이다.

  단, 매개변수를 받는 생성자를 하나라도 만든다면 컴파일러가 기본생성자를 제공하지 않는다.

  그래서 매개변수를 받는 생성자를 만들었는데 기본생성자도 필요하다면 직접 만들어줘야 한다.

 

//Customer Class
public class Customer {
  
  int customerId;
  String customerName;
  int price;
  
  public Customer() { //기본생성자
  
  }
  
  public Customer(int id, String name) {
    customerId = id;
    customerName = name;
    price = 10000;
  }
  
  public Customer(int id, String name, int p) {
    customerId = id;
    customerName = name;
    price = p;
  }
  
  public Customer(int id) {
    customerId = id;
    customerName = "ABC";
    price = 5000;
  }
  
  void show(){
    System.out.println(customerId + ", " + customerName + ", " + price);
  }
  
}


//MainClass
public class CustomerTest {
  
  public static void main(String[] args) {
    
    Customer customer = new Customer(); //default constructor
    
    customer.show();
    
    customer.price = 10000;
    customer.customerId = 123;
    customer.customerName = "A";
    
    customer.show();
    
    Customer customer1 = new Customer(234, "B"); //id, name 받는 constructor
    
    customer1.show();
    
    Customer customer2 = new Customer(345, "C", 20000); //id, name, price 받는 constructor
    
    customer2.show();
    
    Customer customer3 = new Customer(456); //id 받는 constuctor
    
    customer3.show();
  }
}

  결과값은

  0, null, 0
  123, A, 10000
  234, B, 10000
  345, C, 20000
  456, ABC, 5000

  이렇게 출력된다.

  main클래스를 봤을때 제일 첫 customer는 기본생성자를 호출한것이다.

  그리고 바로 show를 호출해서 아무 값이 들어가지 않은 상태인것을 확인한것이고 

  .price, customerId, customerName으로 값을 넘겨주고 show로 다시 출력해본것이다.

  그래서 처음에는 아무값도 들어가있지 않으니까 0, null, 0이 출력되지만 값을 넘겨주고 나서는

  123, A, 10000 이 출력된다.

 

  customer1은 생성자 중에서 id와 name을 매개변수로 받는 생성자에 연결된다.

  new Customer(234, "B")로 값이 바로 넘어가는 형태다.

  id와 name은 넘겨줬고 price는 생성자에 따로 넣어줬기 때문에 바로 show를 하면 알아서 출력된다.

 

  이렇게 생성자가 여러개 존재할 수 있는데 이렇게 여러개 존재하는 것을 오버로딩이라고 한다.

  단, 생성자는 클래스와 이름이 동일해야 하기 때문에 매개변수가 다 달라야 한다.

'JAVA' 카테고리의 다른 글

싱글톤 패턴(Singleton Pattern)  (0) 2021.02.01
인터페이스(Interface)  (0) 2021.01.31
다형성(polymophism)  (0) 2021.01.29
상속(inheritance)  (0) 2021.01.28
캡슐화(Encapsulation)  (0) 2021.01.27

다형성(polymophism)이란

  하나의 객체가 여러가지 타입을 가질 수 있는 것을 의미한다.

  자바에서는 다형성을 부모클래스 타입의 참조변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하여

  구현하고 있다.

  같은코드에서 여러 실행결과가 나올 수 있으며 객체지향 프로그래밍의 유연성, 재활용성, 유지보수성에 기본이

  되는 특징이다.

  다형성은 상속, 추상화와 더불어 객체지향 프로그래밍을 구성하는 중요한 특징 중 하나이다.

  

  다형성은 다양한 여러 클래스를 하나의 자료형(상위클래스)으로 선언하거나 형변환하여 각 클래스가 동일한

  메소드를 오버라이딩한 경우, 하나의 코드가 다양한 구현을 실행할 수 있다.

  유사한 클래스가 추가되는 경우 유지보수에 용이하고 각 자료형마다 다른 메소드를 호출하지 않으므로 코드에서

  많은 if문이 사라진다.

 

 

참조변수의 다형성

  자바에서는 다형성을 위해 부모클래스 타입의 참조변수로 자식 클래스 타입의 인스턴스를 참조할 수 있다.

//Animal Class
class Animal{
	public void move() {
    
    }
}

//Human Class
class Human extends Animal {
  public void move() {
  
  }
  
  public void readBooks() {
  
  }
}

//Tiger Class
class Tiger extends Animal {
  public void move() {
  
  }
  
  public void hunting() {
  
  }
}

//main
public class AnimalTest {

  public static void main(String[] args) {
  
    Animal animal = new Animal();
    Animal hAnimal = new Human();
    Animal tAnimal = new Tiger();
    Human human = new Animal(); //오류발생
  }
}
    

 

  Human과 Tiger는 Animal을 상속받고 있다.

  메인에서보면 상위클래스인 Animal 타입으로 하위클래스인 Human과 Tiger를 참조할 수 있도록 하고 있다.

  제일 처음에는 Animal타입으로 Animal을 참조하니까 당연히 가능하고

  두번째와 세번째는 상위클래스타입으로 하위클래스를 참조하기 때문에 가능하다.

  하지만 마지막의 경우는 하위클래스 타입으로 상위클래스를 참조하기 때문에 오류가 발생하는것인데

  코드를 보면 하위클래스인 Human은 move(), readBooks() 두개의 메서드를 갖고 있지만

  Animal은 move()하나만을 갖고 있다.

  그럼 Human타입이면 move와 readBooks를 참조할 수 있어야 한다는 건데 Animal에서 readBooks를 찾을 수 없기

  때문에 참조할 수 없게 되는 것이다.

 

  그리고 hAnimal의 경우 Animal타입으로 참조하기 때문에 move만을 사용할 수 있게 된다.

  readBooks를 사용하는 경우  ((Human) hAnimal).readBooks(); 이렇게 사용하거나

  Human human = (Human)hAnimal;

  human.readBooks();

  이렇게 다운캐스팅 해서 사용할 수 있다.

  

  만약 hAnimal을

  Tiger human = (Tiger)hAnimal;

  human.hunting();

  이렇게 캐스팅을 하게되면 실행하기 전에는 오류가 발생하지 않지만 실행하게 되면

  ClassCastException이 발생한다. Human에 대한 인스턴스인데 Tiger로 강제 캐스팅을 하려고 했기 때문에

  에러가 나는 것이다.

  

 

instanceof

  다형성으로 인해 런타임에 참조변수가 실제로 참조하고 있는 인스턴스의 타입을 확인할 필요성이 생긴다.

  instanceof는 참조변수가 참조하고 있는 인스턴스의 실제 타입을 확인할 수 있도록 해준다.

public class AnimalTest {

  public static void main(String[] args) {
  
    Animal animal = new Animal();
    Animal hAnimal = new Human();
    Animal tAnimal = new Tiger();
    
    
    if(hAnimal instanceof Tiger){
      Tiger human = (Tiger)hAnimal;
      human.hunting();
    }else if(hAnimal instanceof Human){
      Human human = (Human)hAnimal;
      human.readBooks();
    }else{
      System.out.println("Error!");
    }
    
  }
}

  위 코드에서 보자면 hAnimal의 인스턴스가 Tiger라면 이라는 조건문이다.

  hAnimal의 인스턴스는 Human이므로 false를 반환한다.

  예제는 다 직접 작성했지만 매개변수로 넘어왔다거나 한다면 잘못넘어오는 경우가 있을 수 있다.

  이렇게 instanceof를 활용하면 조건에 따라 처리할 수 있으므로 ClassCastException을 피해 실행할 수 있다.

 

 

예제코드

import java.util.ArrayList;

class Animal{
  public void move(){
    System.out.println("동물이 움직입니다.");
  }
}

class Human extends Animal{
  public void move(){
    System.out.println("사람이 걷습니다.");
  }

  public void readBooks(){
    System.out.println("사람이 책을 읽습니다.");
  }
}

class Tiger extends Animal{
  public void move(){
    System.out.println("호랑이가 네발로 뜁니다.");
  }

  public void hunting(){
    System.out.println("호랑이가 사냥을 합니다.");
  }
}

class Eagle extends Animal{
  public void move(){
    System.out.println("독수리가 날아갑니다.");
  }

  public void flying(){
    System.out.println("독수리가 멀리 날아갑니다.");
  }
}

public class AnimalTest {

  public static void main(String[] args) {
    Animal hAnimal = new Human();
    Animal tAnimal = new Tiger();
    Animal eAnimal = new Eagle();

    ArrayList<Animal> animalList = new ArrayList<Animal>();

    animalList.add(hAnimal);
    animalList.add(tAnimal);
    animalList.add(eAnimal);

    AnimalTest test = new AnimalTest();
    test.testDownCasting(animalList);

    System.out.println("-------------------------------");

    for(Animal animal : animalList){
      animal.move();
    }
  }//main end
  
  public void testDownCasting(ArrayList<Animal> list){
    for(int i = 0; i < list.size(); i++){
      Animal ani = list.get(i);

      if(ani instanceof Human){
        Human human = (Human)ani;
        human.readBooks();
      }else if(ani instanceof Tiger){
        Tiger tiger = (Tiger)ani;
        tiger.hunting();
      }else if(ani instanceof Eagle){
        Eagle eagle = (Eagle)ani;
        eagle.flying();
      }else{
        System.out.println("Error!");
      }
    }//for end
  }//testDownCasting end
}

  결과값

    사람이 책을 읽습니다.
    호랑이가 사냥을 합니다.
    독수리가 멀리 날아갑니다.
    -------------------------------
    사람이 걷습니다.
    호랑이가 네발로 뜁니다.
    독수리가 날아갑니다.

 

 

 

 

레퍼런스

● 패스트캠퍼스 올인원 패키지 - 자바 객체지향프로그래밍

www.tcpschool.com/java/java_polymorphism_concept

'JAVA' 카테고리의 다른 글

인터페이스(Interface)  (0) 2021.01.31
생성자(Constructor)  (0) 2021.01.30
상속(inheritance)  (0) 2021.01.28
캡슐화(Encapsulation)  (0) 2021.01.27
추상화(Abstraction)  (0) 2021.01.27

상속

  클래스에서 상속의 의미는 새로운 클래스를 정의할 때 이미 구현된 클래스를 상속(inheritance)받아서 속성이나

  기능이 확장되는 클래스를 구현하는 것이다.

 

  class B extends A { }

  B클래스는 A클래스를 상속받아 사용하겠다는 것이다.

  여기서 A클래스가 상위클래스가 되고 B클래스가 하위클래스가 된다.

  객체지향에서 코드의 재사용이라고 보기는 어렵다. 재사용과는 다르다.

 

  상위클래스는 하위클래스보다 일반적인 개념과 기능을 갖고

  하위클래스는 상위클래스보다 구체적인 개념과 기능을 갖는다.

 

  상속에 대해 배울때마다 많이 본 예시가 포유류 - 사람 의 관계이다.

  사람은 포유류에 속하지만 모든 포유류는 사람이 아니다.

  사람은 포유류 중 하나의 개체가 되는것이고 사람만의 특징이 있다.

  상속의 개념으로 보자면 사람은 포유류의 공통적인 개념과 기능을 상속받아 그 개념과 기능을 갖고 있으며

  사람만의 특징을 추가적으로 갖게 된다.

  그럼 사람이라는 하위클래스는 포유류의 공통적인 개념과 기능을 갖고 있으면서 하위클래스에서는 사람만의

  특징을 추가적으로 더 갖게 되는것이다.

 

 

  자바에서는 single inheritance만을 지원하기 때문에 하위클래스에서 extends 뒤에는 하나의 class만 사용할 수 있다.

  

상속 구현 예제

  고객의 등급에 따른 차별화된 서비스를 제공하기 위함이다.

  각 기능은 따로 구현하지 않고 

 

  Customer class

public class Customer {

  protected int customerId;
  protected String customerName;
  protected String customerGrade;

  //Constructor
  public Customer(int customerId, String customerName){
    this.customerId = customerId;
    this.customerName = customerName;

    customerGrade = "Silver";
  }

  //getter setter
  public int getCustomerId() {
    return customerId;
  }

  public void setCustomerId(int customerId) {
    this.customerId = customerId;
  }

  public String getCustomerName() {
    return customerName;
  }

  public void setCustomerName(String customerName) {
    this.customerName = customerName;
  }

  public String getCustomerGrade() {
    return customerGrade;
  }

  public void setCustomerGrade(String customerGrade) {
    this.customerGrade = customerGrade;
  }

  @Override
  public String toString() {

    return "고객번호 " + customerId + " " + customerName + "님의 등급은 "
    + customerGrade + "이며, 적립된 보너스 포인트는 " + bonusPoint + "점 입니다.";
  }
}

  GoldCustomer Class

public class GoldCustomer extends Customer{

  public GoldCustomer(int customerId, String customerName){
    super(customerId, customerName);

    customerGrade("Gold");
  }
}

  VIPCustomer Class

public class VIPCustomer extends Customer{

  public VIPCustomer(int customerId, String customerName) {
    super(customerId, customerName);

    customerGrade("VIP");
  }
}

  CustomerTest Class

public class CustomerTest {

  public static void main(String[] args) {

    Customer customerHong = new Customer(10010, "홍길동");
    customerHong.bonusPoint = 1000;
    System.out.println(customerHong.toString());

    GoldCustomer customerLee = new GoldCustomer(10020, "이순신");
    customerLee.bonusPoint = 5000;
    System.out.println(customerLee.toString());


    VIPCustomer customerKim = new VIPCustomer(10030, "김유신");
    customerKim.bonusPoint = 10000;
    System.out.println(customerKim.toString());
  }
}

  홍길동님의 등급은 Silver이며, 적립된 보너스 포인트는 1000점 입니다. 
  이순신님의 등급은 Gold이며, 적립된 보너스 포인트는 5000점 입니다.
  김유신님의 등급은 VIP이며, 적립된 보너스 포인트는 10000점 입니다.

  이렇게 출력된다.

 

  각 등급클래스들은 Customer를 상속받아 Id와 Name, bonusPoint를 처리하고 Grade를 재정의한다.

  그리고 그 정보들을 toString을 통해 출력하게 된다.

   

  Customer 클래스에서 각 변수들은 protected로 처리함으로써 상속받은 클래스들은 접근할 수 있지만

  상속받지 않은 외부클래스에서는 접근할 수 없다.

 

 

상속에서 클래스 생성과정과 형변환

  하위클래스가 생성될 때 상위클래스가 먼저 생성된다.

  상위클래스의 생성자가 호출 된 다음 하위클래스의 생성자가 호출되며

  하위클래스의 생성자에서는 무조건 상위클래스의 생성자가 호출되어야 한다.

 

  하위 클래스에서 상위클래스의 생성자를 호출하는 코드가 없는 경우 컴파일러는 상위클래스의 기본 생성자를

  호출하기 위한 super()를 실행한다.

  하지만 상위클래스에 기본생성자가 없는 경우(매개변수가 있는 생성자만 존재하는 경우)에는 하위클래스는

  명시적으로 상위클래스의 생성자를 호출해야 한다.

//Customer Class
public class Customer {
  protected int customerId;
  protected String customerName;
  protected String customerGrade;
  
  //default Constructor
  public Customer() {
    customerGrade = "Silver";
  }
  
  //getter setter 구현
  
  //toString 구현
}


//VIPCustomer Class
public class VIPCustomer extends Customer {
  
  public VIPCustomer() {
    //컴파일러가 super();을 추가해 컴파일 수행
    customerGrade = "VIP";
  }
}

  이와 같은 상황에서 Customer에는 기본생성자가 존재하고 VIPCustomer에서는 Customer를 따로 호출하도록 하지

  않았다면 컴파일러가 super()를 추가해 실행함으로써 알아서 Customer의 기본생성자가 호출되는 것이다.

 

  매개변수가 있는 생성자만 존재하고 기본생성자가 존재하지 않는다면 명시적으로 호출해야 한다고 했는데

  상속 예제에 구현한게 명시적으로 호출한 경우이다.

//Customer Class
public class Customer {
  
  protected int customerId;
  protected String customerName;
  protected String customerGrade;
  
  //Constructor
  public Customer(int customerId, String customerName) {
    this.customerId = customerId;
    this.customerName = customerName;
    
    customerGrade = "Silver";
  }
  
  //getter setter
  
  //toString
}


//VIPCustomer Class
public class VIPCustomer extends Customer {
  
  public VIPCustomer(int customerId, String customerName) {
    super(customerId, customerName);
    
    customerGrade = "VIP";
  }
}

  위와 같이 Customer 클래스에는 기본생성자가 없이 매개변수가 들어있는 생성자만이 존재한다.

  그럼 VIPCustomer 클래스에서 명시하지 않는다면 컴파일러는 super()로 Customer의 기본생성자를 찾을것이고

  기본생성자가 존재하지 않기 때문에 오류가 발생한다.

  그렇기 때문에 super(customerId, customerName) 이렇게 매개변수를 넣어줌으로써 이 두 매개변수를 갖고 있는

  생성자를 호출하라고 명시적으로 호출해야 한다.

  매개변수를 넣지 않는다면 현재 Customer는 int와 String을 받고 있으니

  super(0, "a"); 이렇게 호출할 수도 있다.

 

  상위클래스로의 묵시적 형 변환(업캐스팅)

    상위클래스형으로 변수를 선언하고 하위클래스 인스턴스를 생성할 수 있다.

    하위클래스는 상위클래스의 타입을 내포하고 있으므로 상위클래스로 묵시적 형변환이 가능하다.

    상속관계에서 모든 하위 클래스는 상위클래스로 묵시적 형변환이 되지만

    상위클래스는 하위클래스로 묵시적 형변환이 성립하지 않는다.

 

    Customer vc = new VIPCustomer();

 

    이렇게 사용할 수 있으며 상속받아 쓴다는 것은 상황에 따라 다르겠지만 Customer에 있는 것들을 사용할 수 있다는

    것인데 VIPCustomer에서 확장해 사용한다는 의미이다.

    그렇기 때문에 Customer에서는 VIPCustomer처럼 사용할 수 없는 것이다.

    

    예제를 예로 들자면 Customer에는 id, name, grade가 존재한다. 그리고 VIPCustomer에서 할인율인 salesRatio가 

    추가되어서 할인율을 처리하도록 확장했다고 한다면

    VIPCustomer는 상속을 받는 클래스이기 때문에 Customer의 모든것을 사용할 수 있지만 Customer는 상속을

    해주는 입장이지 상속을 받는 클래스가 아니기 때문에 할인율에 대한 처리를 혼자서 할 수 없는 것이다.

 

    단, Customer vc = new VIPCustomer() 이렇게 업캐스팅한다면 vc로 접근할 수 있는 범위는 Customer로 한정된다.

    즉, vc. 으로 사용할 수 있는 것은 Customer에 있는 id, name, grade만 가능하고 VIPCustomer에 추가한

    salesRatio는 사용할 수 없다.

    방법이 아예 없는것은 아니다. ((VIPCustomer) vc).salesRatio 이렇게 사용할 수 있지만 이렇게 사용할거면

    굳이 Customer로 업캐스팅해서 사용할 필요가 없기 때문에 불필요하다고 볼 수 있다.

    물론 상황에 따라 다르겠지만..

 

 

'JAVA' 카테고리의 다른 글

생성자(Constructor)  (0) 2021.01.30
다형성(polymophism)  (0) 2021.01.29
캡슐화(Encapsulation)  (0) 2021.01.27
추상화(Abstraction)  (0) 2021.01.27
클래스(Class)와 인스턴스(Instance)  (0) 2021.01.26

캡슐화(Encapsulation)란?

  캡슐화의 정의는 필요한 속성(Attribute)와 행위(Method)를 하나로 묶고 그 중 일부를 외부에서 사용하지

  못하도록 은닉하는것이다.

  중요목적은 중요한 데이터를 보존, 보호하기 위해 사용하는 것이다.

  즉, 캡슐화는 클래스에 담는 내용중 중요한 데이터나 기능을 외부에서 접근하지 못하게 하기 위해 사용한다.

 

  객체에 직접적인 접근을 막고 외부에서 내부의 정보에 직접적으로 접근하거나 변경할 수 없고, 객체가 제공하는

  필드와 메소드를 통해서만 접근이 가능하다.

 

  캡슐화는 객체의 세부내용이 외부에 은폐(정보은닉)되어, 변경이 발생할 때 오류 발생을 최소화 할 수 있다는

  장점이 있다.

  그리고 데이터가 변경되어도 다른 객체에 영향을 주지 않기 때문에 독립성이 좋고 처리된 결과사용으로

  이식성이 좋으며 객체를 모듈화 할 수 있어 새로운 시스템의 구성에 하나의 모듈처럼 사용이 가능하다.

 

접근제어자

  캡슐화를 하기 위해서는 접근제어자를 통해 설계가 잘 이루어져야 한다.

  자신 내부의 모듈은 감추고, 다른 모듈의 내부작업도 직접적으로 개입하지 못하도록 설계해야 한다.

 

접근제어자

제어자(modifier)   제어자(modifier)란 클래스와 클래스 멤버의 선언 시 사용하여 부가적인 의미를 부여하는 키워드를 의미한다. 자바에서 제어자는 접근제어자(access modifier)와 기타 제어자로 구분

myyoun.tistory.com

 

캡슐화 예제

 

public class Member {
  
  private String memberId;
  private String memberName;
  private String memberAddr;
  
  public String getMemberId() {
    return memberId;
  }
  
  public void setMemberId(String memberid) {
    this.memberId = memberId;
  }
  
  public String getMemberName() {
    return memberName;
  }
  
  public void setMemberName(String memberName) {
    this.memberName = memberName;
  }
  
  public String getMemberAddr() {
    return memberAddr;
  }
  
  public void setMemberAddr(String memberAddr) {
    this.memberAddr = memberAddr;
  }
}

  Member클래스는 Id, Name, Addr을 모두 private으로 선언했으므로 Member클래스 내부에서만 접근이 가능하다.

  그래서 외부클래스에서의 접근을 위해 setter와 getter를 만들어 접근할 수 있도록 해주었다.

  

public class MemberTest {
  public static void main(String[] args) {
    Member mem = new Member();
    
    String id = mem.getMemberId();
    String name = mem.getMemberName();
    String addr = mem.getMemberAddr();
    
    System.out.println("id : " + id + ", name : " + name + ", addr : " + addr);
    
    mem.setMemberId("hong");
    mem.setMemberName("홍길동");
    mem.setMemberAddr("서울");
    
    String id1 = mem.getMemberId();
    String name1 = mem.getMemberName();
    String addr1 = mem.getMemberAddr();
    
    System.out.println("id : " + id1 + ", name : " + name1 + ", addr : " + addr1);
    
  }
}

  이 클래스를 실행하면

  id : null, name : null, addr : null

  id : hong, name : 홍길동, addr : 서울

  이렇게 출력된다.

  Member클래스에서 외부의 접근을 위해 getter와 setter를 만들어 주었다고 했는데 외부클래스인 MemberTest에서

  get, set을 이용해 값을 불러오고 넣어준것이다.

  애초에 Member클래스를 보면 모든 변수가 기본값이 없기 때문에 null이 출력되지만

  set을 이용해 값을 넣어주고 다시 불러와 출력하게 되면 넣어준 값들을 볼 수 있다.

  

  만약 Member클래스에서 get만 만들어놓고 set은 만들지 않았다면 수정이 불가능한 읽기만 가능한 read-only상태가

  된다. 그럼 당연히 Test클래스에서는 위 예제처럼 set으로 수정할 수 없다.

  변경은 해줘야 하는 데이터인지 아니면 절대 변경되어서는 안되는 데이터인지에 따라 설계해야 한다.

 

 

'JAVA' 카테고리의 다른 글

다형성(polymophism)  (0) 2021.01.29
상속(inheritance)  (0) 2021.01.28
추상화(Abstraction)  (0) 2021.01.27
클래스(Class)와 인스턴스(Instance)  (0) 2021.01.26
객체지향프로그래밍(Object Oriented Programming, OOP)  (0) 2021.01.25

+ Recent posts