본문 바로가기
코드 스테이츠

코드 스테이츠 04/25 - 객체 지향 프로그래밍 심화1

by 한휘용 2023. 4. 25.
728x90
코드 스테이츠 11일차

 

상속

자바 언어에서 상속이란 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소를 의미한다.

 

가장 단순한 형태를 생각해 보면, 두 클래스를 상위 클래스와 하위 클래스로 나누어 상위 클래스의 멤버(필드, 메서드, 내부클래스)를  하위 클래스와 공유하는 것을 의미한다.

 

여기서 이 두 클래스를 서로 상속 관계에 있다고 하며, 하위 클래스는 상위 클래스가 가진 모든 멤버를 상속 받게 된다.

따라서 하위클래스의 멤버 개수는 언제나 상위클래스의 그것과 비교했을 때 같거나 많다.

 

왜 상속을 사용할까?

 

결론적으로 말하면, 상속을 통해 클래스를 작성하면 코드를 재사용하여 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있어 코드의 중복을 제거할 수 있다.

또한, 상속은 다형적 표현이 가능하다는 장점이 있다. 여기서 다형적 표현이란 하나의 객체가 여러 모양으로 표현될 수 있다는 것을 말한다.

 

코드 예제

 

자바에서 상속을 구현하는 방법은 extends 키워들 사용하는 것이다.

클래스명 다음에 extends 상위 클래스명을 사용하여 정의한다.

class Person {
    String name;
    int age;

    void learn(){
        System.out.println("공부를 합니다.");
    };
    void walk(){
        System.out.println("걷습니다.");
    };
    void eat(){
        System.out.println("밥을 먹습니다.");
    };
}

class Programmer extends Person { // Person 클래스로부터 상속. extends 키워드 사용 
    String companyName;

    void coding(){
        System.out.println("코딩을 합니다.");
    };
}

class Dancer extends Person { // Person 클래스로부터 상속
    String groupName;

    void dancing(){
		    System.out.println("춤을 춥니다.");
		};
}

class Singer extends Person { // Person 클래스로부터 상속
    String bandName;

    void singing(){
		    System.out.println("노래합니다.");
		};
    void playGuitar(){
		    System.out.println("기타를 칩니다.");
		};
}

public class HelloJava {
    public static void main(String[] args){

        //Person 객체 생성
        Person p = new Person();
        p.name = "김코딩";
        p.age = 24;
        p.learn();
        p.eat();
        p.walk();
        System.out.println(p.name);

        //Programmer 객체 생성
        Programmer pg = new Programmer();
        pg.name = "박해커";
        pg.age = 26;
        pg.learn(); // Persons 클래스에서 상속받아 사용 가능
        pg.coding(); // Programmer의 개별 기능
        System.out.println(pg.name);

    }
}

위의 코드들은 Person 클래스로부터 Programmer, Dancer, Singer 클래스가 확장되어 Person 클래스의 속성과 기능들을 사용할 수 있는 것을 확인 할 수있다.

또한 각각의 클래스의 개별적인 속성과 기능들은 객체 생성 이후 개별적으로 정의해 주었다.

만약 상속이 없었더라면 객체 하나하나 속성과 기능들을 모두 선언해주어야 하는 번거롭고 귀찮은 상황이 되었을 것이다.

 

포함 관계

포함(composite)은 상속처럼 클래스를 재사용할 수 있는 방법으로, 클래스의 멤버로 다른 클래스 타입의 참조변수를 선언하는 것을 의미한다.

 

다음 예시를 통해 확인해보자.

public class Employee {
    int id;
    String name;
    Address address;

    public Employee(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    void showInfo() {
        System.out.println(id + " " + name);
        System.out.println(address.city+ " " + address.country);
    }

    public static void main(String[] args) {
        Address address1 = new Address("서울", "한국");
        Address address2 = new Address("도쿄", "일본");

        Employee e = new Employee(1, "김코딩", address1);
        Employee e2 = new Employee(2, "박해커", address2);

        e.showInfo();
        e2.showInfo();
    }
}

class Address {
    String city, country;

    public Address(String city, String country) {
        this.city = city;
        this.country = country;
    }
}

// 출력값
1 김코딩
서울 한국
2 박해커
도쿄 일본

한 회사의 근로자(Employee)를 표현하기 위한 Employee 클래스의 멤버 변수로 근로자가 사는 개략적인 주소를 나타내는 Address 클래스가 정의되어 있다.

 

원래라면 Address 클래스에 포함되어 있는 인스턴스 변수 city와 country를 각각 Employee 클래스의 변수로 정의해주어야 하지만, Address 클래스로 해당 변수들을 묶어준 다음 Employee 클래스 안에 참조변수를 선언하는 방법으로 코드의 중복을 없애고 포함관계로 재사용하고 있다.

 

객체지향 프로그래밍에서 상속보다는 포함 관계를 사용하는 경우가 더 많고 대다수라 할 수 있다.

 

그렇다면 클래스 간의 관계를 설정하는 데 있어서 상속 관계를 맺어 줄 것 인지 포함 관계를 맺어 줄 것인지를 어떤 기준으로 판별할 수 있을까?

 

가장 손쉬운 방법은 클래스 간의 관계가 ‘~은 ~이다(IS-A)’ 관계인지 ~은 ~을 가지고 있다(HAS-A) 관계인지 문장을 만들어 생각해 보는 것이다. 이부분은 맞춤법 '되' 와 '돼' 를 구분할 때 '하' 와 '해' 를 넣어 구별하는 방법과 유사하다.

 

위의 코드로 예를 들어보면, Employee는 Address이다.라는 문장은 성립하지 않는 반면, Employee는 Address를 가지고 있다.는 어색하지 않은 올바른 문장임을 알 수 있다. 따라서 이 경우에는 상속보다는 포함관계가 적합하다.

 

반면 Car 클래스와 SportCar라는 클래스가 있다고 할 때, SportsCars는 Car를 가지고 있다.라는 문장보다 SportsCar는 Car이다.라는 문장이 훨씬 더 자연스럽다. 따라서 이 경우에는 Car를 상위클래스로 하는 상속 관계를 맺어주는 것이 더 적합하다고 할 수 있다.

 

메서드 오버라이딩

메서드 오버라이딩은 상위 클래스로부터 상속받은 메서드와 동일한 이름의 메서드를 재정의하는 것을 의미한다.

아래의 간단한 코드예시를 통해 살펴보자

public class Main {
    public static void main(String[] args) {
        Bike bike = new Bike();
        Car car = new Car();
        MotorBike motorBike = new MotorBike();
        
        bike.run();
        car.run();
        motorBike.run();
    }
}

class Vehicle {
    void run() {
        System.out.println("Vehicle is running");
    }
}

class Bike extends Vehicle {
    void run() {
        System.out.println("Bike is running");
    }
}

class Car extends Vehicle {
    void run() {
        System.out.println("Car is running");
    }
}

class MotorBike extends Vehicle {
    void run() {
        System.out.println("MotorBike is running");
    }
}

// 출력값
Bike is running
Car is running
MotorBike is running

Vehicle 클래스에 run() 메서드가 정의되어 있으며, Bike, Car, MotorBike 클래스에서 run() 메서드를 재정의함으로써 Vehicle 클래스의 run() 메서드를 오버라이딩하고 있다.

 

따라서 Bike, Car, MotorBike의 인스턴스를 통해 run() 메서드를 호출하면 Vehicle의 run()이 아닌, Bike, Car, MotorBike의 run()이 호출된다.

 

상위클래스의 메서드를 오버라이딩하려면 3가지 조건을 반드시 만족시켜야 한다.

  1. 메서드의 선언부(메서드 이름, 매개 변수, 반환 타입)가 상위클래스의 그것과 완전히 일치해야 한다.
  2. 접근 제어자의 범위가 상위 클래스의 메서드보다 같거나 넓어야 한다.
  3. 예외는 상위 클래스의 메서드보다 많이 선언할 수 없다.

 

super 키워드와  super()

이전에 객체지향 프로그램이 기초2에서 this키워드와 this()를 다뤘었다.  

this 키워드는 자신의 객체, this() 메서드는 자신의 생성자 호출을 의미한다.

 

super와 super()도 같은 개념이다.super 키워드는 상위 클래스의 객체, super() 는 상위클래스의 생성자를 호출하는것을 의미한다.

 

예시 코드를 통해 super 키워드 먼저 알아보자.

public class Example {
    public static void main(String[] args) {
        SubClass subClassInstance = new SubClass();
        subClassInstance.callNum();
    }
}

class SuperClass {
    int count = 20; // super.count
}

class SubClass extends SuperClass {
    int count = 15; // this.count

    void callNum() {
        System.out.println("count = " + count);
        System.out.println("this.count = " + this.count);
        System.out.println("super.count = " + super.count);
    }
}


// 출력값
count = 15
count = 15
count = 20

예시 코드에서 SubClass는 SuperClass로부터 변수 count를 상속받는데, 공교롭게도 자신의 인스턴스 변수 count와 이름이 같아 둘을 구분할 방법이 필요하다.

 

이런 경우, 두 개의 같은 이름의 변수를 구분하기 위한 방법이 바로 super 키워드이다.

 

만약 super 키워드를 붙이지 않는다면, 자바 컴파일러는 해당 객체는 자신이 속한 인스턴스 객체의 멤버를 먼저 참조한다.

반면 경우에 따라서 상위 클래스의 변수를 참조해야 할 경우가 종종 있는데 그 경우 super 키워드를 사용하면 부모의 객체의 멤버 값을 참고할 수 있다.

 

상위 클래스의 멤버와 자신의 멤버를 구별하는 데 사용된다는 점을 제외한다면 this와 super는 기본적으로 같은 것이라 말할 수 있다.

 

다음 코드예시는 super() 메서드를 사용했다. 

public class Test {
    public static void main(String[] args) {
        Student s = new Student();
    }
}

class Human {
    Human() {
        System.out.println("휴먼 클래스 생성자");
    }
}

class Student extends Human { // Human 클래스로부터 상속
    Student() {    
        super(); // Human 클래스의 생성자 호출
        System.out.println("학생 클래스 생성자");
    }
}

// 출력값
휴먼 클래스 생성자
학생 클래스 생성자

super()this() 메서드처럼 생성자를 호출할 때 사용한다.

단, this()는 같은 클래스의 다른 생성자를 호출하는 데 사용되지만, super()는 상위 클래스의 생성자를 호출하는 데 사용된다.

 

위의 코드예시를 보면, Human 클래스를 확장하여 Student 클래스를 생성하고, Student 생성자를 통해 상위 클래스 Human 클래스의 생성자를 호출하고 있습니다.

super() 메서드 또한 this()와 마찬가지로 생성자 안에서만 사용 가능하고, 반드시 첫 줄에 와야 한다.

 

 

Object 클래스

 

클래스의 정점, Object 클래스는 자바의 크랠스 상속 계층도에서 최상위에 위치한 상위클래스 이다.

따라서 자바의 모든 클래스는 Object 클래스로부터 확장된다는 명제는 항상 참이다.

 

실제로 자바 컴파일러는 컴파일 과정에서 다른 클래스로부터 아무런 상속을 받지 않는 클래스에 자동으로 extends Object를 추가하여 Object 클래스를 상속받도록 한다.

class ParentEx {  //  컴파일러가 "extends Object" 자동 추가 

}

class ChildEx extends ParentEx {

}

위의 예시에서, ParentEx 클래스를 상속받아 ChildEx 클래스를 만들었을 때 상위클래스 ParentEx는 아무것도 상속하고 있지 않기에 컴파일러는 extends Object를 삽입하는 것이다.

앞서 설명한 것처럼 Object 클래스는 자바 클래스의 상속계층도에 가장 위에 위치하기 때문에 Object 클래스의 멤버들을 자동으로 상속받아 사용할 수 있다.

 

Object 클래스에서 확장되어 사용 메서드들은 매우 많지만, 대표적인 메서드 몇 가지만 알아보자.

메서드 명 반환 타입  주요 내용
toString() String 객체 정보를 문자열로 출력
equals(Object obj) boolean 등가 비교 연산(==)과 동일하게 스택 메모리값을 비교
hashCode() int 객체의 위치정보 관련. Hashtable 또는 HashMap에서 동일 객체여부 판단
wait() void 현재 스레드 일시정지
notify() void 일시정지 중인 스레드 재동작

 

 

캡슐화

캡슐화란?

특정 객체 안에 관련된 속성과 기능을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것을 말한다.

이렇게 캡슐화를 해야 하는 이유로는 크게 두 가지 목적이 있다.

  1. 데이터 보호의 목적
  2. 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출 방지

정리하면, 캡슐화의 가장 큰 장점은 정보 은닉(data hiding)에 있다고 정리할 수 있다.

즉, 외부로부터 객체의 속성과 기능이 함부로 변경되지 못하게 막고, 데이터가 변경되더라도 다른 객체에 영향을 주지 않기에 독립성을 확보할 수 있다.

더 나아가 유지보수와 코드 확장 시에도 오류의 범위를 최소화할 수 있어서 효과적으로 코드를 유지보수하기에 용이하다.

 

패키지

패키지(package)란?

특정한 목적을 공유하는 클래스와 인터페이스의 묶음을 의미한다.

클래스를 정의할 때 관련 있는 속성과 기능을 묶어 데이터들을 효율적으로 관리할 수 있었듯, 패키지는 클래스들을 그룹 단위로 묶어 효과적으로 관리하기 위한 목적을 가지고 있습니다.

 

컴퓨터를 사용할 때 폴더를 만들어 그 폴더와 관련된 파일들을 관리하는 것과 유사하다고 할 수 있다.

 

자바에서 패키지는 물리적인 하나의 디렉토리(directory)이고, 하나의 패키지에 속한 클래스나 인터페이스 파일은 모두 해당 패키지에 속해있다.

더 나아가, 이 디렉토리는 하나의 계층 구조를 가지고 있는데, 계층 구조 간 구분은 점(.)으로 표현된다.

마지막으로, 패키지가 있는 경우 소스 코드의 첫 번째 줄에 반드시 package 패키지 명이 표시되어야 하고, 만약 패키지 선언이 없으면 이름 없는 패키지에 속하게 된다.

 

// 패키지를 생성했을 때
package practicepack.test; // 패키지 구문 포함. 패키지가 없다면 구문 필요없음

public class PackageEx {

}

자바에 기본적으로 포함되어 있는 대표적인 패키지로 자바의 기본 클래스들을 모아 놓은 java.lang, 확장 클래스를 묶어 놓은 java.util, 자바의 입출력과 관련된 클래스를 묶어놓은 java.io와 java.nio 등이 있다.

 

예를 들면, 우리가 주로 사용하는 String 클래스의 실제 이름은 java.lang.String인데, 여기서 java.lang은 패키지 명을 나타내고 점(.)을 사용하여 디렉토리 계층 구조를 나타내고 있다.

 

이렇게 패키지로 클래스를 묶는 것의 또 하나의 장점은 클래스의 충돌을 방지해 주는 기능에 있다. 예를 들면, 같은 이름의 클래스를 가지고 있더라고 각각 다른 패키지에 소속되어 있다면 이름명으로 인한 충돌이 발생하지 않는다.

 

Import 문

Improt 문이란?

다른 패키지 내의 클래스를 사용하기 위해 사용하며, 일반적으로 패키지 구문과 클래스 문 사이에 작성한다.

 

예를 들면, import 문 없이 다른 패키지의 클래스를 사용하기 위해서는 매번 패키지 명을 붙여 주어야 하는데, import 문을 사용하면 사전에 컴파일러에게 소스 파일에 사용된 클래스에 대한 정보를 제공하여 이러한 번거로움을 덜어준다.

package practicepack.test;

public class ExampleImport {
		public int a = 10;
		public void print() {
			System.out.println("Import 문 테스트")
		}
}
package practicepack.test2; // import 문을 사용하지 않는 경우, 다른 패키지 클래스 사용방법

public class PackageImp {
		public static void main(String[] args) {
			practicepack.test.ExampleImport example = new practicepack.test.ExampleImport();
		}
}

위의 예시를 보면, import 문을 사용하지 않고 다른 패키지의 클래스를 사용하기 위해서 패키지 명을 모두 포함시켜서 클래스의 패키지에 대한 정보를 제공해야 한다는 사실을 알 수 있다.

 

import 문을 사용하면 다음과 같이 쉽게 작성할 수 있다.

package practicepack.test;

public class ExampleImp {
		public int a = 10;
		public void print() {
			System.out.println("Import 문 테스트")
}
package practicepack.test2; // import 문을 사용하는 경우

import practicepack.test.ExampleImp // import 문 작성

public class PackageImp {
		public static void main(String[] args) {
			ExampleImp x = new ExampleImp(); // 이제 패키지 명을 생략 가능
		}
}

작성 방법은 다음과 같다.

import 패키지 명.클래스 명; 또는 import 패키지 명.*;

먼저 import 키워드를 써주고 패키지 명과 패키지 명을 생략하고자 하는 클래스명을 함께 써주면 된다.

만약 같은 패키지에서 여러 클래스가 사용된다면, import 문을 여러번 사용하는 것 보다 import 패키지 명.*으로 작성하면 패키지의 모든 클래스를 패키지 명 없이 사용할 수 있다.

 

접근 제어자

 

제어자(Modifier)

 

자바 프로그래밍에서 제어자는 클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드를 의미한다.

파란 하늘', ‘ 붉은 노을'에서 ‘파란'과 ‘붉은'처럼 명사를 꾸며주는 형용사의 역할과 같다고 할 수 있다.

 

제어자는 크게 접근 제어자기타 제어자로 구분할 수 있다.

접근 제어자 public, protected, (default), private
기타 제어자 static, final, abstract, native, transient, synchronized 등

 

제어자는 클래스, 필드, 메서드, 생성자 등에 주로 사용되며 ‘ 맛있는 빨간 사과'에서 사과를 수식하기 위해 ‘맛있는'과 ‘빨간'이라는 형용사가 두 번 사용된 것처럼 하나의 대상에 대해서 여러 제어자를 사용할 수 있다.

하지만, 각 대상에 대해서 접근 제어자는 단 한 번만 사용할 수 있다.

 

접근 제어자(Access Modifier)

 

접근 제어자를 사용하면 클래스 외부로의 불필요한 데이터 노출을 방지(data hiding)할 수 있고, 외부로부터 데이터가 임의로 변경되지 않도록 막을 수 있다. 이것은 데이터 보호의 측면에서 매우 중요하다고 할 수 있다.

 

자바 접근 제어자로 다음의 4가지가 있습니다.

접근 제어자 접근 제한 범위
private 동일 클래스에서만 접근 가능
default 동일 패키지 내에서만 접근 가능
protected 동일 패키지 + 다른 패키지의 하위 클래스에서 접근 가능
public 접근 제한 없음

public(접근 제한 없음) > protected(동일 패키지 + 하위클래스) > default(동일 패키지) > private(동일 클래스) 순으로 정리할 수 있다.

 

이중 default의 경우는 아무런 접근 제어자를 붙이지 않는 경우 기본적인 설정을 의미한다. 즉 변수명 앞에 아무런 접근 제어자가 없는 경우에는 자동으로 해당 변수의 접근 제어자는 default가 된다.

 

아래 코드 예시를 살펴보자

package package1; // 패키지명 package1 

//파일명: Parent.java
class Test { // Test 클래스의 접근 제어자는 default
    public static void main(String[] args) {
        Parent p = new Parent();

//        System.out.println(p.a); // 동일 클래스가 아니기 때문에 에러발생!
        System.out.println(p.b);
        System.out.println(p.c);
        System.out.println(p.d);
    }
}

public class Parent { // Parent 클래스의 접근 제어자는 public
    private int a = 1; // a,b,c,d에 각각 private, default, protected, public 접근 제어자 지정
    int b = 2;
    protected int c = 3;
    public int d = 4;

    public void printEach() { // 동일 클래스이기 때문에 에러발생하지 않음
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);
    }
}

// 출력값
2
3
4

먼저 동일한 패키지에 속한 경우를 살펴보도록 하자.

 

아래 Parent 클래스를 먼저 살펴보면

a, b, c, d 모두 에러 없이 정상적으로 접근 가능함을 확인하실 수 있다.

동일한 패키지의 동일한 클래스 내에 있기 때문에 가장 접근 제한이 엄격한 private 변수도 접근이 가능하다.

 

반면 위의 Test 클래스에서 객체를 생성하여 접근을 시도했을 때는 private 접근 제어자가 있는 a에는 접근이 불가하여 에러가 발생하는 모습을 확인할 수 있다.

 

그렇다면 다른 패키지에 있는 경우는 어떨까?

package package2; // package2 

//파일명 Test2.java
import package1.Parent;

class Child extends package1.Parent {  // package1으로부터 Parent 클래스를 상속
    public void printEach() {
        // System.out.println(a); // 에러 발생!
        // System.out.println(b);
        System.out.println(c); // 다른 패키지의 하위 클래스
        System.out.println(d);
    }
}

public class Test2 {
    public static void main(String[] args) {
        Parent p = new Parent();

//        System.out.println(p.a); // public을 제외한 모든 호출 에러!
//        System.out.println(p.b);
//        System.out.println(p.c);
        System.out.println(p.d);
    }
}

먼저 package1의 Parent 클래스로부터 상속받아 만들어진 Child 클래스를 살펴보면, 같은 클래스와 같은 패키지 안에 있는 private(a)와 default(b) 접근 제어자를 사용하는 멤버에는 접근이 불가능한 반면, 다른 패키지의 하위 클래스에 접근 가능한 protected(c)와 어디서나 접근이 가능한 public(d)에는 접근이 가능하다는 사실을 확인할 수 있다.

 

마지막으로 Test2 클래스는 상속받은 클래스가 아니기 때문에 다시 protected(c)에는 접근이 불가능하고 public(d)에만 접근이 가능하다.

 

결론적으로 다시 정리하면, 우리는 접근 제어자를 통해 외부로부터 데이터를 보호하고, 불필요하게 데이터가 노출되는 것을 방지할 수 있다.

 

getter 와 setter 메서드

 

객체지향의 캡슐화의 목적을 달성하면서도 데이터의 변경이 필요한 경우는 어떻게 할 수 있을까?

이 경우 getter 와 setter 메서드를 사용할 수 있다.

아래의 코드예시를 통해 알아보자

public class GetterSetterTest {
    public static void main(String[] args) {
        Worker w = new Worker();
        w.setName("김코딩");
        w.setAge(30);
        w.setId(5);

        String name = w.getName();
        System.out.println("근로자의 이름은 " + name);
        int age = w.getAge();
        System.out.println("근로자의 나이는 " + age);
        int id = w.getId();
        System.out.println("근로자의 ID는 " + id);
    }
}

class Worker {
    private String name; // 변수의 은닉화. 외부로부터 접근 불가
    private int age;
    private int id;

    public String getName() { // 멤버변수의 값 
        return name;
    }

    public void setName(String name) { // 멤버변수의 값 변경
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age < 1) return;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

// 출력값
근로자의 이름은 김코딩
근로자의 나이는 30
근로자의 ID는 5

setter 메서드

외부에서 메서드에 접근하여 조건에 맞을 경우 데이터 값을 변경할 수 있게 해 주고 일반적으로 메서드명에 set-을 붙여서 정의한다.

예시를 보면 이름을 변경하기 위해 setName()이라는 메서드를 사용하고 있음을 확인할 수 있다.

 

getter 메서드

setter 메서드로

설정한 변수값을 읽어오는 데 사용하는 메서드이다. get-을 메서드명 앞에 붙여서 사용한다.

 

예시를 좀 더 자세히 살펴보면, 먼저 Worker 클래스를 기반으로 객체 인스턴스를 생성해 주고 같은 타입을 가지고 있는 참조변수 w에 담았다.

 

다음으로 w의 setter 메서드를 사용하여 이름, 나이, 아이디에 대한 데이터값을 저장하고, getter 메서드를 통해 해당 데이터 값을 불러와 변수에 담아 출력해 주고 있다.

 

이렇게 setter와 getter 메서드를 활용하면 데이터를 효과적으로 보호하면서도 의도하는 값으로 값을 변경하여 캡슐화를 보다 효과적으로 달성할 수 있다.

 

코드 스테이츠 10일차 종료 후기
객체 지향 프로그래밍 심화로 들어오면서 아직 기초에서 배운 클래스, 메서드 , 생성자와 같은 개념을 다시 읽어 보았다. 아직 완벽하게 개념을 이해하지 못한 상태라 계속 여기봤다 저기봤다 번갈아가면서 보지만 코드 예시들을 보면서 이해를 하려고 노력하다보니 조금은 머리에 들어 오는 것 같기도 하다. 앞으로 갈길이 많이 멀었구나 생각했다. 상속의 개념은 부모 자식의 개념을 전공 수업때 배웠던 기억이 다시 나면서 쉽게 이해가 되었지만, 캡슐화는 아직 시간이 조금 더 필요할 것 같다.
728x90