본문 바로가기
프로그래밍 언어/Java 프로그래밍

Java Day7 - 객체지향 프로그래밍(클래스, 객체, ...)

by Hyeon_ 2021. 11. 9.

객체지향 프로그래밍

  • Object Oriented Programming
  • 객체지향 프로그래밍이 왜 나왔을까?
    • 커지는 소스코드의 양을 감당할 수 X
    • 하드웨어의 발전 -> 처리량 증가 -> 프로그램이 복잡해짐
  • 대규모 소프트웨어 개발의 어려움 해소하기 위해 등장한 개념
  • 소프트웨어를 관리하고 개발하는 전체적인 방법론에 대해 얘기
    • 기획 -> 요구사항 분석 -> 설계 -> 구현(개발) -> 테스트 -> 유지보수
    • 소프트웨어 공학
  • 클래스와 객체는 자바에서만 사용할 수 있는 고유 기능이 아님
    • 객체지향 방법론을 이루고 잇는 하나의 개념(기술. 정의)이고, 그런 개념을 사용할 수 있는 수단을 제공하는 것

자바의 변수 종류

  • 지역변수
    • 메서드 내에서 정의된 변수(매개변수 포함)
  • 객체변수(인스턴스 변수)
    • 메서드 외부, 클래스 내부에서 정의된 변수
    • 반드시 객체를 통해서만 접근 가능
    • 객체.속성명으로 접근 => 객체가 만들어지지 않으면 해당 속성 접근 불가
    • 객체 변수는 각 객체에 속한 고유 메모리에 만들어짐(공유 불가)

클래스

  • 사용자가 정의하는 새로운 타입
    • 기본 타입을 제외한 String, Array 등의 자바에서 미리 정의된 클래스
    • 미리 정의된 클래스 뿐만 아니라, 직접 클래스를 정의해서 사용 가능
    • 클래스를 정의하는 기본 형태
class 클래스 이름{
    // 클래스 블록
}
  • 일반적으로 클래스의 이름은 대문자로 시작함.
    • 클래스를 제외한 변수의 이름이나, 메서드 이름은 소문자
    • 반드시 지킬 필요는 없지만, 권장함
    객체
  • Object(객체) 또는 Instance(인스턴스)
  • 클래스는 정의
    • 정의된 클래스랄 사용하기 위해서는 메모리에 만들어줘야 함
    • 정의된 클래스를 메모리에 생성하게 되면 객체라고 부름
    • 인스턴스화한다고 표현
  • 객체는 메서드 호출과 같음
    • 메서드를 호출하면, 메모리에 만들어짐(콜스택)
    • 클래스를 호출하면, 클래스가 메모리에 만들어짐
      • 메서드는 실행이 끝나면 메모리에서 사라짐.
      • 객체는 종료라는 개념이 존재하지 X (직접 삭제 전까지 존재)
  • 같은 클래스에 객체를 여러개 생성 가능
    • 동일한 타입의 변수가 여러개인 것으로 볼 수 있음
    • 타입이 같을뿐, 생성되는 변수는 다름(메모리의 위치에 따라 구분)
  • 모든 것은 주소를 기준으로 구분 가능
    • 변수, 메서드, 클래스, 객체 등을 구분하는 기준은 메모리
    • 같아 보이는 객체도, 주소가 다르면 다른 객체
    • 달라 보이는 객체도, 주소가 같으면 같은 객체

생성자(Constructor)

  • 생성 메서드로 이해. 약간 특별한 메서드
  • 초기화할 때 주로 사용

static method Vs. Instance method

  • Instance(인스턴스) 메서드
    • 생성자를 제외한, 지금까지 정의된 메서드는 전부 instance 메서드
    • 객체를 통해서 호출 가능
  • 클래스 메서드(static method)
    • 클래스 변수와 객체 변수는 메모리에 생성되는 시점이 다름
    • 클래스 변수: 객체보다 먼저 메모리에 생성
    • 객체변수: 객체가 메모리에 생성될 때 메모리에 생성
    • 객체가 생성되기 전에 메모리에 만들어진 클래스 변수를 객체를 통해 접근해야 한다?
      • 그래서, 객체가 없어도 호출 가능한 메서드를 정의 ==> 클래스 메서드
      • 클래스 메서드 ==> 클래스 이름으로 호출 가능
class Person{
    static int share = 10;
    String name;
    int age;
    // 클래스 메서드(static method)
    static void get_share(){
        System.out.println(share);

        // 객체 변수에 대한 접근은 불가능
        // System.out.println(this.name);
    }
}

public class Static_Method {
    public static void main(String[] args){
        // 객체를 생성하지 않아도 클래스를 통해서 호출 가능
        Person.get_share();
    }
}

클래스의 종류

  • 추상 클래스
    • 인터페이스
  • 이너 클래스
  • 메타 클래스
  • 익명 클래스

클래스의 구조

  • 속성(변수)과 기능(메서드)로 정의
    • 클래스 내의 변수를 멤버변수라고 표현
    • 하나의 클래스에는 여러 개의 변수와 여러 개의 메서드를 정의
    • 클래스 내의 변수와 메서드는 해당 클래스의 특징 반영 -> 속성이라 표현
class Person{
    // 변수 정의(사람에 대한 속성)
    // 사람이라면 누구나 가지는 속성
    String name;
    int age;
}

public class Chapter6_Class{
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();

        // 클래스에는 .을 활용하여 접근
        // 객체.속성
        // 객체.가능
        p1.name = "홍길동";
        p1.age = 20;

        System.out.printf("이름: %s, 나이: %d\n", p1.name, p1.age);
        // 서로 다른 객체(정의하지 않았기 때문에 null, 0으로 출력)
        // 객체 변수는 공유되지 X
        System.out.printf("이름: %s, 나이: %d\n", p2.name, p2.age);
    }
}

클래스의 특징

  • 클래스: 메서드와 변수를 하나의 타입으로 묶어놓은 것
    • 비슷한 특징을 가지고 있는 여러 메서드와 변수를 특징별로 클래스화 한 것(관리 용이)
  1. 은닉성(캡슐화)
    • 클래스를 보호하기 위한 장치
  2. 상속(inheritance)
    • 클래스의 재사용성을 더 확대한 개념
  3. 다형성(Polymorphism)
    • 오버로딩(Overloading), 오버라이딩(Overriding)

은닉성(캡슐화)

  • 클래스 내에서 정의된 변수나 메서드에 대해 외부에서 함부로 접근하지 못하도록 비공개로 하는 것을 은닉성이라 함
    • 객체지향에서 클래스는 제공되는 기능이 어떻게 동작하는지 알 필요가 없다고 말하는 것
    • 외부에서는 클래스에 정의도니 속성이나 기능을 전부 알 수 없도록 함
    • 필요한 만큼만 공개, 나머지는 몰라도 사용할 수 있음이라 하는 것
  • 일반적으로 객체지향 프로그래밍 내에서는 모든 속성을 비공개로 하는 것이 원칙
    • 외부 접근이 필요한 경우, 메서드를 통해서 접근할 수 있도록 제공하는 것이 좋음(접근제어자)
  • 접근 제어자(Access Modifier) (1~4로 갈수록 접근 허용 범위 증가)
    1. private: 동일 클래스 내에서만 접근
    2. default: 동일 패키지에서만 접근
    3. protected: 해당 클래스를 상속받은 외부 패키지의 클래스까지 가능
    4. public: 제한이 없음

상속(inheritance)

  • 물려받는다는 의미
  • 클래스의 재사용성을 더 극대화한 개념
    • 잘 정의된 클래스가 있다면, 다시 정의해서 사용하지 않고, 재사용할 수 있게 하자
    • 물려받은 클래스를 더 확장해서 사용
  • 클래스와 클래스 간의 관계를 표현
    • 하나의 프로그램을 만들 때 하나의 클래스만 사용되지 않고 여러 개의 클래스가 서로 유기적으로 잘 연결되어 동작
    • 객체지향 프로그래밍에서 가장 중요한 것은 클래스의 설계
    • 클래스들의 관계
      • is-a(상속)
      • has-a(다른 클래스의 객체를 속성으로 갖는 경우)
  • 예를 들어, 모든 학생사람이다
    • 학생 is사람
    • 사람 is 학생은 성립하지 않음
    • 클래스의 상속은 큰 개념에서 작은 개념으로 상속
    • 학생도 사람이기 때문에, 사람이 갖는 기본적인 속성이가 기능은 같을 것이라 가정
      • 학생이라는 클래스를 정의한다면?
        • 사람에 대한 속성이나 기능 새로 정의X
        • 미리 정의된 사람 클래스를 상속 받아서, 나머지 학생에 대한 속성이나 기능만 추가
  • 부모 클래스(기반 클래스): 물려주는 클래스
  • 자식 클래스(파생 클래스): 물려받는 클래스
class Person{
    int share = 10;

    String name;
    int age;

    static void get_share() {
        System.out.println(share);
    }

    void info() {
        System.out.printf("이름: %s, 나이: %d\n", this.name, this.age);
    }

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

    String get_name() {
        return this.name;
    }

}

// Person 클래스를 상속받음
class Student extends Person{
    // 부모로부터 물려받은 속성, 기능 그대로 가져오게 됨
    // 정의된게 없어도 물려받은 속성, 기능 사용 가능
}

public class inheritance {
    public static void main(String[] args){
        Student s1 = new Student();
        s1.info();
    }
}

다형성(Polymotphism)

  • 추가 예정

오버로딩(Overloading)

  • 상속과는 무관한 개념
  • 오버로딩의 조건
    1. 오버로딩 하는 메서드의 이름은 반드시 같아야 함
    2. 오버로딩 하는 메서드의 매개변수는 반드시 달라야 함
      • 매개변수의 개수와 타입
class Person{
    int share = 10;

    String name;
    int age;

    // 생성자 오버로딩
    Person() {

    }

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

    static void get_share() {
        System.out.println(share);
    }

    void info() {
        System.out.printf("이름: %s, 나이: %d\n", this.name, this.age);
    }

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

    String get_name() {
        return this.name;
    }

}

// Person 클래스를 상속받음
class Student extends Person{
    // 입력되는 수가 없어도, 혹은 1개여도, 3개여도 동작하려면?
    // 메서드 이름이 같아도, 파라미터 갯수가 다르기 때문에 각각을 다르게 사용할 수 있다.
    int add(){
        return 10+20;
    }
    int add(int a){
        return a+20;
    }
    int add(int a, int b){
        return a+b;
    }
    /*
    변수가 다르더라도 파라미터의 갯수가 같기 때문에 구분이 불가능하다.
    int add(int c, int d){
        return c+d;
    }
    */
    // 파라미터의 갯수는 같지만, 타입이 int와 String으로 다르기 때문에 구분 가능
    String add(String a, String b){
        return a+b;
    }
    int add(int a, int b, int c){
        return a+b+c;
    }
}

public class Overloading {
    public static void main(String[] args){
        Student s1 = new Student();

        System.out.println(s1.add());
        System.out.println(s1.add(10));
        System.out.println(s1.add(10,20));
        System.out.println(s1.add("Hello ", "Java"));
        System.out.println(s1.add(10,20,30));

        // 생성자 오버로딩을 이용하면 객체를 다양하게 생성 가능
        Person p1 = new Person();
        p1.info();

        Person p2 = new Person("김준면", 31);
        p2.info();
    }
}

오버라이딩(Overriding)

  • 메서드 재정의
  • 부모 클래스로부터 물려받은 메서드를 그대로 사용하지 않고, 자식이 재정의 후 사용
  • 오버라이딩의 조건
    1. 재정의 하려는 메서드와 물려받은 메서드의 이름이 같아야 함(같지 않을 경우 자식 클래스에서 메서드가 하나 추가되는 것과 같음)
    2. 리턴 타입, 매개변수의 개수, 타입이 모두 같아야 함
class Person{
    static int share = 10;

    String name;
    int age;

    // 생성자 오버로딩
    // 기본 생성자느 아무것도 하지 않음
    Person() {

    }

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

    static void get_share() {
        System.out.println(share);
    }

    void info() {
        System.out.printf("이름: %s, 나이: %d\n", this.name, this.age);
    }

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

    String get_name() {
        return this.name;
    }

}

// Person 클래스를 상속받음
class Student extends Person{  
    String school;
    // 생성자는? -> 상속되지 않음
    // 생성자를 정의하지 않으면? 
    // -> default 생성자가 존재하게 됨 (아무것도 하지 않는 기본 생성자)

    Student() { };
    Student(String name, int age, String school){
        //super(name, age); // 부모 클래스의 생성자를 직접 호출
        this.name = name;
        this.age = age;
        this.school = school;        
    }


    // info() 메서드를 재정의 한다면
    void info() {
        System.out.println("이 메서드는 재정의 되었습니다");
        // 부모 클래스의 메서드를 사용하고 싶다면?
        // super: 부모, super.속성
        // super(): 부모 클래스의 생성자
        // this: 객체 자기 자신
        super.info();
        System.out.printf("학교이름: %s\n", this.school);
    }
}

public class Overriding {
    public static void main(String[] args){
        Student s1 = new Student("김준면", 20, "multicampus");
        s1.info();
    }
}

싱글톤 패턴(Singleton Pattern)

  • 디자인 패턴(Design Pattern) 중 하나
  • 동일한 타입(클래스)의 여러 객체 생성
    • 하나의 객체만 생성하게 강제
    • 즉, 클래스 하나당 객체 하나
class Singleton{

    private Singleton() {

    }
}

public class SingletonTest {
    public static void main(String[] args) {
        // 아무도 객체를 만들 수 없게 함
        Singleton single = new Singleton();
    }
}
  • 실행되지 않음
package Standard_Java;

class Singleton{

    private static Singleton one = null;
    private Singleton() {

    }
    // 아무도 생성할 수 없으니, 생성을 도와주는 메서드
    public static Singleton getObject() {
        // 생성자 호출 가능
        if(one == null){
            one = new Singleton();
        }
        return one;
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        Singleton single = Singleton.getObject();
        System.out.println(single);

        // 더 이상의 새로운 객체는 생성 불가
        Singleton single2 = Singleton.getObject();
        System.out.println(single2);

        System.out.println( Singleton.getObject() );
    }
}