< 해당 내용의 사진은 한빛출판사의 이미지를, 강의 내용은 내일 배움 캠프를 통해 서술했습니다. >
객체지향
객체지향은 자바에서 가장 중요한 기능 중 하나로 체계적인 프로그래밍을 가능하게 만들어준다. 사전적 의미로는, 현실 세계에서 어떠한 제품을 만들기 위해 부품들을 하나씩 조립해서 완성시키는 것처럼 소프트웨어 또한 필요한 부품들을 만들고 하나씩 조립해서 하나의 완성된 프로그램을 만들 수 있는데 이러한 기법을 ‘객체지향 프로그래밍’이라고 부른다.
단순히 말하면 기능, 혹은 데이터 별로 쪼개어 하나의 부품단위로 만들고 이를 잘 합쳐서 프로그램을 만든다 보면 된다.
객체
객체란 세상에 존재하는 물체를 뜻하며 식별이 가능한 것을 말한다. 강하지, 반려동물, 자동차와 같이 물질적인 것 부터 개념적인 부분까지 식별이 가능하면 객체라 불릴 수 있다. 객체는 일정한 속성을 가지고 있으며 행동을 할 수 있다.
이처럼 현실 세계에 있는 객체를 소프트웨어의 객체로 설계하는 것을 ‘객체 모델링’이라고 부른다.
객체 간의 협력
객체간의 상호작용을 말한다.
- 사람이 자동차의 가속 페달을 밟으면 자동차는 이에 반응하여 속도를 올리며 앞으로 이동합니다.
- 사람이 자동차의 브레이크 페달을 밟으면 자동차는 이에 반응하여 속도를 줄이며 정지합니다.
자바에서 객체는 메서드를 통해서 서로 상호작용하면서 데이터를 주고 받을 수 있다.
예를 들어 사람이 자동차 객체가 가지고 있는 가속페달 메서드gasPedal(50);을 호출하면 자동차는 이 메서드에 반응하여 속도 속성의 값을 50으로 수정시킨다. 그러면 사람 객체는 자동차 객체의 브레이크 페달 brakePedal();을 호출한다.
+ 여기서 50은 매개값, 파라미터 라고 불리며 메서드 호출시 데이터를 포함해 호출하게 해준다
또한 자동차 객체는 gasPedal(50); 메서드에서 속도를 바꾸는 작업을 수행한 후 사람 객체에게 실행 결과인 속도의 값을 반환할 수 있습니다. 이때 반환되는 값을 ‘리턴값’이라 표현한다.
객체 간의 관계
- 객체간의 협력에도 종류가 있는데, 크게 사용관계, 포함관계, 상속관계가 존재한다.
사용 관계
- 사람 객체는 자동차 객체를 사용한다.
포함 관계
- 타이어 객체, 차 문 객체, 핸들 객체는 자동차 객체에 포함되어 있다.
상속 관계
- 만약 공장에 자동차만 생산하는 게 아니라 기차도 생산한다고 가정해 보자.
- 자동차와 기차 객체는 하나의 공통된 기계 시스템 객체를 토대로 만들어진다.
- 그렇다면 자동차 객체와 기차 객체는 기계 시스템 객체를 상속받는 상속 관계가 된다.
자바는 효율성을 강조한 언어이다. 위의 blueprint를 Car, Train 객체에서 각각 1번씩 정의하는 방식보다는, 그 상위 객체인 MachineSystem에서 한번만 정의하고 하위 객체에게 주소를 넘겨주는게 메모리 효율성이 높다.
// 부모 클래스
class Animal {
// 공통 메소드
public void eat() {
System.out.println("This animal eats food.");
}
}
// 자식 클래스
class Dog extends Animal {
// 자식 클래스에만 있는 메소드
public void bark() {
System.out.println("The dog barks.");
}
// 부모 클래스의 메소드 오버라이딩
@Override
public void eat() {
System.out.println("The dog eats dog food.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
// 상속받은 메소드 사용
dog.eat(); // The dog eats dog food.
dog.bark(); // The dog barks.
}
}
코드 설명
- Animal 클래스: eat() 메소드를 가지는 부모 클래스입니다.
- Dog 클래스: Animal 클래스를 상속받아 bark() 메소드를 추가했고, eat() 메소드를 오버라이딩하여 개에 맞는 동작을 정의했습니다.
- 오버라이딩: eat() 메소드를 자식 클래스에서 재정의하여 다형성을 구현했습니다.
캡슐화
- 캡슐화란 속성(필드)와 행위(메서드)를 하나로 묶어 객체로 만든 후 실제 내부 구현 내용은 외부에서 알 수 없게 감추는 것을 의미합니다. (안전성 확보)
- 외부 객체에서는 캡슐화된 객체의 내부 구조를 알 수 없기 때문에 노출시켜 준 필드 혹은 메서드를 통해 접근할 수 있습니다.
- 필드와 메서드를 캡슐화하여 숨기는 이유는 외부 객체에서 해당 필드와 메서드를 잘못 사용하여 객체가 변화하지 않게 하는 데 있습니다.
- Java에서는 캡슐화된 객체의 필드와 메서드를 노출시킬지 감출지 결정하기 위해 접근 제어자를 사용합니다.
단순히 설명하면, 알약 캡슐처럼 내용을 하나의 알약에 담는다. 다만 알약이 밖에서 볼때 어떤 성분이 있는지 확인할 수 없듯 기본적으로 캡슐화가 된 클래스의 내용은 확인할 수 없다. 하지만 프로그래머가 예외로 설정한 데이터, 메소드에는 접근할 수 있다.
public class User {
// private 접근 제어자를 사용하여 필드를 외부에서 직접 접근하지 못하게 함
private String name;
private int age;
// 생성자를 통해 객체 초기화
public User(String name, int age) {
this.name = name;
this.age = age;
}
// getter 메소드: 필드에 대한 접근을 제공
public String getName() {
return name;
}
public int getAge() {
return age;
}
// setter 메소드: 필드를 수정할 수 있게 제공
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
if(age > 0) { // 유효성 검사를 통해 데이터 보호
this.age = age;
} else {
System.out.println("Age must be greater than 0.");
}
}
}
위의 private는 '개인적으로' 라는 의미로 캡슐만이 알 수 있고 외부에서 접근할 수 없는 데이터나 메소드를 만들어준다.
반대로 public은 '공공적으로' 라는 의미로 외부에서 접근이 가능하고 수정까지도 할 수 있게 해준다.
상속
- 객체지향 프로그래밍에는 부모 객체와 자식 객체가 존재합니다.
- 부모 객체는 가지고 있는 필드와 메서드를 자식 객체에 물려주어 자식 객체가 이를 사용할 수 있도록 만들 수 있습니다.
- 위에서 설명했던 대로, 효율성을 추구하는 자바라는 언어 특성상 굳이 곂치는 내용을 여러번 정의하는 것을 꺼린다. MachineSystem의 자식인 Car, Train에서 bluePrint를 정의하지 않고 부모인 MachineSystem에서 데이터를 상속받아 사용한다.
상속의 특징
- 코드 재사용성: 기존에 작성한 클래스의 코드와 기능을 자식 클래스에서 재사용할 수 있습니다. 이는 중복 코드를 줄이고 코드 유지보수를 쉽게 해줍니다.
- 계층 구조 생성: 부모-자식 관계의 계층 구조를 만들어 시스템을 구조적으로 관리할 수 있습니다. 이를 통해 클래스 간의 관계를 명확히 할 수 있습니다.
- 확장성: 새로운 기능이 필요할 때 기존 클래스에 추가하기보다 자식 클래스를 만들어 확장할 수 있습니다. 이렇게 하면 기존 클래스는 변경하지 않고 기능을 확장할 수 있어 코드의 안정성이 높아집니다.
- 다형성(Polymorphism): 상속을 통해 생성된 자식 클래스는 부모 클래스의 메소드를 자신의 방식대로 재정의(오버라이딩)할 수 있습니다. 이를 통해 부모 클래스를 참조하는 변수로 여러 자식 클래스의 인스턴스를 다룰 수 있습니다.
상속의 장점
- 코드 중복 감소: 자식 클래스가 부모 클래스의 속성과 메소드를 상속받아 사용할 수 있으므로 중복 코드를 줄일 수 있습니다.
- 유지보수 용이: 공통 기능을 부모 클래스에서 관리하기 때문에 수정 사항이 있을 때 모든 자식 클래스에 일관되게 반영할 수 있습니다. 예를 들어, 부모 클래스의 메소드를 수정하면 자식 클래스에서도 그 변화가 반영됩니다.
- 모듈화: 관련 있는 기능을 부모 클래스에 모아 두고, 세부적인 기능은 자식 클래스에 추가하여 코드가 모듈화되어 구조적인 프로그램을 작성할 수 있습니다.
- 다형성: 상속을 통해 다형성을 구현하여 코드의 유연성과 확장성을 높일 수 있습니다. 이를 통해 동일한 부모 클래스를 참조하는 변수가 다양한 형태로 동작할 수 있습니다.
추상화
- 객체에서 공통된 부분들을 모아 상위 개념으로 새롭게 선언하는 것을 추상화라고 합니다
- 자식들 중 공통된 특징(데이터, 메소드)이 있다면 부모에 선언하는 방식
1-1이 자동차고, 그 하위가 페라리, 포터, 벤츠라고 하면 자식들은 바퀴가 4개인 특징이 있다. 이런 공통적인 특징이 있다면 1-1인 자동차에서 바퀴가 4개이다를 정의하고 아래 자식들에게 뿌려주면 된다. 이런 역전 방식을 추상화라한다.
추상화의 특징
- 중요한 정보만 노출: 추상화는 클래스나 객체의 중요한 속성과 동작을 중심으로 표현하여, 불필요한 세부 사항을 감춥니다. 사용자는 인터페이스나 메소드의 기본 동작 방식만 알면 되며, 내부 동작 원리를 몰라도 됩니다.
- 추상 클래스와 인터페이스 사용: 추상화는 주로 abstract 키워드로 선언된 추상 클래스나 인터페이스를 통해 구현됩니다. 추상 클래스는 공통 동작을 정의하고, 인터페이스는 반드시 구현해야 할 메소드를 선언함으로써 각기 다른 클래스가 공통된 동작을 갖도록 합니다.
- 구현의 강제성: 추상 클래스나 인터페이스를 사용하면, 상속받는 클래스가 특정 메소드를 반드시 구현하도록 강제할 수 있습니다. 이를 통해 코드의 일관성과 안정성을 높일 수 있습니다.
- 유연한 확장 가능성: 추상화를 사용하면 클래스 간의 결합도를 낮출 수 있어, 코드의 유연성과 확장 가능성이 높아집니다. 시스템이 확장되거나 변경되어도 기존 코드를 최소한으로 수정하면서 기능을 추가할 수 있습니다.
추상화의 장점
- 코드의 단순화: 불필요한 세부 사항을 숨기고 중요한 정보만 제공하여 코드의 복잡성을 줄이고 이해하기 쉽게 만듭니다. 사용자 입장에서 복잡한 내부 로직을 알 필요 없이 필요한 기능만 사용할 수 있습니다.
- 유지보수성 향상: 인터페이스나 추상 클래스는 공통된 작업 방식을 정의하고 구현체는 이를 따르기 때문에, 유지보수가 쉬워집니다. 새로운 기능을 추가하거나 수정할 때 전체 시스템에 미치는 영향을 줄일 수 있습니다.
- 모듈화 및 확장성: 추상화를 통해 각 클래스가 독립적으로 동작하면서도 동일한 인터페이스나 추상 클래스의 규칙을 따르기 때문에, 시스템의 모듈화가 용이해집니다. 이는 새로운 기능을 추가하거나 기존 코드를 수정할 때 유연성을 제공합니다.
- 다형성 지원: 추상 클래스와 인터페이스를 통해 다양한 객체들이 동일한 인터페이스를 따르도록 할 수 있어 다형성(Polymorphism)을 구현하기 쉽습니다. 예를 들어, 동일한 메소드를 다양한 방식으로 구현하여 객체마다 다른 동작을 수행하게 할 수 있습니다.
// 추상 클래스
abstract class Shape {
// 추상 메소드 - 구체적인 구현은 자식 클래스에서 정의
public abstract double calculateArea();
// 공통 메소드
public void display() {
System.out.println("This is a shape.");
}
}
// 구체 클래스 - Circle
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
// 추상 메소드 구현
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
// 구체 클래스 - Rectangle
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
// 추상 메소드 구현
@Override
public double calculateArea() {
return width * height;
}
}
public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
circle.display(); // This is a shape.
System.out.println("Circle Area: " + circle.calculateArea()); // Circle Area: 78.5398...
rectangle.display(); // This is a shape.
System.out.println("Rectangle Area: " + rectangle.calculateArea()); // Rectangle Area: 24.0
}
}
코드 설명
- 추상 클래스 Shape: Shape 클래스는 calculateArea()라는 추상 메소드를 선언했습니다. 이 메소드는 구체 클래스에서 정의되어야 합니다.
- 구체 클래스 Circle과 Rectangle: Shape 클래스를 상속받아 calculateArea() 메소드를 각각 원과 사각형에 맞게 구현했습니다.
- 추상화의 효과: Shape 클래스를 상속받은 모든 도형 클래스는 calculateArea() 메소드를 반드시 구현하도록 강제됩니다. 이를 통해 각 도형 클래스가 calculateArea() 메소드를 통해 면적을 계산할 수 있다는 점에서 일관된 사용법을 제공하게 됩니다.
객체와 클래스
- 우리는 객체를 생성하기 위해서 설계도가 필요합니다.
- 현실 세계에서는 자동차를 만들기 위해 자동차 설계도를 토대로 자동차를 생산합니다.
- 마찬가지로 소프트웨어에서도 객체를 만들기 위해서는 설계도에 해당하는 클래스가 필요합니다.
- 이때 클래스를 토대로 생성된 객체를 해당 클래스의 ‘인스턴스’라고 부르며 이 과정을 ‘인스턴스화’라고 부릅니다.
- 동일한 클래스로 여러 개의 인스턴스를 만들 수 있습니다.
- 이때 객체와 인스턴스는 거의 비슷한 표현이지만 자세하게 구분해 보자면 아래와 같습니다.
자동차 클래스를 통해 만들어진 하나의 자동차를 인스턴스라고 부르며 이러한 여러 개의 인스턴스들을 크게 통틀어서 자동차 객체라고 표현할 수 있다.
class Car {
String model;
String color;
// 기본 생성자
public Car(String model, String color) {
this.model = model;
this.color = color;
}
// gasPedal 메서드: 속도를 설정하는 메서드로 kmh 값을 speed 필드에 저장하고 반환
public double gasPedal(double kmh) {
speed = kmh; // 자동차의 현재 속도를 kmh 값으로 설정
return speed; // 설정된 속도 반환
}
}
public class Main {
public static void main(String[] args) {
// 인스턴스화 과정
Car car1 = new Car("Sedan", "Red"); // Car 클래스로부터 car1 객체 생성
Car car2 = new Car("SUV", "Blue"); // Car 클래스로부터 car2 객체 생성
System.out.println(car1.model); // Sedan 출력
System.out.println(car2.model); // SUV 출력
}
}
슬슬 헷갈릴 건데 간단히 하면
인스턴스(객체) | car1, car2 |
인스턴스화 | new |
메서드 | public double gasPedal(double kmh) |
클래스 | class Car |
생성자 | public Car(String model, String color) / 생성자는 메서드와 다르게 클래스와 이름이 같다 |
Car car1 = new Car("Sedan", "Red");는 Car 클래스를 사용하여 "Sedan"과 "Red"라는 속성값을 가지는 새로운 Car 객체를 생성하고, 그 객체를 car1이라는 참조형 변수에 참조하도록 하는 코드
'Back-End (Web) > JAVA' 카테고리의 다른 글
[JAVA] 객체의 필드와 메서드 (0) | 2024.11.12 |
---|---|
[JAVA] 클래스 설계와 객체 생성 (0) | 2024.11.12 |
[JAVA] 참조형 자료구조 정리(LIST / STACK / QUEUE / SET / MAP) (0) | 2024.11.11 |
[JAVA] 배열 (0) | 2024.11.11 |
[JAVA] 반복문 (0) | 2024.11.11 |