자바 문법 종합반

more·2023년 5월 25일

자바 문법 종합반

목록 보기
3/4

객체 지향 프로그래밍

  • 객체 : 세상에 존재하는 물체, 식별이 가능한 것 (물리적 혹은 개념적인 것)
    속성 (필드) + 행위 (메서드) 로 이루어져 있다.
    ex) 자동차 = 속성 (회사, 모델, 색상, 가격, 속도 등) + 행위 (가속, 브레이크, 기어변속, 조명, 경적등)

  • 객체 간의 관계

    • 사용 관계

      사람이 자동차를 사용

    • 포함 관계

      타이어 객체, 차문 객체, 핸들 객체는 자동차 객체에 포함

    • 상속 관계

      자동차와 기차 객체는 하나의 공통된 기계시스템 객체를 토대로 만들어진다고 가정
      자동차 객체와 기차 객체는 기계시스템 객체를 상속 받는 상속 관계

  • 객체 지향 프로그래밍의 특징

    • 캡슐화
      • 속성 (필드)와 행위 (메서드)를 하나로 묶어 객체로 만든 후 실제 내부 구현 내용은 외부에서 알 수 없게 감추는 것
      • 외부 객체에서는 캡슐화된 객체의 내부 구조를 알 수 없기 때문에 노출시켜 준 필드 혹은 메서드를 통해서만 접근 가능
      • 필드와 메서드를 캡슐화 하여 숨기는 이유는 외부 객체에서 해당 필드와 메서드를 잘못 사용하여 객체가 변화하지 않게 하는데 있다. -> 보안 (남들이 함부로 값을 변경하지 못하도록)
      • Java에서는 캡슐화된 객체의 필드와 메서드를 노출 시킬지 감출 지 결정하기 위해 접근 제어자를 사용

    • 상속
      • 부모 객체가 자신이 가지고 있는 필드와 메서드를 자식 객체에 물려주어 자식 객체가 이를 사용할 수 있도록 만드는 것
      • 상속의 장점
        1. 각각의 객체들을 상속 관계로 묶음으로써 객체 간의 구조를 파악하기 쉬워집니다.
        2. 필드와 메서드를 변경하는 경우 부모 객체에 있는 것만 수정하게 되면 자식 객체 전부 반영이 되기 때문에 일관성을 유지하기 좋습니다.
        3. 자식 객체가 부모 객체의 필드와 메서드를 물려받아 사용할 수 있기 때문에 코드의 중복이 줄어들며 코드의 재사용성이 증가됩니다.

    • 다형성
      • 객체가 연산을 수행할 때 하나의 행위에 대해 각 객체가 가지고 있는 고유한 특성에 따라 다른 여러가지 형태로 재구성되는 것 (오버라이딩 같음...)

    • 추상화
      • 객체에서 공통된 부분들을 모아 상위 개념으로 새롭게 선언하는 것
      • 예를 들면 자동차의 속성과 행위를 가진 가상의 자동차 모델을 만들어서 그것을 토대로 각 자동차의 설계도를 완성해 나가는 것

📌 객체는 현실에 있는 자동차, 클래스는 그 자동차를 만들기 위한 설계도, 그 설계도대로 만들어진 객체를 인스턴스라고 한다.

  • 클래스 설계
    1. 만들려고 하는 설계도를 선언(클래스 선언)
    2. 객체가 가지고 있어야할 속성(필드)을 정의
    3. 객체를 생성하는 방식을 정의(생성자)
    4. 객체가 가지고 있어야할 행위(메서드)를 정의

  • 객체 생성
new Car(); // new 라는 키워드를 통해서 클래스로부터 객체를 생성 -> 기본 생성자
Car car1 = new Car(); // Car클래스의 객체인 car1 인스턴스 생성
Car[] cars = new Car[5]; // 객체도 배열로 생성 가능
  • 필드

    • 객체의 데이터를 저장하는 역할

      - 고유 데이터 : 객체가 가지고 있는 고유한 데이터
      - 상태 데이터 : 객체의 상태를 의미하는 데이터
      - 객체 데이터 : 객체로 이루어진 데이터

    • 필드의 초기값과 초기화
      - 기본적으로 초기값을 제공하지 않을 경우 객체가 생성될 때 자동으로 기본값으로 초기화


    • 필드 사용

      
        Car car = new Car(); 
        car.color = "blue"; // 외부 접근
      
        double brakePedal() { // 내부 접근
            speed = 0;
            return speed;
        }	
      
        외부에서 필드에 접근해서 값을 바꾸는가, 클래스 내부의 메서드가 필드 값을 변경하는 가의 차이 인 거 같음
  • 메서드

    • 객체의 행위, 객체간의 협력을 위해 사용

      리턴타입 메서드명(매개변수, ...) {
                   실행할 코드 작성
      } // 선언
      
      car.gasPedal(100, 'D'); // 호출 (외부 접근)
      
      double gasPedal(double kmh, char type) {	// 호출 (내부 접근)
          changeGear(type);
          speed = kmh;
          return speed;
      }
  • 오버로딩 (println도 오버로딩임)

    • 하나의 메서드 이름으로 여러 기능을 구현하도록 하는 기능
    • 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도, 매개변수의 개수 또는 타입, 순서가 다르면 동일한 이름을 사용해서 메서드를 정의할 수 있다.
    • 조건
      • 메서드의 이름이 같고, 매개변수의 개수 or 타입 or 순서가 달라야한다.
      • '응답 값 (return value)만' 다른 것은 오버로딩을 할 수 없다.
      • 접근 제어자만 다른 것도 오버로딩을 할 수 없다.
      • 오직 매개변수의 차이로만 구현
    • 장점
      • 메서드 이름 하나로 상황에 따른 동작을 개별로 정의할 수 있다.
      • 메서드의 이름을 절약할 수 있습니다.

  • 기본형 매개변수와 참조형 매개변수의 차이

    • 기본형 매개변수는 지정된 값이 매개변수로 복사되어서 넘겨지기 때문에 변수의 원본 값이 변경되지는 않는다.
    • 참조형 매개변수는 지정된 값의 주소값을 넘겨주기 때문에 변수의 원본 값을 변경 할 수 있다.

  • 멤버 = 필드 + 메서드

    • 인스턴스 멤버 = 인스턴스 필드 + 인스턴스 메서드
    • 클래스 멤버 = 클래스 필드 + 클래스 메서드
  • 인스턴스 멤버

    • 인스턴스 멤버는 객체를 생성해야 사용할 수 있다
    • 객체의 인스턴스 필드는 각각의 인스턴스 마다 고유하게 값을 가질 수 있다.
    • 객체가 인스턴스화 할 때마다 객체의 메서드들은 인스턴스에 포함되어 매번 생성이 될까?
      • 매번 저장한다면 중복 저장으로 인해 메모리 효율이 매우 떨어지기 때문에 메서드는 메서드 영역에 두고서 모든 인스턴스들이 공유해서 사용
      • 대신 무조건 객체를 생성 즉, 인스턴스를 통해서만 메서드가 사용될 수 있도록 제한을 걸어둔 것

  • 클래스 멤버

  • 클래스는 Java의 클래스 로더에 의해 메서드 영역에 저장되고 사용
  • 클래스 멤버란 메서드 영역의 클래스와 같은 위치에 고정적으로 위치하고 있는 멤버
  • 객체의 생성 필요없이 바로 사용이 가능
    -> 메서드로 사용시, 객체.method()로 사용하지 말고 Class.method()로 사용하는 편이 낫다.
  • 필드와 메서드를 클래스 멤버로 만들기 위해서는 static 키워드를 사용
    • 일반적으로 인스턴스마다 모두 가지고 있을 필요없는 공용적인 데이터를 저장하는 필드는 클래스 멤버로 선언하는 것이 좋다.
    • 인스턴스 필드를 사용하지 않고 실행되는 메서드가 존재한다면 static 키워드를 사용하여 클래스 메서드로 선언하는 것이 좋다.
  • 주의할 점
    • 클래스 멤버로 선언된 메서드는 인스턴스 멤버를 사용할 수 없다.
    • 반대로 인스턴스 멤버로 선언된 메서드는 클래스 멤버를 사용할 수 있다.
    • 클래스 멤버는 객체 생성없이 바로 사용 가능하기 때문에 객체가 생성되어야 존재할 수 있는 인스턴스 멤버를 사용할 수 없다.
      -> 클래스 멤버는 .java를 .class로 바꾸어 줄 때 만들어지고, 인스턴스 멤버는 runtime시에 만들어지기 때문

  • 지역변수

    • 메서드 내부에 선언한 변수
    • 메서드가 실행될때마다 독립적인 값을 저장하고 관리
    • 지역 변수는 메서드 내부에서 정의될때 생성되어 메서드가 종료될 때까지만 유지
      -> 메서드가 종료되거나 메서드의 범위 {} 를 넘어갈 경우 존재하지 않는다.

  • final 필드

    • 초기값이 저장되면 해당값을 프로그램이 실행하는 도중에는 절대로 수정할 수 없다.
    • 반드시 초기값을 지정해야한다.
    • C언어에서 const라고 생각하면 될 거 같다. (상수)

  • 생성자

    • 객체가 생성될 때 호출되며 객체를 초기화하는 역할
    • 반환 타입이 없고 이름은 클래스의 이름과 동일
    • new 할때 생성된다
    • 기본 생성자
      • 괄호( ) 안에 아무것도 넣지않는 생성자
      • 생성자를 만들지 않았다면 컴파일러가 자동으로 만들어줌
        -> 하나라도 생성자를 만들었다면 만들어주지 않음
      • 컴파일러에 의해 생성되면 해당 클래스의 접근 제어자를 따른다.
    • 오버로딩 가능

  • this

    • 인스턴스 자신을 표현하는 키워드
    • 객체 내부 생성자 및 메서드에서 객체 내부 멤버에 접근하기 위해 사용
    • 객체의 메서드에서 리턴타입이 인스턴스 자신의 클래스 타입이라면 this를 사용하여 인스턴스 자신의 주소를 반환
  • this()

    • 인스턴스 자신의 생성자를 호출하는 키워드
    • 객체 내부 생성자 및 메서드에서 해당 객체의 생성자를 호출하기 위해 사용
    • 생성자를 통해 객체의 필드를 초기화할 때 중복되는 코드를 줄여준다.
    • this() 키워드를 사용해서 다른 생성자를 호출할 때는 반드시 해당 생성자의 첫 줄에 작성

  • 접근제어자

    • 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미를 부여

    • 클래스 내부에 선언된 데이터를 보호하기 위해서 사용 (함부로 데이터에 접근하여 수정하지 못하게 하기 위해서)

    • 종류

      • 접근 제어자 : public, protected, default, private
        • public : 접근 제한이 전혀 없다.
        • protected : 같은 패키지 내에서, 다른 패키지의 자손클래스에서 접근이 가능
        • default : 같은 패키지 내에서만 접근이 가능
          • 클래스, 멤버변수, 메서드, 생성자에 사용되고, 지정되어 있지 않다면 default
        • private : 같은 클래스 내에서만 접근이 가능
      • 그 외 제어자 : static, final, abstract
      • 사용 가능한 부분
        • 클래스 : public, default, final, abstract
        • 메서드 : public, protected, default, private, final, abstract, static
        • 멤버변수 : public, protected, default, private, final, static
        • 지역변수 : final
      • 접근제어자는 하나만 사용가능, 그 외 제어자는 여러개 사용 가능
      • 주의점
        • 메서드에 staticabstract를 함께 사용할 수 없다.
        • 클래스에 abstractfinal을 동시에 사용할 수 없다.
        • abstract메서드의 접근 제어자가 private일 수 없다.
        • 메서드에 privatefinal을 같이 사용할 필요는 없다.
    • 이제부터 클래스에 접근시에는 은닉성을 위해서 getter랑 setter를 만들어서 접근한다.

      • getter
        • private으로 지정된 필드 값을 리턴해준다.
      • setter
        • private으로 지정된 필드 값을 변경하기 위해서 사용

  • package

    • 클래스의 일부분이면서 클래스를 식별해 주는 용도로 사용
    • package 상위패키지.하위패키지;
    • 디렉토리로 이루어진 경로라고 보면 될 듯

  • import

    • 다른 패키지에 있는 클래스를 사용하기 위해 명시하는 키워드
    • 클래스 이름을 생략하고 * 를 사용하여 표현하면 아래에 있는 모든 클래스를 사용할 수 있다.
  • 상속

    • 부모 클래스의 필드와 메서드를 자식 클래스에게 물려주는 행위
    • 적은 양의 코드로 새로운 클래스를 작성할 수도 있고, 공통적인 코드를 관리하여 코드의 추가와 변경이 쉬워진다.
    • 코드의 중복이 제거되고 재사용성이 크게 증가하여 생산성과 유지보수성에 매우 유리
    • 상속의 키워드는 extends
      -> 확장의 개념으로 이해해야한다.
      1. 부모 클래스에 새로운 필드와 메서드가 추가되면 자식 클래스는 이를 상속받아 사용할 수 있다.
      2. 자식 클래스에 새로운 필드와 메서드가 추가되어도 부모 클래스는 어떠한 영향도 받지 않는다.
      3. 따라서 자식 클래스의 멤버 개수는 부모 클래스보다 항상 같거나 많다.
    • 클래스 간의 관계로 이해
      • 상속관계 : is - a (”~은 ~(이)다”)
      • 포함관계 : has - a (”~은 ~을(를) 가지고 있다”)
    • 다중 상속은 허용하지 않는다. 자식 클래스에서 상속받는 부모 클래스의 멤버들 간의 구분이 불가능하기 때문
    • 클래스나 method에 final 키워드를 추가하면 상속을 받을 수 없다.
    • ex) Object
      -> Java 내 모든 클래스들의 최상위 부모 클래스
      -> 모든 클래스는 Object의 메서드를 사용 가능
      -> 부모 클래스가 없는 자식 클래스는 컴파일러에 의해 자동으로 Object 클래스를 상속
      -> Object clone() : 해당 객체의 복제본을 생성하여 반환함.
      -> boolean equals(Object object) : 해당 객체와 전달받은 객체가 같은지 여부를 반환함.
      -> Class getClass() : 해당 객체의 클래스 타입을 반환함.
      -> int hashCode() : 자바에서 객체를 식별하는 정수값인 해시 코드를 반환함.
      -> String toString() : 해당 객체의 정보를 문자열로 반환함. & Object 클래스에서는 클래스이름 @해쉬코드값 리턴함.
  • 오버라이딩

    • 부모 클래스로부터 상속받은 메서드의 내용을 재정의 하는 것
    • 부모 클래스의 메서드를 그대로 사용 가능하지만 자식 클래스의 상황에 맞게 변경을 해야하는 경우 사용
    • 조건
      1. 선언부가 부모 클래스의 메서드와 일치해야 합니다.
      2. 접근 제어자를 부모 클래스의 메서드 보다 좁은 범위로 변경할 수 없습니다.
      3. 예외는 부모 클래스의 메서드 보다 많이 선언할 수 없습니다.

    • super
      • 부모 클래스의 멤버를 참조할 수 있는 키워드 (this가 본인 클래스면 super는 부모 클래스라고 이해하면 될듯)
      • 객체 내부 생성자 및 메서드에서 부모 클래스의 멤버에 접근하기 위해 사용
      • 자식 클래스 내부에서 선언한 멤버와 부모 클래스에서 상속받은 멤버와 이름이 같을 경우 이를 구분하기 위해 사용
    • super()
      - 부모 클래스의 생성자를 호출할 수 있는 키워드 (this()랑 비교해서 외워둘 것)
      - 객체 내부 생성자 및 메서드에서 해당 객체의 부모 클래스의 생성자를 호출하기 위해 사용
      - 자식 클래스의 객체가 생성될 때 부모 클래스들이 모두 합쳐져서 하나의 인스턴스가 생성
      * 이 때 부모 클래스의 멤버들의 초기화 작업이 먼저 수행이 되어야함
      - 자식 클래스의 생성자에서는 부모 클래스의 생성자가 호출
      - 부모 클래스의 생성자는 가장 첫 줄에서 호출 되어야함

  • 다형성 (참조변수의 타입 변환)

    • 여러 가지 형태를 가질 수 있는 능력

    • 참조변수 타입변환을 활용해서 다형성을 구현

    • 매개변수에도 다형성이 적용

    • 반환타입에도 다형성이 적용

    • instance of

      • 다형성 기능으로 인해 해당 클래스 객체의 원래 클래스명을 체크할 때 필요
      • {대상 객체} instance of {클래스 이름}
      • 내가 의도한 클래스로 만든 객체인지 확인할 때 사용

  • 추상 클래스

    • 미완성된 설계도
    • abstract 키워드를 사용하여 선언
    • 자식 클래스에 상속되어 자식 클래스에 의해서만 완성
    • 여러개의 자식 클래스들에서 공통적인 필드나 메서드를 추출
    • 예를 들면 현대 자동차, 테슬라 자동차, 포드 자동차 등의 설계도인 클래스가 각각 따로 존재하는데, 각 클래스가 가지고 있는 공통된 기능 (시동걸기, 출발, 브레이크 등) 을 하나로 모아둬서 이런 기능이 있다라고 적어둔 설계도 설명서 정도로 이해하면 될 듯 싶다.
  • 추상 메서드
    • abstract 리턴타입 메서드이름(매개변수, ...);
      -> 블록 {} 이 없음. 그냥 이런게 있다지 각각의 기능들은 각각의 클래스에서 구현할 것이라서
    • 상속받은 클래스에서 추상 클래스의 추상 메서드는 반드시 오버라이딩 되어야한다.

  • 인터페이스
    • 두 객체를 연결해주는 다리 역할
    • 상속 관계가 없는 다른 클래스들이 서로 동일한 행위 즉, 메서드를 구현해야할 때 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장
      • 스팩이 정의된 메서드들의 집합
      • 인터페이스의 구현 클래스들은 반드시 정의된 메서드들을 구현해야함
      • 구현 클래스들의 동일한 사용 방법과 행위를 보장
        -> 인터페이스에 다형성을 적용
    • interface 키워드를 사용하여 선언
    • 멤버
      • 모든 멤버변수는 public static final
        • 생략 가능
      • 모든 메서드는 public abstract
        • 생략 가능 (static 메서드와 default 메서드 예외)
      • 생략되는 제어자는 컴파일러가 자동으로 추가
    • implements 키워드를 사용하여 구현
      • 인터페이스의 추상 메서드는 구현될 때 반드시 오버라이딩 되어야함.
      • 인터페이스의 추상 메서드를 일부만 구현해야 한다면 해당 클래스를 추상 클래스로 변경
    • 상속은 extends 키워드를 사용
      • 다중 상속이 가능

=> 아니 근데 이러면 인터페이스랑 추상클래스랑의 차이가 뭐냐?

  • 추상클래스는 미완성의 설계도, 인터페이스는 기본 설계도
    • 추상 클래스는 상속을 위한 클래스라서 객체를 따로 생성할 수 없음
    • 인터페이스는 다중 상속 (구현) 이 가능
  • 해당 클래스의 구분을 추상클래스 상속을 통해 해결, 할 수 있는 기능들을 인터페이스로 구현
  • 추상클래스 사용 시기 : 상속 관계를 쭉 타고 올라갔을때 같은 조상클래스를 상속하는데 기능까지 완벽히 똑같은 기능이 필요한 경우
  • 인터페이스 사용 시기 : 상속 관계를 쭉 타고 올라갔을때 다른 조상클래스를 상속하는데 같은 기능이 필요할 경우 인터페이스 사용

=> 즉, 현대자동차, 테슬라자동차, 포드자동차처럼 자동차라는 같은 부모 클래스를 가지고 있고, 그 부모의 기능 (시동걸기, 브레이크, 액셀 등) 이 꼭 필요하면 추상클래스
=> 자동차, 비행기, 기차처럼 부모 클래스가 다르지만 같은 기능 (문 열기, 에어컨, 불 켜기 등) 을 구현하고 싶으면 인터페이스
=> 차이점 잘 알아둘 것

  • 자동 타입 변환
    • 인터페이스 변수 = 구현객체;
  • 강제 타입 변환
    • 구현객체타입 변수 = (구현객체타입) 인터페이스변수;

HW3

Main.java
package hw.week3;

public class Main {
    public static void main(String[] args) {
        Calculator calculator  = new Calculator(new AddOperation());
        System.out.println(calculator.calculate(8, 2));

        calculator  = new Calculator(new MultiplyOperation());
        System.out.println(calculator.calculate(13,21));

        calculator  = new Calculator(new SubstractOperation());
        System.out.println(calculator.calculate(20, 9));

        calculator  = new Calculator(new DivideOperation());
        System.out.println(calculator.calculate(20150, 50));

        calculator  = new Calculator(new RemainOperation());
        System.out.println(calculator.calculate(179, 50));

    }
}
AbstractOperation.java
package hw.week3;

public abstract class AbstractOperation {
    public abstract double operate(int firstNumber, int secondNumber);
}

Calculator.java
package hw.week3;

import java.util.Objects;

public class Calculator {

    private AbstractOperation operation;

    public Calculator(AbstractOperation operation) {
        this.operation = operation;
    }

//    public void setOperation(AbstractOperation operation) {
//        this.operation = operation;
//    }

    public double calculate(int firstNumber, int secondNumber) {
        double answer = 0;
        answer = operation.operate(firstNumber, secondNumber);
        return answer;
    }
}
AddOperation.java
package hw.week3;

public class AddOperation extends AbstractOperation {
    @Override
    public double operate(int firstNumber, int secondNumber) {
        return firstNumber + secondNumber;
    }
}

SubstractOperation.java
package hw.week3;

public class SubstractOperation extends AbstractOperation {
    @Override
    public double operate(int firstNumber, int secondNumber) {
        return firstNumber - secondNumber;
    }
}

MultiplyOperation.java
package hw.week3;

public class MultiplyOperation extends AbstractOperation {
    @Override
    public double operate(int firstNumber, int secondNumber) {
        return firstNumber * secondNumber;
    }
}

DivideOperation.java
package hw.week3;

public class DivideOperation extends AbstractOperation {
    @Override
    public double operate(int firstNumber, int secondNumber) {
        return (double)firstNumber / secondNumber;
    }
}

RemainOperation.java
package hw.week3;

public class RemainOperation extends AbstractOperation {
    @Override
    public double operate(int firstNumber, int secondNumber) {
        return firstNumber % secondNumber;
    }
}

0개의 댓글