본문 바로가기

국비과정

[ANDROID 국비과정] 2023.01.26 - 안드로이드 앱 개발자 과정

JAVA


생성자란?

객체가 생성될 때 한번만 자동으로 실행되는 특별한 메소드이며, 인스턴스 변수의 초기화 작업이나 인스턴스 생성 시 실행되어야 하는 작업을 위해서 사용됩니다. 생성자 라는 말 그대로, 클래스의 인스턴스의 생성과 관련이 깊은 메소드 정도로 이해하고 알아봅시다. 

 

생성자 등장 이유

생성자가 등장한 이유를 알아보기 위해 사용자 정보[이름, 나이] 데이터를 설계한 Person 클래스를 설계해보겠습니다.

 

public class Person {
	private String name;
	private int age;
    
	public void show() {
		System.out.println("name : " + name);
		System.out.println("age : " + age);
		System.out.println();
	}
}

 

Person 클래스의 멤버변수 name 과 age 를 만들었고, 그 데이터를 출력하는 메소드 show() 를 만들었습니다. 보통 클래스의 멤버변수들의 접근제한자는 private 이고, 메소드는 public 이 권장됩니다. 멤버변수의 데이터를 쉽게 건드릴수 있게되다 보면 오류가 날 가능성이 생기기 때문입니다. 그런데 Person 클래스의 멤버를 private 으로 설정하다 보니 Main 클래스에서는 Person 클래스의 객체의 정보를 설정해줄 수가 없게 되었습니다. 그래서 우리는 사람의 데이터를 입력하기 위해 Person 클래스에게 이야기합니다. 데이터를 입력해주는 기능을 해줘!

 

-------- Person.java
public class Person {
    private String name;
    private int age;

    public void setMembers(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void show() {
        System.out.println("name : " + name);
        System.out.println("age : " + age);
        System.out.println();
    }
}


--------- Main.java
public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        p.setMembers("sam",20);
        p.show();
    }
}

 

데이터를 입력해주는 기능을 하는 setMembers()를 만들었습니다. 그리고 Person 클래스의 객체를 생성해준 뒤, 데이터를 전달해주어 객체의 정보를 저장합니다. 그러면 추가로 객체를 만들어볼까요?

 

Person p2 = new Person();
p2.setMembers("robin",25);
p2.show();

 

기존에 만들었던 메소드를 이용해 성공적으로 객체를 생성했습니다. 그런데, 이렇게 하다보니 항상 2줄의 코드를 작성해야하고, 데이터의 값을 넣어주는 기능을 하는 메소드도 만들어주어야 하니 좀 짜증이 납니다. 그리고 이 작업을 수백 수천번 한다고 생각을 해봅시다. 너무나도 귀찮을것 같지 않나요? 이러한 연유로 생성자(Constructor) 라는 개념이 나오게 됩니다.

 


생성자 만들기

생성자 규칙

  1. 메소드의 이름은 클래스의 이름과 같다.
  2. 리턴타입을 명시하지 않음.

규칙은 간단합니다. 그럼 이 규칙들을 적용해서 Person 클래스에 생성자를 만들어봅시다.

 

--------- Main.java
public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        p.show()
    }
}

--------- Person.java
public class Person {
    private String name;
    private int age;

    public void show() {
        System.out.println("name : " + name);
        System.out.println("age : " + age);
        System.out.println();
    }

    public Person() {
        System.out.println("Person 객체 생성 ! ");

        name = "익명";
        age = 0;
    }
}

 

생성자 메소드의 이름과 클래스의 이름이 동일하며, 리턴타입은 명시하지 않았습니다. 그리고 출력문 하나와 멤버변수의 초기값을 지정해주었습니다. 또한, 접근제한자를 지정할 수 있습니다. 여기서는 public 으로 지정해주었습니다. 자, 이렇게 생성자를 생성할 수 있습니다.

 

생성자 활용

생성자도 메소드 라고 이야기했습니다. 그러면 혹시 매개변수를 이용할 수 있지 않을까요? 맞습니다. 생성자에 매개변수를 전달하는것도 가능합니다. 아 그러면 메소드에서 가능했던 오버로딩도 가능하지 않을까요? 그것도 가능합니다. 그것들에 대한 예제를 알아봅시다.

 

--------- Person.java
public class Person {
    private String name;
    private int age;

    public void show() {
        System.out.println("name : " + name);
        System.out.println("age : " + age);
        System.out.println();
    }

    public Person() {
        name = "익명";
        age = 0;
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

 

생성자의 오버로딩과 생성자의 목적 중 하나는 멤버변수의 초기화에 있습니다. 그래서 this 키워드를 통해 매개변수로 전달받은 값을 멤버변수에 넣어주어 초기화를 완료했습니다. 또한 생성자 오버로딩을 통해 이름은 같고, 매개변수를 달리해서 사용자가 편리하게 이용할 수 있도록 만들었습니다. 추가적으로 생성자 오버로딩은 갯수의 제한이 없기 때문에 몇개라도 추가로 만들수 있다는 점도 알아둡시다.

 

 

생성자의 접근제한자

아까 생성자에는 접근제한자를 사용할 수 있다고 하였습니다. 그렇다보니 접근제한자의 잘못된 지정 때문에 객체생성이 불가한 상황이 생길 수 있습니다. 그걸 알아보기 위해 aaa 라는 패키지를 만들고 Test 라는 클래스를 만들었다고 합시다.

 

---------- Test.java
package aaa;

public class Test {
    Test(){
        System.out.println("Test 객체가 생성!");
    }
}

---------- Main.java
public class Main {
    public static void main(String[] args) {
        aaa.Test t = new aaa.Test(); // error!!
    }
}

 

위 코드에서 aaa 패키지의 Test 클래스 객체를 생성하려 했더니 바로 에러가 납니다. 왜그럴까요? 바로 Test 클래스의 접근제한자 때문입니다. 객체를 생성할 때 생성자가 자동으로 호출이 됩니다. 그런데, 호출을 하려고 보니 접근제한자가 default 인 상황입니다. default 접근제한자가 붙어있다면, 다른 패키지에서는 호출할 수 없기 때문에 그래서 자바에서는 오류를 띄웁니다.

 


this( ) 생성자

this() 생성자는 멤버변수의 갯수가 많을 때, 값 대입을 매번하기 번거로울때 사용하는 문법입니다. 다음과 같은 코드가 있다고 생각해봅시다.

 

public class Second {
    private int a,b,c,d,e;

    public Second(int a,int b,int c,int d,int e) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.e = e;
    }
    public Second() {
        this(0,0,0,0,0); // Second 클래스의 생성자 호출!
    }
}

 

 

생성자의 존재 이유 중 하나는 멤버변수의 초기화가 목적입니다. 그래서 명시적인 값 전달이 없을 경우에는 각 데이터의 default 값을 대입하는 코드를 작성해주어야 합니다. 그런데 위 코드처럼 혹은 그보다 더 많은 수의 멤버변수들이 있다면 값을 대입하는 과정이 아주아주 귀찮을것 같습니다. 그래서 this() 를 사용하여 Second 의 생성자를 호출해 번거로움을 해결하였습니다.

 


멤버변수 초기화 4단계

초기화가 단계별로 실행되며 실행구문의 순서와는 관계가 없습니다.

 

  1. 기본값 초기화
  2. 명시적 초기화
  3. 초기화 블록
  4. 생성자
public class InitialTest {
    int a; // 1. 기본값 초기화
    int b = 10; //2. 명시적 초기화

    // 3. 초기화 블럭 : 프로그래밍적으로 초기화를 할 수 있다는 장점이 있음.
    {
        int c = 30;
        c++;
        if(c<25) c--;
        System.out.println("초기화블럭!");
    }
    // 4. 생성자 메소드를 이용한 초기화 : 파라미터를 전달받아 초기화를 할 수 있다는 장점.
    public InitialTest (){
        System.out.println("생성자 메소드!!");
    }
}

 

 


static 키워드

Static 변수는 클래스 변수라고도 합니다. static 키워드를 사용하여 변수를 생성한다면 객체를 생성하지 않고도 사용할 수 있는 변수가 됩니다. 그리고 static 변수는 클래스를 생성할 때, 즉 프로그램 시작 과정에서 메모리에 올라가게 됩니다. 그리고 프로그램이 종료되야 메모리에서 해제됩니다. 잘못 사용하다가는 시스템 성능에 악영향을 끼치기 때문에 조심해서 사용해야 하는 키워드라고 볼 수 있습니다. 

 

static 에 대해 알아보기 위해 다음 코드를 먼저 봅시다.

 

----------- Test.java
public class Test {
    public int a;
    public static int b;
}

----------- Main.java
public class Main {
    public static void main(String[] args) {
        Test t1 = new Test();
        Test t2 = new Test();
        Test t3 = new Test();
    }
}

 

Test 클래스에 멤버변수로 int a  static int b 를 설정했습니다. 그리고 Main 클래스에서 Test 클래스의 객체를 생성했습니다. 이때, 그림처럼 static 변수는 각 객체에 존재하지 않습니다. 그리고 변수 a 는 각 객체마다 존재합니다. 그래서 a 변수를 인스턴스 변수 라고 부르기도 하며, static 변수는 클래스에 1개가 존재하므로 클래스 변수 라고 부르기도 합니다. static 은 객체에 존재하지 않고 Test 클래스에 존재할 뿐입니다. 이 사실을 잘 기억해야 합니다. 다음 그림은 위 코드를 도식화 한 그림입니다. 참고합시다. 

 

 

 


Inner Class 

단어 그대로 클래스 안에 클래스가 설계되면, 그 설계된 클래스를 inner class 라고 합니다. 그리고 이너클래스를 가진 클래스를 outer class 라고 합니다.

 

Inner class 의 특징

이너클래스의 특징에 대해 알아봅시다.

 

1. 다른 클래스에서 outer class 이름 없이는 인식이 불가합니다.

------------- Main.java
public class Main {
    public static void main(String[] args) {
        Nice nice; // error!!! 
    }
}

------------- Test.java
public class Test {
    class Nice {
    }
}

 

2. 다른 클래스에서 outer class 이름을 이용해 인식하더라도, 직접적인 객체 생성은 불가합니다.

------------- Main.java
public class Main {
    public static void main(String[] args) {
        Test.Nice nice = new Test.Nice() // ERROR!!!
    }
}

------------- Test.java
public class Test {
    class Nice {
    }
}

 

3. inner class 는 outer class 안에서만 객체 생성이 가능한 클래스 입니다.

------------- Main.java
public class Main {
    public static void main(String[] args) {
        Test t = new Test();
        Test.Nice nice = Test.makeObj();
    }
}

------------- Test.java
public class Test {
    Nice makeObj(){
        return new Nice();
    }
    class Nice {
    }
}

 

4. inner class 안에서 outer class 의 멤버를 내것인양 마음대로 사용이 가능합니다.

------------- Test.java
public class Test {
    int a = 10;
    String str = "hello";
    void show() {
        System.out.println("Hello");
    }
    class Nice {
        a = 50;
        str = "HI";
        show();
    }
}

 

5. 반대로 outer class 안에서는 inner class 의 멤버를 맘대로 사용할 수 없습니다.

------------- Test.java
public class Test {
    k = 10; //ERROR!!!
    class Nice {
        int k = 10;
    }
}

inner class 가 존재하기 위해서는 outer class 가 존재해야 합니다. 그렇기 때문에 inner class 에선 outer class 의 멤버를 자유롭게 사용가능하지만, outer class 가 존재한다고 해서 inner class 가 반드시 존재하는것은 아니기 때문에 outer class 에서는 inner class 의 멤버를 마음대로 사용할 수 없습니다. inner class 는 객체를 안전하게 만들기 위해 사용하는 기법입니다. 즉, 외부에서 아우터 객체 없이 마음대로 생성하지 못하도록 문법적으로 막아두는 기법입니다.

 


Local Class 

메소드 영역 안에 클래스를 설계하면 이 클래스를 Local class 라고 합니다. 로컬클래스는 설계된 지역 안에서만 인식이 가능한 클래스로, 지역클래스, 내장클래스, 내부클래스 라고도 부릅니다.

 

Local class 를 사용하는 이유

클래스가 설계된 메소드가 실행중 일때만, 잠시 1회용 처럼 사용하는 객체를 만들고 싶을때 로컬 클래스를 설계합니다. 나중에 배우게 될 익명 클래스 라는것을 사용할 때 많이 사용됩니다.