[비트캠프] 39일차(8주차4일) - Java(변수, 메모리), myapp-01~02
OOP Language 기본 문법
값을 표현하는 문법을 Literal 이라 한다. 예) 123, 123.5, true/false, 'A', "---"
값을 저장하는 문법을 Variables 라 한다. 예) int a; String s;
값을 다루는 문법을 Operators 라 한다. 예) +, -, *, /, %, <, <=, >, >=, &&, ||, ==, !=, <<, >>, >>>
실행 흐름을 제어하는 문법을 Statement 라 한다. 예) 분기문: if ~ else / switch, 반복문: while / for / do ~ while
명령문을 기능에 따라 묶는 문법을 Method 라 한다. 예) void print( ) { - }
관련된 메서드를 묶는 문법을 Class 라 한다. 예) class System { - }
새 데이터 타입을 정의하는 문법이며, Abstract(추상화) 이다.
코드 재사용을 높이는 문법에 Inheritance(상속), Polymorphism(다형성), Encapsulation(캡슐화) 가 있다.
코드 유지보수를 쉽게하는 문법에 Abstract class(추상 클래스), Interface(인터페이스), Nested Class(중첩 클래스) 등 있다.
Varibales
변수 선언 (declaration)
값을 담는 메모리를 생성 준비시키는 문법
선언 방법은 다음과 같다.
데이터타입 변수명;
자바 원시 타입 (primitive data type)
- 정수
- byte 1byte
- short 2byte
- int 4byte
- long 8byte
- 부동소수점
- float 4byte
- double 8byte
- 논리
- boolean 4/1byte
- 문자
- char 2byte
reference type
- 그 밖에
- 클래스
int a; 라 하면 크기가 4byte 인 메모리를 준비하고 이름을 a라 하겠다는 뜻이다. a를 storage location 이라 한다.
a = 100; 에서 = 을 assignment operator (할당연산자, 배정연산자, 대입연산자) 라 한다.
정수 변수
int a = 100; 에서 4byte 정수 메모리 a에 4byte 정수 리터럴 100이 들어간다.
long b = 100; 에서 8byte 정수 메모리 b에 4byte 정수 리터럴 100이 들어간다.
short c = 100; 에서 2byte 정수 메모리 c에 4byte 정수 리터럴 100이 들어간다.
byte d = 100; 에서 1byte 정수 메모리 d에 4byte 정수 리터럴 100이 들어간다.
→ 이 두 개는 예외 조건: 리터럴 값이 메모리 보다 크기가 크더라도 담을 수 있다면 허용한다!
l-value 와 r-value
a = 100;
a = b * 3;
a = plus(100, 200);
에서 왼쪽은 l-value, 오른쪽은 r-value 이다.
100 = a; 로 사용할 수 없다.
왼쪽은 반드시 메모리여야 한다.
오른쪽은 리터럴, 변수, expression, 메서드 호출 등이 올 수 있다.
Statement 내에 Expression이 있고 Expression은 결과 리턴을 한다.
char 변수
char c; 에서 c는 문자코드(UCS2 = UTF-16)를 저장하는 2byte 메모리이다. 0~65535 범위의 값만 허락된다.
short s; 에서 s는 2byte 메모리이다. -32768 ~ +32767 범위의 값만 허락한다.
c = 65; 에서 65는 문자코드를 의미하는 정수값이다. 출력하면 A가 나온다.
s = 65; 에서 65는 정수를 의미하는 값이다. 출력하면 65가 나온다.
System.out.println(c); 는 이렇게 동작한다.
① 현재 프로그램에서 사용하는 폰트 파일을 찾는다 : D2Coding
② D2Coding 폰트 파일에서 65에 해당하는 문자를 찾는다 : A
③ 폰트 파일에서 읽은 A 문자 그림을 출력한다.
' ' 연산자
'A' 를 입력하면 65 를 return 한다. 65 는 A 문자에 대한 UCS2 (= UTF16 = Unicode2) 코드값이다.
char c = 'A'; 하면 65 가 c에 저장된다.
char c2 = 0xB625; 와 c2 = '똥'; 는 동일하다. '똥' 에서 0xB625 = 46629 이다.
c2 = '똥' - 1; 하면 unicode 값에서 -1 하는 것이므로 '똤' 의 unicode 값 46628 이 저장된다.
배열 문법
같은 종류의 메모리를 여러개 생성할 때 사용하는 문법
데이터타입[ ] 변수명; ← 이 방식을 권장
데이터타입 변수명[ ]; ← C style
변수명은 주소를 담는 변수 = reference 변수
변수명 = new 데이터타입[수량]; 으로 데이터타입의 수량에 따른 메모리 공간 할당한다.
데이터타입[ ] 변수명 = new 데이터타입[수량]; 으로 선언과 동시에 할당해도 된다.
배열 선언
int a1, a2, a3, a4; 하면 각 변수명으로 4byte 짜리 메모리 공간이 확보된다. 이는 변수들이 개별적이기 때문에 연속된 메모리가 아니다!
int[ ] a; 는 int 배열의 변수명으로 a를 사용하겠다 선언이다. 크기는 알 수 없으나 다른 메모리의 주소를 담을 만큼 적당한 크기이다.
a = new int[4]; 하면 메모리 주소 3744가 a에 저장되고 int형 4byte 공간 4개가 생성되는데 이는 연속된 메모리다. JVM은 Heap 영역에 연속된 int 타입의 메모리를 준비한다. 리턴 값은 준비한 메모리의 시작 주소이다. new 명령을 통해 준비한 메모리는 "인스턴스(instance)" 라 한다. 인스턴스의 예) int 배열의 인스턴스
메모리 주소 3741, 3742, 3743 까지 다른 곳에서 사용하다가 3744 부터 int 배열의 인스턴스가 사용한다.
RAM과 메모리 주소
물리적인 RAM은 칩이 여러개 있어도 OS에서는 하나로 합쳐서 1차원으로 인식한다.
App. 을 실행하면 OS가 App이 사용할 메모리를 제공한다. App1, App2, App3 이 사용하는 메모리 공간이 각각 다르다.
App. 의 실행이 종료되면 그 App. 이 사용했던 메모리 영역은 다른 App. 이 사용할 수 있도록 잠금이 해제된다.
일반적으로 App 이 사용하는 메모리 공간은 OS로 부터 사용을 승인 받은 메모리이다. 이는 둘로 나뉘는데 명령문을 보관(code segment), 데이터를 보관(data segment) 하는 곳으로 나뉜다.
메모리는 연속된 바이트들의 1차원 배열이다. 이해를 돕기위해 보통 2차원으로 표현한다.
프로그램 종료로 메모리를 회수해도 값을 0으로 초기화하지는 않는다. 이 값들이 있으면 메모리에 쓰레기 값이 들어있다 한다.
JVM과 메모리
JVM (java.exe)이 메모리를 요청하면 OS 가 승인한 메모리를 사용한다.
이 메모리 영역을 나누어 사용한다.
- Method Area
- 클래스 코드를 둔다.
- static 변수를 둔다.
- Heap
- new 명령으로 준비한 변수를 둔다.
- Constant Pool
- 상수값을 둔다
- Stack
- 스레드(thread) 별로 stack 영역을 관리 (1 thread = 1 stack). 메서드가 실행될 때, 로컬 변수를 둔다.
배열 메모리에 접근
int[ ] arr = new int[4]; 하면 메모리 공간이 확보되고 주소 200 이 arr에 저장된다.
arr[0] = 100;
arr[1] = 200;
arr[2] = 300;
arr[3] = 400;
arr[4] = 500; 실행 오류(runtime exception) = ArrayIndexOutOfBoundsException
배열 reference 변수와 null
배열 reference 변수 : 배열 인스턴스의 주소를 담는 변수
int[ ] arr = new int[4]; 하면 인스턴스의 메모리 공간이 확보되고 주소 200이 arr에 들어간다. 인스턴스의 각 변수는 무조건 0으로 초기화 된다.
레퍼런스의 주소를 초기화시킬 때 arr = 0; 을 할 수 없다. arr = null; 해야한다. 그러면 arr이 가리키는 주소는 null 이 된다.
int a; 에서 로컬 변수는 자동으로 초기화되지 않는다. 반드시 직접 초기화시켜야 한다.
데이터 타입별 초기화 값은 다음과 같다.
byte = 0
short = 0
int = 0
long = 0L
float = 0.0f
double = 0
boolean = false
char = '\u0000'
배열 인스턴스와 가비지(garbage)
가비지 : 인스턴스의 주소를 잃어버려 사용할 수 없는 상태의 인스턴스
int[ ] arr = new int[4]; 하면 메모리 arr에 주소 200이 저장된다. 메모리 주소 200에 공간이 확보되고 값 0으로 초기화된다.
arr[0] = 300;
arr[2] = 400; 하면 arr의 주소 200의 인덱스 0, 2에 값이 저장된다.
arr = new int[3]; 하면 메모리 arr에 기존 주소 200이 삭제되고 주소 2700이 저장되고 값 0으로 초기화된다.
arr[0] = 70; 하면 arr의 주소 2700의 인덱스 0에 값이 저장된다.
이때 주소 200의 인스턴스는 주소를 잃어버려 더이상 사용할 수 없는 인스턴스 "Garbage"이다.
이는 "Garbage Collector"에 의해 자동으로 메모리 해제된다. 메모리 해제: 잠금장치를 풀어서 다른 용도로 재사용 할 수 있게 만든다.
Garbage Collector가 언제 동작할까?
① 메모리 부족할 때
② CPU 한가할 때
가비지와 레퍼런스 카운트
JVM은 레퍼런스 카운트 관리를 한다.
int[ ] arr1 = new int[3]; 하면 메모리 공간이 확보되고 주소 200이 arr1에 저장된다. 주소 : 참조수 = 200 : 1 이 저장된다.
int[ ] arr2; arr2 = arr1; 하면 arr2에 arr1의 주소 200이 저장된다. 주소 : 참조수 = 200: 2 이 저장된다.
int[ ] arr3 = new int[2]; 하면 메모리 공간이 확보되고 주소 300이 arr3에 저장된다. 주소 : 참조수 = 300 : 1 이 저장된다.
arr1 = new int[4] 하면 메모리 공간이 확보되고 주소 400이 arr1에 저장된다. 주소 : 참조수 = 400 : 1 이 저장된다.
arr2 = arr3; 하면 arr3의 주소 300이 arr2에 저장된다. 기존의 주소값 200은 삭제된다. 주소 : 참조수 = 300 : 2 및 주소 : 참조수 = 200 : Garbage 가 저장된다.
System.out.println()
값.println(값) 에서 앞부분 값은 작업수행에 필요한 값이다. println은 작업을 수행하는 역할을 한다. 괄호 안의 값은 작업할 때 사용할 값이다.
System.out.println(no); 에서 System은 도구함(class)이다. out은 변수(field)이다. println은 작업자 = 연산자(method = function) 이다. (no) 는 파라미터이다.
System.out 은 시스템 표준 출력 장치로 Eclipse의 console 창을 의미한다.
"번호: " + no 에서 no는 int 형이다. 이때 문자열과 +로 인해 no의 문자열 생성된다.
"번호: " + "1" 에서 새 문자열 생성해서 "번호: 1" 이 된다. 실행 과정에서 임시로 문자열이 생성된다.
이 값을 System.out.println( ) 괄호 내부로 전달한다.
System.out.printf("번호: %d\n", no); 하면 no 변수의 값이 %d에 삽입된다.
\n는 escape character로 문자가 아니라 명령이다.
키보드 입력
new Scanner(System.in); 에서 System.in은 키보드 정보가 들어있는 변수이다. 키보드 데이터 : 사용자가 키보드의 키를 눌렀을 때 키상태(어떤 키를 눌렀고, 이때 caps lock의 상태는 무엇인지, shift 키의 상태는 무엇인지, ...)
new Scanner는 파라미터로 주어진 입력장치에서 데이터를 읽는 일을 하는 객체이다. 파라미터로 넘겨 받은 정보를 가공해서 입력을 다룰때 사용할 메모리를 준비한다. 키보드에서 읽은 바이트를 가공한 문자 데이터(UTF-16 (UCS2))
keyScanner.nextLine(); 은 한 줄의 문자열을 읽는다. keyScanner (값)의 문자 데이터를 받아서 한 줄의 문자열(사용자가 엔터를 칠때까지 읽어들인다)을 만든 후 리턴한다.
이를 정수 값으로 바꾸고 싶다면 Integer.parseInt() 괄호에 넣는다. 그러면 정수가 리턴된다.
※ 객체 = 데이터(값)가 저장된 메모리
객체와 메서드, 파라미터
keyScanner.nextLine() 에서 keyScanner는 값이고, 문자열을 읽을 도구 정보(키보드) 이다. 키보드에서 읽은 문자 데이터이다.
nextLine() 은 정보를 사용해서 작업 수행한다. 이 괄호에는 값이 들어가지 않는다.
Integer.parseInt(문자열값) 에서 Integer는 도구함 = class 이다. parseInt는 파라미터로 받은 값을 사용하여 작업 수행한다.
객체.메서드(파라미터, 파라미터, ...) 하면 ⇒ 외부 호출자에게 작업 결과 리턴한다.
객체의 값을 메서드가 읽고 파라미터 값 사용해서 작업 수행해서 객체에 작업 결과 저장한다.
작업 수행한 결과를 파라미터에 값 저장하는게 아니라 파라미터에 들어있는 값은 사용만 한다.
데이터가 있어야 호출 가능한 메서드 : 인스턴스 메서드
데이터 없어도 호출 가능한 메서드 : 클래스 메서드
실습 프로젝트 소스
app-01 부터 모든 소스는 아래 폴더에 백업되어 있으니 참고 한다.
C:\Users\bitcamp\git\bitcamp-ncp-teacher\myapp
01. 프로젝트 폴더 준비
### 01. 프로젝트 준비
- Gradle 빌드 도구를 이용하여 프로젝트 폴더를 준비하는 방법
02. 리터럴과 콘솔 출력
### 02. 리터럴과 콘솔 출력
- 실행할 수 있는 클래스를 만드는 방법 = main() 엔트리 포인트 이해
- 콘솔 출력 명령문을 작성하는 방법
- 리터럴 문법을 활용하는 방법
- CLI(command line interface) 환경에서 클래스를 실행하는 방법
package bitcamp.myapp;
public class App {
public static void main(String[] args) {
int no = 1;
String name = "홍길동";
String tel = "010-11110-2222";
String postNo = "06656";
String basicAddress = "서울시 서초구 반포대로23길";
String detailAddress = "101동 201호";
boolean working = false;
char gender = 'M'; // M(남자), W(여자)
byte level = 0; // 0(비전공자), 1(준전공자), 2(전공자)
String createdDate = "2022-12-29";
System.out.printf("번호: %d\n", no);
System.out.printf("이름: %s\n", name);
System.out.printf("전화: %s\n", tel);
System.out.printf("우편번호: %s\n", postNo);
System.out.printf("주소1: %s\n", basicAddress);
System.out.printf("주소2: %s\n", detailAddress);
System.out.printf("재직자: %s\n", working ? "예" : "아니오");
System.out.printf("성별: %s\n", gender == 'M' ? "남자" : "여자");
String levelTitle;
switch (level) {
case 0: levelTitle = "비전공자"; break;
case 1: levelTitle = "준전공자"; break;
default: levelTitle = "전공자";
}
System.out.printf("전공: %s\n", levelTitle);
System.out.printf("가입일: %s\n", createdDate);
}
}
조언
*
과제
저녁 학습
- com.eomcs.lang.ex05 (연산자 편)
- com.eomcs.lang.ex06 (흐름제어문 편)