TIL 2023/10/27 Java

YEONGDO·2023년 10월 27일

[학습 목표]

  1. 객체지향 프로그래밍에 대한 개념을 이해합니다.
  2. 클래스를 설계하는 방법에 대해 학습합니다.
  3. 객체의 구성요소(필드, 메서드, 생성자)에 대해서 학습합니다.
  4. 클래스 변수, 인스턴스 변수의 차이점에 대해서 학습합니다.
  5. 생성자와 생성자 오버로딩에 대해 학습합니다.
  6. this와 this() 키워드에 대해 학습합니다.
  7. 접근 제어자에 대해 학습합니다.
  8. pakage와 import에 대해 학습합니다.
  9. 상속, 오버라이딩 을 통해 기능을 확장하는 방법을 배웁니다.
  10. super와 super() 키워드에 대해 학습합니다.
  11. 다형성의 원리와 구현 방법에 대해 학습합니다.
  12. 추상 클래스에 대해 학습합니다.
  13. 인터페이스의 역할에 대해 이해하고 구성 요소와 구현 방법에 대해 학습합니다.
  14. 인터페이스의 디폴트 메서드와 static 메서드에 대해 학습합니다.
  15. 인터페이스의 다형성 원리와 구현 방법에 대해 학습합니다.

1. 객체지향 프로그래밍

  • 자바는 대표적인 객체지향 프로그래밍 언어이다.
  • 실제세계를 객체라는 단위로 나누고 객체들간의 상호작용을 의미한다.
  • 즉, 객체 지향 프로그래밍은 컴퓨터 프로그래밍 패러다임 중 하나로, 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.

객체란 무엇인지?

  • 객체(Object)는 우리 주변에 있는 모든 것이 될 수 있다.
  • 예를 들어 TV, 컴퓨터, 책, 건물, 의자, 사람 등 모두 객체가 될 수 있다.
  • 객체는 자신만의 고유한 특성과 행동을 가지며 다른 객체들에게 행동을 요청하거나 정보를 주고 받는 등 상호작용을 하면서 존재한다.
  • 객체는 크게 속성(필드, field)와 동작(메서드, method)으로 구성되어있다.
    예)) 학생의 속성: 이름, 학년, 학번 등 / 학생의 동작: 공부하다, 밥먹다, 놀다 등

1) 객체지향의 장점과 단점

- 코드 재사용이 용이
남이 만든 클래스를 가져와서 이용할 수 있고 상속을 통해 확장해서 사용할 수 있다.

- 유지보수가 쉬움
절차 지향 프로그래밍에서는 코드를 수정해야할 때 일일이 찾아 수정해야하는 반면 객체 지향 프로그래밍에서는 수정해야 할 부분이 클래스 내부에 멤버 변수혹은 메서드로 존재하기 때문에 해당 부분만 수정하면 된다.

- 대형 프로젝트에 적합
클래스 단위로 모듈화시켜서 개발할 수 있으므로 대형 프로젝트처럼 여러 명, 여러 회사에서 프로젝트를 개발할 때 업무 분담하기 쉽다.

객체지향의 단점

  • 처리 속도가 상대적으로 느림
  • 객체가 많으면 용량이 커질 수 있음
  • 설계시 많은 시간과 노력이 필요

1) 객체지향의 특징

1) 캡슐화(Encapsulation)

  • 캡슐화란 관련된 필드와 메서드를 하나로 묶고 실제 구현 내용을 외부로부터 감추는 기법으로 정보은닉을 할 수 있다.
  • 외부에서는 공개된 메서드를 통해 접근할 수 있다.
  • 캡슐약을 생각해보면 이해하기 쉬운데, 캡슐에 든 약은 어떤 성분인 지 어떤 색인 지 보이지 않으며 외부의 접근으로부터 안전한 상태와 같습니다.

2) 상속(Inheritance)

  • 상속이란 상위클래스의 모든 걸 하위 클래스가 이어 받는 것
  • 즉, 이미 작성된 클래스(상위클래스)의 특성을 그대로 이어받아 새로운 클래스(하위클래스)를 생성하는 기법이다.
  • 상속이 필요한 이유는 코드의 중복을 없애기 위함. 코드의 중복이 많아지면 개발단계와 유지보수에서 많은 비용이 소요되기 때문에 코드 중복은 피하는 것이 좋다.

3) 추상화(Abstraction)

  • 추상화는 객체에서 공통된 속성과 행위를 추출하는 기법
  • 실제 존재하는 객체들을 프로그램으로 만들기 위해 공통적인 특성을 파악하고 불필요한 특성을 제거하는 과정

4) 다형성(Polymorphism)

  • 사전적 의미로는 다양한 형태로 나타낼 수 있는 능력을 의미
  • 다형성은 같은 이름의 메서드를 호출하더라도 객체에 따라 다르게 동작하는 것
  • 상위 클래스의 동작을 하위클래스에서 다시 정의하여(오버라이딩, Overriding)하는 것 또한 다형성으로 볼 수 있다.
  • 하나의 클래스 내에서 이름은 같지만 서로 다르게 동작하는 메서드를 여러개 만드는 오버로딩(Overloading) 또한 다형성의 사례로 볼 수 있다.

2. 클래스 설계

클래스는 객체를 생성하기 위한 설계도

  • 구성 멤버: 필드, 생성자, 메서드

클래스를 만들기 위한 4가지 STEP

설계도 선언(클래스 선언) > 객체가 가지고 있어야할 속성(필드) 선언 > 객체를 생성
하는 방식을 정의(생성자) > 객체가 가지고 있어야 할 행위(메서드) 정의

1) 선언

public class Car {}
public class Car : 공개된 자동차 클래스 선언을 의미합니다.

2) 필드 정의

String company; // 자동차 회사
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격
    double speed;  // 자동차 속도 , km/h
    char gear; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태
필드는 객체의 속성으로써 데이터를 저장하는 역할을 합니다.

3) 생성자 정의

public Car() {}
public Car() : 자동차 객체의 생성 방식을 선언합니다. 

4) 메서드 정의

double gasPedal(double kmh) {
    speed = kmh;
    return speed;
}
반환타입이 double 인 gasPedal(double kmh) 메서드를 선언합니다.
double brakePedal() {
    speed = 0;
    return speed;
}
반환타입이 double 인 brakePedal() 메서드를 선언합니다.
char changeGear(char type) {
    gear = type;
    return gear;
}
반환타입이 char 인 changeGear(char type) 메서드를 선언합니다.
boolean onOffLights() {
    lights = !lights;
    return lights;
}
반환타입이 boolean 인 onOffLights() 메서드를 선언합니다.
void horn() {
    System.out.println("빵빵");
}
반환값이 없는 horn() 메서드를 선언합니다.

3. 객체 생성과 참조형 변수

new Car(); // Car클래스 객체 생성
  • 객체 생성 연산자인 'new'를 사용하면 클래스로부터 객체를 생성
  • new 연산자 뒤에는 항상 해당 클래스의 생성자 호출 코드 작성
  • 형태가 Car();즉, 기본 생성자의 형태와 같기 때문에 new 연산자에 의해 객체가 생성되면서 기본 생성자가 호출
Car car1 = new Car(); // Car클래스의 객체인 car1 인스턴스 생성
Car car2 = new Car(); // Car클래스의 객체인 car2 인스턴스 생성
  • new 연산자를 통해서 객체가 생성되면 해당 인스턴스의 주소가 반환되기 때문에 해당 클래스의 참조형 변수를 사용하여 받아줄 수 있다.
  • 객체는 참조형 변수와 동일하게 취급되기 때문에 배열 또는 컬렉션에도 저장하여 관리할 수 있다.

4. 객체의 속성: 필드

필드는 객체의 데이터를 저장하는 역할을 한다. 객체의 필드는 크게 고유한 데이터, 상태 데이터, 객체 데이터로 분류

  • 우리가 정의하여 선언한 클래스의 필드들은 기본적으로 초기값을 제공하지 않을 경우 객체가 생성 될 때 자동으로 기본값으로 초기화된다.
  • 초기값을 제공하는 방법은 '필드타입 필드명 = 값;' 이렇게 직접 초기화할 수 있음
  • '필드를 사용한다' 라는 의미는 필드의 값을 변경하거나 읽는 것을 의미

1) 외부 접근

Car car = new Car();
  • 이렇게 객체를 생성했다면 우리는 참조변수 car를 이용하여 외부에서 객체 내부의 필드에 접근하여 사용
  • 이때 객체의 내부 필드에 접근하는 방법은 도트(.) 연산자를 사용

2) 내부 접근

double brakePedal() {
    speed = 0;
    return speed;
}
  • 도트 연산자를 사용하여 외부에서 객체 내부에 접근할 수 있을 뿐만 아니라 객체 내부 메서드에서도 내부 필드에 접근할 수 있다.
  • brakePedal() 메서드 내부에서 객체의 필드 speed를 바로 호출해서 사용할 수 있다.

5. 객체의 행위: 메서드

메서드는 객체의 행위를 뜻하며 객체간의 협력을 위해 사용, 메서드의 행위를 정의하는 방법은 블록{ } 내부에 실행할 행위를 정의

1) 메서드 선언

  • 리턴타입이란
double brakePedal() {...} // double 타입 반환
char changeGear(char type) {...} // char 타입 반환
boolean onOffLights() {...} // boolean 타입 반환
void horn() {...} // 반환할 값 없음

리턴타입이란 메서드가 실행된 후 호출을 한 곳으로 값을 반환할 때 해당 값의 타입을 의미. 주의할 점은 메서드에 리턴타입을 선언하여 반환할 값이 있다면 반드시 return 문으로 해당하는 리턴타입의 반환값을 지정해야한다.

반환할 값이 없을 때는 리턴타입에 void를 작성. 반환값이 없음으로 return문을 반드시 지정할 필요 X, 메서드는 실행할 때 return문을 만나면 그대로 종료하게 되는데 void 타입일 때 return; 이렇게 return문을 사용하여 원하는 지점에서 메서드를 종료할 수도 있다.


  • 매개변수란
double gasPedal(double kmh, char type) {
    speed = kmh;
    return speed;
}

매개변수는 메서드를 호출할 때 메서드로 전달하려는 값을 받기 위해 사용되는 변수. 전달하려는 값이 없다면 생략 가능, 가변길이의 매개변수도 선언할 수 있다.

2) 메서드 호출
메서드는 정의하는 것만으로 실행되지 않는다. 메서드를 정의하는 것은 단순히 동작에 대한 처리 과정을 기술한 것이다. 따라서 메서드를 직접 사용하기 위해 다음과 같은 방법으로 호출해야 한다.

/*
메서드이름(값1, 값2, ...)
*/

method(value1, value2, ...);

3) 메서드 오버로딩
메서드 오버로딩은 같은 메서드여도 매개변수가 다르거나, 리턴받는 데이터타입이 다른 경우 여러 개를 선언할 수 있는 것을 의미

public class Method {
	public static void main(String[] args) {			
		System.out.println("Overloading1 : " + Overloading("홍길동", "20살"));
		System.out.println("Overloading2 : " + Overloading(10, 20));
	}
	// Overloading1 메서드 선언
	public static String Overloading(String Name, String Age)
	{
		return Name + Age;
	}
	
	// Overloading2 메서드 선언
	public static int Overloading(int a, int b)
	{
		return a + b;
	}
}

6. 인스턴스 멤버와 클래스 멤버

멤버 = 필드 + 메서드

1) 인스턴스 멤버

  • 인스턴스 멤버란 New를 통해 객체를 생성한 후 사용할 수 있는 필드와 메서드를 말한다.
  • 인스턴스 멤버에서도 객체 내부의 인스턴스 멤버에 접근하려면 this를 통해 가능

2) 클래스 멤버

  • 객체를 생성하지 않고도 사용할 수 있는 필드와 메서드
  • static 키워드 사용

3) 지역변수

  • 메서드 내부에 선언한 변수를 의미
  • 메서드가 실행될때마다 독립적인 값을 저장하고 관리
  • 지역 변수는 메서드 내부에서 정의될때 생성되어 메서드가 종료될 때까지만 유지
public class Main {
    public static void main(String[] args) {
        Main main = new Main();

        // 메서드 호출 : main.getClass()
        System.out.println("main.getClass() = " + main.getNumber());
        System.out.println("main.getClass() = " + main.getNumber());
        System.out.println("main.getClass() = " + main.getNumber());
    }

    public int getNumber() {
        int number = 1; // 지역 변수
        number += 1;
        return number; // 메서드 종료되면 지역변수 제거됨
    }
}

// 출력
//main.getNumber() = 2
//main.getNumber() = 2
//main.getNumber() = 2

4) final 필드와 상수

final 필드
final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중에 수정할 수 없게 되는 필드이다.

final 타입 필드 = 초기값;
final int num = 123;
  • 필드 선언 시에 초기값을 지정한다.
  • 생성자에서 초기값을 준다.

상수(static final)
상수는 불변의 값을 의미한다. 수학에서 쓰이는 파이 값이 대표적인 예이다. 자바에서는 불변의 값을 저장하는 필드를 상수(constant)라고 한다. final 필드는 한 번 초기화하면 수정할 수 없는 필드이다.

static final 타입 상수 = 초기값;
profile
개발 블로그

0개의 댓글