Java 입문 과정에서 강의 들으며 빠른 학습 진도를 위해 추후에 집중해서 복습할 부분 ➡️메모하기
캡슐화(Encapsulation)란?
객체의 정보를 외부에서 직접 접근하지 못하게 보호하는 개념
클래스 혹은 객체의 캡슐화는 접근제어자 를 통해서 구현할 수 있다
캡슐화를 통해 정보를 보호하고 필요한 경우에만 안전하게 접근할 수 있도록 한다
접근제어자(Access Modifier)


package chapter2.capsulation;
public class Person {
// 속성
private String name;
private String secret;
// 생성자
public Person(String name) {
this.name = name;
}
// 기능
public void methodA() {}
private void methodB() {}
// 게터
public String getName() {
return name;
}
// 세터
public void setName(String name) {
this.name = name;
}
}
package chapter2.capsulation;
public class Main {
public static void main(String[] args) {
// 생성자 호출
Person person = new Person("gygim");
// 인스턴스 변수 접근
// person.name = "gygim";
// person.secret = "??";
// 인스턴스 메서드 접근
// person.methodA();
// person.methodB();
// 게터
String name = person.getName();
System.out.println("이름: " + name);
// 세터
person.setName("Steve");
String name2 = person.getName();
System.out.println("이름: " + name2);
}
}
데이터 접근 - 게터(Getter)와 세터(Setter)
캡슐화가 잘 적용된 클래스는 내부 데이터를 private 으로 보호하고 있다
데이터 조회나 변경이 필요한 경우 안전한 접근방법이 필요하다
→ 그 역할을 수행하는 메서드가 바로 게터(Getter)와 세터(Setter)
package chapter2.capsulation.v1;
// 핵시설 관리하는 개발자
public class DataStore {
private String store;
public void setStore(String store) {
if (store.equals("B")) {
System.out.println("B 가 입력되면 안됩니다.");
} else {
this.store = store;
}
}
}
package chapter2.capsulation.v1;
public class Main {
public static void main(String[] args) {
DataStore dataStore = new DataStore();
// dataStore.store = "B";
dataStore.setStore("B");
}
}
package chapter2.capsulation.v2;
// 로보트를 걷게 만들자
public class Robot {
private boolean leftLeg;
private boolean rightLeg;
private boolean leftArm;
private boolean rightArm;
// ...
// public void setLeftLeg(boolean leftLeg) {
// this.leftLeg = leftLeg;
// }
//
// public void setrightLeg(boolean rightLeg) {
// this.rightLeg = rightLeg;
// }
//
// public void setLeftArm(boolean leftArm) {
// this.leftArm = leftArm;
// }
//
// public void setRightArm(boolean rightArm) {
// this.rightArm = rightArm;
// }
// 의미있는 세터
public void walk(boolean power) {
this.leftLeg = power;
this.rightLeg = power;
this.leftArm = power;
this.rightArm = power;
}
}
package chapter2.capsulation.v2;
public class Main {
public static void main(String[] args) {
Robot robot = new Robot();
// 무분별한 세터 예시
// robot.setLeftLeg(true);
// robot.setRightLeg(true);
// robot.setRightArm(true);
// robot.setRightArm(true);
// 의미있는 세터
robot.walk(true);
}
}
상속(Inheritance) 이란?
클래스간의 관계를 부모(상위), 자식(하위) 로 바라보는 개념
이 구조를 통해 재사용성, 확장이 가능
→ 물려받은 속성과 기능은 자식 클래스에서 재사용할 수도 있고, 확장할 수도 있다
extends 키워드를 사용해서 상속관계를 구현한다
코드 중복을 줄이고 유지보수성을 높일 수 있다
추상화, 다형성을 구현하는데 잘 활용된다

부모 클래스의 내용을 물려받아 그대로 재사용할 수 있다
super - 부모 인스턴스
부모클래스의 멤버(변수, 메서드)에 접근할 때 사용하는 키워드
→ 자식 클래스에서 부모의 변수나 메서드를 명확하게 호출할 때 사용
부모가 먼저 생성되어야 하므로 super() 는 항상 생성자의 첫 줄에 위치해야 한다
확장
부모클래스의 기능을 유지하면서 자식클래스에서 기능을 확장할 수 있다
자식클래스에서 새로운 메서드를 추가하면 된다
재정의 - 메서드 오버라이딩(overriding)
부모 메서드를 자식 클래스에서 변경하여 재정의하는 것을 의미
오버라이드된 메서드에는 @Override 키워드를 붙이는 것을 권장 (없어도 동작)
@Override 를 붙이면 컴파일러가 부모 클래스에 동일한 메서드가 없다고 경고를 줘서 실수를 방지할 수 있다
메서드 이름, 매개변수, 반환타입이 완전히 동일해야 한다
접근 제어자는 부모보다 더 강한 수준으로만 변경 가능하다
package chapter2.inheritance;
public class Parent {
public String familyName = "스파르탄";
public int honor = 10;
public Parent() {
System.out.println("부모 생성자");
}
public void introduceFamily() {
System.out.println("우리 " + this.familyName + "가문은 대대로 ...");
}
}
package chapter2.inheritance;
public class Child extends Parent {
public String familyName = "gygim";
public Child() {
super();
System.out.println("자식 생성자");
}
public void superTest() {
System.out.println(super.familyName);
}
// 부모에는 없지만 자식에만 있는 기능
public void showSocialMedia() {
System.out.println("SNS에서 우리 가문을 소개드립니다.");
}
@Override
public void introduceFamily() {
System.out.println("오버라이드");
}
}
package chapter2.inheritance;
public class Main {
public static void main(String[] args) {
Child child = new Child();
System.out.println("가문이름: " + child.familyName);
System.out.println("명예: " + child.honor);
child.introduceFamily();
child.superTest();
child.showSocialMedia(); // 부모에는 없지만 자식에만 있는 기능
}
}

추상클래스
공통 기능을 제공하면서 하위 클래스에 특정 메서드 구현을 강제하기 위해 사용
객체를 생성할 목적이 아니라 “설계도” 역할을 할때 적합
abstract 키워드로 클래스를 선언하면 추상클래스이다
abstract 키워드로 메서드를 선언하면 자식클래스에서 강제로 구현해야한다
추상클래스로 객체를 생성할 수 없다
일반 클래스처럼 변수와 메서드를 가질 수 있다

package chapter2.inheritance.abstractexample;
// 추상클래스 선언
abstract class Animal {
public String name;
abstract void eat();
public void sleep() {
System.out.println("쿨쿨..");
}
}
package chapter2.inheritance.abstractexample;
public class Cat extends Animal {
@Override
void eat() {
System.out.println("냠냠..!");
}
}
package chapter2.inheritance.abstractexample;
public class Main {
public static void main(String[] args) {
// 추상클래스는 인스턴스화 할 수 없다.
// Animal animal = new Animal();
Cat cat = new Cat();
cat.name = "cat";
cat.sleep();
// 자식에서 강제 구현된 메서드
cat.eat();
}
}

추상화란?
불필요한 정보를 제거하고 본질적인 특징만 남기는 것을 의미
→ 고양이 → 동물 → 생명체
객체지향 프로그래밍에서는 추상화의 계층적 특징을 활용해서 유지보수성이 좋은 프로그램을 만들 수 있다
추상화의 특징은 다형성에서 활용된다

인터페이스 상속를 활용한 추상 계층 표현package chapter2.abstraction.v1;
public interface LifeForm {
void exist(); // 공통: 모든 생명체는 존재한다.
}
package chapter2.abstraction.v1;
public interface Animal extends LifeForm {
void makeSound(); // 공통: 모든 동물은 소리를 낸다.
}
package chapter2.abstraction.v1;
public class Cat implements Animal {
@Override
public void exist() {
System.out.println("고양이가 존재합니다.");
}
@Override
public void makeSound() {
System.out.println("야옹");
}
public void scratch() {
System.out.println("스크래치");
}
}
package chapter2.abstraction.v1;
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.exist();
cat.makeSound();
cat.scratch();
}
}

클래스 상속을 활용한 추상 계층 표현package chapter2.abstraction.v2;
public class LifeForm {
public void exist() {
System.out.println("생명체는 존재합니다.");
}
}
package chapter2.abstraction.v2;
public class Animal extends LifeForm {
public void makeSound() {
System.out.println("동물은 소리를 냅니다.");
}
}
package chapter2.abstraction.v2;
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("야옹!");
}
public void scratch() {
System.out.println("스크래치");
}
}
package chapter2.abstraction.v2;
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.exist();
cat.makeSound();
cat.scratch();
}
}

다형성(Polymorphism)이란?
하나의 타입으로 여러 객체를 다룰 수 있는 객체지향의 4번째 특징
추상 계층이라는 특징을 활용해서 다형성을 구현할 수 있다
추상화의 특징은 다형성에서 활용된다
형변환(Casting)
부모타입으로 자식타입을 다룰 수 있는 이유는 자동으로 형변환(Casting) 이 발생했기 때문
자식타입 → 부모타입: 업캐스팅(UpCasting)
⚠️ 주의사항 : 업캐스팅은 부모의 타입으로 데이터를 다룰 수 있지만 자식 클래스의 고유기능을 활용할 수 없다 → 자식 클래스의 고유 기능을 사용하려면 다운캐스팅이 필요
부모타입 → 자식타입: 다운캐스팅(DownCasting)
⚠️ 주의사항 : 잘못된 다운캐스팅은 컴파일단계에서 감지할 수 없다
→ 그래서 다운캐스팅을 사용할때 항상 instanceof 를 활용해야 한다
package chapter2.polymorphism;
public interface LifeForm {
public void exist();
}
package chapter2.polymorphism;
public interface Animal extends LifeForm {
void makeSound();
}
package chapter2.polymorphism;
public class Cat implements Animal {
@Override
public void exist() {
System.out.println("고양이가 존재합니다.");
}
@Override
public void makeSound() {
System.out.println("야옹");
}
public void scratch() {
System.out.println("스크래치");
}
}
package chapter2.polymorphism;
public class Dog implements Animal {
@Override
public void exist() {
System.out.println("강아지가 존재합니다.");
}
@Override
public void makeSound() {
System.out.println("멍멍");
}
public void wag() {
System.out.println("흔들흔들");
}
}
package chapter2.polymorphism;
public class Main {
public static void main(String[] args) {
// 다형성 활용
Animal animal1 = new Cat();
Animal animal2 = new Dog();
animal1.exist();
animal1.makeSound();
animal2.exist();
animal2.makeSound();
// 업캐스팅 주의사항
// animal.scratch();
// animal.wag();
// 다운캐스팅
Cat cat = (Cat) animal1;
cat.scratch();
Dog dog = (Dog) animal2;
dog.wag();
// 잘못된 다운캐스팅 문제
// Cat cat2 = (Cat) animal2; // animal2 = Dog;
// cat2.scratch();
// 다운캐스팅 instanceof 활용 방법 -> 의도치 않은 예외 입력시 문구 출력
if (animal2 instanceof Cat) {
Cat cat2 = (Cat) animal2;
cat2.scratch();
} else {
System.out.println("객체가 고양이가 아닙니다.");
}
System.out.println("::::");
Animal[] animals = {new Cat(), new Dog(), new Cat()};
for (Animal animal : animals) {
animal.makeSound();
}
}
}
