7 분 소요

자바 복습 및 정리 3P

클래스

객체 지향 프로그래밍에서 중요한 개념 중 하나

데이터와 이를 처리하는 메서드를 하나로 묶어놓은 틀이나 템플릿이다.

클래스는 설계도라고 할 수 있으며, 객체를 생성하는데 사용된다.

클래스는 크게 네 가지 요소로 구성된다.

필드, 메서드, 생성자, 이너클래스

public class ExampleClass {
	int x = 10; // (1)필드
	void printX() {...} // (2)메서드
	ExampleClass {...} // (3)생성자
	class ExampleClass2 {...} // (4)이너 클래스
}

필드 - 클래스의 속성을 나타내는 변수. 자동차로 예를 들면 모델명, 컬러, 바퀴의 수 등이 포함.

메서드 - 클래스의 기능을 나타내는 함수. 시동걸기, 가속하기, 정지하기 등이 포함.

생성자 - 클래스의 객체를 생성하는 역할.

이너 클래스 - 클래스 내부의 클래스.

클래스는 다음과 같은 특징을 가지고 있다.

속성 (멤버 변수, 필드)

클래스 내부에는 데이터를 저장하는 멤버 변수들이 있다.

이 멤버 변수들은 클래스가 표현하는 객체의 상태나 특징을 나타낸다.

메서드 (멤버 함수)

클래스 내부에는 데이터를 처리하고 조작하는 메서드들이 있다.

이 메서드들은 클래스가 가진 기능이나 동작을 정의한다.

생성자

객체가 생성될 때 초기화하는데 사용되는 특별한 메서드이다.

생성자를 통해 객체의 초기 상태를 설정할 수 있다.

접근 제어자

멤버 변수와 메서드에는 접근 제어자(public, private 등)를 사용하여 외부에서의 접근을 제한할 수 있다.

상속

클래스 간에 상속을 통해 기존 클래스의 특성과 기능을 물려받아 새로운 클래스를 정의할 수 있다.

다형성

여러 클래스가 같은 이름의 메서드를 가지면서 다른 동작을 하는 것을 의미하며, 객체의 타입에 따라 다른 메서드가 호출될 수 있다.

추상 클래스

객체 생성을 위한 템플릿이며, 일부 메서드를 추상 메서드로 정의하여 하위 클래스에서 반드시 구현하도록 할 수 있다.

인스턴스화

클래스를 바탕으로 실제 객체를 생성하는 과정을 인스턴스화라고 한다.

인스턴스는 클래스의 구조를 따르며 독립적인 데이터와 동작을 가진다.

정적 멤버

클래스 레벨에서 접근할 수 있는 정적 멤버(변수, 메서드)를 정의할 수 있다.

인스턴스 생성 없이 클래스 이름으로 접근 가능하다.

// 클래스 정의
class Car {
    // 속성 (멤버 변수, 필드)
    String brand;
    String model;
    int year;

    // 생성자
    public Car(String brand, String model, int year) {
        this.brand = brand;
        this.model = model;
        this.year = year;
    }

    // 메서드 (기능)
    public void startEngine() {
        System.out.println("Engine started!");
    }

    public void drive() {
        System.out.println("Driving the " + year + " " + brand + " " + model);
    }
}

public class Main {
    public static void main(String[] args) {
        // 객체 인스턴스화 (클래스로부터 실제 객체 생성)
        Car myCar = new Car("Toyota", "Camry", 2022);

        // 메서드 호출
        myCar.startEngine();
        myCar.drive();
    }
}

객체

클래스가 개별 객체(인스턴스)를 만들기 위한 설계도이며,

클래스를 통해 만들어진 객체가 실제로 사용할 수 있는 실체이다.

public class Person {
    // 속성 (멤버 변수)
    private String name;
    private int age;

    // 생성자
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 메서드
    public void introduce() {
        System.out.println("안녕하세요, 저는 " + name + "이고 " + age + "살입니다.");
    }
}

public class Main {
    public static void main(String[] args) {
        // 객체 생성
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Bob", 30);

        // 메서드 호출
        person1.introduce(); // 출력: 안녕하세요, 저는 Alice이고 25살입니다.
        person2.introduce(); // 출력: 안녕하세요, 저는 Bob이고 30살입니다.
    }
}

메서드

코드를 논리적으로 그룹화하고 재사용성을 높이기 위해 사용되는 블록 단위의 코드 조각.

메서드는 클래스 내에 선언되며 특정한 작업을 수행하는 코드 블록을 정의하고 호출할 때 실행된다.

메서드는 다음과 같은 구성 요소로 구성된다.

1. 메서드 시그니처

메서드의 이름과 매개변수(parameter) 목록으로 이루어진다.

매개변수는 메서드가 실행될 때 필요한 입력값을 나타낸다.

2. 메서드 본문

메서드가 수행할 코드 블록이다.

메서드가 호출되면 이 코드 블록이 실행된다.

3. 리턴 타입

메서드가 값을 반환하는 경우, 그 반환 값의 자료형을 정의한다.

반환하지 않는 메서드는 void를 리턴 타입으로 가진다.

public class MyClass {
    // 메서드 정의
    public int add(int a, int b) { // int a, int b = parameter
        int result = a + b;
        return result;
    }

    public void printMessage(String message) {
        System.out.println(message);
    }

    // 메인 메서드 (프로그램의 시작점)
    public static void main(String[] args) {
        MyClass myObject = new MyClass();
        int sum = myObject.add(5, 7); // 메서드 호출
        myObject.printMessage("Hello, Java!"); // 메서드 호출
    }
}

위 예시에서 add 메서드는 두 개의 정수를 더한 값을 반환하고,

printMessage 메서드는 메서드 문자열을 화면에 출력한다.

메서드는 코드 재사용성과 모듈화를 증가시키며, 큰 프로그램을 더 작고 관리 가능한 조각으로 나눌 수 있게 한다.

public class MyClass {
	public static String getPhoneNumber() {
		String phoneNumber = "010-1234-1234";
		return phoneNumber;
	}

	public static void main(String[] args) {
		String phone = getPhoneNumber();
		System.out.println("전화번호 : " + phone);
	}
}

// 출력
// 전화번호 : 010-1234-1234

참고

public static String hello() {
	...
}

public String hello() {
	...
}

위 두 메서드의 차이점은 static 키워드의 유무로서 메서드가 객체에 종속적인지 클래스에 종속적인지를 나타낸다.

메서드가 static으로 선언되면 해당 메서드는 클래스에 종속되어 객체를 생성하지 않고도 호출할 수 있다.

반면에 static이 없는 메서드는 객체를 생성한 후 해당 객체의 메서드로 호출해야 한다.

위 두 메서드를 예로 들어 코드로 보면 이런 차이점이 있다.

public class MyClass {
    // static 메서드
    public static String staticHello() {
        return "Hello from static method!";
    }

    // 인스턴스 메서드
    public String instanceHello() {
        return "Hello from instance method!";
    }

    public static void main(String[] args) {
        // static 메서드 호출
        String staticMessage = MyClass.staticHello();
        System.out.println(staticMessage);

        // 인스턴스 생성
        MyClass myObject = new MyClass();

        // 인스턴스 메서드 호출
        String instanceMessage = myObject.instanceHello();
        System.out.println(instanceMessage);
    }
}

이 외에 static 키워드 말고도 다양한 키워드가 있다.

final

변수에 사용되면 해당 변수를 상수로 만들어 값을 변경할 수 없게 한다.

메서드에 사용되면 해당 메서드를 오버라이드할 수 없게 하거나 클래스에 사용되면 해당 클래스를 상속할 수 없게 한다.

// final 키워드의 예시
final int constantValue = 10;

// final 메서드 예시
final void finalMethod() {
    // 메서드 내용
}

abstract

클래스나 메서드에 사용될 수 있다.

클래스에 사용되면 추상 클래스를 나타내며, 객체를 직접 생성할 수 없다.

메서드에 사용되면 해당 메서드가 추상 메서드임을 나타내며, 하위 클래스에서 반드시 구현해야 한다.

// abstract 클래스 예시
abstract class AbstractClass {
    abstract void abstractMethod();
}

synchronized

멀티스레드 환경에서 동기화를 제공하는 역할을 한다.

해당 키워드로 둘러싼 블록은 한 번에 하나의 스레드만 실행할 수 있게 한다.

// synchronized 메서드 예시
synchronized void synchronizedMethod() {
    // 메서드 내용
}

volatile

변수를 메인 메모리에 저장하고 캐시를 사용하지 않도록 지정하는 역할을 한다.

멀티스레드 환경에서 변수의 값을 보장하기 위해 사용된다.

// volatile 변수 예시
volatile int volatileValue;

transient

직렬화를 할 때 해당 변수를 제외하도록 지정한다.

// transient 변수 예시
transient int transientValue;

native

자바 코드 내에서 해당 메서드가 네이티브 코드(c, c++ 등)로 작성된 메서드임을 나타낸다.

// native 메서드 예시
native void nativeMethod();

추상 클래스, 추상 메서드

추상 클래스

인스턴스를 생성할 수 없는 클래스로, 다른 클래스들의 공통된 특성을 추상화하여 상속을 통해 확장되는 용도로 사용된다.

추상클래스는 abstract 키워드를 사용하여 정의하며, 추상 메서드를 포함할 수 있다.

추상 클래스 내에는 일반 메서드 또는 멤버 변수도 포함될 수 있다.

추상 메서드

메서드의 선언만 있고 본체가 없는 메서드이다.

이 메서드는 하위 클래스에서 반드시 구현되어야 한다.

추상 메서드는 추상 클래스 내에서 abstract 키워드를 사용하여 선언한다.

// 추상 클래스 정의
abstract class Shape {
    // 추상 메서드 선언 (하위 클래스에서 반드시 구현해야 함)
    abstract double calculateArea();
}

// Circle 클래스는 Shape 클래스를 상속하며 추상 메서드를 구현합니다.
class Circle extends Shape {
    double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double calculateArea() {
        return Math.PI * radius * radius;
    }
}

// Rectangle 클래스도 Shape 클래스를 상속하고 추상 메서드를 구현합니다.
class Rectangle extends Shape {
    double width;
    double height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    double calculateArea() {
        return width * height;
    }
}

오버라이딩의 역할은 다음 포스트에서 확인할 수 있다.

추상클래와 추상 메서드의 사용 목적

공통된 기능을 추상화

여러 하위 클래스에서 공통된 속성과 동작을 추상화하여 정의할 수 있다.

코드의 중복을 줄이고 유지 보수성을 향상시킬 수 있다.

다형성 구현

추상 클래스를 사용하면 다형성을 구현할 수 있다.

서로 다른 하위 클래스들을 추상 클래스 타입으로 관리하고, 추상 클래스에 정의된 추상 메서드를 통해 다양한 하위 클래스의 구체적인 기능을 활용할 수 있다.

메서드 강제 구현

하위 클래스에 특정 메서드의 구현을 강제할 수 있다.

이를 통해 하위 클래스가 반드시 해당 메서드를 구현하도록 보장하고, 일관된 동작을 유지할 수 있다.

확장성

새로운 하위 클래스를 쉽게 추가하거나 기존 클래스를 확장할 수 있다.

추상 클래스의 공통된 속성과 메서드를 상속받아 기능을 추가하거나 수정할 수 있다.

설계의 유연성

인터페이스와 구현의 분리를 실현할 수 있다.

추상 클래스는 인터페이스와 구현 사이의 중간 단계로서, 하위 클래스들이 공통된 틀을 가지며 동시에 각각의 독립적인 특성을 갖을 수 있도록 도와준다.

코드 가독성과 유지 보수성

상속 관계와 메서드의 목적을 명확하게 나타낼 수 있다.

추상화는 복잡한 현실 세계에서 중요한 세부사항을 무시하고 핵심 개념이나 속성을 강조하는 과정을 말한다.

abstract class Animal {
    abstract void makeSound();
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("멍멍!");
    }
}

class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("야옹~");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.makeSound(); // 멍멍!

        Cat cat = new Cat();
        cat.makeSound(); // 야옹~
    }
}

오버로딩

자바에서 동일한 이름을 가진 메서드를 여러 개 정의하는 개념

각 메서드는 매개변수의 개수, 타입 또는 순서가 다르도록 정의된다.

이를 통해 메서드의 이름은 같지만 다양한 매개변수를 받아 처리할 수 있다.

public class Printer {
    public void print(int num) {
        System.out.println("정수: " + num);
    }

    public void print(double num) {
        System.out.println("실수: " + num);
    }

    public void print(String text) {
        System.out.println("문자열: " + text);
    }
}

public class Main {
    public static void main(String[] args) {
        Printer printer = new Printer();

        printer.print(10);          // 출력: 정수: 10
        printer.print(3.14);        // 출력: 실수: 3.14
        printer.print("Hello");     // 출력: 문자열: Hello
    }
}

위 예시에서 Printer 클래스는 print 메서드를 정수, 실수, 문자열 타입에 대해 오버로딩하여 세 가지 버전의 메서드를 가지고 있다.

메서드 호출 시 전달되는 인자의 타입에 따라 적절한 오버로딩된 메서드가 호출되어 처리된다.

댓글남기기