Java 문법 객체지향의 4가지 특징

김현찬·2025년 5월 2일

Java 캡슐화

캡슐화

  • 외부에서 직접 접근하지 못하도록 객체 정보를 보호할 수 있다.

  • 필요한 경우에만 접근제어자를통해 제어할 수 있다.

정보은닉

  • 객체 내부 데이터를 숨기는 개념

접근제어자

  • 클래스, 변수, 메서드, 생성자의 접근 범위를 제한하는 키워드이다.

접근제어자별로 접근할 수 있는 범위가 다르다.

  • public : 전체 접근 가능하다.

  • protected : 상속한 클래스까지 접근 가능하다.

  • default : 같은 패키지까지 접근 가능하다.

  • private : 같은 클래스까지 접근 가능하다.

접근제어자를 명시하지 않을 시 기본 접근제어자는default이다.

메서드A는 public으로 설정하고,
메서드B는 private으로 설정할 시,

서로 다른 클래스인 메서드B로는 접근할 수 없다.

Java 상속

상속

  • 클래스간의 관계를 부모(상위), 자식(하위)으로 바라보는 개념이다.

  • extends 키워드를 사용해서 상속관계를 구현한다.

  • 부모(상위) 클래스에서 작성한 내용을 자식(하위) 클래스가 물려받아 그대로 재사용 할 수 있다.

재사용성과 확장

부모(상위) 클래스에서 작성한 내용을 자식(하위) 클래스가 물려받아 그대로 재사용 할 수 있다.


부모(Parent) 클래스에서 코드를 작성한 후 자식(Child)클래스에서 extends 키워드를 사용해 상속한다.


자식(하위)클래스엔 extends 키워드를 제외한 어떤 코드도 작성되지 않은 모습이다.

그러나 위 예제와 같이 메인 메서드에 부모 클래스에서 작성한 요소를 자식 클래스를 통해 접근하여 불러올 수 있다.

실행 결과

super

  • super : 부모 클래스의 멤버에 접근할 때 사용하는 키워드이다.
  • 자식 클래스에서 부모의 변수나 메서드를 명확하게 호출할 때 사용한다.

만일 자식 클래스에도 부모 클래스와 같은 요소(familyName)가 있다면, 위 메인 클래스를 출력할 시 다음과 같은 결과를 출력하게 된다.

자식 클래스에서 가문 이름에 해당하는 요소의 값을 abc로 할당했기 때문에, 메인 클래스에서 child.familyName을 불러오면 abc를 출력하게 되는 것이다.

이 때 super키워드를 사용하여, familyName을 부모 클래스의 요소를 가져오도록 정의할 수 있다.

super는 부모 인스턴스를 가르키는 키워드이기 때문에 super.familyName는 부모 클래스의familyName를 가르키게 된다.

따라서 메인 클래스에서 child.superTest();를 출력하면, 다음과 같이 부모 클래스의 가문 이름을 출력한다.

super()

상속 클래스는 부모가 먼저 생성되고 자식이 생성되어야 한다. 따라서 자식 인스턴스의 생성자는, 항상

와 같은 구조를 띈다. (super()를 통해 부모클래스의 생성자를 먼저 호출하는 것이다.)

자식 클래스와 부모 클래스의 생성자 우선 순위를 알아보자.

자식 인스턴스의 생성자에 "자식 생성자"라는 문구를 출력하도록 한다.

마찬가지로 부모 인스턴스의 생성자에도 "부모 생성자"라는 문구를 출력하도록 한다.

이후 메인 클래스에서 자식 인스턴스를 불러와 child라는 객체를 생성한 후 출력한다.

하위 인스턴스를 불러와 출력하였음에도, 부모 생성자가 먼저 출력된 모습이다.

확장성

  • 부모 클래스의 기능을 유지하면서 자식 클래스에서 기능을 확장할 수 있다.
  • 부모 클래스엔 존재하지 않는 요소를, 자식 클래스에서 만들어 사용할 수 있다.

부모 클래스엔 존재하지 않는 showSocialMedia라는 요소를 자식 클래스에 생성한다.

이후 메인 클래스에서 showSocialMedia라는 요소를 불러와 사용할 수 있다.

실행 결과

이렇듯 부모 클래스에는 존재하지 않는 요소를, 자식 클래스에서 생성하여 사용할 수 있다.

오버라이딩

  • 부모 클래스의 메서드를 자식 클래스에서 변경하여 재정의하는 것을 의미한다.

  • 메서드 이름, 매개 변수, 반환 타입이 완전히 동일해야 한다.

  • 접근 제어자는 부모보다 더 강한 수준으로만 변경할 수 있다.

부모 클래스의 introduceFamilyName기능을

자식 클래스에서 위와 같이 정의했다면,

메인 클래스에서의 출력값은 자식이 정의한 값으로 나온다. 따라서 오버라이딩이란, 부모 클래스의 기능을 자식 클래스의 입맛대로 변경하여 사용할 수 있다.

추상클래스

  • 추상 클래스는 직접 인스턴스화 할 수 없다.
    즉 메인 클래스에서 추상 클래스를 이용해 객체를 생성할 수 없다.

  • abstract키워드로 클래스를 선언하여 추상 클래스를 선언하면,
    자식 클래스에 특정 메서드 구현을 강제할 수 있다.

  • 추상 클래스 안에서 abstract키워드로 메서드를 선언하면, 자식클래스는 해당 메서드를 강제로 구현해야 한다.

  • 자식 클래스에서 해당 메서드를 구현해야 메인 클래스에서 사용할 수 있다.

실행 결과

부모 클래스에서 그대로 받아온 요소인 쿨쿨..과 자식 클래스에서 강제로 구현한 냠냠이 출력 된 모습이다.

추상 클래스와 인터페이스의 차이점

  • 추상 클래스로 상속시키는것이 계층적 구조를 선언하기 적합하다.

  • 인터페이스는 표준을 제공하는데 적합하다.

  • 인터페이스는 각각의 객체마다 따로 값을 할당할 수 없다.(인스턴스 변수 선언 불가)

  • 즉 각각의 클래스에 표준을 제공할 때에는 인터페이스를 사용하는것이 적합하고,
    계층적 구조를 표현하면서 공통된 속성과 기능을 재사용 할 때에는 추상 클래스를 사용하는것이 적합하다.

Java 추상화

추상화

  • 특정 계층에서 필요한 본질적인 특성은 유지하고, 불필요한 세부 사항을 숨기는 객체지향의 특징이다.

  • 생명체에 관한 기능은 생명체 계층에서, 동물에 관한 기능은 동물 계층에서, 고양이에 관한 기능은 고양이 계층에서 다룰 수 있다.

인터페이스 상속을 활용한 추상 계층 표현

생명체 인터페이스를 생성한다.

동물 인터페이스 생성 후 생명체 인터페이스로부터 상속을 받는다.

고양이 클래스 생성 후 생명체 인터페이스를 상속한 동물 인터페이스를 불러온 후 고양이만의 특징을 작성한다.

메인 메서드에서 고양이 객체를 생성한 후 각각의 값을 불러온다.

실행 결과

  • 고양이 클래스에서 생명체 인터페이스 구문에 고양이는 존재한다는 값으로 오버라이드했기에 cat.exist에서는 고양이가 존재합니다.로 출력된다.

  • 고양이 클래스에서 동물 인터페이스 구문에 고양이의 울음소리인 야옹을 오버라이드했기에 cat.makeSound는 야옹으로 출력된다.

  • 고양이 클래스에서 고양이의 특징인 스크래치라는 요소를 생성한 후 불러왔기에 cat.scratch는 스크래치로 출력된다.

즉 모든 생명체는 존재하고, 존재하는 생명체 중 모든 동물은 소리를 내는데, 동물 중 고양이는 야옹이라는 소리를 내며, 스크래치라는 특성을 갖고 있다는 점을 계층적으로 표현한 것이다.

  • 모든 생명체는 존재한다. -> 생명체가 동물보다 상위 개념이므로 상속된다.

  • 모든 동물은 소리를 낸다. -> 고양이또한 동물의 범주에 포함된다.

  • 그렇기 때문에 고양이 클래스에서 동물 인터페이스를 오버라이드하여 사용할 수 있다.

  • 이후 고양이만의 특징으로 값을 채워준다.

클래스 상속을 활용한 추상 계층 표현과의 차이점

  • 인터페이스 상속을 활용한 계층 표현의 경우 인터페이스끼리 상속한 뒤, 생명체 인터페이스와 상속한 동물 인터페이스를 클래스에서 implements 키워드로 활용하여 표현했다.

  • 클래스 상속을 활용한 계층 표현도, 크게 다르지 않다.

  • 클래스 상속을 활용한 계층 표현의 경우 인터페이스를 활용하는것이 아닌, extends 키워드를 사용하여 생명체 클래스 -> 동물 클래스 -> 고양이 클래스로 상속하여, 표현했다.

Java 다형성

다형성

  • 하나의 타입으로 여러 객체를 다룰 수 있는것이다.

Animal이라는 인터페이스를 Cat 클래스와 Dog 클래스에서 동시에 활용중일 때, 메인 클래스에서 Animal cat = new Cat();이나 Animal dog = new Dog();를 사용할 수 있다.

  • Animal이라는 하나의 타입으로 고양이 객체와 강아지 객체를 모두 사용할 수 있다.

실행 결과

이처럼 Animal이라는 하나의 타입(부모타입)으로 고양이 객체(자식 타입)와 강아지 객체(자식 타입) 모두 사용이 가능한 이유는 업캐스팅과 다운캐스팅 때문이다.

업캐스팅

  • 자식타입을 -> 부모타입으로 변환한다.

  • 자식 타입의 고유한 기능을 사용할 수 없다.

고양이의 고유한 기능인 스크래치나, 강아지의 고유한 특성인 꼬리 흔들기를 사용할 수 없게된다.

자식 타입의 고유한 기능을 사용하기 위해선 다운캐스팅이 필요하다.

다운캐스팅

  • 부모타입을 -> 자식타입으로 변환한다.

자식 타입의 고유한 기능을 사용하기 위해선 다운캐스팅이 필요한데, 다운캐스팅은 다음과 같이 사용할 수 있다.


Animal이라는 큰 타입에 있던 cat이라는 값을 보다 작은 타입인 Cat 타입의 realCat으로 다운캐스팅 해준다.

  • 같은 원리로 dog도 변경해준다.


다운캐스팅 시 자식 타입의 고유한 기능을 사용할 수 있다.

실행 결과

잘못된 다운캐스팅 문제


실행 전 까지는 문제가 검출되지 않다가, 프로그램 실행 시 문제가 검출되는 모습이다.

이처럼 다운캐스팅을 잘못하면 실제로 값을 출력해보기 전 까지는 어떤 값이 담겨있는지 알 수 없다.

그래서 다운캐스팅을 사용할때 항상 instanceof 를 활용해야한다.

instanceof 활용 방법

다운캐스팅 시 위 예제와 같이 담긴 값이 맞다면, 올바른 값을 출력하고, 값이 다르다면, 객체가 다르다는 문구를 출력하도록 작성하여, 미리 객체에 어떤 값이 담겨있는지 확인할 수 있다.

0개의 댓글