클래스 사용법
class A{
int a;
static int b;
}
class A{
int v1;
boolean v2;
void m1(){---}
}
static public void main(String args[]){
A a = new A();
a.m1();
}
→ static feild는 Heap이 아닌 Method Area에 생성된다!!!
class A{
static int v1;
static boolean v2;
}
main() {
A.v1 = 100;
A.v2 = true;
}
소스 파일 실행 -> Hello.exe
Hello.class를 찾는다.
Hello.class를 찾았다면,
Byte code를 검증한다.
Byte code가 검증을 통과했다면,
Method Area에 클래스 파일을 정리해서 관리하기 쉽게 메모리에 로딩(배치)한다.
스태틱 필드를 생성한다.
스태틱 블록을 실행한다.
class Exam0140{
class A{
static int v1;
int v2;
}
main() {
A.v1 = 100;
A p = new A();
p.v2 = 200;
A p2 = new A();
p2.v2 = 300;
}
}
static과 instance를 비유를 통해 알아보자!
학생의 수 → static
각 학생들의 이름, 나이 → instance
package com.eomcs.oop.ex03;
// Member 클래스
public class Member {
public static final int GUEST = 0;
public static final int MEMBER = 1;
public static final int MANAGER = 2;
String id;
String password;
int type; // 0: 손님, 1: 회원, 2: 관리자
}
package com.eomcs.oop.ex03;
// Member 클래스를 외부의 다른 클래스에서도 사용한다면,
// nested class 로 선언하지 많고 패키지 멤버로 분리하라.
//
// 패키지 멤버의 스태틱 필드를 사용할 때는 다음과 같이 import로
// 그 변수의 소속을 미리 밝힐 수 있다.
// => 스태틱 변수의 소속 클래스를 미리 밝혀두면
// 클래스 이름 없이 스태틱 변수를 바로 사용할 수 있다.
import static com.eomcs.oop.ex03.Member.GUEST;
import static com.eomcs.oop.ex03.Member.MANAGER;
import static com.eomcs.oop.ex03.Member.MEMBER;
// 여러 개를 한 번에 명시하고 싶다면 다음과 같이 wildcard(*)로 지정해도 된다.
//import static com.eomcs.oop.ex03.Member.*;
public class Exam0163 {
public static void main(String[] args) {
Member m4 = new Member();
m4.id = "aaa";
m4.password = "1111";
m4.type = GUEST; // import static 명령문에서 변수의 소속을 이미 밝혔기 때문에 클래스 이름을 적을 필요가 없다.
// 만약 import에 선언되지 않았다면 스태틱 변수명 앞에 클래스명을 붙여야 한다.
// 예) Member.GUEST
Member m5 = new Member();
m5.id = "bbb";
m5.password = "1111";
m5.type = MANAGER;
Member m6 = new Member();
m6.id = "ccc";
m6.password = "1111";
m6.type = MEMBER;
}
}
일단 인스턴스로 만들고, 인스턴스 멤버를 사용하지 않으면,
그제서야 스태틱을 붙여 클래스 멤버로 만들자!
public class Exam0220 {
static class A {
int value;
static void m1() {
}
void m2() {
this.value = 100;
}
void m3() {
}
void m4(int value) {
value = 200;
this.value = 300;
}
}
public static void main(String[] args) {
A.m1();
A obj1 = new A();
obj1.m2();
obj1.m1();
A obj2 = new A();
// 인스턴스 메서드의 this 변수는 메서드를 호출할 때 전달한 인스턴스 주소 값을 가진다.
// 그래서 메서드가 호출될 때 마다 this 변수의 값이 바뀐다.
obj2.m2();
}
}
- method는 instance로 선언이 되더라도 Method Area에 생성된다.
public class Exam0230 {
static class Calculator {
int result;
// 주의!
// => 이름에 인스턴스가 붙었다고 해서 인스턴스 메서드는 Heap에 만들어지는 것이 아니다!
// => 클래스의 모든 코드는 Method Area 영역에 로딩 된다.
public void plus(int value) {
this.result += value;
}
public void minus(int value) {
this.result -= value;
}
}
public static void main(String[] args) {
Calculator c1 = new Calculator();
Calculator c2 = new Calculator();
// 인스턴스 메서드든 클래스 메서드든 모두 Method Area 영역에 올라간다.
// 그리고 인스턴스를 가지고 그 메서드를 호출하는 것이다.
// c1이 가리키는 인스턴스를 가지고 Method Area에 있는 plus()를 호출한다.
c1.plus(123);
// c2가 가리키는 인스턴스를 가지고 Method Area에 있는 plus()를 호출한다.
c2.plus(30);
}
}
- 메서드에 인자로 받는 파라미터도 로컬 변수이다!
⇒ JVM Stack에 생성된다.- 따라서 메서드 내부에서, 파라미터만 가지고 작업을 하는 경우에도, 클래스 메서드로 정의한다.
→ 외부 변수, 인스턴스 변수를 사용하는 것이 아니기 때문이다.
⇒ 즉, this.을 붙일 수 있는 변수가 없으면 인스턴스 변수를 이용하는 것이 아니기 때문에 static으로 변경해준다. (this. 으로 구분하면 좋을 것 같다.)
new Score();
Score s = new Score();
public class Exam0420 {
static class Score {
String name;
int kor;
int eng;
int math;
int sum;
float average;
Score() {}
Score(String name, int kor, int eng, int math) {
System.out.println("Score(String,int,int,int) 호출!");
this.name = name;
this.kor = kor;
this.eng = eng;
this.math = math;
this.compute();
}
public void compute() {
this.sum = this.kor + this.eng + this.math;
this.average = this.sum / 3f;
}
}
public static void main(String[] args) {
Score s1 = new Score("홍길동", 100, 90, 77);
Score s2 = new Score("임꺽정", 80, 88, 87);
// 생성자에서 이미 계산을 수행했기 때문에
// 합계와 평균을 계산하기 위해 따로 compute()를 호출할 필요가 없다.
// 이것이 생성자를 사용하는 이유이다.
// 생성자를 사용하면 좀 더 코드가 간결해진다.
System.out.printf("%s, %d, %d, %d, %d, %.1f\n",
s1.name, s1.kor, s1.eng, s1.math, s1.sum, s1.average);
System.out.printf("%s, %d, %d, %d, %d, %.1f\n",
s2.name, s2.kor, s2.eng, s2.math, s2.sum, s2.average);
}
}
- Static 블럭의 목적?
클래스 멤버(스태틱 변수, 스태틱 메서드)를 사용하기 전에 유효한 값으로 초기화 시키는 것이다.
스태틱 변수부터 생성하고 그 다음에 블록 들어가서 값 설정한다!!
Static 블럭은 나눠놔도 결국 하나로 합쳐진다!
클래스 로딩은 프로그램 시작 후 단 한번만 된다!
따라서, static 로딩도 단 한번만 된다!
→ Method Area에!!!
class에서 밑의 코드가 있다고 가정하고 살펴봐 보자!
static int a = 300;
위의 코드는 컴파일시, 결국 밑에와 같이 변하게 된다.
static int a;
satatic{
a = 300;
}
나만의 정리: 즉, static은 Method Area에 Method와 별개로 생성되는 상자가 있다고 생각하자.
또한 static은 변수 선언 따로, 그 변수에 값 설정 따로 된다고 생각하자!!
예시
public class Exam0691 {
static class A {
static int a = 7;
static {
System.out.println("A.static{}");
a += B.b;
}
}
static class B {
static int b = 22;
static {
System.out.println("B.static{}");
b += A.a;
}
}
public static void main(String[] args) {
System.out.println(A.a); // ?
System.out.println(B.b); // ?
}
}
public class Exam0680 {
public static class A {
static {
b = 400;
}
static int a = 100;
static {
a = 200;
System.out.println("static {} 실행");
}
static int b = 300;
// 변수 초기화 문장(variable initializer)을 컴파일 할 때,
// 1) 스태틱 변수 선언에서 변수 초기화 문장을 별도의 스태틱 블록으로 분리한다.
//
// static {
// b = 400;
// }
// static int a;
// static {
// a = 100;
// }
// static {
// a = 200;
// System.out.println("static {} 실행");
// }
// static int b;
// static {
// b = 300;
// }
// 2) 스태틱 블록을 한 개의 블록으로 합친다.
// static int a;
// static int b;
//
// static {
// b = 400;
// a = 100;
// a = 200;
// System.out.println("static {} 실행");
// b = 300;
// }
// - 바이트 코드(Exam0680$A.class)를 확인해 보라!
}
public static void main(String[] args) throws Exception {
System.out.println(A.a);
System.out.println(A.b);
}
}
여러 생성자에 공통으로 들어갈 코드가 있다면, 인스턴스 초기화 블럭에 담아두자!!
인스턴스 초기화 블럭?
class 내부에 아무것도 안붙은 블럭
인스턴스 초기화 블럭 안에 들어 있는 코드는 모든 생성자의 앞부분에 복사된다.
인스턴스 초기화 블럭 코드가 생성자 뒤에 위치하더라도 생성자의 앞부분에 복사된다.
복사된 이후의 인스턴스 블럭은 삭제된다.
object 클래스는 클래스 이름이 없는 익명 클래스이기 때문에 생성자를 만들지 못한다. 따라서, 초기화 명령을 작성하기 위해서는 인스턴스 블록을 이용해야 한다.
public class Exam0760 {
public static void main(String[] args) {
// 생성자를 만들지 못하는 상황?
// - "익명 클래스"를 만들 때이다.
// - 클래스 이름이 없기 때문에 생성자를 만들 수 없다.
//
// 다음은 Object 클래스를 상속 받은 익명 클래스를 정의하고 객체를 만드는 명령이다.
Object obj1 = new Object() {
// 이 클래스는 이름이 없기 때문에 생성자를 만들 수 없다.
// 그래서 초기화 명령을 작성하려면 인스턴스 블록을 이용해야 한다.
{
System.out.println("인스턴스 블록...");
}
};
}
}
public class Exam0820 {
static class A {
int a = 100;
int b = 200;
int c;
// 인스턴스 필드 초기화 문장은
// 생성자의 앞 부분에 삽입된다.
//
public A() {
// 생성자가 있으면,
// - 존재하는 생성자의 앞 부분에 삽입된다.
// - 즉 인스턴스 필드를 초기화시키는 문장이 다음과 같이 삽입된다.
//
// a = 100;
// b = 200;
a = 111;
c = 333;
}
}
public static void main(String[] args) {
A obj1 = new A();
System.out.printf("a=%d, b=%d, c=%d\n", obj1.a, obj1.b, obj1.c);
}
}