객체지향 프로그래밍(OOP - Object Oriented Programming)
객체 지향 프로그래밍은 인간 중심적 프로그래밍 패러다임입니다.
객체는 우리가 일상생활을 하며 인식할 수 있는 모든 사물이라 할 수 있습니다. 다시 말하자면
객체 지향 프로그래밍이란 실제 사물을 프로그래밍으로 옮겨와 모델링 하는 것으로 이해하시면 됩니다.
인간이 보는 사물(Object)는 사실 속성(Variable)과 행위(Method)로 구성되어 있습니다.
이러한 사물, 즉 객체를 만드는 설계도가 클래스(Class) 입니다. 자바를 예로 들자면
public class Person {
string height;
public void walk() {
Sytem.out.println("앞으로 걸어갑니다.")
}
{
Person이라는 클래스 안에 height라는 속성과 walk라는 메서드로 구성된 것을 확인할 수 있습니다.
클래스는 객체를 만드는 틀과 같아서 새로운 객체를 무한정 생성할 수 있습니다.
Person personA = new Person();
person personB = new person();
다음과 같이 하나의 클래스를 활용하여 personA와 personB라는 객체를 생성하였습니다.
객체지향 프로그래밍은 이러한 객체들이 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍
방법입니다.
객체지향 프로그래밍의 장점
코드 재사용에 용이합니다.
상속을 통해 코드의 재사용성을 높일 수 있고, 남이 만든 클래스를 가져와 이용할 수 있습니다.
자연적인 모델링이 가능해집니다.
객체란 일상생활의 모든 것이기 때문에 현실 세계의 생각대로 프로그래밍을 할 수 있습니다.
유지 보수 작업에 우수성을 보입니다.
프로그램 수정을 위해 코드를 추가 하더라도, 캡슐화를 통해 주변에 영향을 적게 끼칩니다. 따라서 유지보수가
쉬워지게 됩니다.
대형 프로젝트에 적합합니다.
클래스 단위로 모듈화가 가능하기 때문에 대형 프로젝트에서 업무 분담을 하기가 쉽습니다.
객체지향 프로그래밍의 단점
처리 속도가 상대적으로 느립니다.
객체가 많으면 용량이 커집니다.
설계시 많은 노력과 시간이 필요합니다.
객체지향 프로그래밍의 특성
1. 추상화
추상의 사전적 정의는 "사물이나 표상을 어떤 성질, 공통성, 본질에 착안하여 그것을 추출하여
파악하는 것" 으로 정의하고 있습니다. 불필요한 정보 외 중요한 정보(공통성, 본질)만 표현함으로써
공통의 속성과 기능을 묶어 이름을 붙이는 것이 추상화라고 할 수 있습니다.
간단하게 예를 들자면, 자동차와 자전거가 있습니다. 둘의 차이점은 많습니다.
프레임 또는 구조, 설비 또는 부품 등 수많은 차이가 있지만, 추상화는 이 둘의 공통적인 특성에
집중 합니다. 둘 모두 이동 수단이라는 공통적인 특성을 가지고 있기 때문에 추상화를 활용한다면 자동차 자전거 둘 모두
이동 수단(Vehicle)이라는 이름으로 묶어서 표현이 가능하게 됩니다.
자바에서는 이러한 추상화를 구현할 수 있는 문법 요소로 추상 클래스와 인터 페이스를 제공합니다.
2. 상속
상속이란 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소를 의미합니다.
상속 받은 하위 클래스는 상위 클래스의 속성과 기능들을 구현할 필요 없이 간편하게 사용할 수 있습니다.
이해하기 쉽게 자바 코드로 예시를 들자면,
public class Human {
public void eat() {
System.out.println("먹습니다.");
}
public void walk() {
System.out.println("걷습니다.");
}
}
다음과 같은 Huma 클래스가 있다고 가정해봅시다.
public class Jeongho extends Human {
public void routine() {
System.out.println("맨날 누워 잡니다.");
}
}
Jeongho 클래스는 Human 클래스를 상속받고 내부적으로 routine이라는 메서드를 지니고 있습니다.
Human에서 정의한 eat과 walk 메서드는 따로 구현하지 않았는데도
public class Main {
public static void main(String[] args) {
Jeongho jeongho = new Jeongho();
jeongho.walk();
jeongho.eat();
jeongho.routine();
}
}
다음과 같이 Human에서 정의한 기능들을 사용할 수 있게 됩니다. 뿐만 아니라 상속 받은 기능 이외에 해당 클래스에만
추가적으로 필요한 기능들도 구현할 수 있습니다.
3. 다형성
다형성은 객체지향 프로그래밍에서 가장 중요한 특성이라고 할 수 있습니다. 다향성이란 어떤 객체의
속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있음을 의미합니다.
이를 비유적으로 설명하자면, 어떤 20대 남자는 상황과 환경에 따라 학생이 될 수 있고, 친구가 될 수 있고
아들 또는 손자가 될 수 있습니다.
객체 지향의 다형성도 이와 마찬가지로 어떤 객체으 속성과 기능이 그 맥락에 따라 다른 역할을 수행 할 수 있는
특성을 의미합니다. 이에 대한 대표적인 예로 오버라이딩과 오버로딩이 있습니다.
여기서 잠깐! 오버라이딩과 오버로딩이 무엇일까?
- 오버로딩 : 메서드 이름은 같지만 파라미터 수와 타입이 다른 메소드를 선언하는 것을 의미합니다.
- 오버라이딩 : 부모 클래스의 메서드 동작방식을 변경하여 우선적으로 사용하는 것을 의미합니다.
오버로딩 코드 예시는 다음과 같습니다.
public class OverLoadingExample {
public int methodA() {
return 0;
}
public int methodA(int a) {
return a;
}
public String methodA(String a) {
return a;
}
}
OverLoadingExample 클래스에는 같은 이름의 methodA가 3개 선언되어 있습니다.
원래 같은 이름의 메서드는 한 클래스에 선언될 수 없습니다. 다음과 같은 조건을 충족하면 오버로딩이 되어
같은 이름의 메서드를 여러개 선언할 수 있게 됩니다.
1. 파라미터 갯수가 달라야 합니다.
2. 파라미터 갯수가 같을 경우 파라미터의 데이터 타입이 달라야합니다.
주의할 점은 이때 리턴형은 달라도 되는 점입니다.
리턴이 String, void, int이든 상관없이 위에서 제시한 1 또는 2 조건을 충족하면 이는 오버로딩이 되게 됩니다.
오버 라이딩 예시 코드는 다음과 같습니다.
public class Human {
public void eat() {
System.out.println("먹습니다.");
}
public void walk() {
System.out.println("걷습니다.");
}
}
public class Jeongho extends Human {
public void routine() {
System.out.println("맨날 누워 잡니다.");
}
@Override
public void eat() {
System.out.println("곱창을 먹습니다");
}
}
Human 클래스의 eat 메서드를 Jeongho 클래스가 오버라이딩 하여 구현하고 있습니다.
오버라이딩의 성립조건은 아래와 같습니다.
1. 오버라이드 하고하자 하는 메서드가 상위 클래스에 존재해야 한다.
2. 메서드 이름이 서로 같아야 한다.
3. 메서드 파라미터 갯수와 자료형이 같아야 한다
4. 메서드 리턴형이 같아야 한다.
5. 상위 메서드와 동일하거나 내용이 추가되어야 한다.
만약 오버로딩 처럼 파라미터 갯수, 파라미터 자료형, 메서드 리턴형 중에 하나라도 다를 경우 이는 오버라이딩으로 성립 되지 않습니다. 다시 말하자면 오버라이딩은 부모 클래스의 메서드 동작 방식(안에 구현 내용) 만 변경하여 사용하는 것을 뜻합니다.
오버라이딩과 오버로딩은 다형성의 한 예시입니다. 하지만 다형성은 이것 보다 훨씬 더 중요한 정의가 있습니다.
객체지향 프로그래밍에서 다형성이란 한 타입의 참조 변수를 통하여 여러 타입의 객체를 참조할 수 있도록 만드는 것을
의미합니다.
글로만 봐선 이해가 되질 않으니 자바 코드를 예시로 설명해보겠습니다.
public class Vehicle {
public void move() {
System.out.println("움직입니다.");
}
}
public class Car extends Vehicle{
public void move() {
System.out.println("차가 움직입니다.");
}
}
다음과 같이 이동 수단을 뜻하는 Vehicle 클래스와 그를 상속받는 Car 클래스가 있다고 가정해봅시다.
두 클래스 모두 move라는 메서드를 정의하고 있습니다. 다형성이 적용되지 않을 경우 car클래스의 move메서드를 사용하려면 다음과 같이 코드를 작성해야 합니다.
public class Main {
public static void main(String[] args) {
Car car = new Car(); // 객체 생성
car.move();
}
}
하지만 다형성이 적용되면 아래와 같이 코드 작성이 가능해집니다.
public class Main {
public static void main(String[] args) {
Vehicle vehicle = new Car(); // 객체 생성
vehicle.move();
}
}
Car의 부모 클래스인 Vehicle을 참조변수로 사용하여 Car 객체의 move 메서드를 사용할 수 있게 된 것입니다.
결과 값은 밑에 사진과 같습니다.

즉 다형성이란 상위 클래스의 참조변수로 하위 클래스의 객체를 참조하는 것을 뜻합니다.
얼핏 보기에는 그게 그거 같은데 다형성을 활용하면 왜 유용할까요?
만약 위의 예시에서 Vehicle을 상속받은 이동 수단이 Car 말고도 여러개가 있다고 가정해봅시다.
public class Bicycle extends Vehicle{
@Override
public void move() {
System.out.println("자전거가 지나갑니다. 따르릉");
}
}
public class Train extends Vehicle {
@Override
public void move() {
System.out.println("기차가 지나갑니다. 칙칙폭폭");
}
}
Vehicle 클래스를 상속받는 기차(Train)와 자전거(Bicycle) 클래스가 추가되었습니다.
만약 이러한 이동수단들을 어떤 이용자가 사용하려면 어떤 방식으로 사용할 수 있을까요?
다형성이 적용되지 않으면 아래와 같을 것입니다.
public class People {
public void Move(Car car) {
car.move();
}
public void Move(Train train) {
train.move();
}
public void Move(Bicycle bicycle) {
bicycle.move();
}
}
People은 이동수단을 이용하는 클래스입니다. 따라서 다음과 같이 모든 이동 수단에 대하여 Move메서드를 구현해줘야 합니다. 하지만 다형성이 지니고 있는 상위 클래스의 참조변수로 하위 클래스 객체를 참조할 수 있다면 코드는 아래와 같이
달라지게 됩니다.
public class People {
public void Move(Vehicle vehicle) {
vehicle.move();
}
}
상당이 코드가 간소화된 것을 확인할 수 있습니다.
이를 main 메서드에서 실행하게 되면
public class Main {
public static void main(String[] args) {
People people = new People();
Car car = new Car();
Bicycle bicycle = new Bicycle();
Train train = new Train();
people.Move(car);
people.Move(bicycle);
people.Move(train);
}
}

아래와 같이 각각의 이동 수단에 맞게끔 move 메서드가 작동한 것을 확인할 수 있습니다.
4. 캡슐화
캡슐화란 서로 연관있는 속성과 기능들을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것을 의미합니다.
캡슐화는 다음과 같은 기능을 수행합니다.
1. 외부로부터 클래스에 정의된 속성과 기능을 보호(데이터 보호)
2. 내부의 동작을 감추고 외부에 필요한 부분만 노출(데이터 은닉)
그렇다면 데이터를 왜 보호하고, 은닉해야 할까요? 그 이유로는 크게 3가지로 나뉩니다.
1. 코드의 중복을 막을 수 있습니다.
한 클래스의 속성을 직접 꺼내와 로직을 작성하게 된다면, 다른 여러 도메인 객체에서 해당 동작이 필요할 경우
코드를 중복적으로 작성할 수 밖에 없습니다. 이것도 마찬가지로 자바코드를 통해 예시를 들어보겠습니다.
public class TestScore {
public int math = 55;
public int english = 75;
}
다음과 같이 시험점수(TestScore)라는 클래스에 수학과 영어 점수가 표기되어 있다고 가정해봅시다.
만약 데이터 보호와 은닉화가 안되어 있다면 해당 점수 계산 다음과 같이 작성할 것입니다.
public class Main {
public static void main(String[] args) {
TestScore testScore = new TestScore();
int totalScore = testScore.math + testScore.english;
System.out.println(totalScore);
}
}
만약 TestScore 객체에서 점수를 가져와 더하는 로직들이 필요로하는 도메인이 많아진다면 어떻게 될까요?
해당 코드를 도메인 별로 중복해서 작성해야 되는 불상사가 일어나게 됩니다. 물론 지금은 +와 같은 비교적 간단한 로직이지만 만약 연산 과정이 복잡해지게 된다면 써야될 코드량을 기하급수적으로 늘어나게 됩니다.
이를 방지하기 위해 데이터를 private 접근제어자를 통해 은닉하게 된다면 직접적으로 해당 속성값을 가져와서 쓸수 없게
됩니다. 따라서 TestScore가 제공하는 메서드를 통하여 해당 로직을 대체해야 합니다. 예시는 다음과 같습니다.
public class TestScore {
private int math = 55;
private int english = 75;
public int getTotalScore() {
return math + english;
}
}
public class Main {
public static void main(String[] args) {
TestScore testScore = new TestScore();
System.out.println(testScore.getTotalScore());
}
}
TestScore의 속성 접근제어자가 private으로 변경되어 더 이상 외부에서 해당 속성에 직접적인 접근을 할 수 없게 되었습니다. 따라서 총합 점수를 얻으려면 public으로 설정된 getTotalScore 메서드를 사용해야 됩니다. 이를 통해 코드의 중복을
줄일 수 있게 되었습니다.
2. 객체에 대해 알아야 하는 지식의 양을 줄여줍니다.
위의 예제 코드에서 살펴본것 처럼, TestScore 객체를 캡슐화 함으로써 우리는 총합 점수를 구할 떄 math와 english 속성 정보들을 몰라도 구할 수 있게 되었습니다. 객체안에 속성 정보가 많으면 많을 수록 캡슐화는 객체를 사용할 때
알아야 하는 속성과 같은 정보, 즉 지식의 양을 줄여주게 됩니다.
3. 외부에서 조작하여 속성을 변경하는 일을 막을 수 있다.
캡슐화가 제대로 되어있지 않다면 클래스 외부에서 속성을 마음대로 조작할 수 있습니다. 만약 객체 내에 비밀번호 또는 주민번호와 같은 민감한 정보가 담겨있다면 이를 변경 못하게 데이터 변경을 막아야합니다. 캡슐화는 이러한 부분에 큰 도움을 줍니다.
public class User {
public int securityNumber;// 주민번호
public User(int securityNumber) {
this.securityNumber = securityNumber;
}
}
public class Main {
public static void main(String[] args) {
User user = new User(960805);
// 캡슐화가 되어있지 않으면 주민번호를 다음과 같이 변경 가능하다.
user.securityNumber = 000000;
System.out.println(user.securityNumber);
}
}
다음과 같이 User 클래스에는 주민번호(securityNubmer) 속성이 정의되어 있습니다. 이는 생성 될 시에 한번 지정되면
더 이상 바뀌지 않아야 하는 변경에 민감한 속성입니다. 하지만 캡슐화가 다음과 같이 안되어 있을 경우 다음과 같이
변경이 되게 됩니다.
만약 캡슐화를 사용한다면 다음과 같이 변경을 못하도록 막을 수 있습니다.
public class User {
private int securityNumber;// 주민번호
public User(int securityNumber) {
this.securityNumber = securityNumber;
}
}
주민 번호 속성의 접근제어자를 public에서 private으로 바꿨습니다. 만약 이를 다른 클래스에서 값을 바꾸게 된다면

다음과 같이 빨간색 밑줄로 변경이 불가능하다고 표시됩니다.
정리하자면, 캡슐화를 통하여 코드의 중복을 막고, 객체에 대해 알아야 하는 지식의 양을 줄일 수 있으며 변경에 민감한
속성의 변경을 막을 수 있습니다.
이상으로 객체지향 프로그래밍에 대한 정리를 마치겠습니다.
긴글 읽어 주셔서 감사합니다!
참고자료
https://backendcode.tistory.com/160
[IT 기술 면접] OOP(객체 지향 프로그래밍) 이란?
2022년 5월 24일에 6개월 국비 교육과정을 수료하였다. 일주일 간 휴식 시간을 보내고, 오늘부터 기술 면접을 준비하고자 한다. 하루에 2~3개씩 정리해서 포스팅 하기 Java 개발자 기술 면접 단골 질
backendcode.tistory.com
객체 지향 프로그래밍의 4가지 특징ㅣ추상화, 상속, 다형성, 캡슐화 -
객체 지향 프로그래밍은 객체의 유기적인 협력과 결합으로 파악하고자 하는 컴퓨터 프로그래밍의 패러다임을 의미합니다. 객체 지향 프로그래밍의 기본적인 개념과 그 설계를 바르게 하기 위
www.codestates.com
객체지향에서 캡슐화가 중요한 이유
결론적으로 상태를 잘 정의된 행동 집합 뒤로 캡슐화하는 것은 객체의 자율성을 높이고 협력을 단순하고 유연하게 만든다. 이것이 상태를 캡슐화해야 하는 이유다.'객체지향의 사실과 오해'에
velog.io
'Java' 카테고리의 다른 글
자바 변수를 메모리 관점에서 뜯어보기 (0) | 2023.04.01 |
---|---|
가비지 컬렉션에 대해 (0) | 2023.03.21 |
Java - ArrayList와 LinkedList (0) | 2022.01.18 |
Java - StringTokenizer (0) | 2022.01.18 |
Java - BufferedReader와 Scanner 차이 (0) | 2022.01.18 |