com.eomcs.basic.ex01.Exam0173.java
Object 클래스 - clone() : shallow 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
포함하고 있는 객체에 대한 복제를 수행하려면 다음과 같이
개발자가 직접 포함하고 있는 객체를 복제하는 코드를 작성해야 한다.
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 값을 다시 꺼내는 일
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 클래스 - 생성자 활용
작은 프로젝트를 통해 배운 문법을 정리해 보자!
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
기존거 복붙하면 됨
mylist-boot/app/build.gradle에서 필요한 것만 복붙하기
① build.gradle 파일 편집
‐ eclipse 플러그인 추가
‐ JDK 설정 추가
‐ 프로젝트명 설정 추가
② eclipse IDE 설정 파일 생성
$ gradle eclipse
프로젝트 폴더에서 gradle eclipse
app 밑에 .settings
, .classpath
, .project
가 자동으로 생긴다
③ eclipse IDE로 프로젝트 import
app까지 들어가서 import 하기
🔹 필요 기술
프로그램 아규먼트 다루기 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 .
$ 프로그램명 명령어
$ 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
$ 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개만 받는다.
① 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);
메서드 문법을 이용하여 코드를 기능 단위로 분리
✓ 코드 중복을 줄인다
✓ 유지보수가 쉽다
✓ 재사용성이 높다
① 사용자가 입력한 명령을 객체로 다루기 → 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로 받는다
관련 기능을 묶어두는 클래스를 만들어보자
암기해야 되는 대상인지 이해하고 넘어가야 되는 건지
견문을 넓히는 용도
메서드를 분류하는 용도로 클래스 사용
① 역할에 따라 메서드 분류
🔹 사용자 입력을 다루는 메서드
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();
}
}
네트워크
설치형 Application의 특징
각각의 컴퓨터에 설치해야 한다.
로컬 컴퓨터 : 어플리케이션을 실행하는 컴퓨터. 사용자가 사용하는 컴퓨터.
리모트 컴퓨터 : 네이버
로컬 컴퓨터의 자원(CPU, RAM, HDD 등)을 사용해서 App. 실행
기능이 변경되면 다시 설치해야 한다.
해결책?
App.을 서버에서 실행하자
서버 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