etc./Code_SEB_BE

[SEB_BE / Day 5] Java(1) 기초 변수, 타입

Smile :DK 2023. 4. 17. 20:57

 

Java란?

Java는 1995년 제임스 고슬링과 그의 동료들에 의해 시작된 프로젝트입니다. 원래 가전제품을 제어하기 위한 언어로 고안되었지만 웹의 등장으로 엄청난 성공을 거두며 주류 언어가 되었습니다.

 

Java 특징

  1. 운영체제에 독립적이다.
  2. 객체지향언어(OOP, Object Oriented Programming)이다.
  3. 함수형 프로그래밍을 지원한다.
  4. 가비지컬렉션(GC, Garbage Collection)을 통해 메모리를 관리한다.

 

Java 프로그램의 동작

  1. 코드(Code, Source) 작성
  2. 컴퓨터가 실행할 수 있는 형태로 코드를 컴파일
  3. 컴파일된 프로그램을 실행

프로그래밍 한다 → 코드(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 vs Underflow

  • 오버플로우(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)