com.eomcs.basic.ex01.Exam0173.java
Object 클래스 - clone() : shallow copy

객체 복제 : shallow copy / deep copy

05-기본클래스 / 12 페이지

자동차 객체는 엔진 객체를 포함한다

🔹 의존 관계 (dependency)
Car ------> GasStation
주유할 때 일시적으로 주유소랑 관계를 맺는다.
특정 작업에서만 그 객체를 쓸 때
특정 메서드가 호출될 때 그때 일시적으로 그 객체를 쓴다

🔹 포함 관계 (aggregation)
Car ♢→ Navigation
지속적으로 쓰는 거
LifeCycle이 다르다.

🔹 복합 관계 (composition)
Car ◆→ Engine
포함하는 관계와 포함되는 관계가 생명주기가 같다.
LifeCycle이 같다.
강한 연결

🔹 일반적인 관계 (association)
지속적인 사용

실무에서는 굳이 나누지 않고 association 관계로 함

Object의 clone() 메서드는 접근범위가 protected

clone() 접근범위를 public으로 확장하기 위해서 오버라이딩
public으로 해야 다른 클래스에서 마음대로 호출할 수 있다

Object의 clone() 메서드의 리턴타입은 Object이다.
오버라이딩하여 리턴타입을 Car로 바꿨다.
리턴타입은 Object의 서브타입으로 변경 가능

JVM이 복제를 할 때는 인스턴스를 통째로 복제

Object의 clone() 메서드는
설계 도면에 따라서 필드를 만드는 게 아니라
메모리를 통째로 복제해버림
그대로 통째로!
인스턴스 메모리 통째로 복사

car에 들어 있는 주소와 car2에 들어 있는 주소가 다르다
System.out.println(car == car2); // false

car의 엔진 주소와 car2의 엔진 주소가 같다.
System.out.println(car.engine == car2.engine); // true

car 엔진의 필드 값을 변경한다.
car2 엔진의 필드 값도 변경되었다.

🔹 shallow copy (얕은 복제)
shallow(얕은) copy는 해당 인스턴스의 값을 그대로 복제한다.
해당 인스턴스가 참조(또는 포함)하는 객체는 복제하지 않는다.
이런 방식의 복제를 shallow copy라고 한다.

shallow copy를 해도 될 때도 있지만 deep copy를 해야 되는 경우가 있다.
shallow copy는 포함하는 다른 객체는 복제하지 않는다.
필드값만 그대로 복제한다.

Object의 clone()은 해당 객체의 필드 값만 복제한다.
그 인스턴스 변수가 가리키고 있는 객체는 복제하지 않는다.
이런 방식의 복제를 "shallow copy(얕은 복제)"라 부른다.

그 객체의 인스턴스 변수가 가리키고 있는 객체까지 복제하는 것을
"deep copy(깊은 복제)"라 부른다.
deep copy는 개발자가 직접 clone() 메서드 안에 deep copy를 수행하는 코드를 작성해야 한다.

com.eomcs.basic.ex01.Exam0174.java
Object 클래스 - clone() : deep copy

05-기본클래스 / 13 페이지

Engine 클래스도 복제 기능 활성화함
Engine implements Cloneable
Engine 클래스도 clone() 메서드 오버라이딩함

포함하는 객체까지 복제하는 게 deep copy

포함하고 있는 객체에 대한 복제를 수행하려면 다음과 같이
개발자가 직접 포함하고 있는 객체를 복제하는 코드를 작성해야 한다.

Wrapper 클래스 - primitive type의 값을 객체로 다루기 위함 + 다양한 변환 기능

05-기본클래스 / 14 페이지

com.eomcs.basic.ex02.Exam0210.java

primitive type의 값을 객체로 다루기 위함 + 다양한 변환 기능

자바스크립트는 primitive type 없음. 모든 타입이 객체.
C#도 다 객체.

primitive type           wrapper class
byte(1)                      Byte
short(2)                     Short
int(4)                       Integer
long(8)                      Long
float(4)                     Float
double(8)                    Double              
char(2)                      Character
boolean                      Boolean
(↑ boolean: 단독적으로 쓰일 때는 4byte int 메모리/boolean[] 배열로 할 때는 1byte)

primitive type을 객체로 다뤄야할 때가 있다

valueOf로 객체 생성하기!
valueOf() ← 팩토리 메서드

형변환 해줘야됨..

정수는 두 가지 종류의 리터럴이 있다
100 (4바이트 int 리터럴)
100L (8바이트 long 리터럴)

할당연산자를 사용해서 변수에 저장할 수 있으면 컴파일러가 OK한다.

byte x = 127; // OK
byte x = 128; // 컴파일 에러

원래는 4byte 값을 1byte 메모리에 저장 못 하는데 언제 허용한다?
할당연산자를 사용해서 값을 넣을 때 그때 허용
범위 안에 있으면 허용

Byte.valueOf((byte)100); // OK
Byte.valueOf(100); // 컴파일 에러

(byte)100 ← 4byte 리터럴 중에서 맨 끝에 1byte만 넘겨줘

자바에서 정수는 기본이 4byte이다. 1byte 리터럴은 없다.

🔹 Wrapper 클래스의 인스턴스 생성
Integer obj = new Integer(100) // deprecated
Integer obj2 = Interger.valueOf(100) // 이 방식으로 하기

valueOf() ← 팩토리 메서드

com.eomcs.basic.ex02.Exam0211.java
Wrapper 클래스를 안 쓰면 이렇게 불편하다

primitive type은 Object의 서브 클래스가 아니다.

primitive type을 객체처럼 다룰 수 있도록 만든 문법이다.

wrapper 클래스로 만들면 Object로 받을 수 있어서 편하다

System.out.printf("wrapper value=%s\n", value);
%s ← int든 뭐든 다 문자열로 출력된다

wapper 클래스는 primitive type의 값을 객체로 다룰 수 있게 해준다.
primitive type에 상관없이 Object 타입의 파라미터로 값을 받을 수 있다.

com.eomcs.basic.ex02.Exam0212.java
Wrapper 클래스 - wrapper 객체에 들어 있는 primitive type의 값 꺼내기

Wrapper 객체에서 값 꺼내기

obj.xxxValue()

com.eomcs.basic.ex02.Exam0220.java ~ Exam0224.java
Wrapper 클래스 - 오토박싱(auto-boxing)/오토언박싱(auto-unboxing)

박싱(boxing) : primitive type의 값을 인스턴스에 담는 일
언박싱(unboxing) : 인스턴스의 담긴 primitive 값을 다시 꺼내는 일

auto-boxing / auto-unboxing

05-기본클래스 / 15 페이지

Integer obj = 100;
↓ 컴파일러가 코드를 변경한다.
Integer obj = Integer.valueOf(100)

Integer obj = 100;
               ↓ 컴파일러가 코드를 변경한다. 
              Integer.valueOf(100)

실제 코드가 변경된다.
이것을 "오토 박싱"이라고 부른다.

int v = obj;
↓ 컴파일러가 코드를 변경한다.
int v = obj.intValue()

int v = obj;
         ↓ 컴파일러가 코드를 변경한다. 
        obj.intValue()

컴파일러가 코드를 변경한다.
컴파일러가 obj를 메서드 호출 문장으로 바꿔버린다.
마치 자동으로 인스턴스에서 값을 꺼내는 거 같다.
이것을 "오토 언박싱"이라 부른다.

com.eomcs.basic.ex02.Exam0230.java

new 연산자를 통해 Integer 객체를 생성하면 Heap에 인스턴스를 생성한다.
그래서 같은 값이더라도 다른 인스턴스가 생성된다.

auto-boxing 으로 Wrapper 객체를 생성할 경우,
String 리터럴처럼 상수 풀에 Integer 객체를 생성한다.

상수 풀

This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

-128 ~ 127 범위를 넘어가는 경우 무조건 새 객체를 만든다.

com.eomcs.basic.ex02.Exam0231.java

wrapper 객체를 생성할 때는 new 연산자를 사용하지 말고,
valueOf()나 auto-boxing 기능을 이용하라.
값을 비교할 때는 반드시 equals()를 사용하라!

모든 wrapper 클래스는 String 클래스처럼
상속 받은 Object의 equals()를 오버라이딩 하였다.
즉 인스턴스를 비교하는 것이 아니라 값이 같은지를 비교한다.

결론
String이나 Wrapper 인스턴스의 값을 비교할 때는 무조건 equals()를 사용하라!

Exam0310 ~ 410 알아서 보기
java.util.Date 클래스 - 생성자 활용
java.util.Calendar 클래스 - 생성자 활용

계산기 만들기 - 1단계: 프로젝트 생성

작은 프로젝트를 통해 배운 문법을 정리해 보자!

java -cp bin/main com.eomcs.app1

$ cd git/bitcamp-study
$ mkdir project-app1
$ cd project-app1
$ gradle init

2: application

3: Java

1: no - only one application project

1: Groovy

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] 그냥 엔터

1: JUnit 4

Project name (default: project-app1): 그냥 엔터
Source package (default: project.app1): com.eomcs.app1

계산기 만들기 - 2단계: eclipse IDE로 프로젝트 로딩

기존거 복붙하면 됨
mylist-boot/app/build.gradle에서 필요한 것만 복붙하기

① build.gradle 파일 편집
‐ eclipse 플러그인 추가
‐ JDK 설정 추가
‐ 프로젝트명 설정 추가

② eclipse IDE 설정 파일 생성
$ gradle eclipse
프로젝트 폴더에서 gradle eclipse
app 밑에 .settings, .classpath, .project가 자동으로 생긴다

③ eclipse IDE로 프로젝트 import
app까지 들어가서 import 하기

계산기 만들기 - 3단계 : 명령창에서 계산기 실행하기

🔹 필요 기술
프로그램 아규먼트 다루기 com.eomcs.lang.ex07.Exam05XX
조건문 com.eomcs.lang.ex06.
출력문 com.eomcs.lang.ex99.
연산자 com.eomcs.lang.ex05.
배열 com.eomcs.lang.ex04.

$ java -cp bin/main com.eomcs.app1.App add 100 200
$ java -cp bin/main com.eomcs.app1.App add 100 200
$ 자바프로그램 명령

$ git add .
$ 프로그램명 명령어

계산기 만들기 - 4단계 : 도움말 기능 추가

$ java -cp bin/main com.eomcs.app1.App

프로그램 아규먼트 안 주고 엔터 치면 도움말 나오게 하기
그리고 minus 추가하기

아규먼트가 없으면 빈 배열이 넘어온다.

사용법:
App [명령] [값1] [값2]
명령:
add   [값1] [값2]      더하기 계산을 수행한다. 예) App add 100 200
minus [값1] [값2]      빼기 계산을 수행한다.   예) App minus 100 200
package com.eomcs.app1;

public class App {
  public static void main(String[] args) {
    if (args.length == 0) {
      System.out.println("사용법:");
      System.out.println("App [명령] [값1] [값2]");
      System.out.println("명령:");
      System.out.println("add   [값1] [값2]      더하기 계산을 수행한다. 예) App add 100 200");
      System.out.println("minus [값1] [값2]      빼기 계산을 수행한다.   예) App minus 100 200");
      return;
    }

    if (args[0].equals("add")) {
      int v1 = Integer.parseInt(args[1]);
      int v2 = Integer.parseInt(args[2]);
      System.out.printf("%d + %d = %d\n", v1, v2, (v1 + v2));
    } else if (args[0].equals("minus")) {
      int v1 = Integer.parseInt(args[1]);
      int v2 = Integer.parseInt(args[2]);
      System.out.printf("%d - %d = %d\n", v1, v2, (v1 - v2));
    }
  }
}

C:\Users\JYH\git\bitcamp-study\project-app1\app>java -cp bin/main com.eomcs.app1.App
사용법:
App [명령] [값1] [값2]
명령:
add [값1] [값2] 더하기 계산을 수행한다. 예) App add 100 200
minus [값1] [값2] 빼기 계산을 수행한다. 예) App minus 100 200

C:\Users\JYH\git\bitcamp-study\project-app1\app>java -cp bin/main com.eomcs.app1.App add 100 200
100 + 200 = 300

C:\Users\JYH\git\bitcamp-study\project-app1\app>java -cp bin/main com.eomcs.app1.App minus 100 200
100 - 200 = -100

계산기 만들기 - 5단계 : 키보드로부터 입력받기

$ java -cp bin/main com.eomcs.app1.App
> help
add   [값1] [값2]      더하기 계산을 수행한다.
minus [값1] [값2]      빼기 계산을 수행한다.
> add 100 200
100 + 200 = 300
> add 100
명령어 입력 형식이 옮지 않습니다.
> multiple 100 200
지원하지 않는 연산자입니다.

필요 기술
키보드 입력을 다루는 방법 (lang.ex99)

com.eomcs.lang.ex99.Exam0210.java
키보드 입력 받기 - System.in 과 java.util.Scanner

System.in

java.util.Scanner keyScan = new java.util.Scanner(System.in);

데코레이터가 아님
데코레이터는 같은 부모 밑에 있는 자식이어야 됨
Scanner의 수퍼클래스는 InputStream이 아니다.
항상 데코레이터는 주객체와 같은 조상 밑의 자식이어야 한다.

nextLine() : String
Scanner 도구를 사용하여 키보드로부터 한 줄의 문자열을 가져올 때 사용하는 명령이다.

int v1 = Integer.parseInt(values[1]);
int v2 = Integer.parseInt(values[2]);

↑ 명령어마다 파라미터 개수가 달라질 수 있으므로 중복되어도 어쩔 수 없다.
예를 들어 제곱이나 절댓값을 구하는 명령은 파라미터를 1개만 받는다.

계산기 만들기 - 6단계 : 리팩토링(메서드 활용)

① prompt 기능을 메서드로 분리 → prompt()

  static String prompt() {
    System.out.print("> ");
    return keyScan.nextLine();
  }
String input = prompt();

기능 여전히 잘 동작하는지 확인하기

② 각 명령어 처리 코드를 메서드로 분리
add 명령 → doAdd()
minus 명령 → doMinus()
help 명령 → doHelp()

  static void doHelp() {
    System.out.println("add   [값1] [값2]      더하기 계산을 수행한다.");
    System.out.println("minus [값1] [값2]      빼기 계산을 수행한다.");
    System.out.println("help                   도움말을 출력한다.");
  }
  static void doAdd(String[] values) {
    if (values.length != 3) {
      System.out.println("add 명령어 입력 형식이 올바르지 않습니다.");
      System.out.println("형식: add 값1 값2");
      System.out.println("예) add 100 200");
    } else {
      int v1 = Integer.parseInt(values[1]);
      int v2 = Integer.parseInt(values[2]);
      System.out.printf("%d + %d = %d\n", v1, v2, (v1 + v2));
    }
  }
doAdd(values);

메서드 문법을 이용하여 코드를 기능 단위로 분리
✓ 코드 중복을 줄인다
✓ 유지보수가 쉽다
✓ 재사용성이 높다

계산기 만들기 - 7단계 : 리팩토링(클래스 활용)

① 사용자가 입력한 명령을 객체로 다루기 → Command 클래스 정의
명령어와 값을 사용하기 쉽게 필드에 분리해서 저장한다.

명령어를 다루는 클래스

명령어와 값을 담는다.

  public Command(String name, String[] values) {
    this.name = name;
    for (String value : values) {
      params.add(value);
    }
  }

명령어 꺼내는 메서드

  public String getName() {
    return this.name;
  }

그 값을 꺼내는 getString

  public String getString(int paramIndex) {
    return (String) params.get(paramIndex);
  }

int값으로 변환해서 주는 메서드

  public int getInt(int paramIndex) {
    return Integer.parseInt((String) params.get(paramIndex));
  }

파라미터 몇 개인지 리턴하는 메서드

  public int gerParamSize() {
    return params.size();
  }

빈 문자열도 공백으로 자르면 길이 1 나옴

배열에서 특정한 부분을 잘라낼 수 있음
Arrays.copyOfRange(배열, 시작인덱스, 끝인덱스+1)
System에도 배열 복사하는 메서드 있음

사용자가 입력한 값을 객체로 다룬다
분리해서 저장하겠다
name : 명령어
values : 나머지 값을 values로 받는다

관련 기능을 묶어두는 클래스를 만들어보자

암기해야 되는 대상인지 이해하고 넘어가야 되는 건지

견문을 넓히는 용도

계산기 만들기 - 8단계 : 리팩토링(클래스 활용)

메서드를 분류하는 용도로 클래스 사용

① 역할에 따라 메서드 분류

🔹 사용자 입력을 다루는 메서드
Console 클래스 정의
기존의 prompt() 메서드를 이 클래스로 옮긴다

🔹 명령을 처리하는 메서드
CommandHandler 클래스 정의
doHelp(), doAdd(), doMinus() 메서드를 이 클래스로 옮긴다

전형적인 스태틱 메서드 활용

Command command = Console.prompt();

Console.close();

굉장히 직관적!

CommandHandler 클래스 생성
향후 여러 개 만들 것을 대비해서
지금은 인스턴스 필드 안 쓰지만
지금은 CommandHandler 객체를 여러 개 만들 이유가 없지만
향후 여러 개 만들 때 문제
static은 인스턴스를 여러 개 못 만드니까
지금 당장은 더 큰 이점이 없지만 향후 어떻게 될 지 모름
그래서 대부분은 아주 특별한 거 아니면 기본적으로 인스턴스 메서드로 만듦

static 다 빼주기

static으로 하면 여러 개 찍어낼 수 없음

그냥 처음부터 인스턴스 메서드로 만든다

package com.eomcs.app1;

public class App {


  public static void main(String[] args) {

    Console console = new Console();
    CommandHandler commandHandler = new CommandHandler();

    while (true) {
      Command command = console.prompt();

      if (command.getName().equals("quit") || command.getName().equals("exit")) {
        break;
      } else if (command.getName().equals("")) {
        continue;
      } else if (command.getName().equals("help")) {
        commandHandler.doHelp();
      } else if (command.getName().equals("add")) {
        commandHandler.doAdd(command);
      } else if (command.getName().equals("minus")) {
        commandHandler.doMinus(command);
      } else {
        System.out.println("지원하지 않는 연산자입니다.");
      }
    }

    console.close();
  }

}

네트워크

Standalone Application

설치형 Application의 특징
각각의 컴퓨터에 설치해야 한다.

로컬 컴퓨터 : 어플리케이션을 실행하는 컴퓨터. 사용자가 사용하는 컴퓨터.
리모트 컴퓨터 : 네이버

로컬 컴퓨터의 자원(CPU, RAM, HDD 등)을 사용해서 App. 실행

기능이 변경되면 다시 설치해야 한다.

해결책?
App.을 서버에서 실행하자

Client/Server Application

서버 App : 원격 컴퓨터에서 실행하는 App.(서비스 제공)

ServerApp 서버App에서 작업을 처리
Server Remote

ClientApp
pc1 Local

클라이언트 App: 로컬 컴퓨터에서 실행하는 App (서비스 요청)

서버에서 작업을 처리하고
클라이언트 App에서 사용자와 상호 작용

사용자와 상호작용 = UI 입출력

클라이언트 App에서 UI 입출력 수행

애플리케이션 서버(AS) : 서버에서 실행하는 프로그램
서버 App : 기능을 실행 ← 기능을 추가/변경/삭제할 때 서버쪽만 재설치하면 된다.
요청↑ ↓응답
클라이언트 App

클라이언트와 연결된 소켓
연결

Web + AS = WAS (Spring Boot)

이번주에 배운 거 복습하기

basic.ex01, ex02 다시 보기
oop

0개의 댓글