Java - 객체 지향 프로그래밍

진경천·2024년 9월 19일

Java

목록 보기
2/9

객체 지향 프로그래밍이란?

객체란 모든 실재하는 것을 의미한다.

객체 지향이란 이러한 객체 개념을 기반으로 프로그램을 설계할 때 객체 단위로 파악하고, 이들 간의 상호작용을 통해 기능을 구현하는 프로그래밍 패러다임이다.

OOP(Object Oriented Programming)라고도 한다.

OOP의 4가지 특징은 아래와 같다.

  • 상속
  • 캡슐화
  • 다형성
  • 추상화

class

클래스는 객체가 어떤 상태, 어떤 동작을 가져야 하는지에 대한 설계도이다.

public class Person{
	// 필드
	private String name;
    private int age;
    
    // 생성자
    public Person(String name){
    	this.name = name;
    }
    
    // 메서드
    public Stirng getName(){
    	return name;
    }
}

클래스는 위처럼 필드, 생성자, 메서드로 구성돼있다.

인스턴스

인스턴스는 실체화된 객체로, 클래스의 설계도로 만들어진 객체라고 생각하면 된다.

인스턴스는 new연산자를 활용해 생성해준다.

Person p = new Person();

위 코드는 Person 클래스의 p 라는 인스턴스를 생성한 것이다.

필드

객체의 상태와 동작을 나타내는 클래스에서 객체의 상태에 해당한다.

일반적인 변수 선언 방식과 동일하며 타입 앞에 접근제어자를 추가해준다.

필드는 private String name = "홍길동";과 같이 선언과 동시에 초기화도 해줄 수 있다.

필드에 접근하기 위해서는 .이라는 접근연산자가 필요하다. 접근 제어자에 따라 필드에 접근 가능여부가 결정된다.

여기서 접근이란 변수를 호출하거나 값을 변경한다는 의미이다.

p.name = "홍길순"; // error
p.age = 15;

Person 클래스의 name은 private이기 때문에 접근이 불가하고, age는 public이기 때문에 접근이 가능하다.

메서드

메서드란 객체가 행하는 동작을 정의한 것이다.
C/C++의 함수와 같다고 보면된다.

public String getName(){
	return name;
}
  • 접근 제어자
  • 반환 타입
  • 메서드 명
  • 파라미터

메서드는 위 4가지를 꼭 포함시켜야 한다.

오버로딩

한 클래스 네에서 동일한 이름을 가진 메서드를 여러개 선언 가능하며 이를 오버로딩이라한다.
단, 오버로딩을 할 땐 메서드명은 같되, 파라미터의 타입과 개수 중 하나 이상이 달라야한다.

public class Person{
	public void walk(){
    	System.out.println("walking");
    }
    
    public void walk(String place){
    	System.out.println("walking at " + place);
    }

자바는 메서드를 구분할 때에 메서드 시그니처를 이용해 판별하는데,
메서드 시그니처는 메서드명, 파라미터의 자료형과 개수이다.

생성자

생성자는 인스턴스를 생성할 때 호출되는 메서드이다.
각 클래스마다 생성자는 하나 이상 꼭 포함되어야 하며 생성자를 명시하지 않았을 경우, 파라미터와 내용이 없는 기본 생성자가 자동으로 추가된다.

생성자는 주로 클래스의 필드를 초기화하는 목적으로 사용된다.

public Person(String name, int age){
	this.name = name;
    this.age = age;
}

생성자의 특징은 아래와 같다.

  • 메서드 선언과 구조가 동일
  • 클래스명과 동일한 메서드명을 가짐
  • 반환값이 없다.
  • 오버로딩 가능
  • this 를 이용해 객체의 필드와 파라미터를 구분

thisthis를 사용하는 클래스 자기자신을 가리킨다. 즉, 자기 자신을 참조한다는 뜻이다.
또한, this()는 자기자신의 생성자를 호출한다.

상속👩‍👧‍👦

상속이란 기존에 존재하던 클래스의 필드드와 메서드를 그대로 물려받아 멤버 변수 및 메서드를 추가하거나 재정의하여 새로운 클래스를 정의하는 것이다.

물려주는 클래스를 상위클래스, super class, 부모 클래스라 칭하며,
물려받는 클래스를 하위클래스, sub class, 자식 클래스라 칭한다.

상속의 특징

  • 코드의 중복 감소
  • 코드 재활용성 증가
  • 유지 보수 용이
  • 클래스 간의 계층 관계표현
    ➡️ 가독성 향상 및 다형성 구현을 가능케함
public class Person{
	private String name;
    private int age;
    
    public void walk(){
    	System.out.println("walking");
    }
    
public class Programmer extends Person{
	private String department;
    
   	public void coding(){
  		System.out.println("coding");
    }
}

위 코드는 Student 클래스가 Person을 상속 받은 것으로 Programmer는 Person의 필드와 메서드를 사용할 수 있다.

클래스의 상속 관계는 위 그림과 같이 자식이 부모를 포함하고 있는 관계이다.
자식 클래스는 부모 클래스의 멤버를 모두 포함하고, 추가적으로 본인의 멤버까지 포함한다.
그렇기 때문에 부모 객체는 자식의 멤버를 호출할 수 없다.

메서드 오버라이딩

오버라이딩은 상속 받은 부모의 메서드를 자식클래스에서 재정의 하는 것이다.
같은 메서드 시그니처를 가진 동작을 자식 클래스에 맞게 수정이 가능하여 새로운 메서드 선언이 불필요해진다.

@Override
public void walk(){
	System.out.println("programmer walking");
}

메서드 오버라이딩의 조건

  • 메서드 시그니처가 부모 클래스의 메서드와 동일해야함
  • 메서드의 접근 제어자는 부모 클래스의 메서드와 같거나 더 넓은 범위이어야함
  • 반환 타입은 부모 메서드와 같거나 하위 클래스이어야 함

@Override는 에노테이션으로 오버라이딩 된 메서드임을 명시해준다.

자식 클래스의 생성

자식 클래스의 생성자는 부모 클래스의 생성자를 포함시켜야 한다.
자식을 생성할 때 부모 클래스를 먼저 생성해야 하기 때문이다.

public class Person{
	private String name;
    private int age;
    
    public Person(String name, int age){
    	this.name = name;
        this.age = age;
    }
}
    
public class Programmer extends Person{
	private String department;
    
   	public Programmer(String name, int age, String department){
    	super(name, age);
        this.department = department;
    }
}

superthis와 같이 참조 변수 역할을 하는데 차이점은 부모 클래스의 멤버를 참조한다는 것이다.
마찬가지로 super()는 부모의 생성자를 호출할 때 사용한다.

캡슐화💊

캡슐화는 외부로부터 클래스에 정의된 멤버 변수와 메서드를 보호하고 필요한 데이터만 외부에 노출함으로써 데이터를 보호하고 은닉하는 것이 주목적이다.

위 그림처럼 멤버 변수와 메서드를 클래스라는 캡슐안에 담는다는 의미에서 캡슐화라한다.

캡슐화를 구현할 때의 핵심은 접근제어자와 getter/setter 메서드를 이용하는 것이다.

접근 제어자

외부에서 객체가 가진 정보에 접근하거나 변경할 수 있다면 의도하지 않은 값으로 조작될 위험이 있다.
특히 협업을 진행하는 과정에서 변경하면 안되는 값을 다른 사람이 변경하거나 삭제를 해버리는 상황이 올 수도 있다.

이러한 불상사를 겪지 않기 위해서 아래의 접근 제어자를 적재적소에 잘 활용해야한다.

접근 제어자의 종류

  • public
    모든 접근 범위에 대해 접근 허용
  • protected
    다른 패키지에서 접근 불가하지만 다른 패키지의 자식 클래스에서는 접근 허용
  • default
    같은 패키지 내에서만 접근 허용
  • private
    같은 클래스 내에서만 접근 허용
접근 제어자 같은 클래스 같은 패키지 자식 클래스
/다른 패키지
다른 패키지
public O O O O
protected O O O X
default O O X X
private O X X X

getter/setter

getter/setter 메서드는 접근 제어자에 의해 접근이 불가한 멤버 변수에 간접적으로 접근 할 수 있는 메서드다.

getter 메서드는 멤버 변수를 반환하고, setter 메서드는 멤버 변수를 변경하는 메서드이다.

public class Person{
	private int age;
    
    public String getAge(){
    	return age;
    }
    
    public void setAge(int age){
    	if(age < 0)
        	throw new IllegalArgumentException("나이는 0세 이상이어야 합니다.");
    	this.age = age;
    }
}

setter 메서드에서 검증 절차를 거쳐 유효한 데이터만 저장할 수 있도록 설계가 가능하다.

다형성

다형성의 생물학적 의미는 동종 개체에서 대립형질이 뚜렷이 구별되어 나타나는 것이다.
이와 같이 프로그래밍에서 다형성은 하나의 이름을 가진 변수, 메서드, 클래스가 다양한 의미로 해석 될 수 있도록 구현하는 것이다.

다형성을 구현하는 핵심적인 3가지 방법은 오버로딩 , 오버라이딩, 클래스 간의 계층 관계를 활용하는 것이다.

多形性(다형성)을 직역하면 여러가지 형태를 가질 수 있는 능력이다.

다형적 참조

상속 관계를 활용하여 한 타입의 변수로 여러 객체를 참조 가능하다.
즉, 상위 클래스 타입의 참조 변수로 하위 클래스 객체를 참조할 수 있다.

Person person1 = new Programmer();
Person person2 = new Singer();
Person person3 = new Dancer();

위와 같이 Person이란 한 타입에 Person을 상속한 여러 객체를 참조한다.

하위 클래스를 참조한 객체는 기본적으로 타입이 부모이기 때문에 부모 클래스의 필드와 메서드만 사용할 수 있다.

단, 동적 바인딩에 의해 오버라이딩 된 메서드는 사용가능하다.

ex)

public class Person{
	public String name;
    public int age;
    
    public void work(){
    	System.out.println("working");
    }
}

public class Programmer extends Person{
	public String company;
    
    @Override
    public void work(){
    	System.out.println("coding");
    }
}
public class Main{
	public static void main(String[] args){
    	Person person = new Programmer();
        
        person.name;
        person.age;
        
        person.company;	// compile error
        person.work();	// coding
    }
}

추상화

추상화의 사전적 의미는 사물이나 표상을 어떤 성질, 공통성, 본질에 착안하여 그것을 추출하여 파악하는 것이다.

이에 착안하여 프로그래밍에서의 추상화는 클래스 간 공통적인 속성을 찾아내어 공통의 조상을 만드는 작업을 의미한다.

추상화의 특징

  • 복잡도 감소 및 분석 용이
  • 재사용성과 유지보수성 향상
  • 일관성 있는 개발 및 협업에 유리
  • 추상 메서드를 하나 이상 포함

추상 클래스는 abstract를 앞에 붙여 선언한다.

public abstract class Animal{
	public String name;
    public int age;
    
	public abstract void eat();
    
    public void run(){
    	System.out.println("run");
    }
}

추상 메서드

추상 메서드는 메서드 시그니처만 있고 구현 로직이 없는 메서드이다.

public abstract void eat();

추상 클래스의 인스턴스를 생성하려면 생성과 동시에 추상 매서드를 정의해줘야 한다.

Animal animal = new Animal() {
    @Override
    public void run() {
    	System.out.println("Animal run");
    }
}

인터페이스

인터페이스는 구현해야 하는 동작(메서드)를 작성하고, 구현을 강제하는 일종의 명세서이다.

인터페이스도 접근 제어자 사용이 가능하며 모든 메서드가 public abstract 제어자로 선언이된다.
즉, 모든 메서드가 추상메서드인것이다.

인터페이스의 특징

  • 상속과 구현 동시에 가능
  • 2개 이상의 다중 구현 가능
  • 상속 가능
  • 필드로 상수만 선언 가능

인터페이스의 목적

  • 계약 제공
    ➡️구현 클래스에 특정 메서드 구현을 강제(계약)함
  • 다형성 활용
    ➡️인터페이스 타입으로 여러 구현체를 다룰 수 있다
  • 결합도 감소
    ➡️인터페이스를 통한 상호 작용으로 유연한 코드 설계 가능

인터페이스를 구현한다는 것은 상속을 받아서 오버라이딩하는 것과 유사하다고 볼 수 있다.

인터페이스 또한 extends를 이용해 다른 인터페이스를 상속 받을 수 있다.

interface A{}
interface B{}
interface Driving extends A, B{}
class Car{}
class GasolineCar extends Car implements Driving{} 

다음은 위 GasolineCar 클래스의 상속과 구현 관계를 그림으로 표현한 것이다.

profile
어중이떠중이

0개의 댓글