1. 클래스(Class)와 객체(Object)
클래스(Class)
: 객체를 정의한 것 ≒ 설계도, 틀
: 필드(Field)와 메서드(Method)의 결합
: 사용자 정의 타입
class TV {
// 멤버변수
String color;
boolean power;
int channel;
// 메서드
void power() {power = !power;}
void channelUp() {++channel;}
void channelDown() {--channel;}
}
객체(Object)
: 실제로 존재하는 것 ≒ 제품
: 속성(멤버변수)과 기능(메서드)으로 구성됨
: 인스턴스 변수들을 묶어놓은 것
인스턴스(Instance)
: 어떤 클래스로부터 생성된 객체
: 참조변수를 통해서만 다룰 수 있으며, 참조변수 타입은 인스턴스와 일치해야 함
: 하나의 참조변수가 여러 개의 인스턴스 가리키는 건 불가능
: 생성자를 통해 Heap 메모리에 생성됨
- 생성 방법
클래스 참조변수; // 참조변수 선언
참조변수 = new 클래스(); // 생성된 객체의 주소를 참조변수에 저장
-> 클래스 참조변수 = new 클래스();
TV t;
t = new TV();
-> TV t = new TV();
객체 배열
: 많은 수의 객체를 다룰 때 사용
: 객체의 주소 저장
: 인스턴스의 참조변수들을 하나로 묶은 참조변수의 배열
- 생성 방법
1) 객체를 다루기 위한 참조변수 배열 생성 -> 객체 생성하여 각 요소에 저장
클래스[] 참조변수 = new 클래스[길이]; // 참조변수 배열(객체 배열) 생성
// 객체를 생성해서 배열의 각 요소에 저장
참조변수[0] = new 클래스();
참조변수[1] = new 클래스();
. . .
참조변수[n] = new 클래스();
TV[] tvarr = new TV[3];
tvarr[0] = new TV();
tvarr[1] = new TV();
tvarr[2] = new TV();
2) 배열의 초기화 블록 사용
클래스[] 참조변수 = { new 클래스(), new 클래스(), ... , new 클래스() };
TV[] tvarr = { new TV(), new TV(), new TV() };
3) for문 사용
클래스[] 참조변수 = new 클래스[길이];
for (int i = 0; i < 참조변수.length; i++) {
참조변수[i] = new 클래스();
}
TV[] tvarr = new TV[100];
for (int i = 0; i < tvarr.length; i++) {
tvarr[i] = new TV();
}
2. 변수와 메서드
변수의 선언위치가 변수의 종류와 범위를 결정한다.
선언위치에 따른 변수의 종류
변수 종류 | 선언 위치 | 생성 시기 |
인스턴스변수(instance variable, iv) | 클래스 영역 | 인스턴스가 생성되었을 때 |
클래스변수(class variable, cv) | 클래스가 메모리에 올라갈 때 | |
지역변수(local variable, lv) | 클래스 이외의 영역 (메서드, 생성자, 초기화 블럭 내부) |
변수 선언문이 수행되었을 때 |
class Variables {
int iv;
static int cv;
void method() {
int lv = 0;
}
}
인스턴스변수(instance variable)
: 클래스의 인스턴스를 생성할 때 만들어짐
: 각 인스턴스마다 다른 값 저장 가능
: 인스턴스 생성 후 사용 가능
: 인스턴스마다 고유한 상태를 유지해야 할 때 사용
: 인스턴스 생성 후, '참조변수.인스턴스변수명' 으로 접근
class Card {
// 인스턴스변수. 각 Card 인스턴스마다 다른 값 가짐
String kind;
int number;
static int width = 100;
static int height = 250;
}
클래스변수(class variable)
: 인스턴스변수 앞에 static 붙임
: 인스턴스 생성하지 않고도 사용 가능
: 모든 인스턴스들이 공통적인 값을 유지해야 할 때 사용
: 클래스가 로딩될 때 생성되어 프로그램이 종료될 때까지 유지
: public을 앞에 붙이면 전역변수의 성격을 가짐
: 인스턴스 생성 없이 '클래스이름.클래스변수' 으로 접근
class Card {
String kind;
int number;
// 클래스변수. 모든 Card 인스턴스는 같은 값 가짐
static int width = 100;
static int height = 250;
}
지역변수(local variable)
: 클래스 이외의 영역에서 생성되어 그 안에서만 사용 가능
: 메서드의 경우, 메서드가 종료되면 소멸되어 사용 불가
메서드(Method)
: 특정한 작업을 수행하기 위한 명령문의 집합
: 클래스 영역에서만 정의 가능
- 선언 방법
반환타입 메서드이름 (타입 변수명, 타입 변수명, ...) {
// 메서드 호출 시 수행될 코드
}
int add(int x, int y) {
return x + y;
}
메서드 호출
: 메서드 호출 시 인ƒ자의 개수와 순서는 매개변수와 일치해야 함
: 인자의 타입은 매개변수와 일치하거나 자동 형변환 가능한 것이어야 함
: static 메서드는 같은 클래스 내에 있는 인스턴스 메서드를 호출할 수 없음
메서드이름(); // 매개변수가 없는 경우
메서드이름(인자1, 인자2, ...); // 매개변수가 있는 경우
int result = add(1, 3);
return문
: 현재 실행 중인 메서드를 종료하고 호출한 메서드로 되돌아감
: 반환타입이 void인 경우, 컴파일러가 자동으로 'return;' 생성
: 반환타입이 void가 아닌 경우, 반드시 return문이 있어야 함
int max(int x, int y) {
if (x > y) {
return x;
}
// if문 안에만 return문 있으면 오류 발생.
return y;
}
JVM의 메모리 구조
- 메서드 영역(Method Area)
: 클래스에 대한 정보와 클래스변수 저장
- 호출스택(Call stack or execution stack)
: 메서드 작업공간
: 메서드가 호출되면 메서드 수행에 필요한 메모리 공간 할당
: 메서드가 종료되면 사용한 메모리 반환
: 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드
- 힙(heap)
: 인스턴스가 생성되는 공간
: 인스턴스변수 생성
매개변수 - 기본형, 참조형
- 기본형 매개변수
: 변수의 값을 읽기만 할 수 있다(read only)
: 변수의 복사본을 전송하므로 원본에는 아무런 영향을 미치지 못함
class Data {int x;}
public class PrimitiveParamEx {
public static void main(String[] args) {
Data d = new Data();
d.x = 10;
System.out.println("main() : x = "+d.x);
change(d.x);
System.out.println("After change(d.x)");
System.out.println("main() : x = "+d.x);
}
static void change(int x) {
x = 1000;
System.out.println("change() : x = "+x);
}
}
main() : x = 10
change() : x = 1000
After change(d.x)
main() : x = 10
- 참조형 매개변수
: 변수의 값을 읽고 변경할 수 있다(read & write)
: 값이 저장된 주소를 넘겨줌
public 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);
}
}
main() : x = 10
change() : x = 1000
After change(d)
main() : x = 1000
반환타입 - 참조형
: 메서드가 객체의 주소를 반환
public 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;
return tmp;
}
}
재귀호출(recursive call)
: 메서드 내에서 메서드 자신을 다시 호출
: 반복문으로 변경 가능하며 반복문보다 성능 나쁜 경우 많음
public class FactorialTest {
public static void main(String[] args) {
int result = factorial(4);
System.out.println(result);
}
static int factorial(int x) {
int result;
if (x <= 0 || x > 12) {
return -1;
}
if (x == 1) {
result = 1;
}
result = x * factorial(x -1);
return result;
}
}
메서드 - 클래스(static), 인스턴스
클래스 메서드와 인스턴스 메서드의 차이는 인스턴스변수(iv)의 사용 여부다.
- 클래스 메서드(static 메서드)
: 인스턴스변수나 인스턴스메서드와 관련없는 작업을 함
: 메서드 내에서 인스턴스 사용 불가
: 객체 생성 없이 '클래스이름.메서드이름()' 으로 호출
- 인스턴스 메서드
: 인스턴스변수나 인스턴스 메서드와 관련된 작업을 함
: 인스턴스변수나 인스턴스 메서드를 사용하지 않는다면 static 붙이는 거 고
: 메서드 내에서 인스턴스 사용 가능
: 인스턴스 생성 후, '참조변수.메서드이름()' 으로 호출
public class MyMathTest2 {
public static void main(String[] args) {
System.out.println(MyMath2.sum(200L, 100L));
System.out.println(MyMath2.subtract(200L, 100L));
System.out.println(MyMath2.multiply(200L, 100L));
System.out.println(MyMath2.divide(200L, 100L));
MyMath2 mm = new MyMath2();
mm.a = 200L;
mm.b = 100L;
System.out.println(mm.sum());
System.out.println(mm.subtract());
System.out.println(mm.multiply());
System.out.println(mm.divide());
}
}
class MyMath2{
long a, b;
// 인스턴스 메서드
long sum() {return a + b;}
long subtract() {return a - b;}
long multiply() {return a *b;}
double divide() {return a / b;}
// 클래스 메서드
static long sum(long a, long b) {return a + b;}
static long subtract(long a, long b) {return a - b;}
static long multiply(long a, long b) {return a * b;}
static double divide(double a, double b) {return a / b;}
}
참조와 호출 - 클래스 멤버, 인스턴스 멤버
같은 클래스의 멤버 간에는 객체 생성이나 참조 변수 없이 참조할 수 있다.
그러나 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();
}
}
※ 인스턴스 생성과 동시에 메서드 호출
: 참조변수 선언을 하지 않았으므로 생성된 인스턴스는 더 이상 사용 불가
MemberCall c = new MemberCall();
int result = c.instanceMethod();
-> int result = new MemberCall().instanceMethod();
클래스 로더를 이용한 인스턴스 생성
: 문자열로 된 클래스, 메소드 이름만 있어도 인스턴스를 생성할 수 있음
Class clazz = Class.forName("패키지 이름을 포함한 클래스풀네임");
Object obj = clazz.newInstance();
3. 메서드 오버로딩(method overloading)
메서드 오버로딩
: 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것
: 같은 기능을 하는 메서드를 하나로 통칭하여 오류 가능성 낮춤
: 간단히 오버로딩(overloading)이라고 함
오버로딩의 조건
- 메서드 이름이 같아야 함
- 매개변수의 개수 또는 타입이 달라야 함
- 매개변수는 같고 반환 타입이 다른 경우는 오버로딩 성립 X
int add(int a, int b) {return a+b;}
long add(long a, long b) {return a+b;}
long add(int[] a) {
long result = 0;
for (int i = 0; i < a.length; i++) {
result += a[i];
}
return result;
}
가변인자(variable arguments)
: 메서드의 매개변수를 동적으로 지정
: 매개변수 중에서 제일 마지막에 선언해야 함
: 가변인자만 있는 경우 인자가 아예 없어도 됨
: 가변인자를 사용한 메서드는 오버로딩하지 않는 것이 좋음
: '타입... 변수명' 으로 선언
<-> 매개변수의 타입을 배열로 하면, 반드시 인자를 지정해줘야 하므로 인자 생략 불가
public PrintStream printf(String format, Object... args) {...}
4. 생성자(Constructor)
생성자
모든 클래스에는 반드시 하나 이상의 생성자가 있어야 한다.
: 인스턴스가 생성될 때마다 호출되는 인스턴스 초기화 메서드
: 어떤 값을 가지고 인스턴스가 만들어지게 하고 싶을 때 사용
: 인스턴스 생성 시 수행될 작업에 사용
- 정의 방법
클래스이름(타입 변수명, 타입 변수명, ...) {
// 인스턴스 생성 시 수행될 코드
}
- 인스턴스 생성 과정
Card c = new Card();
- 연산자 new에 의해서 메모리(heap)에 Card클래스의 인스턴스 생성
- 생성자 Card()가 호출되어 수행
- 연산자 new의 겨로가고, 생성된 Card인스턴스의 주소가 반환되어 참조변수 c에 저장
생성자의 조건
- 생성자의 이름은 클래스 이름과 같아야 함
- 생성자는 리턴 값이 없음
기본 생성자(default constructor)
: 매개변수가 없는 생성자
: 클래스에 생성자가 하나도 없으면 컴파일러가 기본 생성자 추가
: 생성자가 하나라도 있으면 생성 X
클래스이름() {}
매개변수가 있는 생성자
: 매개변수를 선언하여 호출 시 값을 넘겨받아 인스턴스의 초기화에 사용
생성자에서 다른 생성자 호출 - this()
: 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용
: 다른 생성자 호출은 생성자의 첫 문장에서만 가능
: 되도록이면 파라미터를 많이 사용하는 생성자를 this로 호출해주는 것이 유지보수 편리
class Car {
String color;
String gearType;
int door;
Car() {
this("white", "auto", 4);
}
Car(String color) {
this(color, "auto", 4);
}
Car(Car c) {
this(c.color, c.gearType, c.door);
}
}
참조변수 this
: 인스턴스 자신을 가리키는 참조변수
: 인스턴스의 주소가 저장되어 있음
: 인스턴스변수와 지역변수를 구별하기 위해 사용
class Car {
String color;
String gearType;
int door;
Car(String color, String gearType, int door) {
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
5. 변수의 초기화
: 지역변수는 사용 전에 반드시 초기화해야 함
: 멤버변수와 배열은 기본값으로 자동초기화되므로 초기화 생략 가능
멤버변수의 초기화 방법
- 명시적 초기화
- 생성자
- 초기화 블럭
- 인스턴스 초기화 블럭 : { }
생성자에서 공통적으로 수행되는 작업에 사용
인스턴스가 생성될 때마다 생성자보다 먼저 실행
- 클래스 초기화 블럭 : static { }
클래스가 로딩될 때 실행
멤버변수의 초기화 시기와 순서
- 클래스변수
: 클래스가 처음 로딩될 때 단 한 번
- 인스턴스변수
: 인스턴스가 생성될 때마다
클래스 초기화 블럭 -> 인스턴스 초기화 블럭 -> 생성자
class BlockTest {
static {
System.out.println("static { }");
}
{
System.out.println("{ }");
}
BlockTest() {
System.out.println("생성자");
}
public static void main(String[] args) {
System.out.println("BlockTest bt = new BlockTest();");
BlockTest bt = new BlockTest();
}
}
static { }
BlockTest bt = new BlockTest();
{ }
생성자
'Back-end > JAVA' 카테고리의 다른 글
[JAVA] 예외처리 - try-catch문, 예외 선언, 예외 되던지기 (0) | 2023.09.23 |
---|---|
[JAVA] 객체지향 프로그래밍(2) - 상속, 오버라이딩, Package와 import, 제어자, 다형성, 추상클래스, 인터페이스 (1) | 2023.09.16 |
[JAVA] 조건문, 반복문 (0) | 2023.02.28 |
[JAVA] 연산자 - 단항 연산자, 이항 연산자, 삼항 연산자 (0) | 2023.02.17 |
[JAVA] 변수, 변수의 타입, 입출력, 형변환 (0) | 2023.02.14 |