“실제 세계는 사물(유형의 객체)로 이루어져 있으며 발생하는 모든 사건들은 사물(객체)간의 상호작용이다.” 라는 것이 객체 지향의 기본 개념이다.
프로그래밍에서는 상속, 캡슐화, 추상화 개념을 중심으로 가상세계에서 객체(무형의 객체)를 다룰 수 있는 객체지향언어가 만들어졌다.
객체지향 프로그래밍을 위한 객체지향개념은 캡슐화, 상속, 추상화, 다형성 4가지로 나뉜다.
어떤 데이터와 그 데이터를 활용한 메서드를 한 클래스 안에 넣어놓는 것을 캡슐화라고 한다.
클래스가 캡슐인 셈이다.
외부에서 클래스 내부의 멤버변수나 메서드 등에 접근할 필요가 없을 때, 접근제어자를 활용해서 클래스 내부에 선언된 데이터를 보호할 수 있다.
노출하고 싶은 데이터는 해당 범위의 접근제어자로 설정하고,
노출시키지 않는 데이터는 외부에서 데이터를 함부로 변경하지 못하게 접근을 제한해서 데이터가 유효한 값을 유지하도록 하는것이 캡슐화에 해당한다.
class Car {
private String brand; // private 제어자로 외부에서 직접 접근할 수 없는 데이터
private int price;
public void setCar(String brand, int price) { // set 메서드를 통해 변수를 저장한다
this.brand = brand;
this.price = price;
}
public String analysis() { // 자동차 브랜드와 가격별 멘트
if (this.brand.equals("벤츠") || this.brand.equals("아우디") || this.brand.equals("bmw") && this.price > 5000) {
return this.price + "만원의 " + this.brand + " 탑클래스 차를 타시는군요!";
}
else return this.price + "만원의 " + this.brand + "를 타시는군요, " + "당신의 차도 멋져요.";
}
}
public class JavaChapter {
public static void main(String[] args) {
// 캡슐화
Car car = new Car();
car.setCar("벤츠",100000000);
System.out.println(car.analysis());
}
}
실행결과
100000000만원의 벤츠 탑클래스 차를 타시는군요!
상속이란, 기존의 코드를 확장하여 새로운 클래스를 작성하는 것이다.
코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여한다.
일반적으로 사용되는 큰 개념으로 구성된 부모클래스를 기반으로 구체적인 내용으로 이 클래스를 확장하여 새로운 클래스를 생성하는 것을 말한다.
상속은 재사용의 목적보다는 확장에 의미가 있다.
class Car {
String brand; // 자동차 브랜드
int price; // 자동차 가격
public Car(String brand, int price) { // Car 생성자
this.brand = brand;
this.price = price;
}
public String comment() { // 자동차 브랜드와 가격별 멘트
if (this.brand.equals("벤츠") || this.brand.equals("아우디") || this.brand.equals("bmw") && this.price > 5000) {
return this.price + "만원의 " + this.brand + " 탑클래스 차를 타시는군요!";
}
else return this.price + "만원의 " + this.brand + "를 타시는군요, " + "당신의 차도 멋져요.";
}
}
public class JavaChapter {
static class Age extends Car { // Age클래스가 Car클래스를 상속 - Age클래스는 자식클래스, Car클래스는 부모클래스
int age; // Age의 인스턴스 변수
public Age(String type, int price, int age) { // 생성자 오버로딩
super(type, price);
this.age = age;
}
@Override
public String comment() { // 부모클래스 메서드 오버라이딩
return super.comment() + " 부러워요. 전 "+ this.age + "살에 차를 살거에요.";
}
}
static class FuelEfficiency extends Car {
int fuelEfficiency; // FuelEfficiency의 인스턴스 변수
public FuelEfficiency(String type, int price, int fuelEfficiency) { // 생성자 오버로딩
super(type, price);
this.fuelEfficiency = fuelEfficiency;
}
@Override
public String comment() { // 부모클래스 메서드 오버라이딩
if (this.fuelEfficiency >= 15) return super.comment() + " 연비가 " + this.fuelEfficiency + "km로 아주 좋군요.";
else return super.comment() + " 연비는 " + this.fuelEfficiency + "km로 아쉽네요.";
}
}
public static void main(String[] args) {
// 상속
Age age = new Age("bmw", 9000, 29);
System.out.println(age.comment());
FuelEfficiency fe = new FuelEfficiency("중고차", 2000, 13);
System.out.println(fe.comment());
}
}
실행결과
9000만원의 bmw 탑클래스 차를 타시는군요! 부러워요. 전 29살에 차를 살거에요.
2000만원의 중고차를 타시는군요, 당신의 차도 멋져요. 연비는 13km로 아쉽네요.
추상화는 상속과 비슷하지만 반대되는 개념이라고 볼 수 있다.
클래스의 공통점을 찾아서 공통의 클래스를 만드는 작업을 추상화라고 한다.
class Marine { // 보병
int x, y;
void move(int x, int y) {} // 지정된 위치로 이동
void stop() {} // 정지
void stimPack() {}
}
class Tank { // 탱크
int x, y;
void move(int x, int y) {}
void stop() {}
void changeMode() {}
}
class Dropship { // 수송선
int x, y;
void move(int x, int y) {}
void stop() {}
void load() {}
void unload() {}
}
↓
abstract class Unit { // 공통 부분을 추상화
int x, y;
abstract void move(int x, int y);
void stop() {}
}
class Marine extends Unit {
void move(int x, int y) {} // 추상메서드만 상속 (재구현)
void stimPack() {}
}
class Tank extends Unit {
void move(int x, int y) {}
void changeMode() {}
}
class Dropship extends Unit{
void move(int x, int y) {}
void load() {}
void unload() {}
}
객체지향 개념에서 다형성은 '여러가지 형태를 가질 수 있는 능력'을 의미하고
자바에서 다형성은 부모클래스 타입의 참조변수로 자식클래스의 인스턴스를 참조할 수 있다는 것의 의미가 기본이다.
부모타입 <-> 자식타입 간의 형변환이 가능하다.
참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위를 조전하기 위해 사용한다.
자식타입을 부모타입으로 형변환 할 때 (업캐스팅) : 형변환 생략 가능
부모타입을 자식타입으로 형변환 할 때 (다운캐스팅) : 형변환 생략 불가능
형변환 검사
참조변수 instanceof 클래스명(타입)
가 true이면 참조변수의 타입을 해당 클래스의 타입으로 변환할 수 있다는 뜻이고 false이면 타입변환이 불가하다는 뜻이다.
참조변수의 타입에 따른 호출 값의 차이
부모클래스와 자식클래스가 같은 이름의 멤버변수를 갖고 있을 때 멤버의 사용 범위가 달라지기 때문에 아래와 같이 멤버변수의 호출 값이 달라진다.
class Car {
int speed = 20;
}
class myCar extends Car {
int speed = 50;
}
public class JavaChapter {
public static void main(String[] args) {
Car c = new myCar();
myCar m = new myCar();
System.out.println(c.speed);
System.out.println(m.speed);
}
}
실행결과
20
50
메서드의 매개변수에도 다형성을 적용할 수 있다.
자식클래스가 부모클래스를 상속받았다면 메서드의 매개변수에 부모타입의 참조변수를 통해 자식클래스의 인스턴스를 사용하는 것이 가능하다.
다형성이 가장 중요하다.
스프링에서 의존성 주입, ioc 컨테이너 등 모두 다형성을 활용한 것이다.
객체지향 프로그래밍에 대해 공부하는데 꽤 오래 시간을 쏟았다.
아직 온전히 이해하지 못했고 내용도 아직 다 채워지진 않았지만 여러번 되새기면서 더 적절한 예제도 많이 만들어봐야할 듯 하다.🫠💪🏻