클래스 (class)
6 minute read
클래스
사용
1) 일반 변수 사용
- 어떠한 데이터를 다루고 싶을 때 원하는 형식의 값을 저장할 수 있는 메모리 유형을 제공하지 않는다.
- 데이터의 최소 형식인 byte, short, int, long, float, double, boolean, char, String 등의 값을 담을 수 있는 메모리만 제공한다.
- 그렇기 때문에 낱개의 데이터를 저장할 변수를 여러개 선언해야 한다.
- 그리고 그 정보를 출력한다.
- 만약 다루고 싶은 데이터의 갯수가 많아진다면?
- 메서드의 파라미터가 많아지고 복잡해진다.
- 여러 종류의 데이터를 묶어서 하나로 사용할 수 있는, 새로운 형태의 데이터 타입을 만들 수 있는 문법을 제공하는데 그것이 클래스이다.
2) 클래스란?
- 개발자가 자신이 개발하는 프로그램에서 사용할 특별한 형식의 데이터를 다룰 수 있는 새로운 데이터 타입을 정의하게 해주는 문법이다.
- 즉, 즉 byte, short, int, long, float, double, boolean, char 외에 새 형식의 새로운 메모리 구조를 갖는 데이터 타입을 정의할 수 있다.
- 이렇게 정의한 새 데이터 타입은 사용자 정의 데이터 타입(user defined data type) 이라 한다.
클래스 문법의 활용
- 사용자 정의 데이터 타입과 그 타입의 값을 다루는 연산자를 정의할 때 사용한다.
- 서로 관련된 일을 하는 메서드를 관리하기 쉽게 분류하여 묶는 용도로 사용한다.
3) 클래스 사용
- 클래스 문법으로 사용자 정의 데이터 타입의 메모리 구조를 설계한다.
- class 새 데이터 타입 이름 {
변수 선언;
…
}
- 새 데이터 타입의 이름
- 대문자로 시작한다.
- 여러 단어가 결합된 이름인 경우 각 단어의 시작도 대문자로 작성한다.
- 보통 명사형으로 짓는다.
- 메모리를 준비하기 위해서는 클래스명 레퍼런스 = new 클래스명(); 문법을 사용해야 한다.
- 설계도에 따라 준비된 메모리를 인스턴스(instance)라 부른다.
- 확보된 메모리를 사용하려면 주소를 보관할 필요가 있다.
- 메모리의 주소를 저장하는 변수를 레퍼런스(reference)라 부른다.
- 즉, 클래스명 레퍼런스 = new 클래스명();
|—> 인스턴스
- 클래스 설계도에 따라 만든 메모리에 값을 넣기 위해선 레퍼런스에 지정된 주소를 이용하여 메모리에 접근한다.
- s에 저장된 주소로 찾아가서 그 메모리의 각 항목 값을 설정한다.
- s에 저장된 주소로 찾아가서 그 인스턴스의 각 변수에 값을 설정한다.
- s가 가리키는 인스턴스의 각 변수에 값을 설정한다.
- s가 가리키는 인스턴스의 각 필드 값을 설정한다.
- s 인스턴스의 필드 값을 설정한다.
- s 객체의 필드 값을 설정한다.
- ex) s.name = “홍길동”;
- 메모리의 값을 꺼내기 위해서는 인스턴스 주소로 찾아가 필요한 항목의 값을 꺼내면 된다.
- ex) System.out.printf(“%s”, s.name);
- 결과 —> “홍길동” 출력.
자바 기본 데이터 타입의 변수선언 vs 클래스의 변수 선언
- 자바 기본 데이터 타입은 변수를 선언하는 순간 메모리에 생성된다.
- int a; // 바로 int 값을 저장할 메모리가 준비된다.
- 클래스의 변수 선언은 주소를 담는 레퍼런스이다. 따라서 주소를 담는 메모리만 준비된다.
- Score s; // 아직 Score 설계도에 따라 변수들이 준비된 상태가 아니다.
- 클래스의 설계도에 따라 메모리를 준비하려면 따로 new 명령을 사용해야 한다.
- 생성된 메모리를 사용하라면 주소를 잘 보관해 두어야 한다.
클래스 vs 배열
- 클래스란 여러 타입을 묶어서 사용자 정의 데이터 타입을 만드는 문법이다.
- 관련된 기능(메서드, 함수)을 관리하기 편하게 묶는 문법이다.
- 배열이란 단일한 타입의 메모리를 묶는 문법이다.
primitive 데이터 타입의 변수와 레퍼런스
- primitive 데이터 타입의 메모리를 만들때는 변수 선언만으로 완료된다.
- 변수 이름이 곧 메모리를 가리키는 이름이 된다.
- 클래스 이름으로 지정한 변수는 메모리 주소를 저장하는 변수(레퍼런스)이다.
- ex) Score s; // s 는 레퍼런스라 부른다.
- 클래스로 메모리를 만들때는 반드시 new 명령을 사용해야 한다.
- ex) new Score(); // 인스턴스라 부른다.
- 메모리를 만든 후에는 그 주소를 변수(레퍼런스)에 저장해야만 그 메모리를 사용한 수 있다.
- ex) Score s; // 메모리의 주소를 저장할 레퍼런스 준비.
s = new Score(); // 메모리를 확보한 후 그 주소를 레퍼런스에 저장.
인스턴스의 각 변수(항목, field) 에 접근하기
- 레퍼런스명.항목명 = 값;
- ex) Score s = new Score();
s.name = “홍길동”;
4) 메서드 활용
- 여러 메서드에서 클래스를 사용한다면 메서드 밖에서 선언해야 한다.
- static 메서드에서 사용할 수 있게 클래스도 static 으로 선언한다.
- 성적 데이터를 출력하는 부분을 다른 메서드로 뽑아서 쓰면 유지보수하기 편하다.
5) 메서드 활용 2
- 클래스를 이용하면 여러개의 값을 한번에 리턴하거나 리턴받을 수 있다.
- 인스턴스를 생성하여 리턴한다.
- 정확하게는 인스턴스를 생성한 후 그 주소를 리턴한다.
- 레퍼런스에 저장된 인스턴스의 주소를 리터할 때 로컬 변수는 스택 메모리에 생성되고, 메서드 호출이 끝나면 삭제된다.
- 인스턴스는 힙 메모리에 생성되어 삭제되지 않는다.
6) 레퍼런스 배열
- 레퍼런스 배열을 사용하지 않는다면
- 수십개의 인스턴스를 저장할 때 레퍼런스도 그 개수에 맞춰 선언해야 한다.
- 이렇게 되면 불편하기 때문에 배열을 만든다.
- 레퍼런스 배열을 사용했을 때
- 배열 문법을 이용하면 한 번에 여러 개의 레퍼런스를 선언할 수 있다.
- 문법 : 클래스명[] 배열명 = new 클래스명[레퍼런스 갯수];
- 레퍼런스 배열이지 인스턴스 배열이 아니다.
- 인스턴스 배열은 만들 수 없다.
- 레퍼런스 배열을 생성하면 모든 항목이 null로 초기화된다.
- 레퍼런스 배열의 각 항목에는 인스턴스 주소가 들어가 있지 않다.
- 레퍼런스 배열의 각 항목에 new 명령어를 통해 인스턴스를 저장한 후 사용해야 한다.
- 문법 : 배열명[인덱스] = new 클래스명();
- 배열의 각 항목에 저장된 인스턴스는
- 배열명[인덱스] 를 사용해서 지정할 수 있다.
- 배열명[인덱스].변수명
- ex) arr[0].name = “홍길동”;
- 반복문을 사용하면 좀 더 편하게 사용할 수 있다.
7) 가비지
- 주소를 잃어버려 사용할 수 없는 메모리를 가비지라고 부른다.
- 가비지는 가비지 컬렉터에 의해 메모리에서 해제된다.
- 가비지는 레퍼런스 카운트가 0이다.
- 객체의 참조 상태를 관리하기 위해 레퍼런스 카운트라는 개념을 사용한다.
- 주소를 알고있는 변수가 늘어나면 레퍼런스 카운트가 늘어나고 변수의 수가 줄면 레퍼런스 카운트가 줄어든다.
패키지 멤버 클래스와 중첩 클래스
1) 패키지 멤버 클래스
- 단독으로 선언하는 클래스이다.
- 패키지 멤버 클래스라 부른다.
- 보통 한 파일에 한 클래스를 선언한다.
- 패키지 멤버 클래스는 접근 권한이 있다면 누구든 사용할 수 있다.
2) 중첩 클래스(nested class)
- 다른 클래스 안에 정의된 클래스
- 그 클래스 안에서만 사용된다.
- 종류
- 스태틱 중첩 클래스(static nested class)
- 논 스태틱 중첩 클래스(non-static nested class = inner class)
- 로컬 클래스(local class)
- 익명 클래스(anonymous class)
스태틱 중첩 클래스
- 이 클래스를 소유하고 있는 클래스 뿐만 아니라 다른 클래스도 사용할 수 있다.
논 스태틱 중첩 클래스( = 이너 클래스)
- 특정 인스턴스에 종속된 클래스인 경우 논-스태틱 중첩 클래스로 정의한다.
로컬 클래스
- 메서드 블록 안에 정의된 클래스.
- 오직 그 메서드 블록 안에서만 사용된다.
익명 클래스
- 클래스 이름이 없는 중첩 클래스이다.
- 딱 한개의 인스턴스를 생성할 때 사용한다.
- 클래스를 정의할 때 수퍼 클래스나 인터페이스를 지정해야 한다.
- 클래스를 정의할 때 new 연산자를 사용하여 즉시 인스턴스를 생성해야 한다.
코드
package com.eomcs.oop.ex01;
// 패키지 멤버 클래스
// bin/main/com/eomcs/oop/ex01/A.class
class A {}
// => bin/main/com/eomcs/oop/ex01/Exam0420.class
public class Exam0510 {
// 1) 스태틱 중첩 클래스
// bin/main/com/eomcs/oop/ex01/Exam0420$B.class
static class B {}
// 2) 논-스태틱 중첩 클래스
// bin/main/com/eomcs/oop/ex01/Exam0420$C.class
class C {}
public static void main(String[] args) {
// 3) 로컬 클래스(local class)
// bin/main/com/eomcs/oop/ex01/Exam0420$1D.class
class D {}
// 4) 익명 클래스(anonymouse class)
// bin/main/com/eomcs/oop/ex01/Exam0420$1.class
Object obj = new Object() {
String name;
int age;
};
}
static void m1() {
// 패키지 멤버 클래스는 그 패키지에 소속된 누구라도 사용할 수 있다.
// 같은 패키지가 아니라도 공개된 패키지 멤버는 누구라도 사용할 수 있다.
A obj1 = new A();
// 같은 스태틱 멤버라면 스태틱 중첩 클래스를 사용할 수 있다.
B obj2 = new B();
// 스태틱 멤버는 논-스태틱 중첩 클래스(인스턴스 멤버)를 사용할 수 없다.
// C obj3 = new C(); // 컴파일 오류!
// 다른 메서드에 정의된 로컬 클래스는 사용할 수 없다.
// D obj4 = new D(); // 컴파일 오류!
}
void m2() {
// 패키지 멤버 클래스는 그 패키지에 소속된 누구라도 사용할 수 있다.
// 같은 패키지가 아니라도 공개된 패키지 멤버는 누구라도 사용할 수 있다.
A obj1 = new A();
// 논-스태틱 멤버(인스턴스 멤버)는 스태틱 중첩 클래스를 사용할 수 있다.
B obj2 = new B();
// 인스턴스 멤버는 다른 인스턴스 멤버(논-스태틱 중첩 클래스)를 사용할 수 없다.
C obj3 = new C(); // OK!
// 다른 메서드에 정의된 로컬 클래스도 사용할 수 없다.
// D obj4 = new D(); // 컴파일 오류!
}
}
퍼블릭 클래스와 기본 클래스
- 같은 패키지에 소속된 클래스 사용
- 다른 패키지에 소속된 클래스 사용
- 다른 패키지의 클래스는 퍼블릭이 아니면 사용할 수 없다.
- 퍼블릭으로 공개된 클래스는 다른 패키지에 소속되어 있어도 사용할 수 있다.
import
- 다른 패키지의 클래스를 사용할 때 마다 패키지명을 적어야 한다면 코드가 너무 길어진다.
- 이러한 문제점을 해결하기 위해 import 라는 명령을 사용해서 클래스를 사용하기 전에 미리 해당 클래스가 어느 패키지에 있는지 명시한다.
- 특정 패키지의 클래스를 많이 사용한다면 패키지명 다음에 *를 붙여도 된다.
java.lang 패키지
- java.lang 패키지에 있는 클래스는 패키지를 지정하지 않아도 된다.
- 즉, 패키지명을 명시하지 않아도 컴파일 오류가 발생하지 않는다.
- 주의
- java.lang 패키지의 하위 패키지는 해당되지 않는다.
- java.lang 패키지의 클래스들은 갖아 많이 사용하는 기본 클래스이기 때문에 자바 컴파일러가 해당 클래스를 자동으로 찾는다.
- 코드
package com.eomcs.oop.ex01;
// # import - java.lang 패키지
public class Exam0740 {
public static void main(String[] args) {
java.lang.Integer obj1; // OK!
java.lang.System obj2; // OK!
java.io.File obj3; // OK!
Integer obj4; // OK!
System obj5; // OK!
//File obj6; // 컴파일 오류!
}
}