Java 문법 종합 3주차

이상인·2023년 5월 25일

객체지향 프로그래밍 이해하기

자바라는 프로그램 안에서 객체를 여러개 만들어서 그 부품을 조립해서 한 제품을 완성시킨다.

※객체 : 세상에 존재하는 물체, 식별이 가능한 것
자동차, 도서관, 계산기
강의, 배달주문, 운동
식별이 가능한 모든 것

객체는 속성(색상, 가격 등)과 행위(브레이크, 기어 변속 등)로 구성이 되어있다.

자바에서는 이러한 속성과 행위를 필드와 메서드로 구현한다

사람(Person) 객체가 자동차의(Car) gasPedal()이라는 메서드를 호출하면 자동차는 return double을 준다


캡슐화(encapsulation)

  • 속성과 메서드를 하나로 묶어 객체로 만든다
  • 실제 내부 구현 내용은 외부에서 알 수 없게 감추는 것
  • 외부 객체에서 해당 필드와 메서드를 잘못 사용하여 객체가 변화하지 않게 하는데 있다.
  • java는 캡슐화된 객체의 필드와 메서드를 노출 시킬지 감출지 결정하기 위해 접근제어자를 사용

상속

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

다형성

  • 객체가 연산을 수행 할 때 각 객체의 고유한 특성에 따라 다른 형태로 재구성 되는 것
  • A와 B의 경적 소리가 다르다면 경적을 울린다는 메서드를 다르게 구현하여 재정의해서 사용 가능

추상화

  • 객체에서 공통된 부분들을 모아 상위 개념으로 새롭게 선언하는 것

객체와 클래스

  • 객체를 생성하기 위해서 설계도가 필요하다
  • 자동차를 만들 때 설계도가 필요하듯 소프트웨어에서도 객체를 만들기 위해 설계도에 해당하는 클래스가 필요하다
  • 클래스로 생성된 객체를 해당 클래스의 '인스턴스'라고 부르며 이 과정을 '인스턴스화'라고 한다.
  • Car class를 바탕으로 자동차 인스턴스1,2,3을 만들면 이것들이 자동차 객체가 된다.
  • class 클래스_이름
    => 객체를 만들기 위한 설계도다

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

Car 클래스에서 만들어놓은 객체로 new키워드를 메인에 만든다

Main에서 Car클래스를 사용할 수 있는 이유는 접근제어자를 public으로 설정하였기 때문

ex)
Car car1 = new Car();

        Car[] carArray = new Car[3]; // 배열 만들기
       Car car1 = new Car();
       car1.changeGear('P');
       carArray[0] = car1;

고유데이터 ex) color / 상태데이터 ex) speed, lights / 객체데이터 (하위)

필드를 사용한다?
=> 값을 변경하거나 읽는다.
클래스를 이용해서 객체를 만들어야 필드를 사용할 수 있다.

만들어진 객체로 클래스의 필드에 접근하는 방법은 2가지 방법이 있다
1. 외부에서 접근

  • Car car = new Car();
  • 도트연산자 사용 car.color = "blue";
  1. 객체 내부 메서드에서 접근
  • 도트연산자 사용
double brakePedal() {
	speed = 0;
    return speed;
}

초기화 해준 값은 그 값이 들어가고 아닌 값은 default가 들어간다


메서드 선언

리턴타입 메서드명(매개변수, ...) {
		실행할 코드 작성
}
  • 리턴타입이 void가 아니면 반드시 return이 있어야 한다.
    <-> 반환할 값이 없을 때는 리턴타입에 void를 넣어야 한다.

ex) gasPedal(double kmh, char type) 메서드가 있을 경우

  • 해당 매개변수에 값을 전달하기 위해서는 1.순서와 2.타입에 맞춰 값을 넣는다.
    garPedal(100,'D')

가변길이 매개변수 선언 가능

  • void carSpeeds(double ... speeds)
  • carSpeeds(100,80)
  • carSpeeds(110,120,150);

메서드 호출

  • 메서드명()
  • 메서드도 객체로 만들어야 객체.메서드명() 으로 사용할 수 있다.

메서드 오버로딩

  • 하나의 메서드 이름으로 여러 기능을 구현하도록 하는 java의 기능

    조건

  • 메서드의 이름이 같고, 매개변수의 개수, 타입 또는 순서가 달라야 한다

  • 응답값만 다른 것은 오버로딩 할 수 없다.

    오버로딩의 장점

  • 메서드 이름 하나로 상황에 따른 동작을 개별로 정의할 수 있다.

  • 메서드의 이름을 절약할 수 있다.


    기본형 매개변수

  • 매개변수의 타입이 기본형일 때는 값 전체가 복사되어 넘어가 매개값으로 지정된 변수의 원본 값이 변동되지 않는다.

  • 읽는 것만 가능하다

  • 원본 객체는 변치 않는다.

    참조형 매개변수

  • 주소를 불러오기 때문에 읽고 변경하는 것도 가능하다

  • 객체에 주소값 자체를 넘겨줬기 때문에 변경 가능


    멤버 = 필드 + 메서드

  • 선언하는 방법에 따라 인스턴스 멤버와 클래스 멤버로 구분

  • 인스턴스 멤버는 객체 생성 후 사용할 수 있고 클래스 멤버는 객체 생성 없이도 사용할 수 있다.

인스턴스 멤버

  • 객체의 메서드들을 매번 저장하면 중복저장으로 인해 메모리 효율이 매우 떨어짐
  • 메서드는 메서드 영억에 두고서 모든 인스턴스들이 공유해서 사용
  • 대신 무조건 객체를 생성 즉, 인스턴스를 통해서만 메서드가 사용될 수 있도록 제한

클래스 멤버

  • 클래스는 java의 클래스 로더에 의해 메서드 영역에 저장 및 사용
  • 메서드 영역의 클래스와 같은 위치에 고정적으로 위치하고 있는 멤버
  • 객체의 생성 필요없이 바로 사용 가능
  • 필드와 메서드를 클래스 멤버로 만들기 위해서는 static 키워드를 사용
  • 일반적으로 공용적인 데이터를 저장하는 필드는 클래스 멤버로 선언
  • 인스턴스 필드를 사용하지 않고 실행되는 메서드가 존재한다면 static 키워드를 사용하여 클래스 메서드로 선언

지역변수 <-> 전역변수(static)
해당 메서드가 실행될 때 마다 독릭적인 값을 저장하고 관리
이 지역변수는 메서드 내부에서 정의될 때 생성된다
이 메서드가 종료될 때 소멸된다.

final

  • 절대로 수정할 수 없다
  • 상수는 값이 반드시 한개이며 불변
  • 인스턴스마다 상수를 저장할 필요가 없다.
  • 전체를 대문자로 적는 것이 관례이다. ex) TESTVALUE

생성자

  • 객체가 생성될 때 호출되며 객체를 초기화하는 역할 수행

  • public Car() {} // 선언 ( 기본생성자 )
    생성자를 안 넣어줘도 컴파일러가 자동으로 추가해줌
    이 경우 해당 클래스의 접근제어자를 따름

  • public Car(String model) {}
    생성자가 한 개 이상 선언되었기 때문에 기본 생성자를 추가하지 않음.

ex) 자동차가 만들어질 때마다 기어의 상태를 'P'로 고정해야 한다면 초기값을 직접 대입하는 것이 좋다.
객체를 만들 때마다 다른 값을 가져야 한다면 ex) Car = sorento / SM5
생성자를 사용하여 필드의 값을 초기화 하는 것이 좋다.


this 와 this()

  • java에서는 자신을 가리킨다.
  • 생성자를 선언하는데 매개변수명과 객체의 필드명이 동일할 경우 오류가 발생하지는 않지만 생성자 블록 내부에서 해당 변수들은 객체의 필드가 아닌 가장 가까운 매개변수명을 가리키게 됨으로 자기 자신에게 값을 대입하는 상황이 되어버린다.
  • 생성자를 제대로 배운다면 무조건 this를 써주는 것이 좋다.
    model = model; -> this.model = model;
  • 객체 자신을 리턴해야되는 메서드가 있다면 return this; 라고 하면 된다.

this()키워드를 사용하면 코드의 중복을 제거할 수 있다.

public Car(String model) {
	this.model = model;
    this.color = "Blue";
    this.price = 50000000;
// -> 자기 자신에 있는 생성자 호출
public Car(String model) {
	this(model, "Blue", 50000000);
}
// 어떠한 로직도 this 위에 있으면 안됨!!
public Car(String model, String color, dounle price) {
	this.model = model;
    this.color = color;
    this.price = price;
}

접근제어자

  • 클래스, 멤버변수, 메서드, 생성자에 사용되어 부가적인 의미 부여, 지정되어 있지 않다면 default
  • public : 접근 제한이 없다
  • protected : 같은 패키지 내에서, 다른 패키지의 자손클래스에서 접근 가능
  • default : 같은 패키지 내에서 접근 가능
  • private : 같은 클래스 내에서만 접근이 가능

사용가능한 접근제어자

  • 클래스 : pub, def
  • 메서드 & 멤버변수 : pub, pro, def, pri
  • 지역변수 : 없음

클래스 내부에 선언된 데이터를 보호하기 위해서 사용
생성자에 접근 제어자를 사용함으로 인스턴스의 생성을 제한할 수 있다.
일반적으로 생성자의 접근 제어자는 클래스의 접근 제어자를 따른다.

객체의 무결성 즉, 변경이 없는 상태를 유지하기 위해 접근 제어자를 사용

Getter와 Setter

  • 직접적으로 값을 변경하거나 읽어오는 것을 막기 위해서 세팅

하나하나의 필드마다 getter와 setter가 존재한다

private boolean lights;
//
public double getPrice() {
	return price;
}
public void setPrice(double price) {
    this.price = price;
}

Package 와 import

  • 패키지는 클래스의 일부분이며, 하위 패키지를 도트로 구분한다.
  • 다른 패키지에 접근하는 방법
    1) 다른 패키지에 접근할 때 경로를 입력해준다.
week03.packageExample.pk1.Car car = new week03.packageExample.pk1.Car();

2) import를 해온다.

import week03.packageExample.pk1.Car;

클래스간의 관계와 상속

  • 객체지향 프로그램에서 부모클래스의 필드와 메서드를 자식 클래스에게 물려줄 수 있다.
  • 상속을 사용하면 적은 양의 코드로 새로운 클래스를 작성할 수 있다.
  • 공통적인 코드를 관리하여 코드의 추가와 변경이 쉬워질 수도 있다.
    => 유지보수성이 좋아진다.

클래스간의 상속은 extends 키워드를 사용하여 정의한다.

public class 자식클래스 extends 부모클래스 {

}

부모클래스와 자식클래스의 관계

  • 부모클래스에 새로운 필드와 메서드가 추가되면 자식클래스는 이것을 상속받아 사용할 수 있다.
  • 자식클래스에 새로운 필드와 메서드가 추가되어도 부모클래스는 영향 받지 않는다. (일방통행)
  • 따라서 자식클래스의 멤버 개수는 부모 클래스보다 항상 같거나 많다.

상속관계와 포함관계

  • 상속관계 : is-a
    스포츠카는 자동차다
  • 포함관계 : has-a
    자동차는 문을 가지고있다.

다중상속과 단일상속

  • 자바는 다중상속을 허용하지 않는다. ( 한 자식이 여러개의 부모클래스를 가질 수는 없다. )
    관계가 복잡해짐 (메서드 이름이 겹친다거나 하는 등)

final 클래스와 final 메서드

  • 클래스에 final 키워드를 지정하여 선언하면 최종적인 클래스가 됨. 더 이상 상속할 수 없다.
    => 변경 안 하고 쓰면 안되냐?
    => 상속이라는 개념은 항상 오버라이딩이라는 개념과 같이 간다.
  • 부모로부터 내려받은 메서드를 재정의해서 쓰는 기법 (java의 디폴트)
    그렇기 때문에 상속할 수 없다.

Object 클래스

  • 최상위 클래스
  • 수많은 클래스가 상속받고 있다.

오버라이딩(overriding)

  • 부모클래스로부터 상속받은 메서드의 내용을 재정의 하는 것
  • 아래 조건을 만족해야 한다.
    1) 선언부가 부모 클래스의 메서드와 일치
    2) 접근 제어자를 부모 클래스의 메서드보다 좁은 범위로 변경할 수 없다
    3) 예외는 부모 클래스의 메서드 보다 많이 선언할 수 없다.
    @Override // 애노테이션(annotation)
    public double brakePedal() {
        speed = 100;
        System.out.println("스포츠카에 브레이크란 없다");
        return speed;
    }

    @Override
    public void horn() {
        booster();
    }

super와 super() <-> this, this()
(각각 생성자와 관련 있겠구나, 인스턴스와 관련 있겠구나)

super

  • 부모클래스의 멤버를 참조할 수 있는 키워드
  • this와 다르게 부모를 지칭하는 것
public void setCarInfo(String model, String color, double price) {
    super.model = model; // model은 부모 필드에 set
    super.color = color; // color는 부모 필드에 set
    this.price = price; // price는 자식 필드에 set
}

자식 클래스의 메서드를 호출하면 super 키워드로 접근한 부모 클래스의 model, color 필드에 매개변수의 값이 저장된다.
this키워드로 접근한 자식 클래스의 price 필드에는 매개변수의 값이 저장된다.


다형성 ( 부모자식간 일어날 수 있는 특징 중 하나)

  • 자동 타입변환
  • 부모타입 변수 = 자식타입객체
Mammal mammal = new whale(); => // 생성된 객체는 결국 포유류다.
//포유류 전부가 수영을 할 수 있는 것은 아님
// 수영하다 메서드는 실행 불가
mammal.swimming(); // 오류발생
// Whale whale = new Mammal(); // 오류발생
  • 강제타입변환
// 자식타입객체가 자동 타입변환된 부모타입의 변수
Mammal mammal = new whale();
mammal.feeding();

// 자식객체 고래의 수영 기능을 사용하고 싶다면
// 다시 자식타입 객체로 강제 타입변환.
Whale whale = (Whale) mammal;
whale.swimming();

강제타입변환 조건

  • 자동형변환이 완료된 객체만 기존 타입으로 돌아갈 수 있다.

instanceof

  • 다형성 기능으로 인해 해당 인스턴스 객체의 클래스명을 체크하는 것이 필요한데 이때 사용하는 명령어이다.
		Parent p = Parent();
        System.out.println(p instanceof Object); // true 출력
        System.out.println(p instanceof Parent); // true 출력
        System.out.println(p instanceof Child);  // false 출력

        Parent c = new Child();

        System.out.println(c instanceof Object); // true 출력
        System.out.println(c instanceof Parent); // true 출력
        System.out.println(c instanceof Child);  // true 출력

추상클래스

  • 클래스가 설계도라면 추상클래스는 미완성된 설계도
    부모는 완성시키지 않은 메서드를 가지고 있다.
    자식이 extends해서 완성시킨다.
  • abstract 키워드를 사용하여 추상클래스를 선언할 수 있다.
public abstract class 추상클래스명 {

}
  • 추상메서드를 포함할 수 있다.
  • 추상메서드가 없어도 추상클래스로 선언할 수 있다.
  • 자식클래스에 상속되어 자식클래스에 의해서만 완성될 수 있다.
  • 추상클래스는 여러개의 자식클래스들에서 공통적인 필드나 메서드를 추출해서 만들 수 있다.

상속 : 부모 -> 자식클래스
추상화 : 자식클래스에 필요한 기능들을 한 군데 모아서 규격을 정한다. 이를 모호하게 추상클래스로 만들어서 다시 나눠준다.

public abstract class 추상클래스명 {
		abstract 리턴타입 메서드이름(매개변수, ...); // 구현부분 {}가 없다.
}

누군가 extends해서 구현을 해야한다.

  • 상속받은 클래스에서 추상 클래스의 추상 메서드는 반드시 오버라이딩 되어야 한다.
  • 자식클래스가 비슷하게 가지고 있지만 다른 것
public class GenesisCar extends Car {

	@Override
    public void horn() { sout("Genesis 빵빵:);}

인터페이스

  • 두 객체를 연결해주는 다리 역할
  • 사람과 삼성티비, 엘지티비
    멀티리모콘 인터페이스가 있으면 두 티비에 다 사용할 수 있다
  • 상속관계가 없는 다른 클래스들이 서로 동일한 행위 즉, 메서드를 구현해야할 때 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장할 수 있다.
publoc interface 인터페이스명 {
}

모든 멤버변수는 public static final이어야 한다.

  • 생략가능, 컴파일러가 자동으로 추가해준다.

모든 메서드는 public abstract 이어야 한다.

  • 생략가능 ( static메서드와 default 메서드 예외 )

인터페이스는 추상 클래스와 마찬가지로 직접 인스턴스를 생성할 수 없기 때문에 클래스에 구현되어 생성된다.
implements 키워드를 사용하여 인터페이스를 구현할 수 있다.


public class 클래스명 implements 인터페이스명 { 
			// 추상 메서드 오버라이딩
			@Override
	    public 리턴타입 메서드이름(매개변수, ...) {
			       // 실행문
	    }
}
  • 인터페이스의 추상 메서드는 구현될 때 반드시 오버라이딩 되어야 한다.
  • 인터페이스의 추상 메서드를 일부만 구현해야 한다면 해당 클래스를 추상 클래스로 변경해주면 된다.
  • 인터페이스간의 상속이 가능
    이 경우 extends 키워드를 사용한다.
  • 인터페이스는 다중상속이 가능하다.

default와 static 메서드

defailt메서드

  • 추상 메서드의 기본적인 구현을 제공하는 메서드
  • 메서드 앞에 default 키워드를 붙이며 블럭{}이 존재해야 한다.
  • 접근제어자는 public이며 생략가능 (컴파일러가 자동으로 넣어줌)
  • 구현한 부분이 있기 때문에 추상메서드x 인터페이스 구현체들에서 재정의 할 필요 없다.

static메서드

  • 객체 없이 호출할 수 있다.
  • 인터페이스. 으로 바로 접근한다 (디폴트와 차이점)

다형성

  • 인터페이스 변수(구현하는 객체가 들여오는 상위 개념) > 구현객체
    자동으로 타입변환

0개의 댓글