클래스와 객체
클래스와 객체의 정의
- 클래스의 정의 : 객체를 정의해 놓은 것
- 클래스의 용도 : 객체를 생성하는데 사용된다
객체와 인스턴스
- 클래스의 인스턴스화(instantiate) : 클래스로부터 객체를 만드는 과정
- 인스턴스 : 어떤 클래스로부터 만들어진 객체
- ex) Tv클래스로부터 만들어진 객체를 Tv클래스의 인스턴스라고 함
인스턴스의 생성과 사용
클래스명 변수명; // 참조변수 선언
변수명 = new 클래스명();
Tv t;
t = new Tv();
객체 배열
- 객체 배열을 다루기 위한 참조 변수 배열을 생성
- 객체를 생성해서 배열의 각 요소에 저장
Tv[] tvArr = new Tv[3]; // 참조변수 배열을 생성
// 객체를 생성해서 배열의 각 요소에 저장
tvArr[0] = new Tv();
tvArr[1] = new Tv();
tvArr[2] = new Tv();
// 다음과 같이 배열의 초기화 블럭을 사용하면 한 줄로 간단히 할 수 있다.
Tv[] tvArr = { new Tv(), new Tv(), new Tv() };
// 객체의 수가 많으면 for문을 사용하자
변수와 메서드
- 인스턴스 변수
- 클래스 영역에서 선언됨
- 클래스의 인스턴스를 생성할 때 만들어짐
- 클래스 변수
- 인스턴스 변수 앞에 static을 붙여 선언
- 클래스 변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유하게 된다.
- 클래스 변수는 인스턴스변수와 달리 인스턴스를 생성하지 않고도 언제라도 바로 사용할 수 있음
- 클래스이름.클래스변수와 같은 형식으로 사용
- 참조변수를 통해 클래스변수를 사용할 수 있지만, 인스턴스 변수로 오해하기 쉬우므로 클래스이름.클래스변수 형식을 사용하자
- 클래스가 메모리에 로딩될 때 생성되어 프로그램이 종료될 때 까지 유지됨
- public을 앞에 붙이면 같은 프로그램 내에서 어디서나 접근할 수 있는 전역변수의 성격을 가짐
메서드
- 같은 클래스 내의 메서드끼리는 참조변수를 사용하지 않고도 서로 호출이 가능하지만, static 메서드는 같은 클래스 내의 인스턴스 메서드를 호출할 수 없다.
JVM의 메모리구조
- 메서드 영역(method area)
- 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일(*.class)을 읽고 분석하여 클래스에 대한 정보를 클래스변수와 함께 이곳에 저장
- 힙(heap)
- 인스턴스가 생성되는 공간. 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다.
- 호출 스택(call stack)
- 메서드의 작업에 필요한 메모리 공간을 제공
- 메서드가 호출되면 호출스택에 호출된 메서드를 위한 메모리가 할당됨
- 이 메모리는 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다.
- 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환되어 비워진다.
- 스택 구조로 함수들이 실행된다
기본형 매개변수와 참조형 매개변수
- 매개변수의 타입이 기본형(primitive type)일 때는 기본형 값이 복사됨
- 변수의 값을 읽기만 할 수 있다.
- 매개변수의 타입이 참조형(reference type)일 때는 인스턴스의 주소가 복사됨
- 변수의 값을 읽고 변경할 수 있음
class Data { int x; }
class ReferenceParamEx {
public static void main(String[] args) {
Data d = new Data();
d.x = 10;
System.out.println("main() : x = " + d.x);
change(d);
System.out.println("After change(d)");
System.out.println("main() : x = " + d.x);
}
static void change(Data d) { // 참조형 매개변수
d.x = 1000;
System.out.println("change() : x = " + d.x);
}
}
- 배열 또한 매개변수로 참조형 변수를 사용하면 값을 변경할 수 있다.
class ReferenceParam2 {
public static void main(String[] args) {
int[] x = { 10 }; // 크기가 1인 배열, x[0] = 10
System.out.println("main() : x = " + x[0]);
change(x);
System.out.println("After change(x)");
System.out.println("main() : x = " + x[0]);
}
static void change(int[] x) { // 참조형 매개변수
x[0] = 1000;
System.out.println("change() : x = " + x[0]);
}
}
참조형 반환타입
- 매개변수 뿐만 아니라 반환타입도 참조형이 될 수 있다. (객체의 주소를 반환)
class Data { int x; }
class ReferenceReturnEx {
public static void main(String[] args) {
Data d = new Data();
d.x = 10;
Data d2 = copy(d);
System.out.println("d.x = " + d.x);
System.out.println("d2.x = " + d2.x);
}
static Data copy(Data d) {
Data tmp = new Data();
tmp.x = d.x + 10;
return tmp;
}
}
클래스 메서드와 인스턴스 메서드
- 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통적으로 사용해야 하는 것에 static을 붙인다
- 클래스 변수는 인스턴스를 생성하지 않아도 사용할 수 있다
- 클래스 메서드는 인스턴스 변수를 사용할 수 없다
- 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다
- 메서드 호출시간이 짧아지므로 성능이 향상된다
클래스 멤버와 인스턴스 멤버간의 참조와 호출
- 같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능하다
- 단, 클래스 멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.
class MemberCall {
int iv = 10;
static int cv = 20;
int iv2 = cv;
// static int cv2 = iv; // 컴파일 에러. 클래스변수는 인스턴수변수를 사용할 수 없음
static int cv2 = new MemberCall().iv; // 이처럼 객체를 생성해야 사용 가능
static void staticMethod1() {
System.out.println(cv);
// System.out.println(iv); // 컴파일 에러. 클래스메서드에서 인스턴스변수 사용 불가
MemberCall c = new MemberCall();
System.out.println(c.iv); // 이처럼 객체를 생성해야 사용 가능
}
void instanceMethod1() {
System.out.println(cv);
System.out.println(iv); // 인스턴스메서드에서는 인스턴스변수를 바로 사용 가능
}
static void staticMethod2() {
staticMethod1();
// instanceMethod1(); // 컴파일 에러. 클래스메서드에서는 인스턴스메서드 호출 불가
MemberCall c = new MemberCall();
c.instanceMethod1(); // 이처럼 객체를 생성해야 호출 가능
}
void instanceMethod2() { // 인스턴스메서드에서는
staticMethod1(); // 인스턴스메서드와 클래스메서드 모두
instanceMethod1(); // (객체를 생성하지 않고도) 바로 호출 가능
}
}
- Tip
MemberCall c = new MemberCall();
int result = c.instanceMethod();
// 위 두 줄을 아래와 같이 한 줄로 할 수 있다.
int result = new MemberCall().instanceMethod();
// 다만 참조변수를 선언하지 않았기 때문에 생성된 MemberCall인스턴스는 더 이상 사용할 수 없다
오버로딩
- 기본적인 내용은 cpp와 동일
가변인자와 오버로딩
- JDK 1.5부터 메서드의 매개변수 개수를 동적으로 지정해 줄 수 있게 됨
- 이 기능을 가변인자 라고 한다
- 가변인자는 '타입... 변수명'과 같은 형식으로 선언하며, printf()가 대표적인 예이다
- 가변인자 이외에도 매개변수가 더 있다면, 가변인자를 매개변수 중에서 제일 마지막에 선언해야 한다
public PrintStream printf(String format, Object... args) { ... }
- 가변인자는 내부적으로 배열을 이용함
- 꼭 필요한 경우에만 가변인자를 사용하자
- 가변인자 대신 배열을 사용하는 경우, 반드시 인자를 지정해 줘야하는 불편함이 있음
class VarArgsEx {
public static void main(String[] args) {
String[] strArr = { "100", "200", "300" };
System.out.println(concatenate("", "100", "200", "300"));
System.out.println(concatenate("-", strArr));
System.out.println(concatenate(",", new String[]{"1", "2", "3"}));
// 다음은 비허용
// System.out.println(concatenate(",", {"1", "2", "3"}));
System.out.println("[" + concatenate(",", new String[0]) + "]");
System.out.println("[" + concatenate(",") + "]");
}
static String concatenate(String delim, String... args) {
String result = "";
for (String str : args) {
result += str + delim;
}
return result;
}
/*
static String concatenate(String... args) {
return concatenate("", args);
}
*/
} // class
생성자
- 인스턴스가 생성될 때 호출되는 '인스턴스 초기화 메서드'
- Cpp와 유사
- 연산자 new가 인스턴스를 생성하는거지, 생성자가 인스턴스를 생성하는 것이 아님
- 기본 생성자가 컴파일러에 의해서 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때 뿐임
생성자에서 다른 생성자 호출하기 - this(), this
- 생성자 간에도 서로 호출이 가능함
- 조건
- 생성자의 이름으로 클래스이름 대신 this를 사용한다
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.
- 잘못된 예시
Car(String color) {
door = 5; // 첫 번째 줄
Car(color, "auto", 4); // 에러1. 생성자의 두 번째 줄에서 다른 생성자 호출
// 에러2. this(color, "auto", 4);로 해야함
}
Car(String color, String gearType, int door) {
this.color = color;
this.gearType = gearType;
this.door = door;
}
- this 는 참조변수로 인스턴스 자신을 가리키고, 이를 통해 인스턴스 변수에 접근할 수 있다.
- this : 인스턴스 자신을 가리키는 참조변수(주소가 저장되어 있음)
- this(), this(argument) : 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용함
- static 메서드에서는 this 역시 사용할 수 없다
생성자를 이용한 인스턴스 복사
- 생성자를 이용해 현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있다.
class CarTest3 {
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car(c1); // c1의 복사본 c2 생성
System.out.println("c1의 color = " + c1.color
+ ", gearType = " + c1.gearType
+ ", door = " + c1.door);
System.out.println("c2의 color = " + c2.color
+ ", gearType = " + c2.gearType
+ ", door = " + c2.door);
c1.door = 100; // c1의 인스턴스변수 door의 값 변경
System.out.println("c1.door = 100; 수행 후");
System.out.println("c1의 color = " + c1.color
+ ", gearType = " + c1.gearType
+ ", door = " + c1.door);
System.out.println("c2의 color = " + c2.color
+ ", gearType = " + c2.gearType
+ ", door = " + c2.door);
}
}
class Car {
String color; // 색상
String gearType; // 변속기 종류 - auto(자동), manual(수동)
int door; // 문의 개수
Car() {
this("white", "auto", 4);
}
Car(String color, String gearType, int door) {
this.color = color;
this.gearType = gearType;
this.door = door;
}
Car(Car c) { // 인스턴스의 복사를 위한 생성자
// this.color = c.color; // this 붙여도 되고 안붙여도 됨
// gearType = c.gearType;
// door = c.door;
/* 아래와 같이 기존의 코드를 활용하는 게 바람직 */
this(c.color, c.gearType, c.door);
}
}
변수의 초기화
- 멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어짐
- 지역변수는 사용하기 전에 반드시 초기화해야 한다
class InitTest {
int x;
int y = x;
void method() {
int i; // 지역 변수
int j = i; // 에러, 지역변수를 초기화 하지 않고 사용
}
}
- 멤버변수의 초기화 방법에는 여러 가지가 있음
- 명시적 초기화
- 생성자
- 초기화 블럭 (인스턴스 초기화 블럭, 클래스 초기화 블럭)
명시적 초기화
- 변수를 선언과 동시에 초기화 하는 것을 명시적 초기화라고 함
class Car {
int door = 4; // 기본형 변수의 초기화
Engine e = new Engine(); // 참조형 변수의 초기화
}
- 복잡한 초기화 작업이 필요할 때는 '초기화 블럭' 또는 생성자를 사용해야 함
초기화 블럭
- 인스턴스 초기화 블럭은 단순히 클래스 내에 블럭{}을 만들고 그 안에 코드를 작성하기만 하면 된다
- 클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에 단순히 static만 덧붙이자
- 초기화 블럭 내에서는 메서드 내에서와 같이 조건문, 반복문, 예외처리구문 등을 자유롭게 사용할 수 있음
class InitBlock {
static { /* 클래스 초기화 블럭 */}
{ /* 인스턴스 초기화 블럭*/ }
//...
}
class StaticBlockTest {
static int[] arr = new int[10];
static {
for (int i=0; i<arr.length; i++) {
// 1과 10 사이의 임의의 값을 배열 arr에 저장
arr[i] = (int)(Math.random()*10) + 1;
}
}
public static void main(String args[]) {
for (int i=0; i<arr.length; i++)
System.out.println("arr["+i+"] : " + arr[i]);
}
}
- 인스턴스 초기화 블럭에서는 모든 생성장에서 공통으로 수행되야 하는 코드를 넣는데 사용한다
참조) Java의 정석 3rd edition(남궁성, 도우출판)
'Programming Language > Java' 카테고리의 다른 글
[Java] 08. OOP - 다형성(polymorphism) (0) | 2024.06.24 |
---|---|
[Java] 07. OOP - 상속과 접근제어자 (0) | 2024.03.12 |
[Java] 05. 배열 (0) | 2024.03.12 |
[Java] 04. 변수 (0) | 2024.03.12 |
[Java] 03. 화면에서 입력받기 (0) | 2024.03.12 |