💡클래스 로딩 (Class Loading)
메인 메소드가 실행되기 전에 여러가지 작업이 일어나는데, 그 중에 하나가 '클래스 로딩'이라는 작업이다.
클래스 로딩은 프로그램을 실행하기 전에 클래스의 정의를 미리 읽는 작업 (인식하는 작업)을 의미하며, 이는 클래스를 사용할 수 있도록 메모리에 기억하는 과정으로 이해할 수 있다.
클래스 로딩 과정에서 모든 static 키워드를 인식하고, static을 만나는 순간 메모리 실체화(구현)을 하게 된다. 그래서 정적 변수는 main()가 만들어지기 전에 만들어진다.
💡객체 변수
private String name;
private int age;
객체 변수는 static이 붙지 않은 변수를 말하며, 개인 데이터를 의미한다.
💡정적 변수
private static String school;
정적 변수는 static이 붙은 변수를 말하며, 공용 데이터를 의미한다. 여기서 static은 클래스, 또는 클래스 멤버에 붙일 수 있다. 식별자가 붙어있다 보니 가끔 클래스 변수라고도 한다.
💡객체 변수와 정적 변수의 사용 예시
public class Ex_static {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("Hong");
s1.setAge(20);
Student s2 = new Student();
s2.setName("Lee");
s2.setAge(25);
Student s3 = new Student();
s3.setName("Kim");
s3.setAge(23);
Student.setSchool("A shcool");
System.out.println(s1.info());
System.out.println(s2.info());
System.out.println(s3.info());
}
}
setName은 3번 호출한다. 각자 자신의 이름을 세팅하기 위해 3번을 호출했지만, setSchool은 한 번만 호출하면 된다.
학생이 3명이든 100명이든 학교 이름은 하나이기 때문이다! 그래서 학교 이름을 초기화하는 것 또한 한 번만 하면 된다.
class Student{
private String name;
private int age;
private static String school;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static String getSchool() {
return school;
}
public static void setSchool(String school) {
Student.school = school;
}
public String info() {
return String.format("%s(%d) %s", this.name, this.age, Student.school);
}
}
만약 사람마다 다른 값을 가져야 한다면 객체 변수를 사용하고, 사람이 달라도 같은 값을 가져야 한다면 정적 변수를 사용하면 된다.
그 예로 이름, 나이, 주소, 전화, 성별은 객체 변수로 구현하고, 사람들이 속한 공동체 이름은 정적 변수로 구현할 수 있다.
💡정적 메소드의 활용
public class Ex_static {
public static void main(String[] args) {
/*
Util util1 = new Util();
int result = util1.add(10, 20);
System.out.println(result);
result = util1.add(30, 40);
System.out.println(result);
Util util2 = new Util();
result = util2.add(50, 60);
System.out.println(result);
*/
int result = Util.add(10, 20);
System.out.println(result);
result = Util.add(30, 40);
System.out.println(result);
}
}
주석 처리한 부분은 클래스 안에 멤버 변수가 없고 객체 메소드만 있는 상태의 메소드를 사용한 부분이다.
이때 util1과 uril2은 상태 값(개인 값)을 가지지 않기 때문에 모양이 완벽하게 똑같다. 이는 메모리 낭비로 이어지게 된다.
이때 사용할 수 있는 게 정적 메소드이다!
class Util {
// public int add(int a, int b) 객체 메소드에서 static 추가
public static int add(int a, int b)
return a + b;
}
}
정적인 성격의 기능만 필요할 때에는 클래스를 만들고, 모든 메소드를 static으로 만들어버리면 단 한 곳에서만 선언되어 관리되며 메모리가 절약된다.
우리 주변에서 정적 메소드가 활용될 수 있는 유틸리티 분야로는 파이 계산 등의 연산 등이 있으며, 특정 물품의 수를 카운트할 때 사용할 수 있다.
펜 개수 카운트
public class Ex_static {
public static void main(String[] args) {
// 요구사항)
// 1. 펜을 생성하시오.
// 2. 생산된 펜의 개수를 세시오.
Pen p1 = new Pen("MonAmi", "black");
Pen p2 = new Pen("MonAmi", "black");
Pen p3 = new Pen("MonAmi", "black");
System.out.printf("펜 개수: %d", Pen.count); // 펜 개수: 3
}
}
class Pen{
public static int count = 0;
private String mode1;
private String color;
public Pen(String mode1, String color) {
this.mode1 = mode1;
this.color = color;
Pen.count++; // 펜 카운트를 생성과 동시에 한다.
}
}
public Pen(String mode1, String color) 안에서 Pen.count를 증감하면 Pen을 호출할 때마다 count가 된다.
이처럼 공통적인 변수를 쓸 때, static 변수를 사용하는 게 가장 이상적이다.
객체 메소드와 정적 메소드의 차이
class Student{
public void objectMethod() {
// 객체 메소드
System.out.println(this.name); // 객체 변수
System.out.println(Student.school); // 정적 변수
this.setAge(20); // 객체 메소드
Student.setSchool("A School"); // 정적 메소드
}
public static void staticMethod() {
// 정적 메소드
System.out.println(Student.school); // 정적 변수
// System.out.println(this.name); // 객체 변수(X)
Student.setSchool("A School"); // 정적 메소드
// this.setAge(15); // 정적 메소드
}
}
객체 메소드는 개인과 관련된 작업, 집합(정적 멤버)과 관련된 작업이 모두 가능하다. 하지만 정적 메소드는 개인과 관련된 작업이 불가능하며, 집합(정적 멤버)과 관련된 작업만 가능하다.
이는 정적 메소드 안에서는 객체 메소드를 접근할 수 없으며, this를 사용하여 호출할 수 없다는 것을 의미한다.