개발하고 싶은 초심자
220824 D+2 변수와 할당, Template(템플릿) 이해하기, 기본자료형, 타입 변환 본문
‣ 기본 템플릿 이해하기
public class App {
public static void main(String[] args) {
System.out.println("Hello, World!"); // 콘솔 출력을 위한 명령어
}
}
(이제부터 하는 모든 설명은 기본 템플릿 코드에 입각하여 작성함)
1. 클래스(class)
‣ 자바는 모든 코드를 클래스를 사용하여 작성하고, 사용할 때 클래스를 인스턴스화하여 객체로 사용하게 된다
(= 자바는 객체지향언어)
2. 접근 제한자(Access Modifier)
: 클래스, 변수, 메서드의 접근 권한을 명시적으로 작성한 키워드.
→ 어떤 클래스의 변수와 메서드를 다른 클래스에서 사용 할 수 있는 지 없는 지에 대해 정의할 필요가 있기 때문에 나왔음.
→ 소프트웨어, 시스템 보안 관련 최소 권한의 원칙(POLP / Principle of least privilege)
: 공개하지 않아도 될 것은 공개하지 않는다, 꼭 필요한 만큼만 공개한다.
‣ 자바에서 사용 가능한 접근 제한자
① public: 모든 접근 허용
② private: 현재 객체 내에서'만' 허용
③ protected: 같은 패키지(폴더)에 있는 객체와 상속 관계의 객체들'만' 허용
④ default: 같은 패키지(폴더)에 있는 객체들'만' 허용
3. static
: 자바는 모든 코드를 객체 안에 작성하게 되기 때문에 클래스 안에 모든 코드를 작성한다.
클래스는 실제로 실행하기 위한 객체는 아니기 때문에 특정 메소드를 바로 사용하기 위해 static 선언을 해줘야 한다.
→ 보통 변수나 메서드(정적 필드, 정적 메서드) 앞에 static 키워드를 붙여 사용한다.
→ 이러한 변수와 메서드를 합쳐 정적 멤버(클래스 멤버)라고 한다.
: 객체에 소속된 멤버가 아니라 클래스에 소속된 멤버
⇒ main 메서드는 실행하기 위한 메서드이므로 바로 사용하기 위해 static 키워드를 사용한다.
4. main
: 자바에서 실행될 코드를 작성해주는 메서드.
public static void main(String[] args) {
System.out.println("Hello, World!");
myPrint("Hello, Java!"); // ② 함수 실행
}
// ① myPrint() 메소드 작성
// 실행하고 싶을 때는 Main메소드 안으로
// myPrint("Hello, Java!"); -> Main 메소드 밖에 있으면 컴파일 에러 발생
static void myPrint(String input) {
System.out.println(input);
}
→ 자바 프로그램 실행 시 main()메서드를 가장 먼저 찾아 그 안에 있는 것을 순차적으로 실행,
전부 실행 완료되면 프로그램을 종료하기 때문에 main() 메소드 바깥에 있으면 컴파일 과정에서 에러를 발생시킨다.
5. return, void
‣ 자바의 메서드는 리턴할 값의 자료형(type)을 명시해주어야 한다.
→ 명시적으로 return 키워드만 쓰거나, return을 작성하지 않은 경우, 즉 데이터를 리턴하지 않을 경우 void 키워드 작성.
특정 자료형을 리턴하는 경우 자료형을 따로 명시해준다.
6. String args의 의미
‣ main 메소드는 실행 시 하나의 인자를 전달받아야 한다.
‣ main 메서드의 첫번째 (그리고 유일한) 인자는 String[] 타입을 가져야 한다.
‣ main 메소드의 첫번째 (그리고 유일한) 인자는 main 메소드의 본문(body)에서 args라는 변수로 접근 가능하다.
✷ 전달 인자의 자료형이 문자열 배열로 되어 있는 이유
main 메서드가 실행될 때, JVM이 길이가 0인 문자열의 배열을 자동 생성하고
이때 전달 인자를 String의 배열로 전달하기 때문에,
main 메서드의 기본 전달 인자가 String args 이러한 형태로 만들어진다고 생각하면 된다.
7. 기본 자료형
① 변수
: 데이터를 저장하는 공간.
→ 변수를 사용하기 위해 정하려는 데이터의 타입(종류)을 항상 명시해야 한다.
int age; // 정수(int) 값을 저장하는 age 변수 선언
타입 | 변수명;
int age = 21; // 변수에 데이터를 할당, 저장할 때 대입 연산자 = 사용
타입 | 변수명 = 값;
// 초기화: 변수를 선언하고 처음으로 값을 할당하는 것
// 재할당: 초기화가 이루어진 후 다시 다른 값을 할당하는 것
class Example {
public static void main(String[] args) {
int num = 1; // 변수 선언과 동시에 초기화
}
}
✷ 변수 명명 규칙
‣ 변수명은 일반적으로 카멜 케이스(camelCase)를 사용한다.
‣ 변수명으로 영문자, 숫자, _, $를 사용할 수 있다.
→ 영문자는 대소문자가 구별되어 인식된다(a와 A는 다른 문자로 인식됨)
‣ 숫자로 시작하는 변수명은 사용할 수 없다.
‣ 자바에서 이미 사용 중인 예약어(reversed word)는 변수명으로 사용할 수 없다.
② 자료형 / 타입(어떤 값의 유형 및 종류)
: 변수에 저장되는 값의 종류와 범위를 결정짓는 요소. 메모리 공간을 얼마나 쓸 것인지에 대한 선언.
‣ 기본 타입(primitive type)
: 값을 참조한다. int, float, char, Boolean 등.
→ 변수에 할당한 데이터가 값(상수)이면 기본 타입
✷ 상수(constant)
: 변하지 말아야 할 데이터를 임시적으로 저장하기 위한 수단
⇒ 재할당이 금지된 변수.
// final이라는 키워드를 사용하여 선언할 수 있다
// 관례적으로 대문자에 언더바_를 넣어 구분하는 SCREAMING_SNAKE_CASE를 사용한다
final double CALCULATOR_PI = 3.14;
‣ 상수를 사용하는 이유
- 프로그램이 실행되면서 값이 변하면 안 되는 경우에 사용한다
- 값을 저장하고 있는 상수명으로 값을 사용할 수 있어 코드 가독성을 높이고 싶은 경우 사용한다
- 코드 유지관리를 손쉽게 하고자 하는 경우 사용한다
→ 상수를 사용했을 때 상수에 할당할 값만 바꾸어주면 된다
// 기존 코드
final double CALCULATOR_PI = 3.14;
// 변경된 코드
final double CALCULATOR_PI = 3.14159;
- 정수 타입(byte, short, int, long)
✷ 각 타입별 표현할 수 있는 범위
타입 | 메모리 | 범위 | 기타 |
byte | 1byte | -128 ~ 127 |
|
short | 2byte | -32,768 ~ 32,767 |
|
int | 4byte | -2,147,483,648 ~ 2,147,483,647 |
|
long | 8byte | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
long 타입 같은 경우 변수에 값을 할당할 때, 이것이 4byte의 데이터가 아니라 8byte의 데이터 값이라는 것을 컴파일러에게 알려주기 위한 목적으로 L을 붙여준다. ex) long amount = 1232L |
// 각 데이터 타입의 표현 범위에 맞는 값을 할당하고 있다.
byte byteNum = 123;
short shortNum = 12345;
int intNum = 123456789;
long longNum = 12345678910L;
// 숫자가 길면 언더바로 구분할 수 있다.
int intNum = 12_345_678_910;
long longNum = 12_345_678_910L;
✷ 데이터 타입의 크기가 표현 범위를 결정한다
byte형은 말 그대로 1byte의 크기를 가진 정수형 데이터 타입이다.
1byte는 8bit이므로 따라서 8자리의 이진수를 표현할 수 있다.
즉, 28 = 256개의 데이터를 표현할 수 있다.
부호를 감안하지 않는다면 0부터 255까지의 256개의 숫자를 1byte로 표현할 수 있다.
하지만 음수의 범위도 표현하기 위해 8비트 중의 맨 앞의 비트를 부호 비트로 사용한다.
즉, 맨 앞의 비트가 0이면 양수, 1이면 음수를 나타내게 된다.
이렇게 하나의 비트로 부호를 표현하고 나면 남은 7비트로 27 = 128개의 숫자를 표현할 수 있다.
⇒ byte형은 8bit의 크기 중 1bit는 부호 표현에 사용하며,
7bit는 숫자 표현에 사용하여 -128 ~ 127의 정수 범위를 표현할 수 있게 된다.
이와 같은 이유로 데이터 타입의 크기는 데이터의 표현 범위에 영향을 미치게 된다.
‣ 변수의 값이 자료형의 범위를 넘을 경우(변수의 선언된 데이터 유형 범위를 벗어난 값을 할당할 때)
오버플로우 / 언더플로우가 발생하여 필요에 따라 long과 같은 타입 사용. 하지만 너무 남발하면 성능 문제가 생길 수 있음.
✷ 정수형의 오버플로우 / 언더플로우
ex) byte형의 표현 범위는 -128 ~ 127까지인데, byte형 값 120에 10을 더하면 130이 되기 때문에 오버플로우가 발생한다.
반대로 -120에서 10을 빼서 -130이라는 값이 나오면 byte형의 표현 범위 중 최솟값을 넘어서는 언더플로우가 발생한다.
‣ 오버플로우: 자료형이 표현 가능한 범위 이상의 값을 표현한 경우 발생하는 현상
→ 최댓값을 넘어가면 해당 데이터 타입의 최솟값으로 값이 순환한다.
예를 들어 최댓값인 127을 값으로 가지는 경우 1을 더하면 -128이 됨.
‣ 언더플로우: 자료형이 표현 가능한 범위 이하의 값을 표현한 경우 발생하는 현상
→최솟값을
예를 들어 최솟값인 -128을 값으로 가지는 경우 1을 빼면 127이 됨.
→ 64bit에서는 int도 8byte로 표현이 되지만 컴파일러는 int가 4byte로 할당되어 있기 때문에
표현 범위 이상에 값이 할당된 경우 에러가 발생한다.
// 각 데이터 타입의 표현 범위에 벗어난 값을 할당하고 있어 에러가 발생한다.
byte byteNum = 130;
short shortNum = 123456;
int intNum = 12345678910;
short num01 = 10; // -32768 ~ 32767
int num02 = 20; // -2147483648 ~ 2147483647
long num03 = 30; // -9223372036854775808 ~ 9223372036854775807
- 실수(소수점을 가지는 값들) 타입(float, double)
// 리터럴 : 3.14f
// float으로 선언된 자료형은 상수의 마지막에 f를 꼭 작성해야 한다
float num1 = 3.14f
// 모든 실수 값의 디폴트 자료형 double
double num2 = 3.141592
‣ float 타입 : 4byte, 정밀도 7자리
‣ double 타입 : 8byte, 정밀도 15자리
→ 컴퓨터에서 실수를 저장할 때 부동소수점 표현 방식으로 저장하는데,
효율적이긴 하지만 약간의 오차범위를 가지고 있어
더 많은 데이터를 기록할 수 있는 double이 float보다 정확도가 더 높다.
⇒ double형은 float형보다 더 큰 실수를 저장할 수 있고, 더 정확하게 저장할 수 있다.
✷ 리터럴
: 문자가 가리키는 값 그 자체.
// 정수형 리터럴 20을 정수형 변수 currentAge에 할당
int currentAge = 20;
// 실수형 리터럴 3.14159를 실수형 변수 pi에 할당
double pi = 3.14159;
// 논리형 리터럴 true를 논리형 변수 boolean에 할당
boolean isGenius = true;
// 문자형 리터럴 'A'를 문자형 변수 firstAlphabet에 할당
char firstAlphabet = 'A';
// 문자열 리터럴 "CodeStates"를 문자열 타입 변수 learnedAt에 할당
String learnedAt = "CodeStates";
float weight = 74.5f;
final long LIGHT_YEAR = 9460730472580L;
float = 9460730472580.0F;
double = 9460730472580.0D;
✷ 실수형의 오버플로우와 언더플로우
‣ 오버플로우: 값이 음의 최소 범위 혹은 양의 최대 범위를 넘어갔을 때 발생한다. 이때의 값은 무한대가 된다.
‣ 언더플로우: 값이 음의 최대 범위 혹은 양의 최소 범위를 넘어갔을 때 발생한다. 이 때의 값은 0이 된다.
- 논리 타입(boolean)
: 논리값(true / false)을 저장할 수 있는 데이터 타입.
→ 두 가지 상태 값을 저장할 필요성이 있을 경우에 사용되나,
상태 값에 따라 조건문과 제어문의 실행 흐름을 변경하는 데 주로 이용된다.
boolean isRun = false;
boolean isWork = true;
// boolean은 true, false의 값을 가진다.
단순 참과 거짓을 표현하기 위해서는 1bit만 있으면 되지만 JVM이 다룰 수 있는 데이터의 최소 단위가 1byte라서
boolean형은 1byte(8bit)의 크기를 갖는다.
- 문자(char) 타입
: 하나의 유니코드를 저장하기 위해 2byte 크기인 char 타입 제공.
→ 하나의 문자에 대해 하나의 코드값 부여.
→ 문자형 리터럴을 작성할 때 작은따옴표''를 사용하여 작성한다.
char alphabet = 'A';
char hangeul = '가';
char letter = 'AB'; // 단 하나의 문자만 할당할 수 있으므로 에러
char letter = "A"; // 작은 따옴표를 사용해야 하기 때문에 에러
char letter = 65;
System.out.print(letter); // A
→ 숫자를 문자형 변수에 할당하면 해당 숫자가 그대로 저장되지만,
변수의 값을 읽어올 때(나중에 변수를 참조할 때) 해당 변수가 저장하고 있는 숫자값을 유니코드로 인식하여
해당 숫자와 일치하는 코드를 가진 문자로 변환해준다.
✷ 타입별 메모리 크기
값의 종류 | 기본 타입 | 메모리 사용 크기 |
정수 | byte | 1byte |
char | 2byte | |
short | 2byte | |
int | 4byte | |
long | 8byte | |
실수 | float | 4byte |
double | 8byte | |
논리 | boolean | 1byte |
✷ 기본 타입으로 선언된 변수와 참조 타입으로 선언된 변수의 차이점
: 저장되는 값이 무엇이냐를 따진다.
→ 기본 타입을 이용해 선언된 변수는 실제 값을 변수에 저장
→ 참조 타입을 이용해 선언된 변수는 메모리의 주소를 값으로 갖는다.
public class Example {
public static void main(String[] args) {
int primitive = 1;
Object reference = new Object();
System.out.println(primitive); // 1
System.out.println(reference); // java.lang.Object@626b2d4a
}
}
// int primitive = 1;은 의사코드 예제의 기본타입변수 = 1;에 구조적으로 대응된다.
// Object reference = new Object();는 의사코드 예제의 참조타입변수 = 객체;에 구조적으로 대응된다.
// System.out.println();은 의사코드 예제의 출력(); 메서드와 대응된다.
8. 타입 변환
: 데이터 타입을 다른 데이터 타입으로 변환하는 것.
① 자동 타입 변환
: 작은 크기를 가지는 자료형에서 큰 크기를 가지는 자료형으로 이동할 때 자동으로 형 변환을 시켜준다.
→ 크기를 구분하는 기준은 메모리 크기.
byte(1) < short(2) < int(4) < long(8) < float(4) < double(8)
→ float이 int와 long보다 더 큰 타입으로 표시하는 이유는 표현할 수 있는 값의 범위가 더 크기 때문이다.
// float이 long보다 정밀하므로 자동으로 타입이 변환된다.
long longValue = 12345L;
float floatValue = longValue;
System.out.println(floatValue); // 12345.0이 출력된다.
→ 덜 정밀한 타입에서 더 정밀한 타입으로 변환할 때 타입이 자동으로 변환된다(정수 → 실수).
② 강제적(수동) 타입 변환(캐스팅 / casting)
: 큰 자료형에서 작은 자료형으로 자동 타입 변환을 할 수 없기 때문에
큰 데이터 타입을 작은 데이터 타입으로 쪼개서 저장하는 것.
→ 캐스팅 연산자 () 사용
→ 연산자 안에 들어가는 타입은 쪼개는 단위를 의미함.
// 작은 크기 타입 = ( 작은 크기 타입 ) 큰 크기 타입
int intValue = 1020304055
byte byteValue = (byte) intValue;
int intValue = 128;
byte byteValue = (byte)intValue;
System.out.println(byteValue); // -128
⇒ 메모리 용량이 넉넉해진 지금은 일반적으로 정수는 int 또는 long, 실수는 double로 사용하기 한다.
(예전에 비해 수동 타입 변환의 사용 빈도가 많이 줄었다)