필드 (전역변수)

클래스에 포함된 변수

객체의 속성값을 지정할 수 있다

 

지역변수

메서드에 포함된 변수

 

필드 (전역변수) VS 지역변수

클래스의 중괄호 안에 선언된 변수를 필드(전역변수)

메서드의 중괄호 안에 선언된 변수를 지역변수

 

필드와 지역변수의 가장 큰 차이점은 생성되는 메모리의 위치

 

필드는 힙 메모리의 객체내부에 생성

지역변수는 스택 메모리에 생성

 

* 메서드의 매개변수 들어오는 곳에 선언한 변수도 지역변수로 본다 

* 객체가 더이상 사용되지 않을때 가비지 컬렉터가 객체 자체를 제거한다

* 정의된 메서드의 중괄호에 해당하는 범위를 메모리에서는 프레임이라 부른다

* 스택메모리는 나중에 들어간 것이 먼저 나오는 LIFO 구조를 띈다

 

필드와 지역변수의 또 다른 차이점

필드는 직접 초기화하지 않아도 강제로 초기화 된다

지역변수는 직접 초기화하지 않으면 저장 공간이 빈 공간 그대로 있어

값을 출력하고자 하면 오류가 발생

 

이는 메모리 영역의 특징인데

힙 영역은 빈공간이 저장될 수 없기에 강제로 초기값을 주는것이고

스택 영역은 강제로 초기화되지 않기에 그렇다

 

메서드

클래스의 기능에 해당하는 요소

 

자바 제어자 리턴(반환) 타입 메서드명 (입력매개 변수) { 메서드 내용 }

public static int sum ( int a, int b) { }

 

리턴타입 : 메서드 종료 이후 반환 되는 값의 자료형

메서드명 : 변수명 선정 규칙과 동일

입렵매개변수 : 메서드를 호출할 때 전달되는 값의 자료형과 전달받은 값을 저장할 지역 변수명

중괄호 안 : 메서드가 수행해야할 기능을 작성

 

리턴타입이 있으면 return값을 메서드 내용 마지막에 넣어줘야 오류가 나지 않는다

리턴타입이 없는 메서드라면 void를 붙여준다

 

* void 리턴 타입은 리턴하지 않는다는 의미이지만 내부에 retrun 키워드를 사용할 수 는 있다

이때 return이 의미하는 것은 메서드를 종료하라는 뜻이다

 

* 같은 멤버 끼리는 클래스 내부에서 얼마든지 객체를 생성하지 않고 서로를 호출할 수 있다

 

입력매개변수가 배열인 메서드 호출하기

public staitc void main (String[ ] arr) {
	int [ ] a = new int [ ] {1, 2, 3};
    	printArray(a);
	// 또는
   	printArray(new int[ ] {1, 2, 3});
}

public static void printArray(int [ ] a) {
   	System.out.println(Arrays.toString(a));
}

 

* 입력매개변수가 기본자료형과 참조자료형 에 따라 달라진다

기본자료형은 전역변수의 값이 넘어가면 메서드에있는 지역변수에 복사되어 사용되기에

메서드에서 값이 바뀌는 작업이 있었더라도 전역변수의 값이 변하지 않는다

 

참조자료형은 스택영역에 힙영역의 주소값을 가지고있고  그 주소값을 메서드에 지역변수로 주기 때문에

메서드에서 값이 바뀌는 작업이 있으면 객체의 값이 변하게 된다

 

오버로딩된 메서드

메서드 에서의 오버로딩은 입력매개변수의 개수나 자료형이 다른

여러개의 동일한 이름을 지닌 메서드를 같은 공간에 정의하는 것을 의미한다

 

즉 동일한 이름을 가진 메서드라도 입력 매개변수에 따라서 다른 메서드를 호출하게 된다

 

우리가 화면에 출력할때 System.out.println()을 많이 사용하는데

공식 API문서에서 println() 메서드를 살펴보면

많은 메서드가 오버로딩 된것을 확인할 수 있다

 

https://docs.oracle.com/javase/7/docs/api/

 

Java Platform SE 7

 

docs.oracle.com

 

가변 길이 배열 입력매개변수 메서드

리턴 타입 메서드명 (자료형 ... 참조 변수명) { }

 

입력 매개변수로 0 ~ 10 까지의 수를 받는다고 가정하면 몇개의 입력이 들어올지 모른다

그래서 메서드를 11개를 만들어야하는데 이러면 비효율적이다

그때 사용하는 것이 가변 길이 배열 입력매개변수를 사용한 메서드이다

오버로딩의 불편함을 덜고자 만들어진 문법이다

 

// int 값 받는 메서드
public static void method1 (int...values) {
	System.out.println("입력매개변수 길이 : " + values.length);
    for (int i=0; i<values.length; i++) {
    	System.out.println(values[i] + " ");
    }
    System.out.println();
}

// String 값 받는 메서드
public static void method1 (String...values) {
	System.out.println("입력매개변수 길이 : " + values.length);
    for (int i=0; i<values.length; i++) {
    	System.out.println(values[i] + " ");
    }
    System.out.println();
}

 

생성자

객체를 생성하는 역할을 지닌 클래스의 내부 구성 요소이다

객체 내에 포함되는 필드의 초기화 또는 주로 생성자 내에서 수행한다

 

생성자 특징

- 반드시 클래스명과 동일한 이름으로 지어야한다

- 메서드와 비슷한 구조를 지니고 있지만 리턴 타입이 없다

 

여기서 return 타입이 없다와 리턴하지 않는다 (void) 와는 다른 개념이다

 

클래스명 (입력매개변수){ }

 

생성자명은 클래스명과 동일하게

입력 매개변수는 생략가능하다

 

A a = new A() 를하면 생성자가 없었더라도 자동으로 입력매개변수가 없는 기본생성자를 추가해준다

 

클래스가 붕어빵 기계이고, 객체가 붕어빵 이라고 한다면

생성자는 붕어빵을 찍는 기능 정도라고 생각하면 된다

즉 생성자가 없으면 붕어빵을 찍는 기능이 없는 클래스가 되기에 존재 이유가 없어진다

 

그래서 클래스를 만들면 적어도 1개의 생성자가 필요해서 매개변수가 없는 기본생성자가 추가되는 것이다

 

만약 생성자를 수동으로 만들어 줘도 기본생성자가 생기느냐? 한다면 아니다

생성자가 이미 존재하기에 기능을하는 클래스이고 굳이 기본 생성자를 만들어줄 필요가 없다

 

생성자의 중괄호 안은 객체가 생성된 이후 할일이 작성되는 부분이다

일반적으로는 여기에서 필드를 초기화 한다

 

생성자 또한 오버로딩이 가능하다

 

this 키워드 와 this( ) 메서드

먼저 this. 키워드에 대해 알아보자면

클래스 외부에서는 멤버를 호출하기 위해 객체를 생성해서 . 을 이용해 호출하지만

내부에서는 객체 생성 없이 this. 을 붙여서 사용한다

 

하지만 우리가 전역변수를 출력할때 System.out.println(this.m) 이렇게 안했다

그냥 m만 했다 그 이유는 컴파일러가 자동으로 this. 을 추가해줬기 때문이다

지역변수는 멤버가 아니므로 this. 이 자동으로 붙지않는다

 

* 인스턴스 메서드 내부에서는 this 를 사용할 수 있지만

static 메서드 내부에서는 사용할 수 없다

 

그렇다면 this. 를 알아서 붙여주니까 신경 안써도 되겠다 싶지만

입력매개변수의 지역변수명과 전역변수명이 같으면 써줘야한다

만약 써주지 않으면 전역변수와 지역변수 모두 사용할 수 있는 영역에서는

사용 범위가 좁은 변수인 지역변수로 인식한다

 

// this를 자동으로 못 붙여줌
class A {
    int m;
    int n;
    void init(int m, int n){
    	m = m;
        n = n;
    }
}

// this를 수동으로 붙여줘야함
class A {
    int m;
    int n;
    void init(int m, int n){
    	this.m = m;
        this.n = n;
    }
}

// this 가 없는데 전역이랑 지역 둘다있으면 더 좁은 개념인
// 지역으로 판단해버림

 

다음으로 this( ) 메서드에 대해 알아보자면

자신이 속한 클래스 내부의 다른 생성자를 호출하는 명령어이다

만약 클래스 명이 A 라면 this( ) 를 하면 A( ) 생성자를 호출하는거라 생각하면 된다

만약 this(3) 이라면 A(3) 데이터 하나를 입력받는 생성자를 호출하라는 거라 생각하면 된다

 

this( ) 메서드도 규칙이 있는데

- 생성자의 내부에서만 사용 할 수 있다

- 생성자의 첫 줄에 위치해야한다

 

이 둘 중 하나라도 지켜지지 않으면 오류가 발생한다

 

일단 A 클래스에 A( ), A(int a) 라는 생성자 2개를 생성했다

A( )는 바로 출력문이있는 생성자이고

A(int a)는 첫줄에 A(  )생성자를 호출하고 다음에 출력문이 있는 생성자이다

이러면 객체 a1 을 생성하면 A( ) 생성자가 호출되고

객체 a2 를 생성하면 A(int a) 생성자를 호출하는데

여기서 볼건 A(int a)를 호출할때 첫줄에 A( )를 호출했기에

A( ) 출력문을 먼저 출력하고 그다음에 A(int a)에 해당하는 출력문을 출력한다는 것이다

 

class A{
	A() {
    	System.out.println("첫번째 생성자");
    }
    
    A(int a){
    	this();
        System.out.println("두번째 생성자");
    }
}

public class Method{
    public static void main(String[ ] args){
        A a1 = new A();
        System.out.println();
        A a2 = new A(3);
    }
}

 

이렇게 this( ) 를 쓰는 이유는 생성자 에서 필드를 초기화 할때 문제 때문이다

만약 A( ), A(int a), A(int a, int b) 생성자 3개가 있다면

각각 생성자에서 전부 필드를 초기화 해줘야하는데

A( )에서 공통으로 가져다 쓸 수 있는게 있다면

따로 필드를 초기화 해줄것이 아니라 A( ) 이렇게만 해주면

A( ) 생성자 안에있는 필드를 그대로 가져다 쓸 수 있기 때문이다

 

// this( )를 쓰기 전
class A{
	A() {
    	m1 = 1;
        m2 = 2;
        m3 = 3;
        m4 = 4;
    }
    
    A(int a){
    	m1 = a;
        m2 = 2;
        m3 = 3;
        m4 = 4;
    }
    
    A(int a, int b){
    	m1 = a;
        m2 = b;
        m3 = 3;
        m4 = 4;
    }
}

// this( )를 쓴 후
class A{
	A() {
    	m1 = 1;
        m2 = 2;
        m3 = 3;
        m4 = 4;
    }
    
    A(int a){
    	this();
    	m1 = a;
    }
    
    A(int a, int b){
    	this(a)
        m3 = 3;
        m4 = 4;
    }
}