본문 바로가기
❤️‍🔥TIL (Today I Learned)

[TIL] 2022-12-01(24day)

by elicho91 2022. 12. 1.

JVM 구조, 메모리와 코드의 흐름


👉 추상클래스

  • 기본적으로 클래스이다. 상속, 확장하여 사용하기 위한 것
  • 여러 하위 클래스가 동일한 구현으로 로직을 수행 + 일부 로직이 다를 때 추상클래스 사용 고민 (Template Method 패턴)

# 클래스를 상속받아 이용 및 확장을 위함

   (부모의 기능을 확장하는 개념)

#다중 상속 불가능 , 단일 상속

# extends를 이용하여 구현

# 추상메소드에 대한 구현 가능

# 생성자와 일반변수 모두 가질 수 있다.


-1) 설계와 구현을 분리한다면, 설계부분에 해당
-2) 키워드 : abstract
-3) 추상메서드 : abstract 키워드를 붙혀 메서드의 선언분(원형)만 있고
구현은 되어있지 않은 형태 (중괄호 부분 없음) 
  public String getNAme(){} 이지만 
->로 표현 public abstract String getName();


-4) 추상클래스 : 일반 맴버 (변수, 메서드)들과 함께 추상 메서드를 포함하는 클래스
추상 메서드가 하나라도 있으면 무조건 추상 클래스
추상 클래스가에는  abstract 키워드를 붙혀야 한다.

abstract class Sharpe{ // 추상클래스
int a ;
abstract void draw(); //추상 메서드
}

-5) 추상 클래스로는 객체 생성 불가!! 불완전한 / 미완성인 클래스이므로 메모리에 올릴 수 없다
Shape s = new Shape ();   -----> (X)
Shape s;  ----> (O) 변수 선언만은 괜찮다.

-6) 상속에서 수퍼클래스로 사용된다. 부모 클래스 될 수 있음. extends 키워드로 상속시킬수 있다.

-7) 추상클래스를 물려받은 자식클래스를 완성시키려면 추상메서드를 오버라이딩(내용물 구현)해야한다.
추상클래스를 단순히 상속만 받은 자식클래스는 그 또한 추상클래스가 된다.

# 추상 클래스 단순 상속  : 상속받은 추상 메서드 구현 안하면,
abstract class Rect extends Shape {
int width
}

# 추상 클래스를 상속 + 구현 
class Rect extends Shape {
int width;
@override
void draw(){....
}
}

8) 추상클래스를 상속받은 자식클래스는 개발자들이 추상메서드를 모두 구현해야함.
-> 추상 클래스를 가이드라인,
자식 클래스는 가이드를 따른 구현된 사용가능한 실제 클래스

 

abstract class Bird {
    private int x, y, z;

    void fly(int x, int y, int z) {
        printLocation();
        System.out.println("이동합니다.");
        this.x = x;
        this.y = y;
        if (flyable(z)) {
            this.z = z;
        } else {
            System.out.println("그 높이로는 날 수 없습니다");
        }
        printLocation();
    }

    abstract boolean flyable(int z);

    public void printLocation() {
        System.out.println("현재 위치 (" + x + ", " + y + ", " + z + ")");
    }
}

class Pigeon extends Bird {
    @Override
    boolean flyable(int z) {
        return z < 10000;
    }
}

class Peacock extends Bird {
    @Override
    boolean flyable(int z) {
        return false;
    }
}

public class Main {
    public static void main(String[] args) {
        Bird pigeon = new Pigeon();
        Bird peacock = new Peacock();
        System.out.println("-- 비둘기 --");
        pigeon.fly(1, 1, 3);
        System.out.println("-- 공작새 --");
        peacock.fly(1, 1, 3);
        System.out.println("-- 비둘기 --");
        pigeon.fly(3, 3, 30000);
    }
}


👉 인터페이스 interface : 규격   / 골격제작

  • 인터페이스를 구현한 객체들에 대한 동일한 동작을 보장하기 위해 사용 (동시 개발 가능)

# 구현하려는 객체의 동작의 명세

# 다중 상속 가능

# implements를 이용하여 구현

# 메소드 시그니처(이름, 파라미터, 리턴 타입)에 대한 선언만 가능

# 생성자와 일반변수 모두 가질 수 없다.


-1) 키워드 : interface  //클래스 대신 사용
키워드 사용해서 클래스를 선언하듯이 인터페이스를 선언
-2) 인터페이스 멤버는 추상메서드와 상수만으로 구성된다.

interface Practice {
	// 상수
	(final/static : 지우라고뜸) 타입 상수명(대문자 convention) = 값;
	String HI = "Hi~";

	// 추상 메서드
	List<String> findAllName();

	// Default 메소드
	default 타입 메소드명(파라미터,...) {...}
	default void printHi() {
		System.out.println(HI);
	}

	// static 메소드
	static void printHi() {
		System.out.println(HI);
	}
}


* 인터페이스 멤버는 어디서라도 접근 가능하게 모두 public으로 만듬

-3) 모든 메서드는 public abstract 이며 생략 가능하다. 

-4) 상수는 public static final 타입이며 생략 가능하다.

-5) 인터페이스는 객체 생성 불가!
메서드는 모두 구현안된 추상메서드이므로 미완성/불완전해 객체 생성 불가능

Phone p = new Phone(); ----> X

-6) 인터페이스 타입의 변수 선안만은 가능 

Phone p; ---> O

-7) 인터페이스 상속
인터페이스는 다른 인터페이스를 상속 받을 수 있다.
인터페이스는 규격과 같은 것으로, 상속을 통해
기존 인터페이스에 새로운 규격을 추가한 새로운 인터페이스를 만들 수 있다.
extends 키워드 사용

interface Mobile extends Phone {
void sendSMS();    // 상수, 추상메서드
void receiveSMS();     규격추가 
}

### 인터페이스는 다중 상속 허용 ###  (구현이 안되니 충돌날일이 없으니깐)
interface DualcameraPhone extends Mobile, Camera {
void makeVideo();
}

-8) 인터페이스 구현
인터페이스의 추상 메서드를 모두 구현한 클래스를 작성하는 것
키워드 : implements

class Iphone Implementes Mobile{
// 추상메서드 모두 구현
public void senCall(){.....}
public void receiveCall(){.....}
public void sendSMS(){...}
public voiud receiveSMS(){...}
//추가적으로 일반 메서드 , 변수선언
public int doubleFingerTouch(){...}



* 추상메서드중 하나라도 구현안하면 에러!!
  키워드 implements는 객체생성가능
                   Mobile m = new Iphone();

-9) 인터페이스의 목적
-10) 인터페이스의 다중 구현
클래스는 하나 이상의 인터페이스 구현 가능.,
class Iphone Implementes Mobile, Camera,

 

interface Bird {
    void fly(int x, int y, int z);
}

class Pigeon implements Bird{
    private int x,y,z;

    @Override
    public void fly(int x, int y, int z) {
        printLocation();
        System.out.println("날아갑니다.");
        this.x = x;
        this.y = y;
        this.z = z;
        printLocation();
    }
    public void printLocation() {
        System.out.println("현재 위치 (" + x + ", " + y + ", " + z + ")");
    }
}

public class Main {

    public static void main(String[] args) {
        Bird bird = new Pigeon();
        bird.fly(1, 2, 3);
//        bird.printLocation(); // compile error
    }
}

👉 스트림

1. 배열이나 컬렉션에 담긴 데이터를 다룰 때, 반복문이나, iterator를 사용하면 코드가 길어지고, 가독성이 떨어진다. 이 문제를 해결하기위해 Stream API 등장.

#특징

  - 스트림은 데이터를 변경하지 않는다.

  - 스트림은 재사용 불가 -> 최종 연산이 실행된 후 재사용 불가.

 

# 스트림 파이프라인

  - 0 ~ N 개의 중개 연산과 1개의 종료 연산으로 구성.

 

# 중개 연산

  - Stream을 리턴.

 

# 종료 연산

  - Stream을 리턴하지 않는다.

 

 

2. 대표 스트림 연습

# 중개 연산자

  - 필터링 :  `filter` , `distinct`
  - 변환 : `map` , `flatMap`
  - 제한 : `limit` , `skip`
  - 정렬 : `sorted`

 

# 최종 연산

  - 요소 출력 : `forEach`
  - 요소 검색 : `findFirst`, `findAny`
  - 요소 통계 : `count`, `min`, `max`
  - 요소 연산 : `sum`, `average`
  - 요소 수집 : `collect`

 

# Stream 훈련

  - ilter + anyElement / firstElement

List<String> elements = Stream.of("a", "b", "c")
										.filter(element -> element.contains("b"))
                    .collect(Collectors.toList());

    Optional<String> anyElement = elements.stream().findAny();
    System.out.println(anyElement.orElse("암것두 없어"));

    Optional<String> firstElement = elements.stream().findFirst();
    System.out.println(firstElement.orElse("한개두 없어"));

 

  - N개의 중개 연산자

Stream<String> onceModifiedStream = Stream.of("abcd", "bbcd", "cbcd");

Stream<String> twiceModifiedStream = onceModifiedStream
										.skip(1)
										.map(element -> element.substring(0, 3));

System.out.println(twiceModifiedStream); //?
System.out.println(twiceModifiedStream.collect(Collectors.toList()));

 

  - map 활용

class Product {
    private int age;
    private String name;

    public Product(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}
List<Product> productList = Arrays.asList(
								new Product(23, "potatoes"),
                new Product(14, "orange"), new Product(13, "lemon"),
                new Product(23, "bread"), new Product(13, "sugar")
);

List<String> collectorCollection = productList.stream()
				.map(Product::getName)
				.collect(Collectors.toList());

System.out.println(collectorCollection);

double averageAge = productList.stream()
                .collect(Collectors.averagingInt(Product::getAge));
System.out.println("나이 평균 : " + averageAge);

int summingAge = productList.stream().mapToInt(Product::getAge).sum();
        System.out.println("나이 총합 : " + summingAge);

 

  - 참고 사이트 : https://www.baeldung.com/java-8-streams 


👉 Optional 

# NPE(Null Pointer Exception) 예외를 Optional이 제공하는 메소드로 간단하게 회피할 수 있다.

# Optional은 메소드 반환 타입으로만 사용해야한다.

# 잘못 사용하면 오히려 코드가 오히려 지저분해지고, 의미 없는 동작, Side-Effect 유발이 많이 발생할 수 있음.

  - NPE은 물론이고, NoSuchElementException이 발생함.

  - 잘못된 Optional 사용으로 새로운 문제들이 발생함.

  - 코드의 가독성을 파괴

  - 시간, 공간적 비용이 증가함

 

# Optional의 객체 꺼내는 방법

Optional<String> opt = Optional.ofNullable("Optional은 Wrapper Class");

System.out.println(opt.get());

 

# Optional 훈련

  - empty()

Optional<String> empty = Optional.empty();

System.out.println(empty.isPresent()); // ?

 

  - of()

Optional<String> empty = Optional.of("assert NotNull");

System.out.println(empty.isPresent()); // ?

Optional.of(null); // ?

//NPE 발생. of는 null이 아님을 확신할때 사용.

 

  - ofNullable()

Optional<String> empty = Optional.ofNullable(null);

System.out.println(empty.isPresent()); // ?

 

  - ifPresent() : Optional에서 꺼낸 객체가 존재한다면, 구문수행.

String name = null;
Optional<String> opt = Optional.ofNullable(name);

opt.ifPresent(n -> System.out.println(n.length()));

 

  - orElse() : Optional에서 꺼낸 객체가 존재한다면 꺼내고, 그렇지 않다면? orElse의 인자값을 반환

System.out.println(Optional.ofNullable(null).orElse("냐옹"));

System.out.println(Optional.ofNullable("Hey!").orElse("냐옹"));

 

  - orElseGet() : orElse()와 비슷하지만, 인자값으로 람다 표현식의 결과값을 출력

System.out.println(Optional.ofNullable(null).orElseGet(String::new));
System.out.println(Optional.ofNullable(null).orElseGet(() -> "냐옹"));
System.out.println(Optional.ofNullable("Hey!").orElseGet(() -> "냐옹"));

 

  - orElseThrow() : Optional에서 꺼낸 객체가 존재한다면 꺼내고, 그렇지 않다면? Exception 던지기

String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow(
        IllegalArgumentException::new);

 

  - 참고 사이트 : https://www.baeldung.com/java-optional


🙋‍♂️ 소감 : 

앞서 미니 프로젝트로 은행관리시스템을 만들면서 추상화 클래스를 사용했는데,공통적은 기능과 전체적인 틀은 한분에서 작업해주셔서 은행별 다르게 작동하는는기능구현 메서드에만 집중을 해서 전체적인 프로젝트 구조를 완전히 이해하지 못하고 마무리가 되었다.또 오늘 심화과정을 들으면서 추상화 클래스와 인터페이스의 차이점을 제대로 이해하지 못했는데,차이점을 확실히 짚어두고, 앞으로 스프링을 하면서 많이 다루게 될 내용이니, 반복학습을 해야겠다.

 

# 추상클래스

  - 추상클래스는 상속을 받아서 기능을 확장하는데 초점

  - 추상메서드 + 일반메서드 공존

  - extends

  - 상속받으면 추상메서드만 오버라이딩

  - 추상클래스는 인스턴스화 할 수 없으며, 인스턴스를 생성하고 싶다면 상속받은 클래스를 사용해야 한다.

    (추상클래스 안의 추상메서드의 구현부가 없으므로...)

 

# 인터페이스

  - 인터페이스는 전체적인 틀을 잡는 설계도 기능에 초점

  - implements

  - 모든 메서드가 추상메서드

  - 상속 받으면 모든 메서드 오버라이딩

  - 다중상속 가능

 

😈 아는 내용이라고 그냥 넘어가지 않기! 😈

'❤️‍🔥TIL (Today I Learned)' 카테고리의 다른 글

[TIL] 2022-12-05(26day)  (0) 2022.12.05
[TIL] 2022-12-02(25day)  (0) 2022.12.02
[TIL] 2022-11-30(23day)  (0) 2022.11.30
[TIL] 2022-11-29(22day)  (0) 2022.11.29
[TIL] 2022-11-28(21day)  (0) 2022.11.28

댓글