대원칙
- 자바는 항상 변수의 값을 복사해서 대입한다.
변수 선언과 초기화
- 변수 선언
- 메모리 공간을 확보해서 데이터를 저장할 그릇을 만드는 것
- 변수 이름을 통해 해당 메모리공간에 접근한다.
- 변수 초기화
- 선언한 변수에 처음으로 값을 저장하는 것
- 초기화하지 않고 사용할 경우 컴파일 에러
- 변수를 선언하면 어떤 메모리 공간을 차지하지만, 해당 공간은 다른 프로그램이 사용하고 종료되어 남겨진 알 수 없는 값이 있을 수 있다.
- 따라서, 초기화하지 않으면 이상한 값이 출력될 수 있으므로, 자바는 문제를 예방하기 위해 변수 접근 전에 초기화를 강제
- 선언 및 초기화 방식 예제
// 1. 변수 선언, 초기화 각각 따로 int a; a = 1; System.out.println(a); // 2. 변수 선언과 초기화를 한번에 int b = 2; System.out.println(b); // 3. 여러 변수 선언과 초기화를 한번에 int c = 3, d = 4; System.out.println(c); System.out.println(d); // 4. 여러 변수 선언을 한번에 int e, f;
컴파일 에러 & 런타임 에러
컴파일 에러는 문법에 맞지 않았을 때 발생하는 에러로 오류를 빠르고 명확하게 찾을 수 있어서 좋은 에러이다.
반면에, 런타임 에러는 프로그램 실행 시 발생하는 에러로 미리 예방이 어려워 나쁜 에러이다. 예를 들어, 고객이 계좌이체를 했는데 내 돈은 나가고 상대방에게 돈이 안들어간 경우 돈이 증발되는 비극이 런타임에서 발생할 수 있다.
주로 사용하는 변수 타입
- 정수
-
int
(기본) -
long
(리터럴 값이 20억이 넘을 것 같을 때 사용)
-
- 실수
double
- 불린형
boolean
- 문자열
-
String
- 문자 하나든 문자열이든 모두 String을 사용하는 것이 편리하다.
- 문자 길이에 따라 메모리 공간이 동적으로 달라짐
-
- 예외: 파일 다루기
-
byte
(파일은 바이트 단위로 다루므로 파일 전송, 파일 복사 등에 주로 사용)
-
변수 명명 핵심 관례
- 클래스는 대문자로 시작, 나머지는 소문자로 시작
- 예외
- 상수는 모두 대문자를 사용하고 언더바로 구분
- 패키지는 모두 소문자로 사용
전위 & 후위 증감 연산자
- 전위 증감 연산자
- 증감 연산이 먼저 수행된 후 나머지 연산 수행
- 후위 증감 연산자
- 다른 연산이 먼저 수행된 후 증감 연산 수행
- 예제
public class OperatorAdd { public static void main(String[] args) { // 전위 증감연산자 int a = 1; int b = 0; b = ++a; System.out.println("a = " + a + ", b = " + b); // a = 2, b = 2 // 후위 증감연산자 a = 1; b = 0; b = a++; System.out.println("a = " + a + ", b = " + b); // a = 2, b = 1 } }
새로운 switch syntax
자바 14부터 조금 더 깔끔한 switch 문법이 도입되었다.
public class Switch {
public static void main(String[] args) {
int grade = 2;
int coupon = switch (grade) {
case 1 -> 1000;
case 2 -> 2000;
case 3 -> 3000;
default -> 500;
};
System.out.println("발급받은 쿠폰 " + coupon);
}
}
삼항 연산자 syntax 예제
String coffee = (time < 3) ? "caffeine" : "decaffeine";
do-while문
조건에 상관없이 무조건 한 번은 코드를 실행한다.
public class DoWhile {
public static void main(String[] args) {
int i = 5;
do {
System.out.println(i);
i++;
} while (i < 3);
}
}
무한 루프 For문
For문은 초기식, 조건식, 증감식의 생략이 가능하고 모두 생략할 경우 무한 루프 반복문이 된다.
(= while true)
public class ForLoopInfinite {
public static void main(String[] args) {
int i = 1;
for (;;) {
if (i >= 10) {
System.out.println("Exit on 10");
break;
}
System.out.println(i);
i++;
}
}
}
다음과 같이 특정식만 생략하는 경우도 가능하다. 결과는 위와 동일하다.
public class ForLoopInfinite {
public static void main(String[] args) {
for (int i = 1; ; i++) {
if (i >= 5) {
System.out.println("Exit on 5");
break;
}
System.out.println(i);
}
}
}
지역 변수와 스코프
- 지역 변수(Local Variable)
- 특정 지역에서만 사용할 수 있는 변수
- 지역은 코드 블록(
{}
)을 의미 - 자신이 선언된 코드 블록 안에서만 생존하고, 블록을 벗어나면 제거된다.
- 스코프(Scope)
-
변수의 접근 가능한 범위
- for문의 초기식은 for문 내 scope에서만 접근 가능하고 바깥에서는 사용할 수 없다.
- 변수의 스코프를 꼭 필요한 곳에 한정해 사용해야 메모리를 효율적으로 사용하고 더 유지보수하기 좋은 코드가 된다.
-
main()
코드 스코프의 변수는 프로그램이 종료될 때까지 메모리에 유지되어 비효율적이다. (비효율적인 메모리 사용) - 필요한 곳 바깥에서부터 변수를 선언하면 생각해야 할 변수가 늘어 복잡하다. (코드 복잡성 증가)
-
-
변수의 접근 가능한 범위
연산 시 주요 핵심
-
같은 타입끼리의 연산 결과는 타입이 동일하다. (
int
+int
는int
다) -
서로 다른 타입의 계산은 큰 범위로 자동 형변환이 일어난다. (
int
+long
은long
+long
)
자동 형변환과 명시적 형변환
- 자동 형변환
- 작은 범위에서 큰 범위로 대입은 허용한다.
-
int
<long
<double
-
- 큰 범위에서 작은 범위는 문제가 발생
- 소수점 버림
- 오버플로우
- 다른 타입끼리의 연산 시 큰 범위로 자동 형변환이 발생한다.
- 작은 범위에서 큰 범위로 대입은 허용한다.
- 명시적 형변환
- 위험을 감수하고 데이터 타입을 강제로 변경하는 것
-
자바는 기본적으로 큰 범위에서 작은 범위의 대입에 대해 컴파일 에러를 발생시킨다.
- 은행 이자를 계산하는데 타입 문제로 (double -> int) 이자가 날아가버리는 등의 큰 문제를 방지하기 위해
- 따라서, 큰 범위에서 작은 범위 대입은 명시적 형변환이 필요하다.
(int) 3.0
- 형변환 예제
public class Casting { public static void main(String[] args) { int div1 = 3 / 2; System.out.println("div1 = " + div1); //1 double div2 = 3 / 2; System.out.println("div2 = " + div2); //1.0 double div3 = 3.0 / 2; System.out.println("div3 = " + div3); //1.5 double div4 = (double) 3 / 2; System.out.println("div4 = " + div4); //1.5 int a = 3; int b = 2; double result = (double) a / b; System.out.println("result = " + result); //1.5 } }
데이터 타입 분류
- 기본형(Primitive type)
-
int
,long
,double
,boolean
처럼 데이터 값을 변수에 직접 저장할 수 있는 데이터 타입 - 데이터 사이즈가 정해져 있음 (정적)
- 더 빠르고 효율적인 메모리 처리
-
- 참조형(Reference Type)
- 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입 (배열, 객체)
- 생성 시점에서 동적 메모리 할당
- 유연성 있고 더 복잡한 데이터 구조를 다룰 수 있음
배열
- 따로 초기화 하지 않는 경우, 배열 생성시 내부 값이 자동으로 초기화된다.
- 숫자는
0
, 불린형은false
, 문자열은null
- 숫자는
- 배열 변수는 실제 배열이 존재하는 메모리 공간에 대한 주소(참조값)를 담는다.
-
배열 초기화 예제
```java
public class ArrayInitialization {
public static void main(String[] args) {
// 기본 선언 및 배열 생성
int[] students1;
students1 = new int[5];// 선언 & 초기화 int[] students2 = new int[]{1, 2, 3, 4, 5}; // 선언 & 더 간단한 초기화 int[] students3 = {1, 2, 3, 4, 5}; // 오류 케이스 // int[] students4; // students4 = {1, 2, 3, 4, 5}; } } ```
For-Each문(Enhanced For Loop)
컬렉션(배열, set etc…)의 요소를 탐색할 때 조금 더 편리한 기능을 제공하는 문법이다.
컬렉션 요소들을 처음부터 끝까지 탐색한다.
public class EnhancedForLoop {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println(number);
}
}
}
메서드(Method) 유의점
- 자바는 항상 변수의 값을 복사해서 대입한다. (인자가 파라미터로 전달될 때도 마찬가지다.)
- 메서드 호출이 끝나면 메서드 내 파라미터 변수, 로컬 변수들이 모두 메모리에서 제거된다.
- 메서드 호출 시에도 인자의 타입이 메서드 파라미터의 타입과 똑같아야 하므로, 명시적 형변환이 필요하거나 자동 형변환이 일어날 수 있다.
메서드 오버로딩(Method Overloading)
이름이 같고 매개변수가 다른 메서드를 여러개 정의하는 것을 의미한다.
- 메서드 시그니처(method signature)
- 자바에서 메서드를 구분할 수 있는 고유한 식별자
- 메서드 시그니처 = 메서드 이름 + 매개변수 타입(순서)
- 반환 타입은 시그니처에 포함되지 않는다.
- 규칙
- 메서드의 이름이 같아도 매개변수의 타입 및 순서가 다르면 오버로딩할 수 있다. (=메서드 시그니처가 달라서 가능)
- 반환 타입은 인정하지 않는다.
- 여러 개의 메서드를 오버로딩했을 때, 자바는 먼저 본인의 타입에 최대한 맞는 메서드를 찾아 실행하고, 그래도 없으면 형변환 가능한 타입의 메서드를 찾아 실행한다.
메서드 오버로딩 유의 케이스
아래 두 가지는 메서드 오버로딩 하지 못한다.
메서드 시그니처가 같기 때문!
int add(int a, int b)
int add(int c, int d)