■필드
필드는 "클래스에 포함된 변수"를 의미하는 것으로 객체의 속성을 정의할 때 사용된다. 자바에서 변수는 크게 세 가지 Class 변수(cv, Class variable), 지역 변수(lv, local variable), 인스턴스 변수(iv, instance variable)로 구분할 수 있다.
이 중 우리가 필드라 부르는 것은 클래스 변수와 인스턴스 변수인데 이는 static 의 유무로 차이를 확인할 수 있다. 좀 더 구체적으로, static 변수와 함께 선언된 변수는 클래스 변수 그렇지 않은 변수는 인스턴스 변수이다. 그리고 이 두 가지 변수 유형에 포함되지 않고 메 서드 내에서 선언된 변수를 지역변수라고 한다.
이 세가지 유형의 변수들은 주로 선언된 위치에 따라 그 종류가 결정되며 각각 다른 유효 범위 (scope)가 결정된다.
class test{ //클래스 영역
static void test() { //클래스 번수 ( static 변수, 공유변수)
}
int instanceVariable // 인스턴스 변수
void method() { //메서드 영역
int localVariable = 0; // 지역 변수
}
}
위에 예시 코드에서는 test라는 클래스 안에 세가지 유형의 변수가 생성되어있다. 클래스 내에 선언한 testinstanceVariable 변수는 멤버 변수이며, static에 유무에 따라 test가 클래스 변수 tnstanceVariable이 인스턴스 변수가 된다. 마지막으로 local 변수는 메서드 내에서만 작동하기 때문에 지역변수이다. 이처럼 변수는 선언 위치와 static의 유무로 구분할 수 있다.
먼저 인스턴스 변수는 인스턴스가 가지는 각각의 교유한 속성을 저장하기 위해 new 생성자() 를 통해 인스턴스가 생성될 때 만들어진다. 클래스를 통해 만들어진 인스턴스는 힙 메모리에 독립적인 공간에 저장되고, 동일한 클래스로 부터 생성되었지만 객채의 고유한 개별성을 가지게 된다. 마치 사람마다 성격, 인종, 나이, 등이 다르듯이 인스턴스 변수도 다른 개별적인 특성을 정의하기 위해 만들어진다.
다음으로 static 키워드로 선언되는 클래스 변수(cv, class variable)가 있다. 클래스 변수는 독립된 저장공간을 가지는 인스턴스 변수와는 다르게 공통된 저장공간을 가지게 된다. 따라서 한 클래스에서 선언되는 인스턴스들이 특정한 값을 공유하기 위해서는 static 키워드를 사용하여 클래스 변수를 선언하게 되는 것이다. 차로 예를 들자면 바퀴의 개수, 엔진의 개수들 공통되는 것을 의미한다.
마지막으로 두 가지 변수와 구분되는 지역변수(lv, localVariale)가 있다. 지역변수는 메서드 내에 선언되며 메서드 내 {}에서만 사용되는 변수이다. 힙에 저장되는 멤버 변수와 다르게 스택에 데이터를 저장하는 지역변수는 프로그램이 종료됨가 동시에 메모리를 삭제시켜 버린다. 또한 힙 메모리에 정보를 저장하는 멤버 변수는 객체가 삭제될 때까지 데이터를 삭제시키지 않지만, 스택에 정보를 저장하는 지역변수는 한동한 저장된 데이터를 사용하지 않을 시 가상 머신에 의해 자동 삭제된다.
마지막으로 필드변수와 지역변수의 차이점은 초기값에 있다. 직접 초기화하지 않으면 출력하였을 때 오류가 나는 지역변수와는 다르게 멤버 변수는 초기값을 지정해 주지 않아도 오류가 나지 않는다. 그 이유는 멤버 변수는 직접 초기화해주지 않아도 강제 초기화가 이루어지기 때문이다. 힙 메모리는 빈 값을 저장할 수 없기에 강제적으로 초기화를 진행하지만 지역 변수에 스택 메모리는 강제로 초기화 되지 않으므로 꼭 초기화를 해주어야 한다.
■ 스태틱 키워드
static은 클래스의 멤버(field, 이너클래스, 메서드)에 사용되는 키워드이다. static 키워드가 붙어있는 멤버를 우리는 정적멤버(static member)라고 부르며 이는 static 키워드가 붙어있지 않은 인스턴스 변수와 구분된다.
이 둘을 구분하는 가장 큰 차이는, 인스턴스 변수는 기존 서술과 같이 인스턴스 객체를 생성한 후 변수와 메서드에 접근하여 해당 멤버를 사용 가능한 한 면, static 키워드로 정의되어있는 클래스 멤버들은 인스턴스 생성 없이도 클래스. 멤버 만으로도 사용 가능하다.
static 키워드를 사용하는 정적멤버를 클래스명. 멤버명으로 사용할 수 있는 이유도 메모리에 저장 위치와 관련이 있다.
앞서 확인했듯이 new 생성자() 키워드를 사용하여 생성된 인스턴스는 힙 메모리에 생성되고 독립적인 공간을 차지하게 되는데, 반면 static 키워드를 사용하여 선언한 정적 멤버는 클래스 내부에 저장공간을 가지고 있기 때문에 객체 생성 없이 곧바로 사용할 수 있다.
public class StaticTest {
public static void main(String[] args) {
Hello hello = new Hello();
System.out.println(hello.num);
System.out.println(Hello.num1);
}
}
class Hello{
int num = 10;
static int num1 = -10;
}
위에 예시를 보면 static 키워드의 유무에 따러서 달라지는 차이를 확인해 볼 수 있다. 실험을 위해, 먼저 Hello 클래스에 static 참조변수가 있는 num1과 static 메서드가 없는 num에 각각 -10,10을 할당하였다. 그리고 StaticTest 클래스에서 각각의 필드 값을 호출해 주었다. 다만 인스턴스 변수는 우리가 아는 일방적인 방법으로 객체 생성 후 포인트 연산자 통하여 그 값을 가져왔고, 클래스 변수는 객체 생성 없이 클래스명을 사용하였다.
여기서 우리가 기억해야할 한 가지는 정적 필드는 객체 각 공유 변수의 성질이 있다는 점이다. 이것은 메서드에도 동일하게 적용된다. 일반적인 메서드 앞에 static 키워드를 사용하면 해당 메서드는 정적 메서드가 된다. 정적 메서드도 정적 필요와 마찬가지로 클래스명만으로 바로 접근이 가능하다.
한 가지 기억해야 할 점은 정적 메서드의 경우 인스턴스 변수 또는 인스턴스 메서드를 사용할 수 없다는 점이다. 정적 메서드는 인스턴스 생성 없이 호출이 가능하기 때문에 정적 메서드가 호출되었을 때 인스턴스가 존재하지 않을 수 없기 때문이다.
public class StaticFieldTest {
public static void main(String[] args) {
StaticField staticField1 = new StaticField(); // 객체 생성
StaticField staticField2 = new StaticField();
staticField1.num1 = 100;
staticField2.num1 = 1000;
System.out.println(staticField1.num1);
System.out.println(staticField2.num1);
staticField1.num2 = 150;
staticField2.num2 = 1500;
System.out.println(staticField1.num2);
System.out.println(staticField2.num2);
}
}
class StaticField {
int num1 = 10;
static int num2 = 15;
}
//출력값
100
1000
1500
1500
StaticField 클래스에 인스턴스 필드(num1)와 정적 필드(num2)를 각각 선언하고, 대조를 위해 staticField1와 staticField2 객체를 생성했다. num1의 경우에는 각각의 변수가 고유성을 가지기 때문에 100과 1000으로 따로 출력되는 반면에, num2의 경우는 앞서 배웠던 것처럼 값 공유가 일어나 1500이 출력 값으로 두 번 반복되고 있다. 이처럼 static 키워드를 사용하면 모든 인스턴스에 공통적으로 적용되는 값을 공유할 수 있다.
결론적으로 static 키워드는 클래스의 멤버 앞에 붙일 수 있다. 정적 멤버의 가장 큰 특징은 인스턴스를 따로 생성하지 않아도 클래스명만으로도 변수나 메서드 호출이 가능하다는 점이었고, 이는 메모리의 저장위치와 관련이 있다는 사실을 알 수 있었다.
■ 메서드
메서드는 "특정 작업을 정리하는 일련의 명령문들의 집합"을 의미하며, 앞서 본 것처럼 클래스의 기능에 해당하는 내용들을 담당한다. 앞서 봤었던 자동차의 예시 시동걸기,출발하기,브레이크 등이 메서드로 구성되어있다.
메서드는 다시 크게 머리에 해당하는 메서드 시그니쳐( method Signature )와 메서드 바디( method Body )로 구분되어있다.
아래 코드를 통해 자세히 알아보도록 하자.
자바제어자 반환타입 메서드명(매게 변수) { //메서드 시그니쳐
// 메서드 바디
}
public void test(int test){
int result = test;
return result;
}
먼저 머리의 메서드는 제어자, 반환타입, 메서드명(참조 변수) {}로 구성되어있는 것을 볼 수 있다. 메서드 시그니쳐는 순서대로 해당 메서드가 어떤 타입을 반환하는지, 메서드의 이름은 무엇인지, 해당 작업을 수행하기 위해서 어떠한 재료들이 필요한지(매개 변수)에 대한 정보들을 담을 수 있다. 다음으로 메서드 바디 {} 에는 메서드가 실행되었을 시 수행되어야 할 코드들이 포함되어있다. 관례적으로 메서드명은 소문자로 표기한다.
public int sum(int x, int y) { //메서드 시그니쳐
int result = x + y; //메서드 바디
return result;
}
위 예시는 앞서 서술한 내용이 실제 코드에서 어떻게 적용되는지 잘 보여주는 예시이다. 이 코드는 메서드 명이 sum이며 매개 변수로 x,y를 반환하고 그 값을 int타입의 결괏값을 반환하는 코드이다.
만약 메서드 반환타입이 void드가 아닌 경우 {} 안에 반드시 return 문이 존재해야한다. 리턴 문은 작업을 수행한 결괏값을 호출한 메서드로 전달한다. 여기서 결괏값은 반드시 반환 타입과 일치하거나 적어도 자동형 변환이 가능한 결괏값을 리턴해야 한다.
예시들을 더 봐보자
void helloPrint() { //void 타입 메서드
Sysyem.out.println("hello");
}
위 코드는 void, 즉 반환 타입이 void라는 메서드이다. 이 메서드는 리턴이 필요하지 않기 때문에 hello를 출력한 후 종료된다.
int getNumSeven() { //매개변수가 없는 메서드
return 7;
}
위 코드는 int타입이지만 매개변수가 없다. 그 이유는 다른 수식 없이 그저 7만 출력하는 메서드 이기 때문이다.
public double Multiply(int x, int y) {
double result = x * y;
return result;
}
위 타입은 매개변수 x, y를 전달받아 x*y가 곱해진 값을 반환 타입이 double인 result로 반환하는 메서드이다.
( int와 double은 int == 4byte, double == 8byte 이기 때문에 자동형 변환이 이루어진다 )
■ 메서드의 호출
메서드를 아무리 잘 정의하였다 하더라도, 메서드를 호출해주지 않으면 그것은 무용지물이다.
메서드도 클래스의 멤버이므로 메서드를 사용하기 위해서는 외부에서 인스턴스를 생성해 주어야 한다. 인스턴스를 생성한 후에 포인트 연산자(.)를 통하여 메서드를 호출할 수 있다. 반면, 클래스 내부에 있는 메서드들은 따로 객체를 생성하지 않아도 서로를 호출할 수 있다.
메서드이름(매개 변수1, 매개변수2) // 메서드 호출방법 매개변수 있을 수도 있고 없을수도 있음
//위 코드들 호출
void printHello();
int getNumSeven();
public double Mutiply(4,4.0);
//출력값
hello;
7;
16.0
메서드 호출 시 () 안에 넣어주는 값을 우리는 인자라 고한다. 인자는 매개변수의 개수와, 순서, 타입과 정확히 일치하여야 한다.
■ 메서드 오버 로딩 ( Method Overloading)
메서드 오버 로딩이란 하나의 클래스 안에 같은 이름의 메서드를 여러 개 선언하는 것이다. "loading"의 사전적 의미가 "과적하다/부담을 지우다"라는 것을 보면 조금 더 이해하기에 용이할 것이다. 보통 메서드는 하나의 기능만을 구현해야 하는데 메서드 오버 로딩을 통해서 여러기능을 구현한다 생각하면 조금더 이해하기 편하 것 이다.
메서드 오버로딩을 이해하기 위해서는 앞서 서술한 메서드 시그니쳐 (method signature)을 제대로 이해하고 있어야 한다. 메서드 시그니쳐는 메서드명과 매개변수의 자료형을 의미하는데 "서명"을 의미하는 용도로 유추할 수 있듯이 각 메서드를 구분하는 용도로도 사용한다. 이것은 메서드의 이름 또늠 매개변수의 타입 다르다면 다른 메서드라 인식하는 자바 가상 머신과도 상관관계에 있다.
public class OverloadingTest {
public static void main(String[] args) {
Shape shape = new Shape();
shape.area();
shape.area(30);
shape.area(40,40);
shape.area(60,100);
}
}
class Shape{
public void area(){
System.out.println("넓이");
}
public void area(int r){
System.out.println("원넓이" + 3.14 * r * r);
}
public void area(int w, int h){
System.out.println("직사각형의 넓이" + w * h);
}
public void area(double b, double h){
System.out.println("삼각형의 넓이" + b * h);
}
}
//출력값
넓이
원넓이2826.0
직사각형의 넓이1600
직사각형의 넓이6000
위 코드를 보면 모든 메서드들이 area()라는 메서드명을 가지고 있지만 각기 다른 값을 출력하는 것을 확인할 수 있다. 여기서 우리가 주의할 점은 같은 메서드명을 쓴다고 해서 무조건 오버 로딩이 되는 것이 아니라는 것이다.
메서드를 오버 로딩하기 위해서는 크게 2가지의 조건을 충족해야 하는데, 먼저 위 코드에서 봤듯이 같은 이름의 메서드 명을 사용해줘야 하고 두 번째로 메서드가 매개변수의 개수 또는 타입 또는 변수명은 달라한다는 것이다 ,
'Programming > java' 카테고리의 다른 글
[Java] 열거형과 에노테이션 (0) | 2022.05.19 |
---|---|
[Java] 제네릭 (0) | 2022.05.17 |
[Java] Type (0) | 2022.05.04 |
[JAVA] IF문 IF ELSE 문 중첩 실행 (0) | 2022.04.26 |