Java 환경 구축 및 기본 문법
1. Java 및 IDE 설치 환경 구성
Java 다운로드
Java 설치 및 환경 변수 등록
-
설치 방식 Java 설치 패키지(.exe)를 실행하는 대신 binary 파일을 다운로드하여 원하는 경로에 압축 해제 후 관리.
-
환경 변수 설정
-
JAVA_HOME: JDK가 설치된 경로를 시스템 변수로 지정.
-
Path (environment-path):
%JAVA_HOME%\bin을 등록하여 어디서든 Java 명령어를 사용할 수 있게 함. -
CLASSPATH: Java 클래스 라이브러리를 참조하기 위한 경로 설정.
-
설정 목적: 특정 버전에 종속되지 않고, 사용자가 원하는 Java 버전을 직접 등록하여 유연하게 사용하기 위함.
Eclipse IDE 설치 및 세팅
- 패키지 :
Eclipse IDE for Enterprise Java and Web Developers(Web Developer 기준) 설치. - 환경설정 : 워크스페이스 인코딩(UTF-8) 설정 및 컴파일러 환경 세팅 완료.
2. Java 실행 원리 (Compile & Runtime)
개념 확인
- Compile (컴파일): 개발자가 작성한
.java소스 코드를 컴퓨터(JVM)가 이해할 수 있는.class(바이트코드) 파일로 변환하는 과정. - Runtime (런타임): 컴파일된 파일을 실제 실행 환경(JVM)에서 메모리에 올려 프로그램을 구동하는 단계.
HelloWorld.java 실행 예시
java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}[터미널 실행 명령어]
bash
# 1. 컴파일 (HelloWorld.class 파일 생성)
javac HelloWorld.java
# 2. 실행 (결과 확인)
java -cp . HelloWorld3. 변수(Variable)와 타입(Type)
- 변수: 데이터를 저장하기 위해 할당받은 메모리 공간의 이름.
- 타입: 변수에 저장할 데이터의 종류와 크기를 결정 (예: 정수형 int, 실수형 double, 문자열 String 등).
4. 표준 출력과 에러 출력 (sout, serr)
각 변수 타입별 출력 예시입니다. System.out은 일반 출력, System.err은 에러 메시지 출력에 사용됩니다.
java
public class OutputExample {
public static void main(String[] args) {
// 변수 선언 및 초기화
String name = "김현진";
int age = 20;
double score = 95.5;
boolean isPassed = true;
// 표준 출력 (sout)
System.out.println("--- Standard Output ---");
System.out.println("이름: " + name);
System.out.println("나이: " + age);
System.out.println("점수: " + score);
System.out.println("합격여부: " + isPassed);
// 표준 에러 출력 (serr)
System.err.println("--- Error Output ---");
System.err.println("시스템 경고: 데이터 로그를 확인하세요.");
}
}5. 특수 문자 표기 방법 (Escape Sequence)
문자열 내에서 특수한 기능을 수행하거나 따옴표 등을 표현할 때 사용하는 Escape 문자 예시입니다.
java
public class EscapeExample {
public static void main(String[] args) {
// \n: 줄바꿈, \t: 탭, \": 큰따옴표, \\: 백슬래시
System.out.println("1. 줄바꿈 예시:\n이 다음은 아랫줄에 출력됩니다.");
System.out.println("2. 탭 예시:\t탭만큼 띄워집니다.");
System.out.println("3. 따옴표 예시: \"Java는 정말 재밌습니다.\"");
System.out.println("4. 경로 예시: C:\\dev\\java_study");
// 혼합 사용
System.out.println("항목\t값\n이름\t\"김현진\"");
}
}6. 다양한 변수 사용에 앞서 고민할 사항
1. 기본형(Primitive) vs 래퍼 클래스(Wrapper Class)
Java 개발 시 가장 먼저 마주하는 선택은 데이터를 기본형으로 선언할 것인지, 아니면 객체 형태인 래퍼 클래스로 선언할 것인지에 대한 결정입니다.
개념 비유를 통한 이해
- 기본형 -> 주머니 속의 현금: 별도의 절차 없이 즉시 계산에 사용할 수 있으며 가볍고 빠릅니다. 하지만 은행의 자동 이체나 명세서 발행과 같은 부가 서비스는 이용할 수 없습니다.
- 래퍼 클래스 -> 은행 계좌: 현금을 입금하거나 출금하는 과정이 필요하여 조금 더 무겁지만, 계좌 잔고가 없는 상태(null)를 표현할 수 있고 은행이 제공하는 다양한 금융 서비스(메서드)를 활용할 수 있습니다.
실무적 선택 기준
- 성능 최적화가 필요한 대량 연산 -> 기본형 사용: 반복문 내에서 수백만 번의 산술 연산이 일어난다면 메모리 사용량이 적고 접근 속도가 빠른 기본형이 압도적으로 유리합니다.
- 데이터베이스 연동 및 결측치 처리 -> 래퍼 클래스 사용: DB의 특정 컬럼이 비어 있을 때(NULL), 이를 Java의 0으로 받을 것인지 아니면 실제 데이터가 없음을 뜻하는 null로 받을 것인지 구분해야 할 때 래퍼 클래스가 필수적입니다.
- 제네릭(Generics) 활용 -> 래퍼 클래스 사용: ArrayList와 같이 컬렉션 프레임워크를 사용할 때는 반드시 객체 타입인 래퍼 클래스를 사용해야 합니다.
Nullable 처리 시 주의점
- 래퍼 클래스는 null을 허용하기 때문에 산술 연산 시 자동으로 기본형으로 변환되는 과정(Unboxing)에서 NullPointerException이 발생할 위험이 큽니다.
- 실무에서는 Optional 클래스나 삼항 연산자를 사용하여 값이 없을 경우의 기본값을 명시적으로 지정하는 방식을 권장합니다.
주제 1 관련 예시 코드
java
import java.util.Optional;
public class PrimitiveWrapperComparison {
public static void main(String[] args) {
// 1. 기본형과 래퍼 클래스 선언
int primitiveAge = 30;
Integer wrapperAge = null; // 결측치 표현 가능
// 2. Nullable 처리 로직 (실무적 방어 코드)
// wrapperAge가 null일 경우 0을 기본값으로 할당하여 안전하게 연산 수행
int safeAge = (wrapperAge != null) ? wrapperAge : 0;
System.out.println("안전하게 변환된 나이 -> " + safeAge);
// 3. Optional을 활용한 null 처리
int optionalAge = Optional.ofNullable(wrapperAge).orElse(-1);
System.out.println("Optional로 처리된 나이 -> " + optionalAge);
// 4. 주의 -> 래퍼 클래스 간 값 비교 시 .equals() 사용 필수
Integer scoreA = 150;
Integer scoreB = 150;
// 주소 비교 (위험)
System.out.println("== 비교 결과 -> " + (scoreA == scoreB));
// 값 비교 (권장)
System.out.println("equals 비교 결과 -> " + scoreA.equals(scoreB));
}
}2. 가변(Mutable) vs 불변(Immutable) 객체
객체의 상태를 생성 후에 변경할 수 있는지 여부는 시스템의 안정성과 멀티스레드 환경에서의 데이터 정합성을 결정짓는 중요한 설계 포인트입니다.
개념 비유를 통한 이해
- 가변 객체 -> 화이트보드: 누구나 언제든지 적힌 내용을 지우고 새로 쓸 수 있습니다. 효율적이지만, 잠시 자리를 비운 사이 누가 내용을 바꿨는지 추적하기 어렵습니다.
- 불변 객체 -> 인쇄된 보고서: 한 번 출력되면 내용을 고칠 수 없습니다. 수정이 필요하면 새로운 보고서를 다시 출력해야 하지만, 보고서를 읽는 모든 사람이 동일한 내용을 보고 있다는 확신을 가질 수 있습니다.
구분의 이유와 장단점
- 가변(Mutable) 객체 -> 상태를 자주 변경해야 하는 경우 유리합니다. (예: StringBuilder) 새로운 객체를 계속 생성하지 않아도 되므로 메모리 절약이 가능합니다. 하지만 데이터가 언제 어디서 바뀌었는지 파악하기 힘든 부작용(Side-Effect)이 발생할 수 있습니다.
- 불변(Immutable) 객체 -> 상태가 변하지 않으므로 신뢰도가 높습니다. 멀티스레드 환경에서 별도의 동기화 작업 없이 공유가 가능하며, 값이 바뀔 염려가 없어 디버깅이 매우 쉽습니다.
불변 설계를 위한 Builder 패턴의 효용성
- 필드가 많은 객체를 불변으로 만들 때, 모든 필드를 생성자에 넣으면 가독성이 떨어집니다.
- Builder 패턴을 사용하면 필요한 데이터만 선택적으로 입력하면서도, 최종적으로 생성된 객체는 수정이 불가능한 상태로 유지할 수 있어 실무에서 선호되는 방식입니다.
주제 2 관련 예시 코드
java
// 불변 객체 설계를 위한 클래스 정의
final class UserAccount {
// 모든 필드를 private final로 선언하여 변경 가능성 차단
private final String accountId;
private final String ownerName;
private final long balance;
// 생성자를 private으로 설정하여 Builder를 통해서만 객체 생성 유도
private UserAccount(Builder builder) {
this.accountId = builder.accountId;
this.ownerName = builder.ownerName;
this.balance = builder.balance;
}
// Getter만 제공하고 Setter는 작성하지 않음
public String getAccountId() { return accountId; }
public String getOwnerName() { return ownerName; }
public long getBalance() { return balance; }
// 정적 내부 클래스로 Builder 구현
public static class Builder {
private String accountId;
private String ownerName;
private long balance;
public Builder accountId(String accountId) {
this.accountId = accountId;
return this;
}
public Builder ownerName(String ownerName) {
this.ownerName = ownerName;
return this;
}
public Builder balance(long balance) {
this.balance = balance;
return this;
}
public UserAccount build() {
return new UserAccount(this);
}
}
}
public class MutableImmutableTest {
public static void main(String[] args) {
// 1. Builder 패턴을 활용한 불변 객체 생성
UserAccount account = new UserAccount.Builder()
.accountId("ACC-12345")
.ownerName("김현진")
.balance(500000)
.build();
// 2. 데이터 조회
System.out.println("계좌 소유자 -> " + account.getOwnerName());
System.out.println("현재 잔액 -> " + account.getBalance());
// 3. 가변 객체의 대표적 예시: StringBuilder
StringBuilder mutableText = new StringBuilder("Initial Text");
mutableText.append(" -> Modified"); // 기존 객체 내부 상태 변경
System.out.println("가변 객체 출력 -> " + mutableText.toString());
}
}위 두 주제를 실무에 적용할 때 가장 중요하게 고려해야 할 포인트는 다음과 같습니다.
성능과 가독성의 균형
- 비즈니스 로직의 대부분에서는 불변 객체와 래퍼 클래스를 사용하여 데이터의 안정성을 높이는 것이 유리합니다.
- 그러나 대규모 트래픽을 처리하거나 실시간 연산이 핵심인 모듈에서는 의도적으로 가변 객체와 기본형을 사용하여 시스템 부하를 최소화하는 전략이 필요합니다.
*msi : 마이크로소프트 인스톨러(Microsoft Installer)
*binary : 소스 코드를 컴퓨터가 즉시 실행할 수 있는 영문과 숫자의 집합(기계어)으로 변환한 파일 구조를 바이너리
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.