[SEB_BE / Day 5] Java(1) 기초 변수, 타입
Java란?
Java는 1995년 제임스 고슬링과 그의 동료들에 의해 시작된 프로젝트입니다. 원래 가전제품을 제어하기 위한 언어로 고안되었지만 웹의 등장으로 엄청난 성공을 거두며 주류 언어가 되었습니다.
Java 특징
- 운영체제에 독립적이다.
- 객체지향언어(OOP, Object Oriented Programming)이다.
- 함수형 프로그래밍을 지원한다.
- 가비지컬렉션(GC, Garbage Collection)을 통해 메모리를 관리한다.
Java 프로그램의 동작
- 코드(Code, Source) 작성
- 컴퓨터가 실행할 수 있는 형태로 코드를 컴파일
- 컴파일된 프로그램을 실행
프로그래밍 한다 → 코드(Code = Source)를 작성한다 → 설계도
용어
- Java SE(Java Platform, Standard Edition)
- 자바의 표준안, 자바라는 언으는 어떠한 문법적인 구성을 가졌는지와 같은 것들을 정의
- 구체적인 소프트웨어가 아닌 그 소프트웨어의 설계도, 소프트웨어에서는 설계도라는 표현 대신 명세서(spec, specification)이라는 말을 사용
- 이 명세에 따라 Java가 만들어지며 Java SE8은 버전 8에 대한 명세서
- 이 명세서는 JCP(Java Community Process, http://jcp.org)라는 조직을 통해 만들어짐
- JDK(Java Development Kit)
- Java SE의 표준안에 따라 만들어진 구체적인 소프트웨어
- Java 개발자라면 JDK를 다운받아 설치해야 하며 JDK에는 Java 프로그램을 실행하면 Java 코드를 컴파일 하는 컴파일러와 개발에 필요한 각종 도구 그리고 JRE가 포함되어 있다.
- Java 개발자를 위한 자바 버전이다.
- JRE(Java Runtime Environment)
- java가 실제로 동작하는 데 필요한 JVM, 라이브러리, 각종 파일들이 포함되어 있다.
- 자바로 만들어진 프로그램을 구동하려고 한다면 JRE를 설치 해야한다.
- 일반인을 위한 자바 버전이다.
- JVM(Java Virtual Machine)
- 자바가 실제로 구동하는 환경
- 자바로 만들어진 소프트웨어는 JVM이라는 가상 컴퓨터에서 구동되고 하드웨어나 운영체제에 따라 달라질 수 있는 호환성의 문제는 운영체제 버전에 따라 만들어진 JVM이 알아서 해결한다.
- 하나의 자바 프로그램을 만들면 어떤 환경에서도 실행할 수 있는 것이 JVM의 역할
Java(LTS) 버전별 특징
- JDK 8
- 람다식 및 메서드 참조 도입
- 컬렉션에 Stream API 사용 가능
- 인터페이스 내부에 default 메서드 선언 가능
- Optional 클래스 도입 등
- JDK 11
- String 클래스에 strip, isBlack, lines 메서드 추가
- Files 메서드에 readString 메서드 추가
- 람다식의 인수 선언 시 var 키워드 사용 가능
- 소스 파일을 javac를 통한 컴파일 없이 스크립트로 실행 가능
- 차세대 가비지 컬렉터 도입 등
- JDK 17
- 텍스트 블록 추가
- 의사 난수 생성기 기능 향상
- 향상된 switch문 기능 추가
- 봉인 클래스 추가
Version 예시
Java SE 8 = JDK 8 = JDK 1.8
실행
컴파일(Compile)
자바의 문법은 사람만이 이해할 수 있는 형식으로 되어 있기 때문에 컴퓨터가 이해할 수 있는 상태로 변환해주는 과정이 필요한데 이 것을 컴파일(Compile)이라고 하고, 이 작업을 하는 소프트웨어를 컴파일러(Compiler)라고 부릅니다. 자바의 컴파일러는 javac라는 이름을 갖고 있으며 만약 Helloworld.java라는 코드를 컴파일 한다면 아래와 같은 형식으로 javac를 실행합니다.
javac Helloworld.java
Code → Byte Code (→가 컴파일러가 하는 일)
실행
컴파일러로 코드를 컴파일 하게 되면 .class라는 이름의 파일이 생성되며 이 파일이 컴파일된 파일이고 실행 파일이라고 할 수 있습니다. 하지만 이 파일은 파일 이름만으로 실행할 수 있는 것은 아니고 컴파일된 파일을 실행시켜주는 프로그램을 이용해야 합니다. 이 작업을 하는 프로그램을 런처(Launcher)라고 하고 아래와 같이 실행합니다.
java Helloworld(.class가 생략)
Javac Java
↓ ↓
Code → Byte Code → JVM
변수(Variable)
값이 변할 수 있는 데이터를 임시로 저장하기 위한 수단
컴퓨터는 메모리에 데이터를 저장하며 메모리는 1Byte 크기의 데이터를 저장할 수 있는 메모리 셀들이 모여서 만들어진다. 각 메모리셀에는 고유 번호가 오름차순으로 매겨져 있고 이 메모리셀에 붙여져 있는 고유 번호를 메모리 주소라고 한다.
메모리를 아파트로 비유하면 아래와 같다
메모리 | 아파트 |
메모리셀의 집합체 | 집의 집합체 |
각 메모리셀에 데이터를 저장할 수 있다. | 각 집에 사람들이 들어가 살 수 있다. |
각 메모리셀을 가리키는 주소가 있다. ex) ox77d2a3b |
각 집을 가리키는 호수가 있다. ex) 912호 |
변수의 선언과 할당
// 변수 선언
int number;
// 할당 or 초기화
number = 1;
// 선언과 동시에 할당하기
int number2 = 2;
변수명 규칙
Java 는 일반적으로 카멜 케이스(CamelCase)를 사용한다. 카멜 케이스란 낙타 등 모양을 닮아 붙여진 이름으로 두 개 이상의 단어가 붙는다면 두 번째 단어 첫 글자를 대문자로 시작해 구분한다. 영문자는 대소문자가 구별되어 인식된다.
상수(Constant)
변하지 않는 데이터를 임시로 저장하기 위한 수단으로 재할당이 금지된 변수이다. 상수는 final 이라는 키워드를 사용하여 선언할 수 있으며 관례로 대문자에 언더바(_)를 넣어 구분하는 CREAMING_SNAKE_CASE를 사용한다.
// 상수(Constant)
final double PI = 3.141592;
상수를 사용하는 이유?
1. 프로그램이 실행되면서 값이 변하면 안되는 경우 사용
2. 코드 가독성을 향상시키기 위해
3. 코드 유지관리를 손쉽게 하고자 하는 경우
타입(Type)
타입은 어떤 값의 유형 및 종류를 의미하며 타입에 따라 값이 차지하는 메모리 공간의 크기와 값이 저장되는 방식이 결정된다.
1. 값이 차지하는 메모리 공간의 크기
: 예를 들어 정수형 타입의 int 는 4byte 문자형 타입의 char 는 2byte로 타입에 따라 메모리 공간의 크기가 달라진다.
2. 값이 저장되는 방식
: 타입은 저장하고자 하는 값을 그대로 저장하는 기본 타입과 저장하고자 하는 값을 임의의 메모리 공간에 저장 후 메모리 공간의 주소를 저장하는 참조 타입으로 분류된다.
기본 타입(Primitive Type)과 참조타입(Reference Type)
자바의 타입은 실제 값을 의미하는 기본 타입과 어떤 값이 저장된 주소를 값으로 갖는 참조 타입, 두 가지 데이터 타입을 가지고 있다.
- 기본 타입(primitive type)
- 값을 저장할 때, 데이터의 실제 값이 저장된다
- 정수 타입(byte, short, int, long), 실수 타입(double, float), 문자 타입(char), 논리 타입(boolean)
- 참조 타입(reference type)
- 값을 저장할 때, 데이터가 저장된 곳을 나타내는 주소 값이 저장된다.
- 객체의 주소를 저장, 8개의 기본형을 제외한 나머지 타입
// 기본 타입과 참조 타입의 차이
int primitive = 1;
Object reference = new Object();
System.out.println(primitive);
System.out.println(reference);
// output
1
java.lang.Object@43a25848
리터럴(Literal)
사전적으로 '문자 그대로의'라는 뜻을 가지며 프로그래밍에서의 리터럴은 문자가 가리키는 값 그 자체를 의미한다.
int num; // 변수 선언
num = 7; // 값 할당
// 위의 예제에서는 할당된 7이 리터럴이다.
// 변수 뿐만아니라 상수에도 할당할 수 있다.
// 정수형 리터럴 20을 정수형 변수 currentAge에 할당
int currentAge = 30;
// 실수형 리터럴 3.14159를 실수형 변수 pi에 할당
final double PI = 3.14159;
// 논리형 리터럴 true를 논리형 변수 boolean에 할당
boolean isHuman = true;
// 문자형 리터럴 'A'를 문자형 변수 firstAlphabet에 할당
char firstAlphabet = 'A';
// 문자열 리터럴 "CodeStates"를 문자열 타입 변수 learnedAt에 할당
String hello = "Hello Java";
// float 타입의 변수에 실수형 리터럴을 할당할 때, 리터럴 뒤에 접미사 f를 붙여줘야한다.
// long 타입의 변수에 정수형 리터럴을 할당할 때, 리터럴 뒤에 접미사 L을 붙여줘야 한다.
//소문자 l을 붙여도 되지만, 숫자와의 혼동을 방지하기 위해 보통 대문자 L을 사용한다.
float weight = 74.5f;
final long LIGHT_YEAR = 9460730472580L;
float f = 9460730472580.0F;
double d = 9460730472580.0D;
정수 타입
Type | Memory | Range |
byte | 1byte | -128(-27) ~ 127(27 - 1) |
short | 2byte | -32,768(-215) ~ 32,767(215 - 1) |
int | 4byte | -2,147,483,648(-231) ~ 2,147,483,647(231 - 1) |
long | 8byte | -9,223,372,036,854,775,808(-263) ~ 9,223,372,036,854,775,807(263 - 1) |
// 각 데이터 타입의 표현 범위에 맞는 값을 할당
byte byteNum = 123;
short shortNum = 12345;
int intNum = 123456789;
long longNum = 12345678910L;
// 각 데이터 타입의 표현 범위에 벗어난 값을 할당하고 있어 에러 발생
byte byteNum = 130;
short shortNum = 123456;
int intNum = 12345678910;
// 숫자가 길면 언더바로 구분 가능
int intNum2 = 12_345_678;
long longNum2 = 12_345_678_910L;
정수형 오버플로우와 언더플로우
- 오버플로우(Overflow)
- 자료형이 표현할 수 있는 범위 중 최대 값 이상의 값을 표현하는 경우 발생
- 최대 값을 넘어가면 해당 데이터 타입의 최소 값으로 값이 순환
- ex) byte overflowTest = 127 + 1 != 128
- == -128 (최소값)
- 언더플로우(Underflow)
- 자료형이 표현할 수 있는 범위 중 최소 값 이하의 값을 표현한 경우 발생
- 최소 값을 넘어가면 해당 데이터 타입의 최대 값으로 값이 순환
- ex) byte underflowTest = -128 - != -129
- == 127
실수 타입
실수는 소숫점을 가지는 값을 의미하며 float형과 double형이 있다.
Type | memory | range | 정밀도 |
float | 4byte | 음수 : -3.4 * 1038 ~ -1.4 * 10-45 양수 : 1.4 * 10-45 ~ 3.4 * 1038 |
7자리 |
double | 8byte | 음수 : -1.8 * 10308 ~ -4.9 * 10-324 양수 : 4.9 * 10-324 ~ 1.8 * 10308 |
15자리 |
- 실수 타입도 정수 타입과 같이 범위를 벗어나면 오버플로우와 언더플로우가 발생하며 다른 점은 오버플로우에서 최대 범위를 벗어났을 경우 값이 무한대가 되며 언더플로우에서 최소 범위를 벗어났을 경우 값이 0이 된다.
- 주의 사항으로 double 타입 리터럴에는 접미사 d or D를 붙여도 붙이지 않아도 되지만, float타입 리터럴에는 반드시 접미사 f를 붙여줘야 한다.
- double 타입은 float 타입보다 더 큰 실수를 저장할 수 있으며 더 정확하게 저장할 수 있다.
// float형 리터럴을 float형 변수에 할당
float num1 = 3.14f;
// double형 리터럴을 double형 변수에 할당
double num2 = 3.141592d;
double num3 = 3.141592;
논리 타입
참 or 거짓을 저장할 수 있는 데이터 타입으로 오직 true or false를 값으로 가진다.
// boolean형 리터럴 할당
boolean isRainy = true;
boolean isAdult = false;
문자 타입
단 하나의 문자만 저장할 수 있는 데이터 타입이다.
// char 형 리터럴 할당
char letter1 = 'a';
char letter2 = 'ab'; // 에러 : 단 하나의 문자만 할당할 수 있다.
char letter3 = "a"; // error: 오직 작은따옴표('')를 사용해야 한다
char letter = 65; // Unicode로 변환
System.out.print(letter); // 출력 결과 : A
타입 변환 = 형변환
boolean을 제외한 모든 기본 타입은 서로 타입을 변환할 수 있으며 자동으로 타입이 변환되는 경우를 자동 타입 변환(자동 형변환)이라 하고 수동으로 변환해주는 경우를 수동 타입 변환(강제 형변환)이라 한다.
자동 타입 변환
1. 바이트 크기가 작은 타입에서 큰 타입으로 변환할 때(ex. byte → int)
2. 덜 정밀한 타입에서 더 정밀한 타입으로 변환할 때 (예 : 정수 → 실수)
수동 타입 변환
차지하는 메모리 용량이 더 큰 타입에서 작은 타입으로는 자동 타입 변환이 되지 않으며 이때 더 큰 데이터 타입을 작은 데이터 타입의 변수에 저장하기 위해서는 수동으로 타입을 변환해 주어야 하는데 이를 캐스팅(casting)이라 한다.
// 좌측 타입이 우측 타입으로 자동 타입 변환될 수 있음.
// 반대로 우측 타입이 좌측 타입으로 타입을 변경할 경우 강제 타입 변환(Casting)이 필요함
byte(1) -> short(2)/char(2) -> int(4) -> long(8) -> float(4) -> double(8)