-
[자바 코딩 인터뷰 완벽 가이드] Chapter 6. 객체지향 프로그래밍 Part1STUDY/JAVA 2024. 8. 7. 17:24
6.1 객체지향 프로그래밍의 개념 이해
- 객체(object)
- 클래스(class)
- 추상화(abstraction)
- 캡슐화(encapsulation)
- 상속(ingeritance)
- 다형성(polymophism)
- 연관(association)
- 집약(aggregation)
- 구성(composition)
6.1.1 객체란 무엇인가?
- 객체는 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 객체는 실세계의 개체이다.
- 객체는 상태(필드)와 동작(메서드)를 가진다.
- 객체는 클래스의 인스턴스를 나타낸다.
- 객체는 메모리에서 공간을 차지한다.
- 객체는 다른 객체와 소통할 수 있다.
- 객체는 각기 다른 접근 제어자(access modifier) 및 가시성 범위를 가질 수 있다.
- 객체는 가변 또는 불변 속성을 가진다.
- 객체는 가비지 컬렉터(garbage colletor)를 통해 수집된다.
객체는 객체지향 프로그래밍의 핵심 개념 중 하나로, 차, 탁자, 고양이와 같은 실세계의 개체와 같다. 수명주기(life cycle)동안 객체는 상태와 동작을 가진다. 자바에서 객체는 new 키워드를 통해 만들어진 클래스의 인스턴스로 필드에 상태를 저장하고 메서드로 동작을 표현한다. 각 인스턴스는 메모리 공간을 차지하며 다른 객체와 소통할 수 있다.
6.1.2 클래스란 무엇인가?
- 클래스는 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 클래스는 객체를 생성하기 위한 템플릿 또는 청사진이다.
- 클래스는 인스턴스화하기 전까지는 메모리의 힙 영역을 소모하지 않는다.
- 클래스는 여러 번 인스턴스화할 수 있다.
- 하나의 클래스는 하나의 작업만 한다.
- 클래스는 각기 다른 접근 제어자 및 가시성 범위를 가질 수 있다.
- 클래스는 로컬, 클래스 그리고 인스턴스 변수 등 다양한 타입의 변수를 지원한다.
- 클래스는 abstract, fianl 또는 private으로 선언할 수 있다.
- 클래스는 다른 클래스(내부 클래스)에 중첩될 수 있다.
클래스는 객체지향 프로그래밍의 핵심 개념 중 하나로, 특정 타입의 객체를 만드는 데 필요한 지침의 집합이다. 클래스는 템플릿, 청사진 또는 객체를 만드는 방법을 알려주는 레시피하고 할 수 있으며, 객체를 만드는 과정을 '인스턴스화한다'고 하며 new 키워드로 수행할 수 있다. 클래스는 여러 번 인스턴스화하여 원하는 만큼 많은 객체를 만들 수 있다. 클래스의 정의는 파일 형태로 하드 드라이브에 저장될 뿐 메모리의 힙 영역을 소모하지 않는다. 클래스는 단일 책임 원칙을 따르고, 이 원칙에 따라 단 하나의 일을 할 수 있도록 설계 및 작성되어야 한다.
6.1.3 추상화란 무엇인가?
- 추상화는 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 추상화는 사용자와 관련 있는 내용만 노출하고 나머지 세부 내용은 숨기는 개념이다.
- 추상화를 통해 사용자는 애플리케이션이 일을 수행하는 방법이 아니라 애플리케이션이 수행하는 일 자체에 집중할 수 있다.
추상화는 사용자를 위해 무언가를 최대한 간단하게 만들고자 하는 객체지향 프로그래밍의 핵심 개념 중 하나이다. 객체지향 프로그래밍의 객체는 사용자에게 높은 수준의 작업 집합만 노출하고 작업의 내부 구현 내용은 숨겨야 한다는 말이 있다. 이 개념을 구현하는 추상화를 통해 사용자는 애플리케이션이 일을 수행하는 방법이 아니라 수행하는 일 자체에 집중할 수 있다. 즉, 내용을 노출하는 복잡성을 줄이고 코드의 재사용성을 높이며 코드 중복을 방지하고 낮은 결합도와 높은 응집도를 유지한다. 또한 중요한 내용만 공개하여 애플리케이션의 보안과 재량권을 유지한다.
[예시]
차를 운전하는 사람이 있을 때, 사람은 각각의 페달이 무슨 일을 하는지, 핸들이 무슨 일을 하는지 알고 있지만, 페달과 핸들에 힘을 실어주는 차 내부의 메커니즘은 전혀 알지 못하는 경우가 많다. 이것이 추상화이다.
public interface Car { public void speedUp(); public void slowDown(); public void turnRight(); public void turnLeft(); public String getCarType(); } public class ElectricCar implements Car { private final String carType; public ElectricCar(String carType) { this.carType = carType; } @Override public void speedUp() { ... 구현 내용 } @Override public void slowDown() { .. 구현 내용 } @Override public void turnRight() { .. 구현 내용 } @Override public void turnLeft() { .. 구현 내용 } @Override public String getCarType() { return this.carType; } } public class Main { public static void main(String[] args) { Car electricCar = new ElectricCar("BMW"); Car petrolCar = new ElectricCar("Audi"); System.out.println("Driving the electric car: " + electricCar.getCarType() + "\n"); electricCar.speedUp(); electricCar.turnLeft(); electricCar.slowDown(); System.out.println("\n\nDriving the petrol car: " + petrolCar.getCarType() + "\n"); petrolCar.slowDown(); petrolCar.turnLeft(); petrolCar.turnRight(); } }
6.1.4 캡슐화란 무엇인가?
- 캡슐화는 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 캡슐화는 객체 상태가 외부로부터 숨겨진 상황에서 이 상태에 접근하는 일련의 공개 메서드만 노출하는 기법이다.
- 캡슐화는 각 객체가 클래스 내에서 객체의 상태를 비공개로 유지할 때 성립한다.
- 캡슐화는 정보 은닉(infomation-hiding) 메커니즘이라고도 한다.
- 캡슐화는 느슨한 결합, 재사용성, 보안 및 테스트하기 쉬운 코드와 같은 여러가지 중요한 이점을 제공한다.
- 캡슐화는 접근 제어자로 구현할 수 있다.
캡슐화는 객체지향 프로그래밍의 핵심 개념 중 하나로, 주로 코드와 데이터를 하나의 작업 단위인 클래스로 결합하고 외부 코드가 이 데이터에 직접 접근하지 못하게 하는 방어막 역할을 한다. 또한 객체 상태를 외부로부터 숨기고 이 상태에 접근하기 위한 일련의 public 메서드를 노출하는 기법이다. 각 객체가 클래스 안에서 상태를 private으로 유지할 때 캡슐화가 성립되었다고 할 수 있다. 따라서 캡슐화는 정보 은닉 메커니즘이라고 불리기도 한다. 캡슐화를 이용하면 몇 가지 장점이 있는데, 먼저 코드의 느슨한 결합이 가능하다. 예를 들어 클라이언트 코드와 어긋나지 않는 상태에서 클래스 변수의 이름을 변경할 수 있다. 또한 재사용할 수 있으며 클래스 내에서 데이터가 어떻게 조작되는지 클라이언트가 인식하지 못하므로 안전하다. 자바에서 캡슐화는 public, private, protected와 같은 접근 제어자로 구현할 수 있다. 일반적으로 객체가 자체 상태를 관리할 때 상태는 private 변수로 선언되고 public 메서드로 접근 및 수정되고, 클래스 외부에서 접근할 수 없는 private 메서드도 가질 수 있다.
[예시]
public class Cat { private int mood = 50; private int hungry = 50; private int energy = 50; public void sleep() { energy++; hungry++; } public void play() { mood++ energy--; } public void feed() { hungry--; mood++; } private void meow() { ... } public int getMood() { return mood; } public int getHungry() { return hungry; } public int getEnergy() { return energy; } } public static void main(String[] args) { Cat cat = new Cat(); cat.feed(): cat.play(); cat.feed(); cat.sleep(); }
6.1.5 상속이란 무엇인가?
- 상속은 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 상속을 통해 다른 객체를 기반으로 하는 새로운 객체를 만들 수 있다.
- 상속은 객체가 다른 객체의 코 드를 재사용할 수 있도록 허용하여 코드의 재사용성을 유지한다. 또한 각 객체만의 로직도 추가할 수 있다.
- 상속은 IS-A 관계라고 하며, 부모-자녀 관계라고도 한다.
- 자바에서 상속은 extends 키워드로 구현할 수 있다.
- 상속된 객체는 슈퍼클래스라고 하고, 슈퍼클래스를 상속받은 객체는 서브클래스하고 한다.
- 자바에서는 여러 개의 클래스를 상속할 수 없다.
상속은 객체지향 프로그래밍의 핵심 개념 중 하나이다. 상속을 통해 다른 객체를 기반으로 객체를 만들 수 있다. 상속은 서로 다른 객체가 상당히 유사하고 몇 가지 공통된 로직을 공유하지만 완전히 동일하지는 않을 때 유용하다. 상속은 객체가 다른 객체의 코드를 재사용할 수 있도록 허용하여 코드의 재사용성을 유지하고 각 객체만의 로직도 추가할 수 있다. 이를 IS-A 관계라고 하며 부모-자녀 관계라고도 한다. 예를 들어 '고양이 IS-A 고양이과의 동물' 또는 '기차 IS-A 교통수단' 과 같이 상속 관계를 표현할 수 있다. 자바에서 상속은 extends 키워드로 부모 클래스로부터 자식 클래스를 파생시켜 구현한다. 상속된 객체는 슈퍼클래스 또는 부모 클래스하고 하고 슈퍼클래스를 상속받은 객체는 서브클래스 또는 자식 클래스라고 하며, 자바에서는 여러 개의 클래스를 상속할 수 없다.
[예시]
public class Employee { private String name; public Employee(String name) { this.name = name; } } public class Programmer extends Employee { private String team; public Programmer(String name, String team) { super(name); this.team = team; } }
6.1.6 다형성이란 무엇인가?
- 다형성은 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 다형성을 뜻하는 'polymorphism'이라는 단어는 그리스어로 '많은 형태'를 의미한다.
- 다형성은 때에 따라 객체가 다르게 동작할 수 있도록 한다.
- 다형성은 (컴파일 타임 다형성이라고 하는) 메서드 오버로딩(overloading)이나, IS-A 관계의 경우(런타임 다형성이라고 하는) 메서드 오버라이딩을 통해 형성될 수 있다.
다형성은 객체지향 프로그래밍의 핵심 개념 중 하나로, 객체가 때에 따라 다르게 동작할 수 있게 해주거나 어떤 종작이 다른 방법으로 동작할 수 있도록 하는 역할을 한다. 다형성을 구현하는 방법 중 하나는 메서드 오버로딩이 있다. 여러 개의 메서드가 동일한 이름을 가지고 있지만 매개변수가 다른 경우 컴파일러가 오버로드된 메서드 가운데 어떤 형식을 호출할 것인지 컴파일 시간에 식별할 수 있으므로 컴파일 타임 다형성이라도고 한다. 또 다른 방법은 메서드 오버라이딩이 있다. 메서드 오버라이딩은 IS-A 관계가 있을 때 일반적으로 사용하는 방법이며, 런타임 다형성 또는 동적 메서드 디스패치라고도 한다. 보통 여러 가지 메서드를 포함하는 인터페이스 구현으로 시작하며, 각 클래스는 특화된 동작을 수행하기 위해 인터페이스에 있는 메서드를 오버라이드한다. 이때 다형성이 타입에 대한 혼란 없이 이 클래스들을 부모 인터페이스와 똑같이 사용할 수 있게 하는데, 런타임에 자바가 이런한 클래스를 구별할 수 있고 어느 클래스가 사용되는지 알고 있기 때문에 가능한 일이다.
[예시] 메서드 오버로딩을 활용한 컴파일 타임 다형성
public class Triangle { public void draw() { System.out.println("Draw default triangle..."); } public void draw(String color) { System.out.println("Draw a triangle of color " + color); } public void draw(int size, String color) { System.out.println("Draw a triangle of color " + color + " and scale it up with the new size of " + size); } }
[예시] 메서드 오버라이딩을 활용한 런타임 다형성
public interface Shape { public void draw(); } public class Triangle implements Shape { @Override public void draw() { System.out.println("Draw a triangle ..."); } } public class Rectangle implements Shape { @Override public void draw() { System.out.println("Draw a rectangle ..."); } } public class Circle implements Shape { @Override public void draw() { System.out.println("Draw a circle ..."); } }
6.1.7 연관이란 무엇인가?
- 연관은 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 연관은 서로 독립적인 두 클래스 간의 관계를 의미한다.
- 연관은 소유자가 없다.
- 연관은 일대일, 일대다, 다대일, 다대다 관계가 될 수 있다.
연관은 객체지향 프로그래밍의 핵심 개념 중 하나로, 서로 독립적인 두 클래스 간의 관계를 의미하여 객체 간의 다중 관계라고도 한다. 연관에서는 소유자가 없다. 연관 관계에 있는 객체들은 서로 사용 가능한 양방향 연관 관계를 가지거나 한 객체만 다른 객체를 사용할 수 있는 단방향 연관 관계를 가지며 자체 수명을 가진다. 예를 들어 사람과 주소 객체에는 양방향 다대다 관계가 있을 수 있다. 한 명의 사람은 여러 개의 주소와 연관될 수 있으며 하나의 주소는 여러 사람에게 속할 수 있다. 그러나 사람은 주소 없이 존재할 수 있고 주소도 사람 없이 존재할 수 있다.
[예시]
public class Person { private String name; public Person(String name) { this.name = name; } } public class Address { private String city; private String zip; public Address(String city, String zip) { this.city = city; this.zip = zip; } } public static void main(String[] args) { Person p1 = new Person("Andrei"); Person p2 = new Person("Marin"); Address a1 = new Address("Banesti", "107050"); Address a2 = new Address("Bucuresti", "229344"); System.out.println(p1.getName() + " lives at address " + a2.getCity() + ", " + a2.getZip() + " but it also has an address at " + a1.getCity() + ", " + a1.getZip()); System.out.println(p2.getName() + " lives at address " + a1.getCity() + ", " + a1.getZip() + " but it also has an address at " + a2.getCity() + ", " + a2.getZip()); }
6.1.8 집약이란 무엇인가?
- 집약은 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 집약은 단방향 연관 관계의 특별한 경우이다.
- 집약은 HAS-A 관계를 나타낸다.
- 집약 관계에 있는 두 객체는 자체 수명 주기를 가지며 객체 중 하나는 HAS-A 관계의 소유자이다.
집약은 객체지향 프로그래밍의 핵심 개념 중 하나이다. 집약은 단방향 연관 관계의 특별한 경우이다. 연관이 서로 독립적인 두 클래스 간의 관계를 의미한다면 집약은 두 객체 간의 HAS-A 관계를 의미한다. 여기서 집약 관계의 두 객체는 자체 수명 주기를 가지며 객체 중 하나는 HAS-A 관계의 소유자이다. 자체 수명 주기를 갖는다는 것은 한 객체가 종료되어도 다른 객체에 영향을 미치지 않는다는 의미이다. 예를 들어 테스트 선수는 라켓을 가지는데, 라켓은 테니스선수를 사용할 수 없으므로 단방향 연관 관계이고, 테니스 선수가 죽더라도 라켓은 영향을 받지 않는다.
[예시]
public clas Racket { private String type; private int size; private int weight; public Racket(String type, int size, int weight) { this.type = type; this.size = size; this.weight = weight; } } public class TennisPlayer { private String name; private Racket racket; public TennisPlayer(String name, Racket racket) { this.name = name; this.racket = racket; } } public static void main(String[] args) { Racket racket = new Racket("Babolat Pure Aero", 100, 300); TennisPlayer player = new Tennisplayer("Rafael Nadal", racket); }
6.1.9 구성이란 무엇인가?
- 구성은 객체지향 프로그래밍의 핵심 개념 중 하나이다.
- 구성은 좀 더 제한적인 집약 관계이다.
- 구성은 단독으로 존재할 수 없는 객체를 포함하는 HAS-A 관계를 나타낸다.
- 구성은 코드 재사용성과 객체의 가시성 제어를 유지한다.
구성은 객체지향 프로그래밍의 핵심 개념 중 하나로, 좀 더 제한적인 집약 관계이다. 집약이 자체 수명 주기를 가지는 두 객체 간의 HAS-A 관계를 의미한다면, 구성은 단독으로 존재할 수 없는 객체를 포함하는 HAS-A 관계를 의미한다. 이러한 연결고리를 강조하기 위해 PART-OF 관계라고도 한다. 예를 들어 차는 엔진을 가지는데, 차가 파괴되면 엔진도 파괴되는 것을 의미한다. 구성은 객체의 가시성을 제어하고 코드를 재사용하므로 상속보다 더 낫다고 말한다.
[예시]
public class Engine { pirvate String type; private int horsepower; public Engine(String type, int horsepower) { this.type = type; this.horsepower = horsepower; } } public class Car { private final String name; private final Engine engine; public Car(String name) { this.name = name; Engine engine = new Engine("petrol", 300); this.engine = engine; } }
출처 : 안겔 레오나르드, 『자바 코딩 인터뷰 완벽 가이드』, 동양북스(2022), p105-p126