백기선 선생님의 자바 스터디 2주차 과제다
github.com/whiteship/live-study/issues/2
- 프리미티브 타입 종류와 값의 범위 그리고 기본 값
- 프리미티브 타입과 레퍼런스 타입
- 리터럴
- 변수 선언 및 초기화하는 방법
- 변수의 스코프와 라이프타임
- 타입 변환, 캐스팅 그리고 타입 프로모션
- 1차 및 2차 배열 선언하기
- 타입 추론, var
가장 먼저 변수에 대해 간단히 보자
변수란?
: 하나의 값을 저장할 수 있는 메모리 공간이다.
프로그램을 작성할 때 여러 값들이 사용된다. 이런 값들을 저장하는 곳이 변수이다.
변수에는 다양한 타입의 값을 저장할 수 없고, 한가지 타입의 값만 저장할 수 있다.
모든 변수에는 타입이 있으며, 타입에 따라 저장할 수 있는 값의 종류와 범위가 달라진다. 처음 선언된 타입은 이후 변경할 수가 없다.
변수 선언할 때에 어떤 타입으로 할지 잘 선택해야한다.
프리미티브 타입 종류와 값의 범위 그리고 기본 값
프리미티브(primitive) 타입이란 기본형 혹은 원시형이라고도 불리운다.(회사 혹은 개발자마다 말하는 용어가 다르나 뜻은 같으니 헷갈리지 않도록 주의하자) 이러한 타입은 정수, 실수, 문자, 논리 리터럴을 직접 저장하는 타입을 말한다.
값의 종류 | 기본 타입 | 메모리 사용 크기 | 저장되는 값의 범위 | 기본 값 | |
정수 | byte | 1 byte | 8 bit | -128 ~ 127 | 0 |
short | 2 byte | 16 bit | -32,768 ~ 32,767 | 0 | |
int | 4 byte | 32 bit | -2,147,483,648 ~ 2,147,483,647 | 0 | |
long | 8 byte | 64 bit | 약 -920경 ~ 약 920경 | 0L | |
실수 | float | 4 byte | 32 bit | (+/-)1.4E-45 ~ (+/-)3.4028235E38 | 0.0f |
double | 8 byte | 64 bit | (+/-)4.9E-324 ~ (+/-)1.7976931348623157E308 | 0.0d | |
논리 | boolean | 1 byte | 8 bit | true, false | false |
문자 | char | 2 byte | 16 bit | 0 ~ 65535 (유니코드:\u0000~\uFFFF) | '\u0000' |
개인적으로 위의 값을 힘들게 모두 외우는건 시간낭비이다. 대략적인 범위만 기억하자, 하나하나 외울 시간보다 진도를 나가는게 이득이다.
어차피 타입마다 최대값, 최소값을 알려주는 MAX_VALUE, MIN_VALUE 상수가 존재한다.
메모리에는 이진법으로 0과 1을 저장하는 최소의 단위 1 bit 가 있다.
0 |
이 bit 8개를 묶어서 1 byte 라고 한다
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
short 는 2 byte 로 다음과 같다.
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
여기서 양과 음을 비교할 때 사용되는게 최상위 비트이며 최상의 비트는 맨 앞을 뜻한다. 위의 최상위 비트만 굵고 기울기로 표시해놨다.
최상위 비트가 0 이면 양의 정수이고, 1 이면 음의 정수이니 잊지말자. 만약에 다음과 같은 byte 타입의 최대값 127 이 있다고 가정해보자
프로그램을 사용하다보면 값이 자주 변경된다.
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
만약에 최대값 127 을 넘은 128 로 저장이 되는 경우엔 어떻게 될까?
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-128 이 되어버린다. 이는 또다른 정수 타입인 short, int, long 도 동일하다. 이런 경우 변수값이 엉터리가 되어버린다. 조심하자.
실수 타입인 float 과 double 은 각각 4 바이트, 8 바이트로 int, long 과 동일하지만 부동 소수점 방식을 사용하기에 훨씬 큰 범위의 값을 저장할 수 있다. 이 부동 소수점 방식은 다음 형태로 표현한다
float 과 double 의 bit 사용 방법이다
float : 부호 (1bit) + 지수 (8bit) + 가수 (23bit) = 32bit = 4byte
1 | 지수 (8bit) | 가수 (23bit) |
double : 부호 (1bit) + 지수 (11bit) + 가수 (52bit) = 64bit = 8byte
1 | 지수 (11bit) | 가수 (52bit) |
프리미티브 타입과 레퍼런스 타입
1주차에 학습한 JVM 구성요소 메모리 중에서 힙(Heap)과 스택(Stack)을 기억하는가? 위에서 기본형이 직접 저장된다고 했는데, 그 저장되는 위치가 바로 스택이다. 스택에 값이 직접 저장되는게 기본형이고, 참조형(reference)도 스택에 저장하지만 기본형과 다른 점은 주소가 저장된다는 것이다. 이 주소는 바로 참조형의 실제 값이 저장된 힙의 주소를 뜻한다. 주소를 통해 객체를 참조한다고 해서 참조형이라 부른다.
기본형과 참조형의 구분 방법은 단순하다. 위에서 언급한 기본형을 제외한 나머지가 참조형이라고 보면 된다.
리터럴
그냥 단순하게 코드에 직접 기술한 값을 리터럴이라고 부른다.
byte b = 127; // 127은 byte 타입의 리터럴이다
float f = 3.14f; // 3.14f는 float 타입의 부동소수점 리터럴이다.
상수와 비슷하지만 상수는 변하지 않는 변수를 뜻하고 리터럴은 변하지 않는 데이터를 뜻한다.
변수 선언 및 초기화 방법
자바에서 변수를 선언하는 방법은 단순하다
어떤 타입으로 저장할 것인지 그리고 변수명을 적으면 끝난다.
int age; // int 타입의 age 변수 선언
double money; // double 타입의 money 변수 선언
같은 타입의 변수는 , 를 이용하여 동시에 선언할 수 있다.
int a, b, c; // 같은 타입의 여러개 동시 선언
변수의 초기화는 변수에 초기값을 주는 걸 뜻한다.
초기화 방법도 간단하다 대입 연산자 = 를 사용해서 값을 명시하면 끝.
age = 10;
자바에서 변수 선언할 때 꼭 알고넘어가야 할 명명규칙이 존재한다.
1. 첫번째 글자는 문자이거나 '$', '_' 만 가능하고 숫자로 시작할 순 없다.
2. 영어 대소문자가 구분된다. 대소문자가 다르면 다른 변수이다 (strName 과 strname 은 다른 변수이다)
3. 카멜케이스라는 번역하면 낙타법으로 첫문자는 영어소문자이면서 다른 단어가 붙을 경우 대문자로 표기한다 firstName, maxSpeed 등등
4. 변수의 길이 제한은 없다. (변수의 명이 너무 길어지는 경우도 많다. 보통 회사 혹은 프로젝트마다 용어사전이 있으니 약어를 찾아서 사용한다. firstName -> firNm, middleName -> midNm)
5. 예악어로 사용할 수 없다. (변수명에 boolean, byte, private 사용 못한다)
변수의 스코프와 라이프타임
스코프란 단순하게 생각하면 선언된 변수를 사용할 수 있는 영역을 뜻한다. 자바에서는 해당 영역을 {} 블록으로 구분한다.
변수의 선언 위치에 따라 클래스 변수, 인스턴스 변수, 지역 변수로 나눠지며 차이점은 다음과 같다.
인스턴스 변수는 인스턴스가 생성될 때 사용이 된다. 각 인스턴스마다 서로 다른 값을 가지게 된다
클래스 변수는 인스턴스 변수에 static 이 붙는 경우로 인스턴스 변수와 다르게 인스턴스마다 모두 공유해서 공통의 값을 이용할 때 사용한다 (public 이 붙으면 프로그램내에서 접근이 가능하다. 클래스변수만 있는 클래스를 만들어서 활용하기도 한다.)
지역 변수는 메서드내에서만 사용이 가능하다
class Week02 {
public static int CLASS_AGE = 30; // 클래스 변수, static 이 붙는다. Week02 인스턴스가 여러개 생성되도 모두 공유하여 사용한다. 다른곳에서도 사용이 가능하다.
private int insAge = 35; // 인스턴스 변수
public void fnScope () {
int scopeAge = 20; // 지역 변수, fnScope 내에서만 사용가능하다.
System.out.println(scopeAge); // 20 출력
System.out.println(insAge); // 인스턴스 변수, Week02 클래내에서는 어디든지 접근이 가능
}
}
클래스 변수의 라이프타임은 클래스로더가 로딩할 때 초기화되고 프로그램이 종료될 때까지 사용이 된다
인스턴스 변수는 인스턴스가 생성되고 해당 인스턴스가 참조 안될 때 GC 에 의해서 사라진다
지역 변수는 {} 블록 내에서 변수가 선언될 때 생성되고 블록이 끝나는 시점에 사라진다
타입 변환, 캐스팅(묵시적) 그리고 타입 프로모션(명시적)
타입 변환은 말 그대로 데이터 타입을 다른 타입으로 바꾼다는 말이다.
간단히 byte 를 int 로 혹은 반대로 int 를 byte 로 바꾸는 거다.
타입에는 2가지 유형이 있다. 자동(묵시적) 타입 변환과 강제(명시적) 타입 변환이다.
자동 변환은 프로그램 실행 중 자동으로 변환되는 걸 의미하며 보통 작은 크기가 큰 크기로 저장할 때 발생한다.
간단한 예시로는 다음과 같다.
// 자동 변환 예
public static void fnAutoCast() {
byte byteVal = 10;
int intVal = byteVal; // byte -> int
System.out.println(intVal);
char charVal = '가';
intVal = charVal; // char -> int
System.out.println(intVal);
intVal = 100;
long longVal = intVal; // int -> long
System.out.println(intVal);
intVal = 200;
double douVal = intVal; // int -> double
System.out.println(intVal);
}
반대로 큰 타입에서 작은 타입으로 자동 변환은 할 수 없다. 강제 변환으로 직접 제어하면 변환은 가능하다.
문제는 큰 대야에서 작은 컵으로 물을 옮긴다면 흘러 넘치는 것처럼
4byte 를 사용하는 값을 1byte 로 옮기면 데이터가 제대로 들어갈 수 있을까? 4byte 중 일부분만 들어가서 잘못된 데이터가 되어버린다.
하지만 4byte 중에서 1byte 범위에 맞게 사용되고 있다면? 해당되는 값이 제대로 들어간다.
다음은 간단한 예시이다.
public static void fnExpCast() {
byte byteVal = 10;
short shortVal = 20;
int intVal = 100;
long longVal = 1000000L;
byteVal = (byte) shortVal;
shortVal = (short) intVal;
intVal = (int) longVal;
System.out.println("byte : " + byteVal); // 20
System.out.println("short : " + shortVal); // 100
System.out.println("int : " + intVal); // 1000000
// 넘치는 값으로 강제 변환하면?
shortVal = 32767;
intVal = 2100000000;
longVal = 2200000000L;
byteVal = (byte) shortVal;
shortVal = (short) intVal;
intVal = (int) longVal;
System.out.println("byte : " + byteVal); // -1
System.out.println("short : " + shortVal); // 29952
System.out.println("int : " + intVal); // -2094967296
}
위의 코드 실행 시 처음 선언된 값으로 강제 변환하면
큰 타입의 값이 작은 타입의 범위에 해당되기에 문제 없지만
작은 타입의 범위를 초과하는 값으로 강제 변환하면 잘못된 결과가 나오는 걸 볼 수 있다.
1차 및 2차 배열 선언하기
자바에서 값을 저장하려면 변수를 선언해야한다. 프로그램에서 단순히 값을 몇가지만 사용한다면 직접 하나하나 변수를 선언해도 상관 없을 것이다. 하지만 세상은 그리 단순하지가 않다. 많은 데이터가 입력되고 사용되기에 저장되는 변수도 많아진다. 그렇다면 개발자가 직접 하나하나 변수를 선언해야할까?
다음은 일반적으로 학교에서 한반의 평균 점수를 구하는 코드이다.
// 수학 평균 점수를 구해보자
int sum = 0;
int score01 = 80; // 1번 학생
int score02 = 80; // 2번 학생
int score03 = 80; // 3번 학생
int score04 = 80;
int score05 = 80;
// ....
int score26 = 80;
int score27 = 80;
int score28 = 80;
int score29 = 80; // 29번 학생
int score30 = 80; // 30번 학생
30명의 학생 만큼 수학 점수용 변수를 선언했다.
이를 또 수학 점수를 더하고 구하려면?
sum += score01;
sum += score02;
sum += score03;
// ...
sum += score29;
sum += score30;
int avg = sum / 30; // 평균 점수
변수를 30번 호출하고 더 해야한다. 내가 개발자인지 막노동꾼인지 알 수가 없다.
이런 문제로 같은 타입의 값들을 사용하는 방법이 배열이다. 배열은 같은 타입의 데이터를 연속적으로 나열시키고 각 데이터에 index를 부여한 자료구조이다. 위의 예제를 배열로 바꾸면 다음과 같이 된다.
int[] scoreArr = {80, 80, 70, 75, 90, ... , 60, 100};
편안하게 배열 변수하나에 30명의 점수가 저장이 되었다.
배열을 초기화하는 방법은 간단하다
변수를 선언할 때 변수의 타입이나 변수명에 [] 를 붙여주면 된다. ( [] 는 인덱스라 불리운다. )
int[] scoreArr;
int scoreArr[];
선언할 때 크기를 지정할 수 있다.
int[] scoreArr = new int[10]; // 크기 10 으로 지정, 이후 변경할 수 없다.
선언할 때 초기값과 크기를 동시에 지정할 수 있다.
int[] scoreArr = new int[]{100, 90, 80, 70, 60}; // 크기와 인덱스별 초기값 지정
int[] scoreArr = {100, 90, 80, 70, 60};
배열 변수 사용 방식은 단순하다 변수명과 인덱스만 언급하면 된다
첫번째 학생의 점수를 알고 싶으면?
scoreArr[0]; // 100 을 가져온다
보통 일상에서는 첫번째를 1 이라고 표시한다. 하지만 자바에서는 첫번째 인덱스를 0 으로 표시한다. (원하는 번호의 - 1 을 해야한다.)
이제 단순히 한반의 점수가 아니라
1학년의 반별로 점수를 저장해야 하는 경우엔 어떨까?
1반 30명
2반 30명
3반 30명
int[] scoreArr01;
int[] scoreArr02;
int[] scoreArr03;
배열 3개를 선언해야할까? 반이 더 많아진다면? 그 만큼 선언해야할까?
이 경우엔 다차원 배열을 이용하면 하나의 변수에 모두 저장할 수가 있다.
선언은 다음과 같다
int[][] scoreArr = new int[3][30];
[][] 를 2번 사용한 2차원 배열이다 첫번째 [3] 는 반의 수를 넣고 그 뒤에 [30] 은 학생수를 넣었다.
초기값 선언도 1차 배열과 똑같다.
int[][] scoreArr = {
{100,90,80,70, ... 80}
, {90, 80, 70, 60, ..., 10}
, {90, 80, 70, 60, ..., 10}
}
1반의 1번 학생의 점수는?
System.out.println(scoreArr[0][0]); // 100
이제 1학년부터 6학년까지 점수를 저장해야하는 경우엔?
int [][][] scoreArr = new int[6][3][30];
[][][] 를 3번 사용하는 3차원 배열을 이용하면 된다.
int[][][] scoreArr = {
{
{100,90,80,70, ... 80}
, {90, 80, 70, 60, ..., 10}
, {90, 80, 70, 60, ..., 10}
}, {
{100,90,80,70, ... 80}
, {90, 80, 70, 60, ..., 10}
, {90, 80, 70, 60, ..., 10}
}, {
{100,90,80,70, ... 80}
, {90, 80, 70, 60, ..., 10}
, {90, 80, 70, 60, ..., 10}
}, {
{100,90,80,70, ... 80}
, {90, 80, 70, 60, ..., 10}
, {90, 80, 70, 60, ..., 10}
}, {
{100,90,80,70, ... 80}
, {90, 80, 70, 60, ..., 10}
, {90, 80, 70, 60, ..., 10}
}, {
{100,90,80,70, ... 80}
, {90, 80, 70, 60, ..., 10}
, {90, 80, 70, 60, ..., 10}
}
}
2학년 1반 2번의 점수는?
System.out.println(scoreArr[1][0][1]); // 90
타입 추론, var
자바10 에서 새롭게 var 가 추가되었다. 자바스크립트를 사용해봤으면 익숙하면서도 자바에서도 var 가??? 라며 의구심을 표출할 수 있다.
var 변수명 = 값;
byte, String 같은 타입 대신 쓰는 var 는 변수에 저장되는 값에 따라 자동적으로 컴파일러에서 타입을 추측해주는 기능이다
var strVal = "안녕";
var byteVal = 100;
var intVal = 100000000;
나중에 다시 메인주제로 리뷰해 봐야겠다. (아마도 자바8 부터 공부한 이후에 할듯.)
참고
- 기초부터 실무 응용까지 자바 마스터북, 제이펍
- 이것이 자바다 신용권의 Java 프로그래밍 정복1, 한빛미디어
'개발 & 방법론 > Java' 카테고리의 다른 글
5주차 : 클래스(Class) (0) | 2020.12.21 |
---|---|
4주차 : 제어문 (0) | 2020.12.12 |
3주차 과제 : 연산자 (0) | 2020.11.27 |
1주차 : JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가. (0) | 2020.11.17 |
자바 스터디 같이 할래? (0) | 2020.11.17 |