스프링 부트 1주차 CRUD

결국엔 해냈습니다!!

물론.. 수업에서 실습한 코드를 베이스로 작성한 것이지만요.

오늘 구현한 기능은 아래와 같습니다.

  • 전체 게시글 수정 및 삭제: 제목, 작성자명, 작성 내용을 비밀번호를 일치 여부 확인 후 실행
  • 삭제 버튼 추가
  • 삭제 버튼 누르면 비밀번호 input 박스와 삭제 확인 버튼 생성

아래는 신나서 만든 시현 영상입니다.

 

 

 

  • 그냥 비밀번호만 확인한다고 해도 앞에 ${id}-변수 값을 넣어줘서 해당 id에 붙도록 해야 합니다.

비밀번호 확인을 서버에서 하는데 아래 코드를 썼습니다.

 

 //비밀번호 확인하기
    @PutMapping("/api/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        Memo memo = memoRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("아이디가 존재하지 않습니다.")
        );
        if (memo.getPassword().equals(requestDto.getPassword())) {
            memoService.update(id, requestDto);
            return id;
        } else return 0L;
    }

    @DeleteMapping("/api/memos/{id}")
    public Long deleteMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        Memo memo = memoRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("아이디가 존재하지 않습니다.")
        );

        if (memo.getPassword().equals(requestDto.getPassword())) {
            memoService.deleteMemo(id);
            return id;
        } else return 0L;
    }
}

 

자바랑 Spring 공부 좀 하다가 최종 제출 전에 암호화를 한 번 해보려고 합니다.

그리고 날짜 포맷 바꾸는 것도 할 수 있으면 해보려고 합니다.

 

이번주가 주특기 1주차인데 벌써 공부할 게 산더미처럼 쌓였습니다.

김영한님의 스프링 강의를 추천받았습니다.

우선 내일은 자바의 정석 7강부터 듣고 혼공자 언어스터디 분량 공부하고 오후 쯤 스프링 강의 구매를 하려고 합니다.

 

 

 

GitHub - sooni2/Spring_study: for studying java Spring

for studying java Spring. Contribute to sooni2/Spring_study development by creating an account on GitHub.

github.com

아직 main에 머지는 안했습니다.

 

 

 

Spring Java Study

JPA 연습

각 데이터 별로 Entity와 Repository를 만들었습니다.

아래처럼 localhost:8080/h2-console 을 입력해서 브라우저에서도 JPA 가 잘 작동하는지 확인했습니다.

JPA H2 console

 

 

상속을 사용해서 생성 수정 시간 관리하기

1. Timestamped 객체를 생성한다.

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class) 
public class Timestamped {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column
    private LocalDateTime modifiedAt;
}

2. extends로 상속 받아서 사용 한다.

@Entity // 게시글
public class Post extends Timestamped {
	  @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false, unique = true)
    private String content;
}

 

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;

@LastModifiedDate
@Column
private LocalDateTime modifiedAt;
  • “extends Timestamped” 으로 상속받는 객체들은 자동으로 생성, 수정시간을 데이터베이스에 입력
  • 필요하다면 해당값을 편하게 Get 할수 있음

 

Spring Data JPA

  • JPA 를 편리하게 사용하기 위해, 스프링에서 JPA 를 Wrapping 함
  • 스프링 개발자들이 JPA 를 사용할 때 필수적으로 생성해야 하나, 예상 가능하고 반복적인 코드들 → Spring Data JPA 가 대신 작성
  • Repostiory 인터페이스만 작성하면, 필요한 구현은 스프링이 대신 함

공식문서

 

Spring Data JPA - Reference Documentation

Example 119. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

 

메모장 사이트 만들기

  • readOnly 메서드가 추가가 안되서 구글링했더니 아래와 같이 org.springframework.transaction.annotation.Transactional 을 해야하는 거였습니다. 
  • 자동으로 해당 패키지가 import되어서 앞부분 다시 지우고 @Transactional 만 넣어줬습니다.

 

 

The attribute readOnly is undefined for the annotation type Transactional

I am getting this error when I am putting this piece of code on a service method @Transactional(readOnly =true) I am writing this code to make a transaction read only. Can you please tell me Wh...

stackoverflow.com

 

이미지 깨짐 현상

아래처럼 static.images에 넣어준 이미지들이 깨진 것을 확인하고 문제 해결을 위해 구글링을 하고 슬랙에 질문을 했습니다.

스프링 이미지 깨짐

경로 설정이 잘못되었던 것이었습니다...

강의에서 static.images 파일에 이미지 저장하라고 해서 했는데

static 파일안에 images 파일을 만들어서 저장하라는 말이었습니다.

 

이럴수가.

 

 

개인 과제

유스케이스(usecase) 만들기

- 관련 자료

 

유스케이스

주어진 자료를 바탕으로 유스케이스 작성을 해보았습니다.

 

 

이게 맞아....??

 

일단 수업에서 한 실습 자료를 바탕으로 기능 추가 구현을 해보도록 하겠습니다.

혼자공부하는자바 패키지와 접근제한자

 

  • 패키지: 파일 시스템의 폴더 기능 + 클래스를 유일하게 만들어주는 식별자 
  • 패키지가 다르면 클래스 이름이 동일해도 다른 클래스로 인식

 

상위패키지.하위패키지.클래스

 

패키지 선언

package 상위패키지.하위패키지;

public class ClassName{ ... }
  • 패키지는 클래스의 일부
  • 패키지 이름
    • 숫자로 시작해서는 안되고 _, $를 제외한 특수 문자 사용 x
    • java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용 x
    • 모두 소문자로 작성하는 것이 관례

 

import문

  • import문으로 해당 패키지의 클래스 또는 인터페이스를 가져와 사용할 것을 컴파일러에게 알려줌

 

import 상위패키지.하위패키지.클래스이름;

import 상위패키지.하위패키지.*;

 

  • *를 이용해서 해당 패키지에 소속된 클래스들을 사용할 것을 알려줌
  • import문은 개수에 제한이 없음
  • 상위 패키지를 import했다고 하위까지 import되는 것은 아님

 

 

접근 제한자(Access Modifier)

  • public : 외부 클래스가 자요롭게 사용
  • protected: 같은 패키지 또는 자식 클래스에서 사용할수 있음
  • private: 외부에서 사용 x
  • default: 같은 패키지에 소속된 클래스에서만 사용

 

클래스의 접근 제한

//defalut 접근 제함

class 클래스 { ... }



//public 접근 제한

public class 클래스 { ... }

public 접근 제한

  • 클래스를 다른 개발자가 사용할수 있도록 라이브러리 클래스로 개발한다면 반드시 public 접근 제한을 갖도록 해야함
  • 인터넷으로 배포되는 라이브러리 클래스도 모두 public 접근 제한을 가지고 있음

 

생성자의 접근 제한

  • public 접근 제한: 모든 패키지에서 제한없이 생성자 호출
  • protected 접근 제한 : 같은 패키지에 속하는 클래스에서 생성자 호출 가능; 다른 패키지여도 해당 클래스의 자식클래스라면 생성자호출가능
  • default 접근 제한: 같은 패키지 내에 제한없이 생성자 호출 가능
  • private 접근 제한: 오직 클래스 내부에서만 생성자 호출하고 객체를 만들수 있음

 

필드와 메소드의 접근 제한

//필드 선언

[public | protected | private ] [static] 타입 필드;



/메소드 선언

[public | protected | private ] [static] 리턴 타입 메소드(..) { ... }

 

  • public : 제한 없음
  • protected: 다른 패키지에 속하는 클래스에서 필드와 메소드를 사용가능; 다른 패키지 클래스가 자식 클래스라명 사용 가능
  • default: 같은 패키지 제한 없고 다른패키지 x
  • private: 오직 클래스 내부에서만 사용

 

Getter와 Setter 메소드

  • 일반적으로 객체 지향 프로그래밍에서는 객체의 필드를 객체 외부에서 직접적으로 접근하는 것을 막는다
  • 외부에서 마음대로 변경할 경우 객체의 무결성이 깨질 수 있기 때문
  • 메소드를 필드를 변경하는 방법을 선호
  • 필드는 외부에서 접근할 수 없도록 막고 메소드는 공개해서 외부에서 메소드를 통해 필드에 접근하도록 유도
  • 메소드는 매개값을 검증해서 유효한 값만 객체의 필드로 저장할 수 있기 때문 -> Setter
  • 외부에서 객체의 데이터를 읽을 때도 메소드를 사용 -> Getter
  • 클래스를 선언할 때 가능하다면 필드를 private으로 선언해서 외부로부터 보호하고 필드에 대한 Setter와 Getter 메소드를 작성해서 필드값을 안전하게 변경/사용
  • Getter: 필드의 값을 외부로 리턴해주는 메소드
  • Setter: 외부에서 값을 받아 필드를 변경하는 메소드

 

Getter와 Setter

  • 외부에서 필드값 read only로 하려면 Getter 메소드만 선언 또는 Setter 메소드에 private 접근 제한 선언

 

 

확인문제

  • 접근 제한자는 클래스, 필드, 생성자, 메소드의 사용을 제한한다. (o)
  • public 접근 제한은 아무런 제한 없이 해당 요소를 사용할 수 있게 한다. (o)
  • default 접근 제한은 해다 클래스 내부에서만 사용을 허가한다. (x)
  • 외부에서 접근하지 못하도록 하려면 private 접근 제한을 해야 한다. (o)

 

 

 

 

 

 

혼자공부하는자바 인스턴스 멤버와 정적 멤버

 

  • 클래스 멤버를 인스턴스 멤버와 정적 멤버로 구분해서 선언할 수 있음
  • 인스턴스 멤버: 객체마다 가지고 있는 멤버
  • 정적 멤버: 클래스에 위치시키고 객체들이 공유하는 멤버

 

인스턴스 멤버와 this

  • 인스턴스 멤버: 객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드
  • 인스턴스 멤버 선언
public class Car {

//필드

int gas;



//메소드

void set Speed(int speed) {....}

}
  • 메모리 블록 내부에 인스턴스 필드 등이 사용되는 경우가 있음
  • 인스턴스 필드가 사용되면 메소드는 객체 없이 실행할 수 없음

 

 

this

  • 객체 내부에서 인스턴스 멤버에 접근하기 위해 this를 사용
  • 객체는 자신을 this라고 함
  • 주로 생성자와 메소드의 매개 변수 이름이 필드와 동일한 경우, 인스턴스 멤버인 필드임을 명시할 때 사용

 

정적 멤버와 static

  • 정적 멤버: 클래스에 고정된 멤버로 객체를 생성하지 않고 사용할 수 있는 필드와 메소드
  • 정적 멤버 선언 -> static 키워드를 추가적으로 붙이면 됨
public class 클래스 {

//정적 필드

static 타입 필드 [=초기값];



//정적 메소드

static 리턴 타입 메소드( 매개변수선언, ...) {...}

}

 

  • 클래스에 고정된 멤버로 클래스 로더가 클래스(바이트 코드)를 코딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리
  • 객체마다 가지고 있어야 할 데이터라면 인스턴스 필드로 선언
  • 객체마다 가지고 있을 필요가 없는 공용 데이터라면 정적 필드로 선언

 

정적 멤버 사용

  • 클래스 이름과 함께 도트(.) 연산자로 접근
클래스.필드;

클래스.메소드(매개값, ...);

 

정적 메소드 선언 시 주의할 점

  • 정적 메소드를 선언할 때는 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없음
  • this 키워드도 사용 불가능
  • 정적 메소드에서 인스턴스 멤버를 사용하고 싶다면 객체를 먼저 생성하고 참조 변수로 접근

 

싱글톤(Singleton)

  • 전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우
  • 클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 막아야 함 -> private 접근 제한자 붙이기
  • 자신의 타입인 정적 필드를 선언하고 자신의 객체를 생성해 초기화
  • 외부에서 호출할 수 있는 정적 메소드 getInstance() 선언하고 정적 필드에서 참조하고 있는 자신의 객체를 리턴

 

public class 클래스 {

//정적 필드

private static 클래스 singleton = new 클래스();



//생성자

private 클래스() {}



//정적 메소드

static 클래스 getInstance() {

   return singleton;

}

}

 

  • 외부에서 객체를 얻는 유일한 방법은 getInstance() 메소드를 호출하는 방법 -> 단 하나의 객체만 리턴

 

final 필드와 상수

  • final 필드: 초기값이 최종적인 값이 되어서 프로그램 실행 도중에 수정할 수 없음
final 타입 필드 [=초기값];
  • final 필드의 초기값을 주는 방법
    • 필드 선언시 주는 방법
    • 생성자에서 주는 방법

 

상수 constant

  • final 필드는 상수가 아님 
  • 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 때문에 상수 x
  • 상수는 static이면서 final 이어야함

 

static final 타입 상수= 초기값;
  • 상수 이름은 모두 대문자로 작성
  • 혼합된이름은 (_)로 연결

 

확인문제

  • 정적 멤버는 static으로 선언된 필드와 메소드를 말한다 (o)
  • 인스턴스 필드는 생성자에서 초기화될 수 없다.(x)
  • 정적 필드와 메소드는 객체 생성 없이 클래스를 통해 접근할 수 있다. (o)
  • 인스턴스 필드와 메소드는 객체를 생성하고 사용해야 한다 (o)
  • final 필드와 상수는 초기값이 저장되면 값을 변경할 수 없다 (o)
  • final 필드와 상수는 생성자에서 초기화 될 수 있다. (x)
  • 상수의 이름은 대문자로 작성하는 것이 관례이다 (o)
  • 상수는 객체 생성없이 클래스를 통해 사용할 수 있다 (o)

2주차

벌써 부트캠프를 정식으로 시작한지 2주가 지났습니다. pre-onboarding 주차까지 합치면 3주가 흘렀습니다.

 

이번주는 객체지향언어인 자바에 대해 더 알아가는 시간을 가졌습니다. 이제 시작한 초짜인 저는 걷기반에 들어가 알고리즘 공부를 하면서 자바 문법과 다양한 메소드를 알게 되었습니다. 따로 자바에 대한 공부를 하라고 하지는 않았지만 남궁성의 자바의 정석 유튜브 강의를 챕터 6까지 들었습니다.

 

 

'부트캠프/자바의 정석' 카테고리의 글 목록

프로 개발자가 되기 위한 핏짜의 항해 기록

pizzathedeveloper.tistory.com

 

객체지향언어로 각각 객체, 메소드를 따로 만들고 필요할 때마다 불러서 쓰는 개념에 대해 배웠는데 금요일에 제출해야했던 SA 과제는 좀 버겁긴 했습니다. 생각대로 작동이 안되서 다시 한번 손 볼 필요가 있습니다.

 

 

TIL 221125 객체지향 과제

생각대로 되지는 않았고 고쳐야 하는 곳이 많지만 일단은 이렇게 올렸습니다. 버스 정원이 왜 저따구로 되는지 모르겠지만 다시 고치러 돌아오도록하겠습니다. 일단 Spring이랑 자바 강의부터 마

pizzathedeveloper.tistory.com

 

Spring boot 시작

아직 객체 지향을 완벽히 다룰 수가 없는데 스프링을 시작하려니 머리가 아픕니다.

아직까지는 강의 코드를 복붙하고 있는데 본격적으로 프로젝트를 들어가기 전에 자바를 많이 공부해야할 듯 합니다. 자바 공부를 하는 시간이 커리큘럼에 더 많이 배정되면 어땠을까 하는 아쉬움이 있습니다. 이제 ABCD를 배우고 있는데 갑자기 영어로 레포트 쓰라고 하는 느낌입니다.

 

 

 

TIL Spring boot 1주차 javax 에서 jakarta로 업데이트 221126

항해99에서 제공해주는 강의는 기초적인 내용은 겉햟기로 지나가는 경우가 많은 것 같다. 시간을 들여서 깊이 공부해야하는 부분을 하면서 "마! 알아서 익혀라!"라는 식이거나 "마! 이미 너는 알

pizzathedeveloper.tistory.com

 

생각보다 시간이 빨리가는 느낌입니다. 이제 연말이라 한해의 끝이 다가오는데 연말 분위기에 휩쓸리지 않고 월드컵에도 휩쓸리지 말고 집중해서 개발자가 되는 것에 최선을 다하려고 합니다. 

 

같은 관심사와 목표를 공유하는 좋은 조원들을 많이 알게 되서 기분 좋은 한주 였습니다. 

 

항해99에서 제공해주는 강의는 기초적인 내용은 겉햟기로 지나가는 경우가 많은 것 같다. 시간을 들여서 깊이 공부해야하는 부분을 하면서 "마! 알아서 익혀라!"라는 식이거나 "마! 이미 너는 알고 있을 거다!"라고 하고 진행되는 부분이 상당히 있다. 다행히 나는 공부를 더 많이 하는 거에 대해서는 불만이 없지만 빨리 진도를 따라가야 하는 것에 대해서는 조금 압박이 있긴 하다. 그래도 새로운 것을 배우는 것은 늘 신나는 일이다. 규칙을 알면 간단한데 모르는 규칙이 많아서 얼렁뚱땅하다 보니 에러가 나는듯하다. 규칙을 알게 되서 나오는 "Ah-ha!" 모먼트가 많다.

주특기는 Spring

어제 드디어 Spring 강의가 지급되었고 본격적인 주특기 주차가 시작되었다. 아직 객체지향이라는 개념이 어색한데 SQL 배우는 중입니다.

 

ERD 예시

오늘의 Error

javax가 안뜨고 jakarta가 뜨길래 무슨 오류인가 했더니 Spring 3.0버전 부터는 jakarta로 업그레이드 되었다고 합니다.

관련 문서: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes

 

GitHub - spring-projects/spring-boot: Spring Boot

Spring Boot. Contribute to spring-projects/spring-boot development by creating an account on GitHub.

github.com

 

Jakarta

 

 

자바의 정석 기초편

접근 제어자(access modifier)

  • private: 같은 클래스 내에서만 접근이 가능
  • (default): 같은 패키지 내에서만 접근이 가능
  • protected: 같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능
  • public: 접근 제한이 전혀 없음
  • public > protected > (default) > private

 

캡슐화와 접근 제어자

 

 

  • 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해서

class Time{
    private int hour; //0~23 사이의 값을 가져야 함.
    private int minute;
    private int second;


    public void setHour(int hour){
        if(isValidhour(hour)) return;
        this.hour = hour;
    }

    //매개 변수로 넘겨진 hour가 유효한지 확인해서 알려주는 메서드
    private boolean isValidhour(int hour){
        return hour <0 || hour >23;
    }

    public int getHour() { return hour; };
}


public class TimeTest {
    public static void main(String[] args) {
        Time t = new Time();
//        t.hour = -100;
        t.setHour(21); //hour의 값을 21로 변경
        System.out.println(t.getHour());
        t.setHour(100);
        System.out.println(t.getHour());

    }
}

 

  • 접근제어자의 범위는 최소화 시키는 것이 좋다

다형성(polymorphism)

  • 여러가지 형태를 가질 수 있는 능력
  • 조상 타입 참조 변수로 자손 타입 객체를 다루는 것
  • 타입 불일치해도 괜찮다
  • 객체와 참조변수의 타입이 일치할 때와 일치하지 않을 때의 차이
  • 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없음
  • 참조변수의 타입은 인스턴스의 타입과 일치하는 것이 보통이지만 일치 하지 않을 수도 있음
  • 참조변수가 조상타입일 때와 자손 타입일 때의 차이
    • 참조변수로 사용할 수 있는 멤버의 갯수가 달라짐
  • 자손타입의 참조변수로 조상 타입의 객체를 가리킬 수 없음

 

참조변수의 형변환

  • 사용할 수 있는 멤버의 갯수를 조절하는 것
  • 조상 자손 관계의 참조변수는 서로 형변환 가능
  •  
public class Ex7_7 {
    public static void main(String[] args) {
        Car car = null;
        FireEngine fe = new FireEngine();
//        FireEngine fe2 = null; //car = (Car)fe; 에서 형변환이 생략됨

        FireEngine fe2 = (FireEngine)car; // 조상 -> 자손으로 형변환
        Car car2 = (Car)fe2;              // 자손 -> 조상으로 형변환
//        car2.drive(); //NullPointerException 발생.
//        fe.water();
//        fe2 = (FireEngine) fe; //자손타입 <- 조상타입, 형변환 생략 불가
//        fe2.water();
    }
}

class Car{
    String color;
    int door;

    void drive() {
        System.out.println("drive, Brrrr~~");
    }

    void stop() {
        System.out.println("stop!!");
    }
}

class FireEngine extends Car {
    FireEngine() {
    }

    void water() {
        System.out.println("water!!!");
    }
}



자바의 정석 기초편

패키지(package)

  • 서로 관련된 클래스의 묶음 (Java8 기준 약 4000개 클래스)
  • 클래스는 클래스 파일(*.class), 패키지는 폴더, 하위 패키지는 하위 폴더
  • 클래스의 실제 이름(full name)은 패키지를 포함(java.lang.String)
  • rt.jar는 클래스들을 압축한 파일 (rt = runtime)

 

패키지의 선언

  • 패키지는 소스파일의 첫번째 문장으로 단 한번 선언
  • 같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게 된다
  • 패키지 선언이 없으면 이름없는(unnamed) 패키지에 속하게 된다

 

클래스 패스(classpath)

  • 클래스 파일(*.class)의 위치를 알려주는 경로 (path)
  • 환경변수 classpath로 관리하며, 경로간의 구분자는 ';'를 사용
  • classpath(환경변수)에 패키지의 루트를 등록해줘야 함

 

import문

  • 클래스를 사용할 때 패키지 이름을 생략할 수 있다.
  • 컴파일러에게 클래스가 속한 패키지를 알려준다
  • java.lang(자바의 기본패키지)의 클래스는 Import하지 않고도 사용할 수 있다.
    • String, Object, System, Thread ...

 

import문의 선언

  • import문을 선언하는 방법
    • import 패키지명.클래스명;
    • import 패키지명.*;
  • imprt문은 패키지문과 클래스 선언 사이에 선언

import문의 선언

  • 이름이 같은 클래스가 속한 두 패키지를 import할 때는 클래스 앞에 패키지 명을 붙여줘야 한다

 

static import문

  • static 멤버를 사용할 때 클래스 이름을 생략할 수 있게 해준다

static import문

 제어자(modifier)

  • 클래스와 클래스의 멤버(멤버 변수, 메서드)에 부가적인 의미 부여
  • 접근 제어자 public, protected, (default), private
  • 그 외: static, final, abstract, native, transient, synchronized, volatiled, strictfp
  • 하나의 대상에 여러 제어자를 같이 사용 가능(접근제어자는 하나만)

 

static -클래스의, 공통적인

 

static

 

final - 마지막의, 변경될 수 없는

  • 부모가 될 수 없는 마지막 클래스 final

final

 

abstract - 추상의, 미완성의

  • 미완성 설계도 -> 제품생산불가
  • 추상클래스를 상속받아서 완전한 클래스로 만들고 객체 생성가능

abstract

 

자바의 정석 상속

상속(Inheritance)

  • 기존의 클래스로 새로운 클래스를 작성하는 것 (코드의 재사용)
  • 두 클래스를 부모와 자식으로 관계를 맺어주는 것

 

class Parent{}
class Child extends Parent{
...
}
  • 자손은 조상의 모든 멤버를 상속받는다.(생성자, 초기화블럭 제외)
  • 자손의 멤버 개수는 조상보다 적을 수 없다(같거나 많다)

상속

  • 자손의 변경은 조상에 영향을 미치지 않는다.

 

 

포함 관계 

 

  • composite(포함): 클래스의 멤버로 참조변수를 선언하는 것
  • 상속관계 '~은 ~이다.(is-a)'
  • 포함관계 '~은 ~을 가지고 있다.(has-a)' ->대부분 포함관계를 씀

 

클래스 간의 관계 설정하기

class MyPoint{
    int x;
    int y;
}

//class Circle extends MyPoint { //상속
//    int r;
//}

class Circle {
    MyPoint p = new MyPoint();
    int r;
}


public class InheritanceTest {
    public static void main(String[] args) {

        Circle c = new Circle();
        c.p.x =1;
        c.p.y =2;
        c.r = 3;
        System.out.println("c.p.x="+c.p.x);
        System.out.println("c.p.y="+c.p.y);
        System.out.println("c.r="+c.r);
    }

}

 

단일 상속(Single Inheritance)

  • Java는 단일 상속만을 허용한다.
  • 조상은 하나만!
  • 비중이 높은 클래스 하나만 상속관계로, 나머지는 포함관계로 한다.

Object클래스 - 모든 클래스의 조상

  • 부모가 없는 클래스는 자동적으로 Object 클래스를 상속받게 된다.
  • 모든 클래스는 Object에 정의된 11개의 메서드를 상속받는다 (toString(), equals(), hashCode(),...)

 

public class InheritanceTest {
    public static void main(String[] args) {

        Circle c = new Circle();
        System.out.println(c.toString()); //Circle@340f438e
        Circle c2 = new Circle();
        System.out.println(c2.toString()); //Circle@30c7da1e
    }

}
  • toString()을 안써도 똑같이 출력된다.
  • println()의 기능

 

오버라이딩(overriding)

  • 상속받은 조상의 메서드를 자신에 맞게 변경하는 것
  • 선언부는 변경 불가, 내용만 변경가능

오버라이딩

class Point {
    int x;
    int y;
    String getLocation(){
        return "x: "+ x + ", y:" +y;
    }
}

class MyPoint3 extends Point {
    int z;
    //조상의 getLocation()을 오버라이딩
    String getLocation(){
        return "x: "+ x + ", y:" +y+" ,z:"+z;
    }

}

public class OverrideTest {
    public static void main(String[] args) {
        MyPoint3 p = new MyPoint3();
        p.x = 3;
        p.y =5;
        p.z =7;
        System.out.println(p.getLocation());
    }
}

 

오버라이딩의 조건

  • 선언부가 조상 클래스의 메서드와 일치해야 한다.
  • 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
  • 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.

 

오버로딩 vs 오버라이딩

근본적으로 관계 없음

overloading : 기존에 없는 새로운 메서드를 정의하는 것 (new)

overriding: 상속받은 메서드의 내용을 변경하는 것 

 

 

참조변수 super

  • 객체 자신을 가리키는 참조변수, 인스턴스 메서드(생성자)내에만 존재
  • 조상의 멤버를 자신의 멤버와 구별할 때 사용

super() - 조상의 생성자

  • 조상의 생성자를 호출할 때 사용
  • 조상의 멤버는 조상의 생성자를 호출해서 초기화

조상의 생성자

  • 클래스 이름 대신 super를 사용
  • 생성자의 첫 줄에 반드시 생성자를 호출해야 한다.
  • 그렇지 않으면 생성자의 첫 줄에 super();를 삽입한다.(조상의 기본 생성자)

 

 

생각대로 되지는 않았고

고쳐야 하는 곳이 많지만 일단은 이렇게 올렸습니다.

버스 정원이 왜 저따구로 되는지 모르겠지만 다시 고치러 돌아오도록하겠습니다.

일단 Spring이랑 자바 강의부터 마스터하러 갑니다...

 

Transport

public class transport { //상위 클래스 대중교통

    String number; //번호
    int gas = 100; //주유량: 주어진 기본값
    int speed = 0; //속도: 주어진 기본값
    int speedChange; //속도 변경
    String status = ""; //주행 상태
    int passengerMax; //최대 승객 수
    int fee; //요금



    //getter, setter

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public int getGas(int i) {
        return gas;
    }

    public int setGas(int gas) {
        this.gas = gas;
        return gas;
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public int getSpeedChange() {
        return speedChange;
    }

    public void setSpeedChange(int speedChange) {
        this.speedChange = speedChange;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public int getPassengerMax() {
        return passengerMax;
    }

    public void setPassengerMax(int passengerMax) {
        this.passengerMax = passengerMax;
    }

    public int getFee(int now2) {
        return fee;
    }

    public void setFee(int fee) {
        this.fee = fee;
    }



    //메서드

    //운행 시작
    public void start(){
        System.out.println("운행 시작");
    }

    //속도 변경
    public int changeSpeed(int change){
        this.speedChange = change;
        return change;
    }

    //상태 변경
    public String statuschange(String statusChange){
        this.status = statusChange;
        return statusChange;
    }

    //max passenger call
    public int callPassengerMax(){
        return passengerMax;
    }

    //주유 하기
    public int addgas(int addgas){
        return this.gas += addgas;

    }










}

Bus

public class Bus extends transport{

    int passengerNow=0; //현재 승객 수

    Bus() {}; //기본 생성자 선언



    //getter, setter
    public int getPassengerNow() {
        return passengerNow;
    }

    public int setPassengerNow(int passengerNow) {
        this.passengerNow = passengerNow;
        return passengerNow;
    }



    //승객 탑승 가능 여부
    public boolean boardingcheck(){
        return super.callPassengerMax() >= passengerNow;
    }

    //승객수 추가


    //승객 탑승
    //탑승가능체크도 다 따로 케이스 만들어서 해보자
    public int boarding(int passenger) {


        if ((passengerNow += passenger)<= super.callPassengerMax()){
            if (status.equals("운행중")) {

                System.out.println("승객 " + passenger + "명이 탑승하였습니다");
                System.out.println("총 탑승 승객: " + this.passengerNow + "명");
            }


        } else {
        System.out.println("정원이 초과되었습니다.");
        }
    return this.passengerNow;
    }





    // 잔여 승객 수
    public int available(int passengerNow){
        return super.callPassengerMax()-this.passengerNow;
    }

    //버스 요금
    public int busfee(int passengers){
        return this.fee = (passengers*1000);
    }




    //주유 알람
    //연료가 10이상일때 운행 가능
    boolean leftFuel() {
        return this.gas >= 10;
    }

    boolean isRunning() {

        if(!status.equals("운행중")){  //운행 종료 시 차고지행
            super.setStatus("차고지행");
        }

        if(leftFuel()) {    //운행중 연료 체크
            System.out.println("주유량이 " +this.gas + " 남았습니다." );
            return true;
        }

        if(!leftFuel()) {   //주유량이 떨어지면 차고지행
            System.out.println("주유가 필요합니다.");  //경고메세지
            super.setStatus("차고지행");
        }

        return true;
    }

    //속도 변경
    public int speedChange(int speedChange){
        if(leftFuel()){
            speed += speedChange;
        }
        System.out.println("현재 주행속도는 "+speed+"km/hr 입니다.");
        return speed;
        }











}

Taxi

public class Taxi extends transport {

    String destination="";
    int basicDistance;
    int distancetoDes;

    int basicfee =3000; //기본요금

    int passengerNow;

    Taxi() {};

    Taxi(
            String number,
            int gas,
            int speed,
            String destination,
            int basicDistance,
            int distancetoDes,
            int fee,
            int basicfee,
            int passengerMax,
            String status,
            int passengerNow

    ){
        this.number =number;
        this.gas = 100;
        this.speed = 0;
        this.destination = destination;
        this.basicDistance =1000;
        this.distancetoDes = distancetoDes;
        this.fee = 1000;
        this.basicfee = basicfee;
        this.status = "일반";
        this.passengerMax = 4;
        this.passengerNow = passengerNow;

    }

    //getter setter


    public String getDestination() {
        return destination;
    }

    public void setDestination(String destination) {
        this.destination = destination;
    }

    public int getBasicDistance() {
        return basicDistance;
    }

    public void setBasicDistance(int basicDistance) {
        this.basicDistance = basicDistance;
    }

    public int getDistancetoDes() {
        return distancetoDes;
    }

    public void setDistancetoDes(int distancetoDes) {
        this.distancetoDes = distancetoDes;
    }

    public int getBasicfee() {
        return basicfee;
    }

    public void setBasicfee(int basicfee) {
        this.basicfee = basicfee;
    }

    public int getPassengerNow() {
        return passengerNow;
    }

    public void setPassengerNow(int passengerNow) {
        this.passengerNow = passengerNow;
    }



    //메서드

    //택시 요금 계산
    public int finalFee(int distancetoDes){
        int finalFee = basicfee+fee*(distancetoDes-basicDistance);
        System.out.println("최종 요금은 "+finalFee+"입니다.");
        return finalFee;
    }

    //탑승 가능 여부
    public boolean boardingcheck(){
        return passengerNow==0;
    }

    //승객수 추가
    public int addPassenger(int addPassenger){
        this.passengerNow += addPassenger;
        return this.passengerNow;
    }

    //승객 탑승
    public int boarding(int passenger){
        while(boardingcheck()){
            if(status.equals("운행")){
                System.out.println("승객"+addPassenger(passenger)+"명이 탑승하였습니다");
                System.out.println("총 탑승승객: "+passengerNow+"명");
                super.setStatus("운행 중");
            }
            break;
        }
        if(!boardingcheck()){
            int overPassenger = (addPassenger(passenger)+passengerNow)-passengerMax;
            System.out.println(overPassenger+"명 초과");
            System.out.println("탑승 불가");
            super.setStatus("탑승 불가");
        }
        return passengerNow;
    }

}

Operation

import java.util.Scanner;

public class Operation {
    public static void main(String[] args) {


        Scanner scanner = new Scanner(System.in);

        System.out.println("이동수단을 선택해주세요.");
        System.out.println("1. 택시 타기");
        System.out.println("2. 버스 타기");
        System.out.print("선택 >>");

        int option =0;
        option = scanner.nextInt();


        while(true){

            if(option==2){

                Bus bus1 = new Bus(); //새로운 버스 1
                bus1.setNumber("항해99");
                String bus1Number = bus1.getNumber();

                Bus bus2 = new Bus(); //새로운 버스 2
                bus2.setNumber("헤엘666");
                String bus2Number = bus2.getNumber();

                System.out.println("버스 2대 중 선택해주세요.");
                System.out.println("1. 항해99");
                System.out.println("2. 헤엘666");

                int option2 =0;
                option2 = scanner.nextInt();



                if(option2==1){
                        int passnum1 = bus1.getPassengerNow();
                        bus1.setPassengerMax(30);
                        bus1.setStatus("운행중");
                        int pass1 = 2;
                        bus1.boarding(pass1);
                        int passengerNow = bus1.getPassengerNow();
                        System.out.println("잔여 승객 수는 "+ bus1.available(pass1)+"명 입니다.");

                        int busfee1 = bus1.busfee(pass1);
                        System.out.println("버스 요금은 "+busfee1+"원 입니다.");
                    System.out.println();
                    System.out.println();

                    System.out.println("버스 기사가 주유량을 체크합니다.");

                    int gas1 = bus1.setGas(50);
                    bus1.isRunning();

                    System.out.println("버스 기사가 차고지로 향합니다.");
                    bus1.statuschange("차고지행");
                    System.out.println();
                    System.out.println("-----차고지-----");
                    int addgas1 = bus1.addgas(10);
                    System.out.println("주유량이 "+ addgas1+ "로 변했습니다.");

                    bus1.statuschange("운행중");
                    System.out.println();
                    System.out.println("다음 정류장에 도착했습니다.");
                    int pass2 = 45;

                    bus1.boarding(pass2);

                    System.out.println("정원초과여서 정류장을 지나칩니다.");
                    System.out.println();
                    System.out.println("다음 정류장에 도착했습니다.");

                    int pass3 = 5;
                    bus1.boarding(pass3);

                    int now2 = bus1.getPassengerNow();
                    System.out.println(now2);

                    System.out.println("잔여 승객 수는 "+ bus1.available(now2)+"명 입니다.");

                    int busfee2 = bus1.busfee(pass3);

                    System.out.println("버스 요금은 "+busfee2+"원 입니다.");

                    int minusgas = bus1.addgas(-55);
                    System.out.println("주유량이 " + minusgas + "로 변했습니다.");
                    bus1.isRunning();
                    System.out.println(bus1.getStatus());









                    }
                }


            break;

            }


        }


    }

+ Recent posts