Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 자바의 정석 기초편 ch2
- 스프링 mvc1 - 서블릿
- 자바의 정석 기초편 ch4
- 자바 중급1편 - 예외 처리
- 스프링 db2 - 데이터 접근 기술
- 스프링 mvc2 - 타임리프
- 스프링 mvc2 - 검증
- jpa - 객체지향 쿼리 언어
- 스프링 입문(무료)
- 자바의 정석 기초편 ch9
- 자바의 정석 기초편 ch12
- 2024 정보처리기사 수제비 실기
- 게시글 목록 api
- 자바 기본편 - 다형성
- 스프링 db1 - 스프링과 문제 해결
- 자바의 정석 기초편 ch13
- 자바의 정석 기초편 ch1
- 자바의 정석 기초편 ch14
- 스프링 고급 - 스프링 aop
- 자바의 정석 기초편 ch6
- 스프링 mvc2 - 로그인 처리
- 자바 중급1편 - 날짜와 시간
- jpa 활용2 - api 개발 고급
- @Aspect
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch5
- 스프링 mvc1 - 스프링 mvc
- 코드로 시작하는 자바 첫걸음
- 자바의 정석 기초편 ch7
- 자바의 정석 기초편 ch11
Archives
- Today
- Total
나구리의 개발공부기록
final, final 변수와 상수, final 변수와 참조 본문
728x90
출처 : 인프런 - 김영한의 실전 자바 - 기본편 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. final 변수와 상수
1) final
(1) final 키워드
- final 키워드는 이름 그대로 끝이라는 뜻으로 변수에 final 키워드가 붙으면 더는 값을 변경할 수 없음
- 참고로 final은 class, method를 포함한 여러 곳에 붙을 수 있음
2) final - 지역변수
(1) FinalLocalMain
- final을 지역 변수에 설정할 경우 최초 한번만 할당할 수 있으므로 이후에 변수의 값을 변경하면 컴파일 오류가 발생함
- final을 지역 변수 선언시 바로 초기화 한 경우 이미 값이 할당되었기 때문에 값을 할당할 수 없음
- 매개변수에 final이 붙으면 메서드 내부에서 매개변수의 값을 변경할 수 없어 메서드 호출 시점에 사용된 값이 끝까지 사용됨
package final1;
public class FinalLocalMain {
public static void main(String[] args) {
// final 지역 변수
final int data1;
data1 = 10; // 최초 한번만 할당 가능
// data1 = 20; // 컴파일 오류 발생
// final 지역 변수
final int data2 = 10;
// data2 = 20; // 컴파일 오류 발생
method(10);
}
private static void method(final int parameter) {
// parameter = 20; // 컴파일 오류, 중간에 파라미터 값을 바꿀 수 없음
}
}
3) 필드(멤버변수)
(1) ConstructInit
- final을 필드에 사용할 경우 해당 필드는 생성자를 통해서 한번만 초기화 될 수 있음
package final1;
// final 필드 - 생성자 초기화
public class ConstructInit {
final int value;
public ConstructInit(int value) {
this.value = value;
}
}
(2) FieldInit
- final 필드를 필드에서 초기화 하면 이미 값이 설정되었기 때문에 생성자를 통해서도 초기화 할 수 없음
- static 변수에도 final을 선언할 수 있으며 CONST_VALUE로 변수 작명 방법을 대문자로 하였는데 이부분은 자바의 관례로 상수파트에서 설명함
package final1;
// final 필드 - 필드 초기화
public class FieldInit {
static final int CONST_VALUE = 10;
final int value = 10;
}
(3) FinalFieldMain
- ConstructInit과 같이 생성자를 사용해서 final 필드를 초기화 하는 경우 각 인스턴스마다 final필드에 다른 값을 할당할 수 있으며 final을 사용했기 때문에 생성 이후에는 이 값을 변경하는 것은 불가능함
- 그러나 final 필드를 필드에서 초기화 하는 경우 모든 인스턴스가 동일한 값(예제에서는 10)을 가지게 되는데, 생성자 초기화와 다르게 필드 초기화는 필드의 코드에 해당 값이 미리 정해져있기 때문임
- 모든 인스턴스가 같은 값을 사용하기 때문에 결과적으로 메모리를 낭비하게 됨(물론 JVM에 따라서 내부 최적화를 시도할 수 있음)
- 또한 메모리 낭비를 떠나서 같은 값이 계속 생성되는 것은 개발자가 보기에 명확한 중복이므로 이럴 때 사용하면 좋은 것이 static 영역임
package final1;
public class FinalFieldMain {
public static void main(String[] args) {
// final 필드 - 생성자 초기화
System.out.println("생성자 초기화");
ConstructInit constructInit1 = new ConstructInit(10);
ConstructInit constructInit2 = new ConstructInit(20);
System.out.println(constructInit1.value);
System.out.println(constructInit2.value);
// final 필드 - 필드 초기화
System.out.println("필드 초기화");
FieldInit fieldInit1 = new FieldInit();
FieldInit fieldInit2 = new FieldInit();
FieldInit fieldInit3 = new FieldInit();
System.out.println(fieldInit1.value);
System.out.println(fieldInit2.value);
System.out.println(fieldInit3.value);
// 상수
System.out.println("상수");
System.out.println(FieldInit.CONST_VALUE);
}
}
/* 실행 결과
생성자 초기화
10
20
필드 초기화
10
10
10
상수
10
*/
(4) static final
- FieldInit.CONST_VALUE는 static 영역에 존재하고 final 키워드를 사용하여 초기화 값이 변하지 않음
- static 영역은 단 하나만 존재하는 영역이므로 JVM상에서 하나만 존재하기 때문에 코드 중복과 메모리 비효율 문제를 모두 해결할 수 있음
- 이런 이유로 필드에 final + 필드 초기화를 사용하는 경우 static을 붙여서 사용하는 것이 효과적임
4) 상수
(1) 상수(Constant)
- 상수는 변하지 않고 항상 일정한 값을 갖는 수를 말하며 자바에서는 보통 단 하나만 존재하고 변하지 않는 고정된 값을 상수라 함
(2) 자바 상수 특징
- static final 키워드를 사용함
- 대문자를 사용하고 구분은 _(언더스코어)로함(관례)
- 상수는 기능이 아니라 고정된 값 자체를 사용하는 것이 목적이고 상수는 값을 변경할 수 없어 필드에 직접 접근해도 데이터가 변하는 문제가 발생하지 않으므로 필드를 직접 접근해서 사용함
(3) Constant
- 애플리케이션 안에는 수학, 시간 등등 실생활에서 사용하는 상수부터 애플리케이션의 다양한 설정을 위한 상수들이 존재할 수 있음
- 보통 이런 상수들은 애플리케이션 전반에서 사용되기 때문에 public을 자주 사용하며 특정 위치에서만 사용하게 된다면 다른 접근 제어자를 사용하면 됨
- 상수는 중앙에서 값을 하나로 관리할 수 있다는 장점도 있음
- 상수는 런타임에 변경할 수 없으며 상수를 변경하려면 프로그램을 종료하고 코드를 변경한 다음에 프로그램을 다시 실행해야 함
package final1;
// 상수
public class Constant {
// 수학 상수
public static final double PI = 3.14159265358979323846;
// 시간 상수
public static final int HOURS_IN_DAY = 24;
public static final int MINUTES_IN_HOUR = 60;
public static final int SECONDS_IN_MINUTE = 60;
// 애플리케이션 설정 상수
public static final int MAX_USERS = 1000;
}
(4) ConstantMain1 - 상수 없음
- 아래의 프로그램에서 최대 참여자 수를 1000명에서 2000명으로 변경해야 한다고 하면 2곳의 변경포인트가 발생하게 됨
- 만약 애플리케이션의 100곳에서 이숫자를 사용했따면 100곳 모두 변경해야함
- 이런 의미를 정확하게 알 수 없는 하드코딩된 숫자를 매직 넘버라고하는데 이러한 문제를 상수로 해결할 수 있음
package final1;
public class ConstantMain1 {
public static void main(String[] args) {
System.out.println("프로그램 최대 참여자 수 " + 1000);
int currentUserCount = 999;
process(currentUserCount++);
process(currentUserCount++);
process(currentUserCount++);
}
private static void process(int currentUserCount) {
System.out.println("참여자 수: " + currentUserCount);
if (currentUserCount > 1000) {
System.out.println("대기자로 등록합니다.");
} else {
System.out.println("게임에 참가합니다.");
}
}
}
(5) ConstantMain2 - 상수 사용
- 1000이 입력된 부분에 Constant.MAX_USERS 상수를 사용하여 최대 참여자 수를 변경해야 한다면 해당 상수의 값만 변경하면 됨
- 하드 코딩된 숫자가 아니라 사람이 인지할 수 있는 MAX_USERS라는 변수명으로 코드가 작성되어 매직 넘버 문제를 해결함
package final1;
public class ConstantMain2 {
public static void main(String[] args) {
System.out.println("프로그램 최대 참여자 수 " + Constant.MAX_USERS);
int currentUserCount = 999;
process(currentUserCount++);
process(currentUserCount++);
process(currentUserCount++);
}
private static void process(int currentUserCount) {
System.out.println("참여자 수: " + currentUserCount);
if (currentUserCount > Constant.MAX_USERS) {
System.out.println("대기자로 등록합니다.");
} else {
System.out.println("게임에 참가합니다.");
}
}
}
2. final 변수와 참조
1) final 변수와 참조
(1) final은 변수의 값을 변경하지 못하게 막음
- 변수는 크게 기본형 변수와 참조형 변수가 있으며 기본형 변수는 10, 20 같은 값을 보관하고 참조형 변수는 객체의 참조값을 보관함
- final을 기본형 변수에 사용하면 값을 변경할 수 없고 참조형 변수에 사용하면 참조값을 변경할 수 없음
(2-1) Data
- int value: final이 아닌 변경할 수 있는 변수가 있음
package final1;
public class Data {
public int value;
}
(2-2) FinalRefMain
- 참조형 변수 data에 final을 붙이고 변수 선언 시점에 참조값을 할당했으므로 참조값을 변경할 수 없음
- 그러나 참조 대상의 객체의 값은 변경할 수 있는데, Data.value는 final이 아니므로 값을 변경할 수 있음
- 즉, 참조형 변수 data에 final이 붙으면 참조형 변수에 들어있는 참조값을 다른 값으로 변경하지 못하는 것(다른 객체를 참조할 수 없는 것)이지 이 변수 이외의 다른 곳에 영향을 주는 것이 아님
- 정리하면 참조형 변수에 final이 붙으면 참조 대상 자체를 다른 대상으로 변경하지 못하는 것이고 참조하는 대상의 값은 변경할 수 있음
package final1;
public class FinalRefMain {
public static void main(String[] args) {
final Data data = new Data();
// data = new Data(); // final 이므로 변경 불가, 컴파일 오류 발생
// 참조 대상의 값은 변경
data.value = 10;
System.out.println(data.value);
data.value = 20;
System.out.println(data.value);
}
}
2) 정리
(1) 유용한 제약
- final은 매우 유용한 제약이므로 만약 특정 변수의 값을 할당한 이후에 변경하지 않아야 한다면 final을 사용하면 됨
- 예를 들어서 고객의 id를 변경하면 큰 문제가 발생하므로 final을 선언하고 생성자로 값을 할당하면 어디선가 실수로 id값을 변경하려고 시도한다면 컴파일러가 오류를 내어 문제를 방지할 수 있음
(2) Member
- changeData()메서드에서 final인 id값을 변경하려고 시도하면 컴파일 오류가 발생함
package final1.ex;
public class Member {
private final String id; // final 키워드 사용
private String name;
public Member(String id, String name) {
this.id = id;
this.name = name;
}
public void changeData(String id, String name) {
// this.id = id; // 컴파일 오류 발생
this.name = name;
}
public void print() {
System.out.println("id: " + id + " name: " + name);
}
}
(3) MemberMain
- changeData() 메서드로 id를 변경을 시도해도 id값이 변경이 되지않음
- 애초에 id는 변경하지 않는 것이 목적이기 때문에 changeData()메서드의 파라미터를 name만 입력받도록 변경하는 것이 좋음
- 만약 Member의 id필드에서 final을 사용하지 않아 id값이 변경이 되었다면 대부분 DB에서 사용자와 관련된 정보를 id로 조회하기 때문에 고객이 구매한 내역, 장바구니에 담긴 내역이 갑자기 사라지게 되므로 매우 위험함
- 항상 제약이 중요함! 본인이 짠 코드도 나중에 지나면 모르는 경우가 많은데 상수를 보면 이유는 몰라도 바꾸면 안된다는 의미이기 때문에 확실한 제약을 줄 수 있음
package final1.ex;
public class MemberMain {
public static void main(String[] args) {
Member member = new Member("myId", "kim");
member.print();
member.changeData("myId2", "ma");
member.print();
}
}
/* 출력결과
id: myId name: kim
id: myId name: ma
*/
728x90