이번 장에서는 무엇을?

  • 객체 지향 4대 특성을 넘어 자바가 객체 지향을 확장하기 위해 사용하는 키워드와 개념
  • 어떤 키워드?
    1. abstract
    2. 생성자 -> 생략
    3. final
    4. instance of 연산자
    5. package 키워드 -> 생략
    6. interface & implements 키워드
    7. this 키워드
    8. super 키워드

1. abstract 키워드

  • abstract는? 추상화를 의미, 추상화란?!
    - 구체적인 값이 정해지지 않은 공통적인 특징만을 뽑아낸 것!
  • abstract Method (추상메서드)
    - 선언부는 있는데 구현부가 없는 메소드
public abstract class 개발자 { //-> 추상 클래스
	abstract void 코딩하다(); // 추상메소드 
}
  • abstract Class (추상 클래스)
    - 추상 메소드를 가진 클래스를 -> 추상 클래스
class Naver extends 개발자 {
	@Override
	void 코딩하다() {
    	// 네이버 웹툰을
    }
}

class 당근 extends 개발자 {
    @Override
    void 코딩하다() {
    	// 물건 판매 채팅을
    }
}

class 배민 extends 개발자 {
    @Override
    void 코딩하다() {
    	// 티켓 판매 쿠폰을
    }
}

  • 장점은? 추상 클래스를 이용하면 하위 클래스(네이버, 당근, 배민)클래스에게 구현을 강제할 수 있다. (빠뜨릴수 있는 부분을 강제 적용)

  • 정리를 하면 추상 클래스는 공통적으로 개발해야 할 부분을 적는 부분이라고 생각하면 된다.

  • 질문? abstract클래스만 상속을 받아 사용할까?!

답은 NO
우리가 사용했던 BaseTimeEntity를 살펴보자

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {
    @CreatedDate // entity가 생성되어 저장될때 시간이 자동 저장
    private LocalDateTime createdDate;
    @LastModifiedDate // entity 변경할때 시간이 자동 저장
    private LocalDateTime modifiedDate;
}

  • 상속받아 사용가능하다
  • 단 강제로 명시는 안해도 된다! 이유는?! 상속받은 클래스가 abstract 클래스가 아니기 때문에!

2. static 블록

  • 클래스 생성 시의 바로 메모리에 올라가서 실행되는 블록
  • 고정적으로 바로 메모리에 올릴때 사용
public class global {
	public static String tcpIp = "192.168.1.101";
	public static String port = 56;

	public static String udpIP = "192.168.1.102";
	public static int udpPort = 57;
    
}
  • static 블록이란? - 하나의 블록처리를 해서 공통으로 static 적용시키는 것
public class global {
    static {
        int port=8080;
        String ip="192.168.1.172";
    }
  }
  • 객체에서 static (공통)으로 사용되는 부분을 건들일수 있을까?!
    만약) 서비스 1, 서비스 2 -> static HashMap을 건들이면?!

  • Map에는 변경된 값이 들어가서 Error 발생!

  • 이런 Lock을 방지할 수 있는 Map이 있는데 Concurrent HashMap

Concurrent HashMap

  • Thread-Safe 함을 보장하면서도 높은 성능을 보장
  • multi-trhead환경에서 안정적으로 동작
  • 반대로 static에서 객체를 건들일 수 있을까?!
    - 정답은 NO

    • static 블록에서 사용할 수 있는 속성과 메소드는 static 멤버 뿐

    • 객체 멤버에 접근할 방법이 없음.

    • static 블록은 메모리에 이미 올라가있기 때문에 객체에서 접근이 가능

    • static 블록에서는 객체를 못찾기 때문에 접근 불가

3. final 키워드

  • 마지막, 최종이라는 단어
  • 어디서? 변수, 메소드, 클래스
  • 변수에 붙이면?
    - PI(3.141592...)를 생각하면 된다. -> 변하지 않는 값으로 이것을 이용해서 원의 둘레, 넓이를 구할 수 있음.
  • 메소드에 붙이면?
    - 해당 클래스를 사용할때 상속받아 사용할때 오버라이딩 금지!
public class 기밀문서 {
	final void MAKED_1급비밀() {
    	// 절대 보안
    }

}

class 공군 extends 기밀문서 {
	void MAKED_1급비밀() { // **컴파일 에러 발생!**
    
    }
}
  • 클래스에 붙이면?
    - 상속을 허락하지 않는다.
public final class 기밀문서 { // 상속 허락 안함 
	
}

public class 육군 extends 기밀문서 {} //상속 못함

4. instanceof 연산자

  • 만들어진 객체가 특정 클래스의 인스턴스인지 물어보는 연산자
  • 결과로 true or false를 Return
class 동물 {

}
class 조류 extends 동물 {

}
class 펭귄 extends 조류 {

}


public class Test{
	public static void main(String[] args) {
    	동물 동물객체=new 동물();
        조류 동물객체=new 조류();
        펭귄 펭귄객체=new 펭귄();
        
        동물객체 instanceof 동물; // true 
        
        조류객체 instanceof 동물; //true
        조류객체 instanceof 조류; //true 
        
  
        펭귄객체 instanceof 동물; //true
        펭귄객체 instanceof 조류; //true
        펭귄객체 instanceof 펭귄; //true
        
        펭귄객체 instanceof Object // true
       
    }
}
  • 이러면?!
    	동물 동물객체=new 동물();
        동물 동물객체=new 조류();
        동물 펭귄객체=new 펭귄();
        
        동물객체 instanceof 동물; // true 
        
        조류객체 instanceof 동물; //true
        조류객체 instanceof 조류; //true 
        
  
        펭귄객체 instanceof 동물; //true
        펭귄객체 instanceof 조류; //true
        펭귄객체 instanceof 펭귄; //true
        
        펭귄객체 instanceof Object // true
  • LSP (리스코프 치환 원칙)
    - 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작
    • 특정 메소드가 상위 타입을 인자로 사용한다고 할때, 그 타입의 하위타입도 문제 없이 정상적으로 동작
public int calucate(final Item item) {
	return item.calcurate();
}

public int calucate(final Apple apple) {
	return apple.calcurate();
}
  • LSP를 지키지 않는 대표적인 문제 직사각형 - 정사각형 문제
  • 직사각형은 정사각형이 아니지만, 정사각형은 직사각형
public class Rectangle {
    private int width;
    private int height;

    public void setWidth(final int width) {
        this.width = width;
    }

    public void setHeight(final int height) {
        this.height = height;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }
}
public class Square extends Rectangle {

    @Override
    public void setWidth(final int width) {
        super.setWidth(width);
        super.setHeight(width);
    }

    @Override
    public void setHeight(final int height) {
        super.setWidth(height);
        super.setHeight(height);
    }
}
  • 다른 클래스에서 Rectangle을 호출한다면?!
    - 정사각형이 아닌 직사각형에 대해서는 위 메소드가 올바르게 작동
    • 예를 들어, 가로가 3, 세로가 2라고 한다면, 세로의 길이는 4
    public void increaseHeight(final Rectangle rectangle) {
        if (rectangle.getHeight() <= rectangle.getWidth()) {
            rectangle.setHeight(rectangle.getWidth() + 1);
        }
    }
  • 정사각형의 경우는 다르다.
  • 정사각형은 항상 가로와 세로의 길이가 같으므로 위 메소드를 실행하게 되면 가로와 세로의 길이가 모두 1씩 증가됨으로 직사각형의 길이는 가로보다 세로가 길어야 한다."는 가정이 깨지게 되는 것
  • 이런식으로 코드를 작성할 수 있지만 이는 개방 폐쇄 원칙에 어긋나는 코드, 왜냐하면, increaseHeight()가 확장에는 열려 있지 않기 때문에.
    public void increaseHeight(final Rectangle rectangle) {
        if (rectangle instanceof Square) {
            throw new IllegalStateException();
        }

        if (rectangle.getHeight() <= rectangle.getWidth()) {
            rectangle.setHeight(rectangle.getWidth() + 1);
        }
    }
  • 그럼 애초에 뭐가 잘못되었냐?!
    - 상속을 잘못받음. Square 클래스는 Rectangle 클래스를 상속받으면 안 된다.

    • 정리
      • 상위 타입에서 정한 명세를 하위 타입에서도 그대로 지킬 수 있을 때 상속을 해야 한다.
      • 상속을 하게 된다면 해당 메소드setHeight() 메소드를 정의하지 않아야 한다.
      • 이는 개방 폐쇄 원칙, 즉 확장이 자유로워야되는 문제를 일으킴, 코드를 계속 수정해야 됨으로
      • 따라서 만약 instacneof를 쓴다면 LSP를 위반하는지 의심해보자!
  • 그럼 LSP를 지키기 위해서는 어떻게 설계를 해야 할까?!

  • 상속을 사용할때는 계층도 /조직도에 따라 설계를 하는것이 아니라 a kind of ~에 따라 상속을 이용하자.

       

5. interface 키워드와 implements 키워드

  • interface에서는 메서드에 public, abstarct를 생략해도 된다. (자동 생성된다.)
  • interface에서는 속성에 public, static, final을 생략해도 된다. (자동으로 알아서 붙여준다)
  • 코드로 알아보자
interface Speakable {
	double PI=3.14;
    	double ZEROPOINT=-275.15;
	void sayHello();
}
  • 위 코드와 아래코드는 같다.
interface Speakable {
	public static final double PI=3.14;
    	public static final double ZEROPOINT=-275.15;
    
        public abstract void sayHello();
}
  • 다시한번 보는 상속 VS Interface
  • 상속 (Extends) : a kind of 상위 클래스
  • Interface (Implements) : is able to 상위 Interface

6. THIS 키워드와 SUPER 키워드

THIS - 객체가 자기 자신을 지칭할때 쓰는 키워드

class TT {
    int var=10;

    void test() {
        int var=20;
        System.out.println(var); // 20
        System.out.println(this.var); //10
    }

}
  • 정리하면 변수의 이름이 같을경우 (지역변수와 속성의 이름) 지역변수가 우선
  • 객체 변수를 이용하려면 this를 이용하자

SUPER - 바로 위 상위 클래스의 인스턴스를 지칭하는 키워드

class 사람 {
	void method() {
    	System.out.println("사람");
    }	
}

class 네이버 extends 사람 {
	void method() {
    	super.method(); // 사람 
    	System.out.println("네이버"); //네이버
    }	
}

class 네이버_웹툰 extends 네이버 {
	void method() {
    	super.method(); // 네이버 
    	System.out.println("네이버_웹툰"); 
    }	
}

class 네이버_웹툰 extends 네이버 {
	void method() {
    	super.super.method(); // 사람? NO 불가능 
    	System.out.println("네이버_웹툰"); 
    }	
}

7. 추가로?!

Class 펭귄 {
	void test() {
    // TEST
}

public class Driver {
	    public static void main(String[] args) {
        펭귄 뽀로로 =new 펭귄();
        뽀로로.test(); 
        }
}
  • 이러면 뽀로로가 들어가야 될것같지만 실상은 펭귄이 들어감
  • 즉 만약에 펭귄 객체를 이용해서 100개를 만든다면?! 힙 영역에는 100개가 만들어져야 할것 같지만, 실상은 객체 멤버 test()를 스태틱 영역에 단 하나만 보유, 그리고 this 객체 참조 변수로 넘김
    펭귄 뽀로로1=new 펭귄();
    펭귄 뽀로로2=new 펭귄();
    펭귄 뽀로로3=new 펭귄();
    ...
    펭귄 뽀로로100=new 펭귄();

  • 의문사항 : 알아서 싱글톤을 유지한다는 소리인가?!!

REFERENCE

profile
BackEnd Developer

0개의 댓글