2023.01.26 - 안드로이드 앱개발자 과정

CHA·2023년 1월 27일
0

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 이 권장됩니다. 멤버변수의 데이터를 건드릴수 있게되다 보면 오류가 날 가능성이 생기기 때문입니다. 그런데 이러다 보니 Main 클래스에서 객체의 정보를 설정해줄 수가 없게 되었습니다. 그래서 우리는 사람의 데이터를 입력하기 위해 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 의 생성자의 접근제한자를 default 로 설정하고 Main 클래스에서 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!!
    }
}

바로 에러가 납니다. 왜그럴까요? 바로 Test 클래스의 접근제한자 때문입니다. 객체를 생성할 때 생성자가 자동으로 호출이 됩니다. 그런데, 호출을 하려고 보니 접근제한자가 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. 명시적 초기화

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

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 astatic int b 를 설정했습니다. 그리고 Main 클래스에서 Test 클래스의 객체를 생성했습니다. 이때, 그림처럼 static 변수는 각 객체에 존재하지 않습니다. 그리고 변수 a 는 각 객체마다 존재합니다. 그래서 a 변수를 인스턴스 변수 라고 부르기도 하며, static 변수는 클래스에 1개가 존재하므로 클래스 변수 라고 부르기도 합니다. static 은 객체에 존재하지 않고 Test 클래스에 존재할 뿐입니다. 이 사실을 잘 기억해야 합니다.


Inner Class & Local Class


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();
    }
}

inner class 가 존재하기 위해서는 outer class 가 존재해야 합니다. 그렇기 때문에 사용이 가능합니다.

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

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

outer class 가 존재한다고 해서 inner class 가 반드시 존재하는것은 아니기 때문에 에러를 일으킵니다.

inner class 는 객체를 안전하게 만들기 위해 사용하는 기법입니다. 즉, 외부에서 아우터 객체 없이 마음대로 생성하지 못하도록 문법적으로 막아두는 기법입니다.

Local class 의 개념

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

Local class 를 사용하는 이유

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

profile
Developer

0개의 댓글