728x90
객체 지향 프로그래밍(Object Oriented Programming)이란?
- 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고, 그 객체들 간의 유기적인 상호 작용을 통해 로직을 구성하는 프로그래밍 방법
객체 지향 프로그래밍의 장점과 단점
- 장점
- 코드 재사용 용이
- 클래스와 객체를 재사용하거나 상속을 통해 기존 코드의 기능을 확장하는 것이 가능
- 또한, 다형성을 통해 동일한 인터페이스를 사용하여 다양한 객체에 대해 작업을 수행 가능
- 유지 보수가 쉬움
- 절차 지향 프로그래밍에서는 코드를 수정해야할 때 일일이 찾아 수정해야하는 반면 객체 지향 프로그래밍에서는 캡슐화와 모듈화 덕분에 클래스와 객체 단위로 변경을 적용할 수 있어 유지보수가 상대적으로 용이
- 대형 프로젝트에 적합
- 클래스 단위로 모듈화시켜서 개발할 수 있으므로 대형 프로젝트처럼 여러 명, 여러 회사에서 프로젝트를 개발할 때 업무 분담이 쉬움
- 코드 재사용 용이
- 단점
- 처리 속도가 상대적으로 느림
- 객체를 생성하고 메모리에 할당하므로, 메모리 사용량이 증가할 수 있음
- 설계 시 많은 시간과 노력이 필요
- 러닝 커브가 높음
- 과도한 추상화나 객체 간의 복잡한 관계는 코드의 가독성을 떨어뜨릴 수 있음
클래스(Class)와 인스턴스(객체, Object)
- 클래스(Class)
- 어떤 문제를 해결하기 위해 관련된 속성(데이터)과 기능(메서드)를 모아둔 설계도
- 객체를 만들기 위한 청사진
Ex) 자동차 설계도(속성 : 바퀴 수, 색상, 엔진 / 기능 : 가속, 정지)
- 인스턴스(객체)
- 클래스에서 정의한 속성과 행동을 실제 메모리에 할당하여 만든 구체적인 객체
- Ex) 자동차 설계도를 토대로 만든 실제 자동차
- 1개의 클래스(설계도)로 원하는 만큼의 인스턴스(자동차) 생성 가능
- 클래스는 추상적 개념이지만, 인스턴스는 메모리에 존재하는 구체적 데이터
- 각각의 인스턴스는 독립적으로 동작
Ex) A 자동차 : 빨간색 / B 자동차 : 파란색
객체 지향 프로그래밍의 특징 4가지
추상화(Abstraction)
- 불필요한 세부 사항을 숨기고 중요한 정보만을 드러냄
- 객체의 공통된 속성(데이터)나 기능(메서드)을 추출하여 정의하는 것
- 멤버 필드(속성)는 public static final 생략되어 상수이며, 기능(메서드)는 public abstract가 생략되어 추상 메서드
Ex) 현대에서 자동차와 오토바이를 생산한다고 했을 때 공통된 속성으로는 회사 이름, 공통된 기능은 엑셀, 브레이크가 있음
public interface Vehicle {
public static final String company = "현대";
public abstract void go();
void stop();
}
상속(Inheritance)
- 기존 클래스(부모 클래스)의 속성(필드)과 기능(메서드)을 자식 클래스가 물려받아 재사용할 수 있게 하는 개념
- 상속을 통해 코드의 재사용성을 높이고, 기존 클래스를 확장하여 새로운 기능을 추가하거나, 필요에 따라 메서드를 재정의(오버라이딩) 하여 일부 기능 변경 가능
- 즉, 부모 클래스의 공통된 기능을 유지하면서, 고유한 속성이나 기능을 추가 가능
자동차와 오토바이 클래스
- 바퀴 수, 색상은 공통으로 가지는 속성(데이터)
- 전진과 정지는 공통으로 가지는 기능(메서드)
- 공통으로 가지는 것을 묶어 코드의 중복을 제거할 수 있음
public class Car {
int wheel;
String color;
public void go(){
System.out.println("엑셀을 밟습니다.");
}
public void stop(){
System.out.println("브레이크를 밟습니다.");
}
public void booster() {
System.out.println("부스터를 씁니다.");
}
}
public class MoterBike {
int wheel;
String color;
public void go(){
System.out.println("엑셀을 밟습니다.");
}
public void stop(){
System.out.println("브레이크를 밟습니다.");
}
public void circus() {
System.out.println("앞바퀴를 듭니다.");
}
}
Vehicle 클래스 상속
public class Vehicle {
int wheel;
String color;
public void go(){
System.out.println("엑셀을 밟습니다.");
}
public void stop(){
System.out.println("브레이크를 밟습니다.");
}
}
public class Car extends Vehicle{
public void booster() {
System.out.println("부스터를 씁니다.");
}
}
public class MoterBike extends Vehicle{
@Override
public void go() {
System.out.println("엑셀을 좀 더 강하게 밟습니다.");
}
public void circus() {
System.out.println("앞바퀴를 듭니다.");
}
}
다형성(Polymorphism)
- 하나의 인터페이스나 부모 클래스를 통해 여러 자식 클래스의 객체를 다룰 수 있는 것을 의미
- 코드는 더 유연해지고, 같은 메서드를 호출하더라도 해당 객체의 타입에 맞는 동작을 하게 할 수 있음
- 예를 들어, 이동 수단을 컬렉션으로 묶어서 관리를 하려고 할 때 자료형을 Car와 MoterBike로 선언을 했을 경우에는 두 개의 타입이 같지 않아 컬렉션에 저장할 수가 없음
- 하지만, Car와 MoterBike가 상속 받은 상위 클래스(Vehicle)을 사용한다면, 컬렉션으로 관리도 가능할 뿐더러 각각의 클래스에 맞는 기능(메서드)가 동작하게 됨
- 이를 업캐스팅이라고 하는데 주의할 점은 부모 클래스(Vehicle)에 없는 하위 클래스 고유의 기능은 사용할 수 없어 다운 캐스팅을 하여 사용해야 함
- 단, 다운 캐스팅은 필요한 경우에만 신중히 사용하며, 안전한 타입 변환을 위해 instanceof 사용 권장
public static void main(String[] args) {
Car car = new Car();
MoterBike moterBike = new MoterBike();
// List<Car, MoterBike> vehicleList = new ArrayList<>();
}
public static void main(String[] args) {
List<Vehicle> vehicleList = new ArrayList<>();
vehicleList.add(new Car());
vehicleList.add(new MoterBike());
vehicleList.get(0).go();
vehicleList.get(1).go();
// vehicleList.get(0).booster(); // 에러
((Car)vehicleList.get(0)).booster();
// Result
// 엑셀을 밟습니다.
// 엑셀을 좀 더 강하게 밟습니다.
// 부스터를 씁니다.
}
캡슐화(Encapsulation)
- 클래스 내부의 데이터(속성)와 메서드(기능)를 외부에서 직접 접근하지 못하도록 보호하여, 각 객체의 독립성과 안정성 보장
- 이로 인해 외부 코드가 객체 내부의 데이터에 무분별하게 접근하거나 수정하지 못하게 하고, 객체의 무결성과 안전성을 유지
- 필요한 경우에만 객체의 데이터를 외부에 노출하여 접근 가능
캡슐화 방법 2가지
- 접근 제어자(Access Modifiers)
- 클래스의 속성과 메서드에 접근 제어자를 설정하여 외부에서의 접근 범위를 제어 가능
- Ex) 속성을 private로 설정하면 외부에서 해당 속성에 직접 접근이 불가하고 오직 해당 클래스 내에서만 접근이 가능
- 이렇게 설정된 접근 제어자는 클래스 외부로부터 특정 멤버의 접근을 제한하여 정보 은닉을 강화하고, 클래스의 데이터를 보호
| 접근 제어자 | 클래스 내 | 패키지 내 | 다른 패키지의 하위 클래스 | 패키지 외 | 설명 |
| private | O | X | X | X | 동일 클래스 내에서만 접근 가능 |
| default | O | O | X | X | 동일 패키지 내에서만 접근 가능 |
| protected | O | O | O | X | 동일 패키지 + 다른 패키지의 하위클래스 접근 가능 |
| public | O | O | O | O | 제한 없음 |
package package1;
class Test {
public static void main(String[] args) {
SuperClass superClass = new SuperClass();
// System.out.println(superClass.a); // 클래스 외부이기 때문에 에러
System.out.println(superClass.b);
System.out.println(superClass.c);
System.out.println(superClass.d);
}
}
public class SuperClass {
private int a = 1;
int b = 2;
protected int c = 3;
public int d = 4;
public void printEach() {
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
package package2;
import package1.SuperClass;
class SubClass extends SuperClass {
public void printEach() {
// System.out.println(a); // private - 동일 클래스 아님
// System.out.println(b); // default - 동일 패키지 아님
System.out.println(c); // protected - 상속 받았기 때문에 가능
System.out.println(d);
}
}
public class Test {
public static void main(String[] args) {
SuperClass superClass = new SuperClass();
// System.out.println(superClass.a); // private
// System.out.println(superClass.b); // default
// System.out.println(superClass.c); // protected - 다른 패키지의 경우 상속 받지 않아 불가능
System.out.println(superClass.d);
}
}
- Getter/Setter 메서드:
- private로 감춘 속성을 외부에서 읽거나 변경해야 할 때, Getter와 Setter 메서드를 통해 안전하게 접근
- Getter 메서드는 속성 값을 반환하며, Setter 메서드는 속성 값을 설정할 때 유효성 검사를 포함할 수 있어 데이터 무결성을 보장
- 이렇게 제공된 접근 메서드를 통해 클래스의 내부 구현을 숨기고, 외부에는 필요한 정보만 제공하는 방식을 유지 가능
public class Person {
// 접근 제어자를 이용하여 외부로부터 데이터를 보호
private String name;
private int age;
// Getter 메서드를 통해 외부에서 name에 접근
public String getName() {
return name;
}
// Setter 메서드를 통해 age를 설정하면서 유효성 검사도 수행 가능
public void setAge(int age) {
if (age > 0) { // 나이는 양수여야 한다는 검증
this.age = age;
}
}
}
참고 자료
728x90
'Language > Java' 카테고리의 다른 글
| [JSP] 기본 문법 / 내장 객체 / 태그 (1) | 2024.12.02 |
|---|---|
| [GC] 가비지 컬렉터의 개념, 원리, 알고리즘 (1) | 2024.11.13 |
| [Java] Java의 컴파일 과정 (1) | 2024.11.13 |
| [JVM] JVM의 개념과 필요한 이유 (0) | 2024.11.13 |
| Call by Value와 Call by Reference의 차이 (0) | 2024.09.10 |