Static이란?
java에서 Static 키워드를 사용한다는 것은 메모리에 한번 할당되어 프로그램이 종료될 때 해제되는 것을 의미한다.
일반적으로 우리가 만든 Class는 Static 영역에 생성되고 , new 연산을 통해 생성한 객체는 Heap 영역에 생성된다.
객체 생성 시 할당된 힙은 가비지 컬렉터를 통해 수시로 관리를 받는다.
하지만 Static 키워드를 통해 Static 영역에 할당된 메모리는 모든 객체가 공유하는 메모리가 되고,
가비지컬렉터의 관리 영역 밖에 존재하므로 프로그램 종료시 까지 메모리가 할당된 상태로 존재한다.
때문에 자주 사용하게 되는 static은 성능에 도움을 줄 수 있지만
자주 사용하지 않는 프로그램이나 static을 무분별하게 사용한다면 성능에 악영향을 주게 된다.
전에 작성한 java 메모리에 관한 포스팅
[JAVA] 스택과 힙 , 가비지 컬렉터 — 간편 웹프로그래밍 (tistory.com)
Static 변수 특징
Static 변수는 클래스 변수이다. 객체를 생성하지 않고도 Static 자원에 접근이 가능하다.
public class aaa{
public static abc = "abc";
public static int add(int x, int y){
return x + y;
}
public int min(int x, int y) {
return x - y;
}
}
aaa.add(1,2); // 성공 - static 메소드 이므로 객체 생성없이 사용가능
aaa.min(1,2); // 에러 - 객체생성없이 사용불가
aaa bbb = new aaa();
bbb.add(1,2); // 성공 - 가능하지만 권장하지 않음
bbb.min(1,2); // 성공
Static 변수의 사용
public class Person{
private String name = "aaa";
public void printName(){
System.out.println(name);
}
}
위와 같은 클래스를 통해 100번의 aaa라는 이름을 호출해야 한다.
100번의 Person 객체를 new 키워드로 생성하면 ,
"aaa"라는 값을 갖는 객체가 Heap 메모리에 100개의 임의의 위치에 생성된다.
이러한 경우에 static을 사용한다면 여러 객체가 하나의 메모리를 참조하기 때문에, 메모리 효율이 높아진다.
또한 "aaa"라는 이름은 변하지 않는 값이므로 final을 붙여주고,
이러한 이유로 일반적으로 static 변수는 public 및 final과 함께 사용된다.
주의!
Static 메서드는 객체의 생성없이 호출이 가능하며, 객체에서는 호출이 불가능.
Static 변수에 접근하기 위해선 반드시 Static 메소드가 필요하다.
왜? Static 변수에는 Static 메소드가 필요할까?
OS가 관리하는 프로그램은 프로세스 단위로 실행된다.
프로세스의 메모리는 code , date , heap , stack 순이며
date 구간에 전역변수 및 Static 변수가 위치하게 된다.
public static String aaa = " aaa " ;
public String bbb = " bbb " ;
두개의 변수가 있을 때 date 부분에서 이미 aaa는 메모리가 할당되었지만
bbb는 new 연산을 통해 객체가 생성되므로 heap 에서 메모리가 할당된다.
Static 메소드는 객체의 생성 없이 접근하는 함수이므로, 할당되지 않은 메모리 영역에 접근하기 때문에
Static 변수에 접근하기 위해서는 반드시 static 메소드가 되어야한다.
final
final이란 알다시지 최종적 이라는 뜻이다. final 필드는 초기값이 저장되면 최종적인 값이 되어
프로그램 실행 도중에 수정 할 수 없다.
final 사용법
final 필드는 초기값을 줄 수 있는 방법이 두가지 방법 뿐이다.
1. 필드 선언시에 초기화
2. 생성자를 통해서 초기화
초기화되지 않은 final 필드가 있다면 에러가 발생한다.
final 객체
객체 변수에 final로 선언하면 그 변수에 다른 참조값을 지정할 수 없다.
한번 생성된 final 객체는 같은 타입으로 재생성이 불가능하다.
객체자체는 변경이 불가능하지만 객체 내부 변수는 변경이 가능하다.
public class Company{
String name = "회사";
public String getName(){
return name;
}
public void SetName(String name){
this.name = name;
}
}
public class Final_Com{
public static void main(String[] args){
final Company company = new Company(); //객체를 한번 생성하면 재생성 불가능
company.setName("new회사"); //클래스의 필드는 변경가능
}
}
final 클래스
클래스에 final을 사용하게 되면 그 클래스는 최종상태가 되어 더이상 상속이 불가능하다.
final클래스여도 필드는 setter를 이용해서 변경이 가능하다.
//final 클래스
final class Company{
String name = "회사명";
}
//상속 불가능
class A_Company extends Company{
}
final 메서드
메서드에 final을 사용하게 되면 상속받은 클래스에서 부모의 final을 오버라이딩 할 수 없다.
자신이 만든 메서드를 변경할 수 없게 하고 싶을 때 사용되고,
시스템 코어 부분의 변경을 원치 않는 메서드에 많이 사용한다.
class Company{
String name = "회사명";
public final void print() {
System.out.println("회사 이름은 :"+name+" 입니다.");
}
}
class A_Company extends Company{
String name = "a회사";
//메서드 오버라이드 불가능
public void print() {
}
}
EX )
@Component
public class FileUtils {
private static final String filePath = "C:\\mp\\whiteRecordImg\\"; // 파일이 메인 위치
public static String getRandomString() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
public String uploadImg(MultipartHttpServletRequest mpRequest) throws Exception {
MultipartFile multipartFile = null;
String originalFileName = null;
String originalFileExtension = null;
String storedFileName = null;
File file = new File("C:\\mp\\tempImg\\"); //임시파일 위치
if (!file.exists()) {
file.mkdirs();
}
multipartFile = mpRequest.getFile("upload");
originalFileName = multipartFile.getOriginalFilename();
originalFileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
storedFileName = String.valueOf(getRandomString()) + originalFileExtension;
file = new File("C:\\mp\\tempImg\\" + storedFileName);
multipartFile.transferTo(file);
String imgPath = "/img/" + storedFileName;
return imgPath;
}
}
스프링에서 Multipart 를 이용해 파일을 처리하는 FileUtils라는 클래스에서
호출 시 여러 번의 로직동안 자주 사용되고, 변경되면 안되는 파일의 메인위치를 static final을 이용해 생성했다.
또한 파일이 저장 될 이름에 중복방지를 위해서 랜덤한 문자열 값을 달아주는 getRamdomString() 함수에 static을 사용,
uploadImg() 함수의 storedFileName 부분을 보면
따로 getRamdomString()을 new로 생성하지 않고 바로 사용하고 있다.
위의 예시는 코드가 많이 줄어들었지만, 파일기능 사용시 여러번 사용되기 때문에 static 함수를 사용했다.
static과 final의 사용과 의미에 대해 알아보았다.
static은 전체 프로그램과 동일한 라이프사이클을 가지게 되기 때문에,
잘 사용하면 그 자체만으로 성능에 도움을 줄 수 있지만 사용을 피해야하는 경우가 많이 있다.
편리하다는 이유로 잘 못 사용하게 되면 의도한 바와 달리 문제를 야기 할 수 있으니 주의하자.
면접질문 ) Static에 대해 설명해보세요.
java에서 Static을 사용한다는 것은 프로그램과 라이프사이클을 같이 하는 것 입니다.
메모리에 할당되어 프로그램 종료 시 까지 해제되지 않으며,
Static 키워드로 data 메모리에 할당 된 메모리는 모든 객체가 공유하는 메모리가 되고
Heap 영역에 존재하는 가비지 컬렉터의 밖에 존재하여 프로그램이 종료될 때 까지 사라지지 않습니다.
때문에 자주 사용하게 되는 메모리는 Static을 이용하면 성능이 향상되겠지만
무분별하게 사용할 경우 악영향을 줄 수 있다.
전역변수 또한 Static과 같이 data 영역에 속합니다.
'JAVA > 자바' 카테고리의 다른 글
[Java] Enum, 열거타입 (0) | 2023.06.07 |
---|---|
[JAVA] SOLID 객체지향 설계 5원칙 (2) | 2022.02.26 |
[JAVA] 스택과 힙 , 가비지 컬렉터 (1) | 2022.02.25 |
[JAVA] String에서 ==와 equals()의 차이점 (0) | 2022.02.20 |
Java 문자열 나누기 - substring , indexOf , charAt (0) | 2021.12.29 |