자바의 정석 책을 공부하고 정리한 글입니다!
혹시라도 틀린 부분이 있다면 친절하게 알려주세요.
감사합니다!
CH 1. 자바를 시작하기 전에
자바는 썬 마이크로시스템즈에서 개발하여 1996년 1월에 공식적으로 발표한 객체지향 프로그래밍 언어를 말합니다.
자바의 특징
- 운영체제에 독립적이다:
자바 응용프로그램은 운영체제나 하드웨어가 아닌 일종의 에뮬레이터인 자바가상머신(JVM)하고만 통신을 합니다.
JVM이 자바 응용프로그램으로부터 전달받은 명령을 해당 운영체제가 이해할 수 있도록 변환하여 전달하죠! 따라서 자바로 작성된 프로그램은 운영체제에 독립적이지만 JVM은 운영체제에 종속적이어서 여러 운영체제 버전의 JVM을 제공합니다. - 객체지향언어이다:
자바는 객체지향개념의 특징(상속, 캡슐화, 다형성)이 잘 적용된 순수한 객체지향언어입니다. - 비교적 배우기 쉽다:
자바는 C++ 과 스몰톡이라는 객체지향언어에서 가져왔습니다. 이 언어들의 장점을 취하고, 복잡하고 불필요한 부분은 과감히 제거하여 단순화함으로서 쉽게 배울 수 있습니다. - 자동 메모리 관리:
프로그램이 실행되면, 가비지 컬렉터(Garbage Collector)가 자동적으로 메모리를 관리해주므로 프로그래머는 메모리를 따로 관리하지 않아도 됩니다. - 네트워크와 분산처리를 지원한다.
- 멀티쓰레드를 지원한다:
일반적으로 멀티쓰레드의 지원은 사용되는 운영체제에 따라 구현방법 및 처리 방식이 달라집니다. 그러나 자바에서 개발되는 멀티쓰레드 프로그램은 시스템과 관계없이 구현이 가능하며, 관련된 라이브러리(Java API) 가 제공되므로 구현이 쉽습니다. 그리고 여러 쓰레드에 대한 스케줄링을 자바 인터프리터가 담당하게 됩니다. - 동적 로딩(Dynamic Loading)을 지원한다:
자바는 동적 로딩을 지원하기 때문에 실행 시에 모든 클래스가 로딩되지 않고 필요한 시점에 클래스를 로딩하여 사용할 수 있습니다. 그 외에도 일부 클래스가 변경되어도 전체 어플리케이션을 다시 컴파일하지 않아도 됩니다.
JVM(Java Virtual Machine)
JVM은 '자바를 실행하기 위한 가상 기계(컴퓨터)' 입니다. 즉, 자바로 작성된 어플리케이션을 실행하기 위해서는 JVM은 필수적입니다.
자바로 작성된 프로그램은 JVM -> OS -> 하드웨어 로 전달됩니다. 이때, 하드웨어에 맞게 완전히 컴파일된 상태가 아니고 실행 시에 해석되기 때문에 속도가 느리다는 단점을 갖고 있습니다.
자바 개발도구(JDK)의 bin 디렉토리에 있는 주요 실행파일
- javac.exe (자바 컴파일러) - 자바소스코드를 바이트코드로 컴파일한다.
- java.exe (자바 인터프리터) - 컴파일러가 생성한 바이트코드를 해석하고 실행한다.
- javap.exe (역어셈블러) - 컴파일된 클래스파일을 원래의 소스코드로 변환한다.
- javadoc.exe (자동문서생성기) - 소스파일에 있는 주석을 이용하여 Java API 문서와 같은 형식이 문서를 자동으로 생성한다.
- jar.exe (압축프로그램) - 클래스파일과 프로그램의 실행에 관련된 파일을 압축한 것을 말한다.
JDK와 JRE
JDK(Java Development Kit): 자바 개발 도구 (JRE + 개발에 필요한 실행 파일)
JRE(Java Runtime Environment): 자바로 작성된 응용프로그램이 실행되기 위한 최소 환경 (JVM + 클래스 라이브러리)
자바프로그램의 실행과정
- 프로그램의 실행에 필요한 클레스(*.class파일)를 로드한다.
- 클래스파일을 검사한다. (파일 형식, 악성코드 체크)
- 지정된 플래스에서 main(String[] args)를 호출한다.
CH 2. 변수
프로그래밍언어에서 변수(variable)란, 값을 저장할 수 있는 메모리상의 공간을 의미합니다. 하나의 변수에는 변수에 단 하나의 값만 저장할 수 있으므로 새로운 값을 저장하면 기존의 값은 사라집니다.
변수의 선언 및 초기화 방법은 다음과 같습니다.
// 선언한 후, 초기화 하기
int year;
year = 2025;
// 선언과 초기화 동시에 하기
int month = 4;
이때 int 와 같은 키워드는 '변수 타입' 이라 부르며, year, month 와 같은 이름은 '변수 이름' 이라고 합니다.
- 변수 타입: 변수에 저장될 값의 타입으로, 저장하고자 하는 값의 종류에 맞게 변수의 타입을 지정한다.
- 변수 이름: 변수에 부여된 이름으로, 이름을 이용하여 값을 저장하고, 저장된 값을 읽어올 수 있다.
변수의 타입(자료형)은 크게 '기본형'과 '참조형으로 나눌 수 있습니다.
- 기본형 변수는 실제 값을 저장한다. (8개의 기본형 존재, 뒤에서 자세히 설명할 예정)
- 참조형 변수는 어떤 값이 저장되어 있는 주소를 값으로 갖는다.
위의 예제에서 사용한 int와 같이 실제 값을 저장하는 타입을 기본형이라고 합니다.
참조형 변수를 선언할 때는 변수의 타입으로 클래스 이름을 사용합니다. 다음은 참조변수를 선언 및 초기화하는 방법입니다:
// 참조변수 선언 및 초기화
Date today = new Date();
위의 코드에서 new 연산자를 통해 생성된 객체의 주소를 today에 저장합니다.
[참고] 참조형 변수는 null 또는 객체의 주소를 값으로 가질 수 있습니다.
변수의 명명규칙
필수적으로 지켜야 하는 규칙은 다음과 같습니다:
- 대소문자가 구분되며 길이에 제한이 없다.
- 예약어를 사용해서는 안 된다.
- 숫자로 시작해서는 안 된다.
- 특수문자는 '_'와 '$' 만을 허용한다.
이때 예약어는 프로그래밍 언어의 구문에서 사용되는 단어를 뜻하며, 다음과 같은 예약어가 있습니다:
[참고] const와 goto는 에약어이기는 하지만 사용되지 않습니다.
const와 goto를 사용하지 않는 이유
const: 자바에서는 const 대신에 final 키워드를 사용합니다.
goto: 흐름 제어의 복잡성 때문에 사용하지 않습니다.
다음은 필수적이진 않지만 자바 프로그래머에게 권장하는 규칙입니다:
- 클래스 이름의 첫 글자는 항상 대문자로, 변수와 메서드 이름의 첫 글자는 항상 소문자로 설정한다.
- 여러 단어들로 이루어진 이름은 단어의 첫 글자를 대문자로 설정한다. ex) lastIndexOf
- 상수의 이름은 모두 대문자로 하며, 여러 단어로 이루어진 경우 '_'로 구분한다. ex) MAX_NUM
위의 규칙들은 반드시 지켜야 하는 것은 아니지만, 코드를 보다 이해하기 쉽게 하기 위한 자바 개발자들의 암묵적인 약속입니다. 실제로 이 규칙을 따리지 않는다고 해서 문제가 되진 않지만 가능하면 지키는 것이 좋습니다. 또한, 변수 이름은 누가봐도 이해할 수 있는 '의미있는 이름'으로 설정하는 것이 바람직합니다.
[참고] 자바에서 모든 이름에 유니코드에 포함된 문자들을 사용할 수 있습니다. 하지만 유니코드를 인식하지 못하는 OS도 있기 때문에 클래스 이름은 ASCII코드로 하는 것이 좋습니다. 유니코드와 아스키코드에 관련된 내용은 다른 글에서 설명하도록 하겠습니다.
상수와 리터럴 (constant & literal)
상수는 변수와 마찬가지로 '값을 저장할 수 있는 공간'이지만, 한 번 값을 저장하면 다른 값으로 변경할 수 없습니다.
상수의 특징은 다음과 같습니다:
- 선언할 때 final 키워들 사용한다.
- 반드시 선언과 동시에 초기화를 해야 하며, 이후로는 값을 변경할 수 없다.
final int YEAR = 2025; // 정상 실행
final int MONTH; // 오류
YEAR = 2026; // 오류
이때 2025와 같이 사용된 숫자 또는 값 자체를 리터럴이라고 부릅니다. 원래는 이러한 숫자 또는 값들이 '상수'인데 프로그래밍에서는 상수를 다른 의미로 정의하였으므로, 리터럴이라는 용어를 사용하는 것입니다!
형식화된 출력 - printf()
'지시자'란 값을 어떻게 출력할 것인지를 지정해주는 역할을 합니다. 이러한 지시자는 println() 메서드에서는 사용할 수 없으며, printf() 함수를 통해 사용할 수 있습니다.
int year = 2025;
System.out.println("%d", year); // 오류
System.out.printf("%d", year); // 정상 출력
하지만 printf()의 경우, 출력 후 줄바꿈을 하지 않으므로, 줄바꿈 지시자인 '%n'을 따로 넣어줘야 합니다.
[참고] '\n'을 사용해도. 되지만, OS마다 줄바꿈 문자가 다를 수 있기 때문에 '%n'을 사용하는 것이 더 안전합니다.
다음은 자주 사용되는 지시자입니다:
지시자 | 설명 |
%b | 불리언 |
%d | 10진수 |
%o | 8진수 |
%x, %X | 16진수 |
%f | 10진수 |
%e, %E | 지수형태표현 |
%c | 문자 |
%s | 문자열 |
기본형
기본형은 논리형, 문자형, 정수형, 실수형으로 구성되어 있으며 총 8개의 자료형이 있습니다.
1. 논리형 - boolean
boolean형 변수는 true와 false 중 하나를 저장할 수 있으며, 기본값(default)은 false입니다.
boolean power = true;
boolean checked = False; // 에러. 대소문자가 구분되므로 true/false만 사용 가능
2. 문자형 - char
문자를 저장하기 위한 변수를 선언할 때 사용되며 단 하나의 문자만을 저장할 수 있습니다.
char a = 'A';
char b = ''; // 에러. 반드시 하나의 문자가 있어야 함
char c = ' '; // 공백은 가능
위의 예제에서 a 변수에 '문자'가 저장되는 것 같지만, 사실은 문자의 유니코드로 저장됩니다. 문자 'A'의 유니코드는 10진수로 65이며, 실제로 변수 a에 'A'가 아닌 65를 저장해도 동일한 결과를 얻습니다.
만약 어떤 문자의 유니코드르 알고 싶으면, char형 변수를 정수향으로 변환하면 됩니다.
char ch = 'A';
int code = (int)ch; // code에는 ch의 유니코드인 65가 저장됨
3. 정수형 - byte, short, int, long
정수형에는 모두 4개의 자료형이 존재하는데, 각 자료형이 저장할 수 있는 값의 범위가 서로 다릅니다.
자료형 | 크기(byte) |
byte | 1 |
short | 2 |
int (기본 자료형) | 4 |
long | 8 |
만약 4bit 2진수의 최대값인 1111에 1을 더하면 어떤 결과과 나올까?
원래 2진수 1111에 1을 더하면 10000이 되지만 4bit로는 4자리의 2진수만 저장할 수 있으므로 0000이 됩니다. 즉, 5자리의 2진수 10000 중에서 하위 4bit 만 저장하게 되는 것이죠. 이처럼 연산 과정에서 해당 타입이 표현할 수 있는 값의 범위를 넘어서는 것을 오버플로우라고 합니다.
부호없는 정수와 부호있는 정수는 표현범위(최대값, 최소값)이 다르기 때문에 오버플로우가 발생하는 시점이 다릅니다.
부호있는 정수의 경우 부호비트가 0에서 1이 될 때 오버플로우가 발생하고, 부호없는 정수의 경우 표현 범위의 값이 무한히 반복합니다.
(부호비트에 관한 내용은 정리해서 따로 올리도록 하겠습니다.)
다음은 오버플로우에 관한 예제 및 출력 결과입니다.
short sMin = -32768;
short sMax = 32767;
char cMin = 0;
char cMax = 65535;
System.out.println("sMin = " + sMin);
System.out.println("sMin - 1 = " + (short) (sMin - 1));
System.out.println("sMax = " + sMax);
System.out.println("sMax - 1 = " + (short) (sMax - 1));
System.out.println("cMin = " + (int)cMin);
System.out.println("cMin - 1 = " + (int)--cMin);
System.out.println("cMax = " + (int)cMax);
System.out.println("cMax + 1 = " + (int)++cMax);
참고로 short 타입의 정수표현 범위는 -32768 ~ 32767, char 타입의 정수표현 범위는 0 ~ 65535 입니다.
최소값 + 1 을 했더니 최소값이, 최소값 - 1 을 했더니 최대값이 나온 것을 확인할 수 있었습니다!
(제가 설명을 정말 못했지만, 자바의 정석 책에 나와있는 표를 참고해서 다시 읽어보면 이해가 될 수도 있어요..!)
4. 실수형 - float, double
실수를 저장하기 위한 타입으로는 float형과 double형이 있는데, 실수형도 저장할 수 있는 값의 범위가 서로 다릅니다.
또한 실수형은 정수형과 달리 '정밀도'라는 것도 고려를 해야합니다.
타입 | 크기(byte) | 정밀도 |
float | 4 | 7자리 |
double | 8 | 15자리 |
정밀도란 소수점 아래 몇 자리까지 표현할 수 있는지에 대한 것입니다. 즉 '얼마나 0에 가깝게 표현할 수 있는가'를 보는 것이죠. 실수형 값을 저장할 때, float타입이 아닌 double타입의 변수를 사용하는 경우는 대부분 '값의 범위' 때문이 아니라. '보다 높은 정밀도' 가 필요해서입니다! 따라서 연산속도 향상이나 메모리를 절약하려면 float, 더 큰 값의 범위 혹은 더 높은 정밀도가 필요하다면 double을 선택해야 합니다.
실수형의 오버플로우
실수형은 정수형과 달리 오버플로우가 발생하면 변수의 값은 무한대가 됩니다. 또한 실수형은 정수형에는 없는 언더플로우가 있습니다. 언더플로우란 너무 작은 수를 저장하려고 할 때, 그 수가 자료형이 표현할 수 있는 최소값보다 작아져서 0으로 가까워지는 현상을 말합니다. 이 때 변수의 값은 0이 됩니다.
형변환
형변환(캐스팅, casting)이란 변수 또는 상수의 타입을 다른 타입으로 변환하는 것을 말합니다.
형변환하는 방법은 다음과 같습니다.
int a = 10;
System.out.println((double)a); // int -> double 형변환
double d = 85.4;
System.out.println((int)d); // double -> int 형변환
위와 같이 변환하고자 하는 리터럴 앞에 변환하고자 하는 타입 + 괄호를 붙여주기만 됩니다.
정수형을 실수형으로 변환하는 경우, 10.0 과 같이 뒤에 소수점이 붙어서 출력됩니다.
반대로 실수형을 정수형으로 변환되는 경우, 소수점 이하의 값은 버려지게 됩니다! (반올림 아닙니다!)
자동 형변환
서로 다른 타입 간의 대입이나 연산을 수행할 때, 형변환으로 타입을 일치시키는 것이 원칙적이지만, 경우에 따라 형변환을 생략할 수도 있습니다. 이러한 경우에는 컴파일러가 자동적으로 형변환을 해줍니다.
자동형변환의 규칙은 다음과 같습니다:
- boolean을 제외한 나머지 7개의 기본형은 서로 형변환이 가능하다.
- 기본형과 참조형은 서로 형변환할 수 없다.
- 서로 다른 타입의 변수 간의 연산은 형변환을 하는 것이 원칙이지만, 범위가 작은 타입에서 큰 타입으로의 형변환은 생략 가능하다.
float f = 1234; // float f = (float)1234 와 같음
byte b = 1000; // byte의 범위(-128~127)를 넘어서는 값을 저장하려하므로 오류 발생
'도서 정리 > 자바의 정석' 카테고리의 다른 글
[자바의 정석] CH06 객체지향 프로그래밍Ⅰ (2) (2) | 2025.04.09 |
---|---|
[자바의 정석] CH06 객체지향 프로그래밍Ⅰ (1) (2) | 2025.04.07 |
[자바의 정석] CH05 배열 정리 (0) | 2025.04.04 |
[자바의 정석] CH04 조건문과 반복문 정리 (0) | 2025.04.03 |
[자바의 정석] CH03 연산자 정리 (1) | 2025.04.02 |