설계도가 있지만, 설계도 자체를 가지고 객체를 생성하지 않는다.
ex. Animal - Cat, Dog, Person 에서 Animal에 대한 이야기. “동물”이라는 객체를 만들지는 않으니까
// 두 클래스 사이에 유사점(공통부분)은 있다.
// 그러나 상속 관계는 아니다.
// => 공통부분을 따로 클래스를 만들어서 관리.
// - 나중에 변경사항이 생기거나, 업그레이드할 때 해당 클래스만 고치면
// 연관된 모든 클래스에 적용할 수 있고, 그게 더 효율적이다.
// - 상속에 따른 계층적 관리 => 코드의 조직화에 도움.
// - 상속관계에 있다면 => 다형성 이용 가능.
// - 전혀 다른 설계도 사이에, 연관성이 생긴다.
public class KFoodChef extends Chef {
// @Override 더이상 오버라이드가 아니게 됨.
public void cook() {
System.out.println("한식을 조리한다.");
}
}
public class JFoodChef extends Chef {
// @Override
public void cook() {
System.out.println("일식을 조리한다.");
}
}
public class Chef {
String name;
int age;
String speciality;
public void eat() {
System.out.println("음식을 먹는다.");
}
// cook() 주석처리 해보자.
public void cook() {
System.out.println("전공에 맞는 조리한다.");
}
}
import test02_inheritance.Chef;
public class ChefTest {
public static void main(String[] args) {
Chef[] chefs = new Chef[2];
chefs[0] = new KFoodChef();
chefs[1] = new JFoodChef();
// chef 설계도에서 쓰이지 않는 cook() 메서드를 삭제했더니
// 자식 객체가 cook() 메서드를 갖고 있다 하더라도 접근 불가.
for(Chef chef:chefs) {
chef.eat(); // 부모 클래스 타입으로 참조
//chef.cook(); // 자식 객체의 행위가 실행됨
// -> 에러 발생 다형성 활용 불가. 동적 바인딩 불가.
}
}
}
cook() 메서드는 자손 클래스에서 반드시 재정의해서 사용되기 때문에 조상의 구현이 무의미.
메서드의 선언부만 남기고 구현부는 ; 으로 대체.
abstract 키워드를 메서드 선언부에 추가abstract를 추가 (abstract 없으면 에러 발생)추상 메서드를 하나라도 가지고 있으면 추상 클래스
public abstract class Chef {
String name;
int age;
String speciality;
public void eat() {
System.out.println("음식을 먹는다.");
}
public abstract void cook();
}
public class ChefTest {
public static void main(String[] args) {
Chef[] chefs = new Chef[2];
chefs[0] = new KFoodChef();
chefs[1] = new JFoodChef();
for(Chef chef : chefs) {
chef.eat();
chef.cook(); // 접근 가능 => 다형성 활용 가능
}
}
}
💡 이때, 구현의 강제성이란?
1 ) 해당 추상 메서드를 반드시 오버라이드하거나, ⇒ 자기 자신이 완전한 클래스가 되거나
2 ) 오버라이드 하지 않고 자기 자신도 추상 클래스가 된다. ⇒ 이걸로는 객체 생성 불가 : 추상 메서드가 없어도 abstract는 붙일 수 있고, 객체 생성은 안된다는 뜻. 즉, 이건 상속해서 써라~
// (2) 의 예시
public class abstract Chef {
String name;
int age;
String speciality;
public void eat() {
System.out.println("음식을 먹는다.");
}
public void cook() {
System.out.println("전공에 맞는 조리한다.");
}
}
abstract 클래스는 상속 전용 클래스
클래스에 구현부가 없는 메서드가 있으므로 객체를 생성할 수 없음
상위 클래스 타입으로 자식을 참조할 수는 있음
조상 클래스에서 상속 받은 abstract 메서드를 재정의하지 않은 경우, 클래스 내부에 abstract 메서드가 있으므로 자식 클래스는 abstract 클래스가 되어야 함
💡 추상 클래스 사용하는 이유
구현의 강제를 통해 프로그램의 안정성 향상
서로 다른 장치들을 연결시켜 주는 규격
public interface 인터페이스 이름 {
public static final 타입 상수 이름1= 10;
(생략 가능) 타입 상수이름2 =10;
public abstract 반환형 메서드이름1(타입 매개변수…);
(생략 가능) 반환형 메서드이름2(타입 매개변수…);
}
인터페이스 이름으로 바로 접근하는게 static
public interface MyInterface {
public static final int MEMBER1 =10;
int MEMBER2 = 20;
public abstract void method1();
void method2();
}


public class MyClass implements MyInterface{
@Override
public void method1() {
// TODO Auto-generated method stub
System.out.println("method1");
}
@Override
public void method2() {
// TODO Auto-generated method stub
System.out.println("method2");
}
}
public class Test {
public static void main(String[] args) {
// 인터페이스의 모든 멤버변수는 static final
System.out.println(MyInterface.MEMBER1);
System.out.println(MyInterface.MEMBER2);
// 객체 생성
MyClass mc = new MyClass();
mc.method1();
mc.method2();
// 해당 객체가 그 인터페이스를 구현했다면,
// 인터페이스를 참조형 타입으로 활용 가능
MyInterface mi = mc;
MyInterface mi2 = new MyClass();
}
}
MyInterface m = new Myinterface(); // 에러 발생. 불가능
interface Shape{}
class Circle extends Shape{} // X
class Circle implements Shape{} // O
인터페이스에 구현부가 있는 메서드를 작성할 수 있음
메서드 앞에 default라는 키워드를 작성해야 함
public 접근제한자를 사용해야하며 생략 가능 ( 생략되어있으면 public)
public interface AbleToFly {
void fly();
public default void print() {
System.out.println("날아요.");
}
}
클래스의 static 메서드와 사용 방법 동일
인터페이스 이름으로 메서드에 접근하여 사용
public interface AbleToSwim {
void swim();
default void print() {
System.out.println("수영해요.");
}
public static void infor() {
System.out.println("static입니다.");
}
}
public class Test {
public static void main(String[] args) {
Duck d = new Duck();
Butterfly b = new Butterfly();
Penguin p = new Penguin();
Swan sw = new Swan();
b.fly();
b.print(); // default 메서드
AbleToSwim.info(); // static 메서드


타입에 대해 일반화된 클래스를 작성하겠다. 타입을 파라미터처럼 사용
클래스 안에서 사용되는 자료형(타입)을 구체적으로 명시하지 않고, T와 같이 타입 매개변수를 이용하는 클래스
클래스 또는 인터페이스 선언 시 <> 에 타입 파라미터 표시
public class CalssName<T>{}
public interface InterfaceName<T>{}
타입 파라미터 : 특별한 의미의 알파벳이라기보단 단순히 임의의 참조형 타입을 말함
generic이 없다면?
class Box {
private Object obj; // 모든 클래스의 조상, 모든 값이 들어갈 수 있음
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
public class BoxTest {
public static void main(String[] args) {
Box box = new Box();
box.setObj("Hi");
box.setObj(11);
box.setObj(22.1);
// 타입 별로 설계도를 만들든지
// Object로 설계도를 만든다.
// => 값을 넣을 때는 편하지만, 뺄 때는 번거롭다 !!
// 항상 object를 반환하므로 => 형변환을 해줘야한다.
Object obj = box.getObj();
double d = obj; // 형 변환 과정에서 에러 발생할수도
// Double은 double 자료형의 wrapper 클래스
if(obj instanceof Double) {
System.out.println("실수입니다");
} else if(obj instanceof String) {
System.out.println("문자열입니다.");
}
}
}
generic 적용 후 효과
// 제네릭 클래스: 타입을 매개변수화 했다.
// <> 타입 파라미터
class Box<T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public class BoxTest {
public static void main(String[] args) {
Box<Integer> intBox = new Box<Integer>(); // integer만 들어갈 수 있는 박스
// intBox.setT("Hi"); // 타입 체크 후 오류
intBox.setT(11);
int num = intBox.getT(); // 형변환 할 필요X
Box<String> strBox = new Box<String>(); // string만 들어갈 수 있는 박스
strBox.setT("Hi");
String str = strBox.getT();
}
}
Box<Student> box = new Box<Student>(); //(O)
Box<Person> box = new Box<Student>(); //(X)
Box<Student> box = new Box<>();
class Box<T extends Person>{
private T obj;
public T getObj(){
return obj;}
}
public void setObj(T obj){
this.obj = obj;
}
}

UserManagerImpl에서 싱글톤 객체 생성 시
//public class UserManagerImpl implements IUserManager { 안에서 ...
private static UserManagerImpl um = new UserManagerImpl();
// 싱글톤 패턴의 기본 생성자, 객체 생성을 외부에서 하지 못하게 막음
private UserManagerImpl() {};
// 외부에서 사용할 수 있도록 UserManagerImpl 인스턴스 반환
public static UserManagerImpl getInstance() {
return um;
}
이걸 인터페이스를 활용해 다른 버전으로도 가능!
private static IUserManager um2 = new UserManagerImpl();
private static IuserManager um3 = new UserManagerImpl_ver2();
public static IUserManager getInstance() {
return um2;
}