[2025백엔드] 헤드퍼스트자바 독서 #6 - 10장. 숫자는 정말 중요합니다

2025. 8. 3. 02:28·언어공부/Java | Kotlin

자바 책보면 딱 저표정임

 

후...

스프링인액션 읽다가 JDBC에서 며칠 동안 멈출 줄이야... 덕분에 스프링인액션을 좀 읽고 헤드퍼스트자바를 읽어야겠다했는데 스프링인액션을 3장 반 정도만 읽고 다시 헤드퍼스트자바로 돌아왔다


Math 메서드

파이썬에도 Math 모듈이 있는데, 그것과 비슷하다고 생각하면 된다.

Math 클래스 내에는 round (반올림) / abs(절댓값) 등 Math 클래스의 메서드는 항상 같은 일만 진행한다. 그 안의 메서드를 바꿀 일도 없고 (바꿔서도 안되긴하지만), 오버라이드 할 일도 없고, 그 메서드의 인스턴스 변수를 쓸 일도 없다. (정적메서드)

 

이런 상황에서는 굳이 힙메모리에 클래스를 저장할 필요가 없다.

이전에 헤드퍼스트자바 #5 - 9장. 객체의 삶과 죽음 편에서 다음과 같은 대목이 있다.

생성자는 객체를 생성할 때 인스턴스 변수를 초기화시키며 힙메모리에 인스턴스 변수를 저장할 공간을 만든다는 변경점이 있지만 우선 뒤로하고

 

인스턴스 변수가 없는 클래스는 생성자가 없어도 된다라는 것의 예시가 된다. 즉 Math 클래스처럼 인스턴스 변수가 필요없는 상황에서는 굳이 new나 생성자를 통해서 객체를 생성하지 않아도 된다.

 

그냥

int x = Math.round(5.1);

이렇게 사용하면 된다.


일반메서드 / 정적메서드

Math처럼 메서드가 클래스의 인스턴스를 필요로하지 않은 경우가 있다. 책에서는 '인스턴스 변수에 따라 행동이 달라지지 않으므로 인스턴스나 객체가 필요하지 않습니다. 클래스만 있어도 됩니다.'라는 경우가 있는데 이 메서드를 정적메서드라고 한다. 메서드 선언할 때 public / private 뒤에 static 이라고 표시하면 정적메서드가 된다.

 

정적메서드는 메서드가 속한 클래스의 인스턴스 변수와는 무관하게 실행된다. 인스턴스 변수를 메서드에 담아 실행시키면 에러가 발생한다. 또한 인스턴스 변수와 무관하기 때문에 일반메서드 역시 사용하지 못한다.

 

따라서 정적메서드는 인스턴스를 만들어도 되지 않으므로, 생성자를 통해 초기화가 필요없으므로 힙에 정적메서드가 담긴 클래스의 인스턴스가 없어도 호출할 수 있다.


정적메서드의 클래스

정적메서드로만 이루어진 클래스의 경우에는 외부에서 그 클래스의 인스턴스를 생성할 수 있으나 딱히 의미가 있는 행위는 아니기에, 그 클래스의 인스턴스 생성을 막는 경우가 있다.

 

변경자를 abstract로 설정하거나, 생성자를 private로 지정하면 다른 코드에서 클래스의 인스턴스를 생성하는 것을 막을 수 있다. abstract는 추상클래스, private은 추상클래스가 아닌 경우에 사용할 수 있다. Math 클래스도 생성자가 private로 지정되어 있으며, 생성자가 private인 클래스를 외부에서 호출하는 순간 컴파일에러가 난다.

 

그런데 한 클래스 내에서 정적메서드와 일반메서드가 혼용되어 있는 경우에는 일반메서드를 위한 인스턴스가 필요하므로 클래스의 생성자를 private로 할 수 없다. 다만 한 클래스에서 정적메서드와 일반메서드를 혼용하는 경우에는 클래스의 인스턴스를 만들 방법을 정의해야한다.


클래스마다 값 공유하기: 정적 변수

다르게 말하자면 클래스 변수

비(非) 객체지향프로그래밍에서 객체지향 프로그래밍으로 넘어오는 과정에서 우스꽝스럽게도 가장 유념해야했던 사실이 '객체마다 가지는 인스턴스 변수의 값이 다르다'였다. 지금이야 당연하지만 그 때는 객체마다 가지는 인스턴스 변수가 다르다는 사실을 계속 상기시켜야 이해가 되는 부분이 있었다.

 

 하지만 같은 type의 모든 클래스마다 값을 공유할 때, 정적 변수를 사용한다. 정적변수는 변수 선언 시 static만 추가하면 된다.

private static Integer hi = 0;

뭐 이런 식으로..

이렇게 되면 객체가 새로 생성되어서 객체 레퍼런스가 새로 생성되어도 그 객체의 정적 변수는 기존의 다른 객체들과 같은 값을 참조한다. (클래스 단위로 같은 값)

정적변수는 정적메서드가 다룰 수 있고, 정적변수에 접근할 때도 정적메서드와 동일하게 클래스명.변수명 으로 접근해야한다.

 

정적변수는 JVM이 클래스를 로딩할 때 초기화된다. 뿐만 아니라

- 정적변수는 클래스에 속하는 객체가 생성되기 전에 초기화된다.

- 정적변수는 클래스에 속하는 정적메서드가 실행되기 전에 초기화된다.

 

정적변수는 일반메서드나 정적메서드 모두 접근가능하다.

컴공개에서 나온 클래스변수가 이거였다니...


static final: 상수

static final로 선언되는 변수가 있는데 (DI 세팅할 때 하는 그거) static은 해당 객체의 모든 인스턴스에 같은 값을 가지게한다는 것이고, final은 지정한 변수는 절대로 그 값을 바꿀 수 없음을 뜻한다. 즉 staic final은 클래스가 로딩되어 있는 기간은 계속 같은 값을 가진다.

 

상수의 변수명은 모두 대문자로 사용한다.

 

final 정적변수는 선언 시 초기화하거나, 정적 초기화 부분에서 초기화를 해야하며, 초기화를 하지 않을 시에는 컴파일 에러가 생긴다.


초기화 부분

클래스나 객체가 생성되었을 때, 값을 초기화하는 방법 두 가지를 배웠다.

public class User {
	private Integer age = 20;
    ...

첫 번째는 인스턴스 변수를 선언할 때 미리 값을 정해두는 것, 

public class User {
	Integer age;
    
    public User() {
    	this.age = 20;
    }
}

또 생성자로 초기화하는 것이 있다.

 

또 한 가지 방법은 초기화 부분 (초기화 블럭)을 이용하는 것인데,

 

- 인스턴스 초기화 블록

class User() {
	private int age;
    private String name;
    
    {
    	this.age = 0;
        this.name = "car";
    }
    ...
}

중괄호를 통해 정의하며, 그 안에서 값을 초기화시킨다.

생성자 오버로딩의 경우 모든 생성자마다 공통으로 초기화해야한다면 인스턴스 초기화 블록을 사용할 수 있다. 즉, 중복방지

또한 생성자보다 먼저 실행되기 때문에 생성자 실행 이전에 미리 값을 지정할 수 있다.

 

- 정적변수 초기화 블록

class User() {
	private int age;
    private String name;
    private static String hello;
    
    {
    	this.age = 0;
        this.name = "car";
    }
    static {
    	this.hello = "hi";
    }
    ...
}

앞선 인스턴스 초기화 블록 앞에 static만 붙이면 된다.

 

변수들은 기본값(null, 0 등) -> 명시적 초기화(선언 후 바로 대입) -> 초기화 블록 -> 생성자 (정적변수가 아닌경우에만) 의 순서로 초기화된다. 출처 https://tcpschool.com/java/java_member_initBlock

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

 


final 다양하게 활용하기

정적 변수 뿐 아니라 final은 다양하게 활용할 수 있고, 그 용도는 한 번 지정한 값을 바꿀 수 없다는 것으로 활용된다.

 

- 인스턴스 변수 / 로컬 변수 / 메서드 인자

변수는 선언할 때 final로 선언할 수 있으며, 그 값이 정해진 다음(생성자를 통해서 초기화나 선언할 때 미리 값을 대입한다거나 등 / 메서드 인자의 경우에는 값이 자동으로 전해지므로 선언만 한다거나)에는 절대로 값을 변경할 수 없다.

 

- 클래스

확장할 수 없다.

 

- 메서드

절대로 오버라이드할 수 없다. main 메서드가 대표적

단, 클래스가 이미 final인 경우에는 이미 확장이나 메서드 오버라이드가 불가능하므로 메서드를 선언할 때 final을 붙이지 않아도 된다.


Math 메서드 더 알아보기

- abs(수) - 절댓값 리턴

- random() - 0 이상 1 미만의 double 값 리턴. java.util.Random이 더 편리함

- round(수) - 반올림. 주어진 인자가 float, int 일 경우에는 int로, double일 경우에는 long으로 반환함

- min(a,b) - 두 인자 중 작은 값 반환. 오버로드 되어 있어 다양한 type 사용 가능

- max(a,b) - 두 인자 중 큰 값 반환. 상동

- sqrt(a) - 양의 제곱근 반환. double과 double 자리에 들어갈 수 있는 모든 인자를 받음


원시타입 관련해서..

원시타입은 변수에 값 자체가 저장되는 구조라 객체의 레퍼런스를 저장하는 객체와 다르다. 원시타입은 객체가 아니다.

근데 ArrayList 등 객체만 받을 수 있는 구조의 경우에는 원시타입을 객체로 바꿔야하는데, 이 과정을 래핑이라고 한다.

 

ArrayList<int> numArray; //컴파일에러
ArrayList<Integer> numArray;

(좌 - 원시타입 / 우 - 객체)

boolean - Boolean

String - Character

int - Integer

byte - Byte

short - Short

long - Long

float - Float

double - Double

이런 식으로 원시타입을 객체로 래핑을 해야하는데

int i = 328;
Integer iWrap = new Integer(i); //래핑, 생성자는 래퍼 생성자
int unWrapped = iWrap.intValue(); //래핑 벗기기

이런 식으로 래핑하고 래핑을 벗기고 자유롭게 쓸 수 있다.

intValue 말고도 booleanValue, doubleValue, charvalue 등으로 변형해서 쓸 수 있다.


신파일러 또 당신입니까..

근데 이렇게는 가능하다. 에러가 나지 않는다.

ArrayList<Integer> numArray = new ArrayList<Integer>;
int x = 32;
numArray.add(x);

int num = numArray.get(0);

ArrayList에 들어가는 값을 Integer로 선언했지만 int 변수를 자유롭게 넣었다 뺐다할 수 있는데, 그 이유는 컴파일러가 자동으로 래핑을 처리해주기 때문이다. ArrayList에 실제로 저장되는 값은 Integer type이다.

 

int가 아니더라도 원시타입 - 객체 관계에 있는 모든 변수의 type에 대해서는 이러한 오토박싱이 자동으로 동작하며 막을 수 없다.

이런 오토박싱을 통해서 자유롭게 Integer - int 값을 왔다갔다하며 개발할 수 있다. Integer로 선언했지만 실제로는 int 값을 넣는다던가 하는 그런.. 메서드 인자, 리턴값, 불리언표현식 등 원시타입이 기존에 가지는 성질들을 그에 상응하는 객체도 그 성질을 가진다. 자세한 내용은 헤드퍼스트자바 10장 334쪽 참고. 내용이 단순하면서 많아서 읽으면서 이해하면 좋다. 모두 컴파일러의 오토박싱 덕분이다.


형변환

래퍼에는 여러 메서드들이 있다. 그 중 하나로 int - String 간 형변환으로.. 파이썬에서는 그냥 int("3") 뭐 이러면 됐는데 자바에서는

Integer.parseInt("2");

이 코드를 사용하면 된다.

Integer 래퍼클래스에서 parseInt 메서드를 호출한다. Int 이외에도

double d = Double.parseDouble("3.14");
boolean b = Boolean.parseBoolean("True"); //대소문자를 구분하지 않음

이렇게 할 수 있다.

단, 파싱에 실패하면 (parseInt("hi") 이렇게 썼다던가..) 에러가 나온다. try-catch문으로 처리 가능하다.


String으로의 형변환과 숫자 포매팅

double d = 3.28;
String stringFromDouble1 = "" + d;
String stringFromDouble2 = Double.toString(d);
String stringFromDouble3 = String.valueOf(d);

가장 간단한 세 가지 방법

 

숫자 포매팅은 숫자를 특정형식으로 변환해서 출력하는 것을 뜻한다.

여기 내용이 C, C++ 내용이 좀 있어서 잘 모르겠다. printf를 좀 알아야한다는데, C / C++은 거의 몰라서.. C로 for문 별찍기하다가 끝낸게 다. GPT에게 물어보니 출력형식(자리수, 콤마, 소수점 등)을 조정하는 행위라고 한다.

 

파이썬의 .format 메서드와 f-string과는 목적은 비슷하나 방식이 다르다. 자바의 포매팅은 C에 더 가깝다.

 

long myLong = 1_000_000_000;
String s = String.format("%,d",myLong);
System.out.println(s);

우선 가져온 예시코드

 

숫자포매팅은 포매팅 지시사항과 포매팅 대상 인자로 이루어져 있다.

format("%,d", 1_000_000_000);

여기서 "%,d"가 지시사항이고 1_000_000_000이 대상 인자다. 인자는 여러 개일 수 있으며, 포매팅 지시자로 포매팅할 수 있어야만 한다.


지시는 어떻게?

format() 메서드의 첫 번째 인자는 포맷 문자열이다. 위의 예시는 그냥 "%,d"라고 했지만 그냥 문자열 그대로 넣어도 된다. "MRCH birth 0328 not today"라던지. 물론 포매팅이 되지는 않을 것이다.

 

문자열의 % 가 있다면 이는 다른 인자(포매팅 대상 인자)를 나타내는 변수다.

String.format("I have %.2f, bugs to fix.", 328.0336);

출력 -> I have 328.03 bugs to fix.

이런식으로 진행된다.

%.2f가 두 번째 인자(첫번째 포매팅 대상 인자)를 표시하기 위한 포맷 지시자이다.

 

즉, %가 있으면 % 자리에 포매팅 대상 인자를 대입하고 % 기호 뒤의 문자를 기준으로 포매팅이 이루어진다.

 

포맷지시자는 지정된규칙이 있다.

%,d는 10진 정수를 쉼표를 집어넣어서 포매팅

%.2f는 부동소수점 수를 소수점 셋째 자리에서 반올림하도록 포매팅

%,.2f는 부동소수점 수를 소수점 셋째 자리에서 쉼표를 집어넣어 포매팅이라는 뜻이다.


포맷지시자

포맷지시자는 다섯 개의 부분으로 나뉘어 질 수 있다.

 

% / 인자번호 / 플래그 / 너비 / .정밀도 / 타입

볼드체는 필수로 들어가야하는 것이다.

 

- 인자 번호는 두 개 이상의 인자가 들어온 경우 어떤 인자를 사용할지 지정. 우선 이 챕터에서는 안다룸

- 플래그는 위에서의 쉼표 집어넣기, 음수는 괄호 안에 등 포매팅 옵션을 지정

- 너비는 사용할 최소 문자개수. 출력문 너비보다 길어지면 그냥 출력되지만 짧을 경우 0이 붙어서 출력됨

- .정밀도는 .2 등 소수점 아래 몇자리까지 출력할 지 지정함

- 타입은 반드시 지정해야함

 

타입은 필수적이다. 대표적으로

%d - 10진정수

%f - 부동소수점 수

%x - 16진수

%c - 문자

등이 있다.


정적임포트

앞서 Math 클래스를 소개할 때 늘 Math.abs, Math.round 이런 식으로 사용했는데,

static import를 사용하면 Math, 즉 패키지명을 빼버리고 바로 메서드 이름을 쓸 수 있다.

 

import static java.lang.Math.*;

...

    System.out.println(abs(-50));
...

이런식으로 정적import를 통해 간단하게 메서드명만으로도 메서드를 사용할 수 있다.

 

다만 정적import는 코드의 가독성을 해칠 수도 있어서 사용에는 신중해야한다.

또한 정적import 후 사용되는 메서드명(위의 코드에서는 abs)은 더이상 변수로 사용하지 못한다.


System 클래스의 정적변수 out을 호출

System.out -> 정적변수를 패키지명.변수명으로 호출함

println이 메서드이다.

 

정적메서드는 객체의 상태가 필요없고 공통적인 로직을 처리할 때 쓰인다.

 

로직 실행 순서는

클래스가 로딩될 경우 초기화블록 → 생성자 이전의 코드 → 생성자 순이다.

 

상속관계의 클래스의 경우에는

초기화블록과 생성자 모두 상위클래스가 먼저 실행된다.

 

생성자는 static할 수 없다.

'언어공부 > Java | Kotlin' 카테고리의 다른 글

[2025백엔드] 헤드퍼스트자바 독서 #8 - 12장. 람다와 스트림  (4) 2025.08.13
[2025백엔드] 헤드퍼스트자바 독서 #7 - 11장. 자료구조  (5) 2025.08.13
[2025백엔드] 헤드퍼스트자바 독서 #5 - 9장. 객체의 삶과 죽음  (3) 2025.07.27
[2025백엔드] 헤드퍼스트자바 독서 #4 - 8장. 심각한 다형성  (2) 2025.07.26
[2025백엔드] 헤드퍼스트자바 독서 #3. - 7장. 객체 마을에서의 더 나은 삶  (1) 2025.07.26
'언어공부/Java | Kotlin' 카테고리의 다른 글
  • [2025백엔드] 헤드퍼스트자바 독서 #8 - 12장. 람다와 스트림
  • [2025백엔드] 헤드퍼스트자바 독서 #7 - 11장. 자료구조
  • [2025백엔드] 헤드퍼스트자바 독서 #5 - 9장. 객체의 삶과 죽음
  • [2025백엔드] 헤드퍼스트자바 독서 #4 - 8장. 심각한 다형성
_cortisol_
_cortisol_
개발을 합니다.
  • _cortisol_
    Cortisol
    _cortisol_
  • 전체
    오늘
    어제
    • 분류 전체보기 (211)
      • 신년사 (3)
        • 2025년 (2)
        • 2026년 (1)
      • CS (59)
        • JVM (12)
        • 백엔드 (20)
        • 언어구현 (1)
        • 객체지향 (1)
        • 논리회로 (5)
        • 컴퓨터구조 (9)
        • 데이터베이스 (1)
        • 컴퓨터 네트워크 (10)
      • 언어공부 (64)
        • Java | Kotlin (48)
        • JavaScript | TypeScript (9)
        • C | C++ (6)
      • 개인 프로젝트 (11)
        • [2025] Happy2SendingMails (3)
        • [2026] 골든리포트! (8)
        • [2026] 순수자바로 개발하기 (0)
        • 기타 이것저것 (0)
      • 팀 프로젝트 (29)
        • [2025][GDG]홍대 맛집 아카이빙 프로젝트 (29)
      • 알고리즘 (13)
        • 백준풀이기록 (11)
      • 놀이터 (0)
      • 에러 수정일지 (2)
      • 고찰 (24)
        • CEOS 23기 회고록 (2)
  • 링크

    • 컬러잇 개발블로그
  • 공지사항

  • 인기 글

  • 태그

    144
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
_cortisol_
[2025백엔드] 헤드퍼스트자바 독서 #6 - 10장. 숫자는 정말 중요합니다
상단으로

티스토리툴바