Frinee의 코드저장소

[Java] 객체지향 프로그래밍 (OOP)

by Frinee
객체지향 프로그래밍 (OOP: Object-Oriented Programming)
: 실제 세계의 개념을 소프트웨어로 모델링하여 효율적이고 직관적인 프로그램을 개발할 수 있도록 하는 프로그램 패러다임

 

1. 클래스와 객체

클래스(Class)

  • 클래스(Class)는 객체를 정의하는 틀 또는 설계도의 의미로 사용됨.
  • 클래스는 속성 변수를 나타내는 필드(field)와 객체의 함수를 나타내는 메소드(method)로 구성됨.
public class Circle {
	// 필드(field)
	public int radius;
	public String name;
	
	// 메소드(method)
	public Circle() { // 생성자 메소드
	}
	
	public double getArea() {  // 원의 면적 메소드
		return 3.14*radius*radius;
	}
}

객체(Object)와 인스턴스(instance)

  • 객체(Object): 물리적으로 존재하거나 개념적인 것 중에서 다른 것과 식별 가능한 것을 말함.
    • 예를 들어 물리적으로 존재하는 자동차,자전거나 개념적인 학과, 강의 등이 모두 객체가 될 수 있음
  • 인스턴스(instance): 클래스에 의해서 만들어진 객체를 의미함.
  • 클래스가 어떤 데이터의 구조 설계도라면, 객체는 설계도를 이용해 찍어낸 실 데이터라고 보면 된다.
// 클래스
class Animal { ... }

public class Sample {
    public static void main(String[] args) {
        // 변수 cat은 객체
        // 변수 catd은 Animal 클래스의 인스턴스
        Animal cat = new Animal(); // 클래스라는 설계도를 통해 객체 데이터를 new 생성
    }
}

필드(field)

  • 클래스에 포함된 변수(variable)을 말한다.
  • 클래스 멤버(member)라고도 불린다.
  • 클래스 필드는 선언된 위치와 선언자에 따라 다음과 같이 구분됨.
    • 클래스 변수(static variable)
    • 인스턴스 변수(instance variable)
    • 지역 변수(local variable)
class Field {
    static int classVar = 10; // 클래스/스태틱 변수 선언
    int instanceVar = 20;     // 인스턴스 변수 선언
    
    int method() {
    	int localVar = 30; // 지역 변수 선언
        return localVar;
    }
}

public class Member01 {
    public static void main(String[] args) {
        System.out.println( Field.classVar ); // 클래스/스태틱 변수 참조
        Field myField1 = new Field();   // 인스턴스 생성
        System.out.println( myField1.instanceVar ); // 인스턴스 변수 참조       
        System.out.println( myField1.method() ); // 메서드안의 지역변수 출력
    }
}
변수 생성 시기 소멸 시기 저장 메모리
클래스 변수 클래스가 메모리에 올라갈 때 프로그램이 종료될 때 메소드 영역
인스턴스 변수 인스턴스가 생성될 때 인스턴스가 소멸할 때 힙 영역
지역 변수 블록 내에서 변수의 선언문이 실행될 때 블록을 벗어날 때 스택 영역
  • 클래스 변수는 인스턴스를 생성하지 않아도 조회할 수 있다.

 

2. 메서드와 생성자

메서드(Method)

  • 클래스 내에 구현된 함수를 말함.
  • 다른 프로그래밍 언어에서는 함수(function)라는 개념을 자바에서는 메소드(method)라 표현함.
  • 메서드 정의
    • 접근 제어자/지정자: 해당 메서드에 접근할 수 있는 범위를 명시
    • 반환 타입(return type): 메서드가 모든 작업을 마치고 반환하는 데이터 타입을 명시
    • 메서드명: 메서드를 호출하기 위한 이름을 명시
    • 매개변수 목록(parameters): 메서드 호출 시에 전달되는 인수의 값을 저장할 변수들을 명시
    • 구현부: 메서드의 고유 기능을 수행하는 명령문의 집합. 중괄호 { } 안에 포함

생성자

  • 객체가 생성될 때 동적으로 인스턴스 변수 초기화를 위해 실행되는 특수한 메서드
  • 생성자 규칙
    • 생성자의 목적은 객체 초기화
    • 생성자 이름은 클래스 이름과 반드시 동일
    • 생성자는 new를 통해 객체를 생성할 때, 객체당 한 번 호출
    • 생성자는 객체가 생성될 때 반드시 호출됨.
    • 생성자는 return 타입을 지정하지 않음
    • 생성자는 여러 개 작성할 수 있음(Overloading)
class Car {
    String modelName;
    int modelYear;
    String color;
    int maxSpeed;
    int currentSpeed;

	// 생성자 (인스턴스 변수 값 초기화)
    Car(String modelName, int modelYear, String color, int maxSpeed) {
        this.modelName = modelName; // 메서드의 입력값으로 인스턴스 변수의 값을 지정
        this.modelYear = modelYear;
        this.color = color;
        this.maxSpeed = maxSpeed;
        this.currentSpeed = 0; // 입력값 없이 디폴트 초기화
    }

    String getModel() {
        return this.modelYear + "년식 " + this.modelName + " " + this.color;
    }
}

public class Main {
    public static void main(String[] args) {
        Car myCar1 = new Car("아반떼", 2016, "흰색", 250); // 생성자의 호출
		Car myCar2 = new Car("제네시스", 2020, "검은색", 500); // 생성자의 호출
        Car myCar3 = new Car("티코", 2003, "빨간색", 100); // 생성자의 호출

        System.out.println(myCar1.getModel()); // 2016년식 아반떼 흰색
        System.out.println(myCar2.getModel()); // 2020년식 제네시스 검은색
        System.out.println(myCar3.getModel()); // 2003년식 티코 빨간색
    }
}
  • this 참조 변수
    • 클래스 자기 자신을 뜻하는 키워드
    • 해당 인스턴스의 주소를 가리키고 있어 자기 자신에 접근이 가능함.
    • this() 메서드
      • 생성자 내부에서 같은 클래스의 다른 생성자를 호출할 때 사용.
        	Car() {
        		this("소나타", 2012, "검정색", 160);	
                // 해당 아규먼트가 일치하는 다른 생성자를 호출함.
        	}

 

3. 접근 제어자와 캡슐화

접근 제어자(access modifier)

  • 변수나 메서드의 사용 권한을 설정하기 위한 기능을 수행
  • 접근 제어자 종류
    • private: 해당 클래스 안에서만 접근 가능
    • default: 접근 제어자를 별도로 설정하지 않으면 자동으로 설정되며 동일한 패키지 안에서만 접근 가능
    • protected: 동일 패키지의 클래스 또는 해당 클래스를 상속받은 클래스에서만 접근이 가능
    • public: 어떤 클래스에서도 접근 가능

캡슐화(Encapsulation)

  • 데이터를 보호하기 위해 접근을 제한하고 외부와의 상호작용은 공개된 메서드(getter, setter)를 통해서만 이루어지도록 하는 객체지향 프로그래밍의 중요한 원칙
    • 데이터 은닉: 중요한 데이터는 private으로 보호
    • 인터페이스 제공: 데이터를 읽거나 수정할 때, public 메서드를 통해 제한적으로 접근 허용
    • 데이터 무결성 보장: 직접 접근을 차단해 데이터를 의도치 않게 변경하지 않도록 보호
public class Student {
    private String name;  // private으로 데이터를 보호
    private int age;

    // Getter 메서드
    public String getName() {
        return name;
    }

    // Setter 메서드
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0) {  // 데이터 무결성을 위한 조건 추가
            this.age = age;
        } else {
            System.out.println("Invalid age!");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Student student = new Student();
        student.setName("Alice"); // setter를 통해 이름 설정
        student.setAge(20);       // setter를 통해 나이 설정

        System.out.println(student.getName()); // "Alice"
        System.out.println(student.getAge());  // 20
    }
}

 

4. 상속과 다형성의 기초

상속(inheritance)

  • 하위 클래스가 상위 클래스의 기능을 그대로 물려받는 기능
  • 상속 기능을 이용하면 상위 클래스의 특징을 하위클래스에서 상속받아 코드의 중복 제거, 코드 재사용성 증대
  • 상속 시 키워드 extends 사용
class Animal {
    int teethCount; 
    int legCount; 
    int tailCount; 
}

class Dog extends Animal { // 상속을 통해 중복 코드를 제거
    void bark();
}

class Cat extends Animal { // 상속을 통해 중복 코드를 제거
    void meow();
}

class Lion extends Animal { // 상속을 통해 중복 코드를 제거
    void roar();
}

 

다형성 1 - 메서드 오버라이딩(Method Overriding)

  • 상위 클래스의 메서드를 하위 클래스가 동일한 형태로 또다시 구현하는 행위
  • 즉, 메서드 덮어쓰기라 할 수 있다.
class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name + " zzz");
    }
}

class HouseDog extends Dog {
	// 메서드 오버라이딩(method overriding) 
    void sleep() {
        System.out.println(this.name + " zzz in house");
    }
}

public class Sample {
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        houseDog.setName("happy");
        houseDog.sleep();  // happy zzz in house 출력
    }
}

 

다형성 2 - 메서드 오버로딩(Method Overloading)

  • 동일한 이름의 메서드를 매개변수의 타입, 개수, 순서에 따라 여러 개 정의
  • 반환 타입만 다른 경우 오버로딩이 가능하지 않고 컴파일 타임에 어떤 메서드를 호출할 지 결정됨.
class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name + " zzz");
    }
}

class HouseDog extends Dog {
    void sleep() {
        System.out.println(this.name + " zzz in house");
    }
		// 메서드 오버로딩(Method Overloading)
    void sleep(int hour) {
        System.out.println(this.name + " zzz in house for " + hour + " hours");
    }
}

public class Sample {
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        houseDog.setName("happy");
        houseDog.sleep();  // happy zzz in house 출력
        houseDog.sleep(3);  // happy zzz in house for 3 hours 출력
    }
}

블로그의 정보

프리니의 코드저장소

Frinee

활동하기