생성자
생성자는 인스턴스 생성 때 호출되는 인스턴스 변수 초기화 메서드이다.
우리가 흔히 착각할 수 있는 게 생성자는 인스턴스를 생성하는 데 사용된다고 생각을 하지만
사실은 new 키워드가 담당하고 있으며 그 뒤에 있는 메서드가 생성자다.
구조적으로는 메서드와 비슷하다고 볼 수 있지만 제일 큰 특징은 바로 리턴 타입을 가지고 있지 않다는 점이다.
또한 클래스와 이름이 같아야 한다는 조건을 가지고 있다.
다만 메서드와 같게 생성자도 오버 로딩이 가능해 여러 개의 생성자가 클래스 내에 존재하기도 한다.
우리가 코드를 작성할 때 사실은 클래스 내에서 생성자 없이 코딩을 할 수 있었다 사실은 클래스 모두 각각 하나의 생성자가
필요하다 하지만 우리가 그러지 않고 작성할 수 있는 이유는 컴파일러에서 자동으로 생성자를 하나 생성하기 때문이다.
클래스명(){} //컴파일러 기본 생성자
NewWorld(){} // 예시) NewWorld 클래스의 기본 생성자
생성자를 통해서 클래스에 다양한 기능(메서드)을 정의해두고 매개변수 유형에 따라 사용한다면
다양한 프로그램을 제작함에 있어서 간결하고 직관적으로 설계 밑 유지보수 가능한 프로그램을 제작할 수 있을 것이다.
this와 this()?
전에 TIL에서 거론한 메서드에서는 메서드 간의 서로 호출이 가능했던 것처럼
생성자에서도 상호 간의 호출이 가능한데 이게 바로 이 섹션의 타이틀인 this() 메서드이다.
this() 메서드를 사용하기 위해서는 큰 틀에서 두 가지 정도의 문법 요소를 충족시키면 된다.
- this() 메서드는 반드시 생성자 내부에서 사용해야 한다.
- this() 메서드는 반드시 생성자의 첫술에 위치하도록 한다
해당 조건을 따라서 코드를 설계한다면 첫 번째 생성자 호출 후 두 번째 생성자를 호출시에 메서드를 사용한다면 기본 첫번째 생성자가 호출된 후에 두번째 생성자가 호출되는 결과를 얻을 수 있다.
public class Test {
public static void main(String[] args) {
Newworld world = new Newworld();
Newworld world2 = new Newworld(220708);
}
}
class Newworld {
public Newworld() {
System.out.println("Newworld의 기본 생성자 호출!");
};
public Newworld(int x) {
this();
System.out.println("Newworld의 두 번째 생성자 호출!");
}
}
//Output
Newworld의 기본 생성자 호출!
Newworld의 기본 생성자 호출!
Newworld의 두 번째 생성자 호출!
그리고 이와 다른 this 키워드라는 기법도 있는데 이 키워드는 인스턴스 변수와 매개변수의 이름이 같으면 구분하기
어려워지는 문제를 해결하기 위해 주로 사용되는 방식이다.
더 자세히 설명하자면 모든 메서드들은 자신이 포함되어 있는 클래스의 객체를 가리키고 있는 this라는 참조 변수를 가지고 있고
컴파일러가 자동으로 추가해줘서 생략하는 경우가 많기도 하다.
즉 this는 인스턴스 자신을 의미하고 참조 변수를 통해서 우리가 인스턴스의 멤버에 접근 가능한 것처럼 this를 통해서도
인스턴스 자신의 변수에 접근이 가능하다는 것을 의미하고 있다.
이름이 서로 다르다면 문제가 없겠지만 웬만하면 자바에서는 메서드의 지역변수들이 필드명과 동일하기 때문에 이 형식을 사용해야 한다.
내부 클래스
내부 클래스는 클래스 내부에 선언을 한 클래스를 의미한다 이점으로는 코드의 복잡성을 줄여주고
외부 클래스와의 접근성을 간결하게 해 주는 등의 간편함을 주며 OOP에서 중요하게 다루는 핵심 원칙 중
하나인 캡슐화를 준수하는데 도움을 주기도 한다.
네 가지의 내부 클래스 방식이 있는데
종류 | 선언 위치 | 사용 가능한 변수 |
인스턴스 내부 클래스 | 외부 클래스의 멤버변수 선언위치에 선언(멤버 내부 클래스) | 외부 인스턴스 변수, 외부 전역 변수 |
정적 내부 클래스 | 외부 클래스의 멤버변수 선언위치에 선언(멤버 내부 클래스) | 외부 전역 변수 |
지역 내부 클래스 | 외부 클래스의 메서드나 초기화블럭 안에 선언 | 외부 인스턴스 변수, 외부 전역 변수 |
익명 내부 클래스 | 클래스의 선언과 객체의 생성을 동시에 하는 일회용 익명 클래스 | 외부 인스턴스 변수, 외부 전역 변수 |
여기서 보편적으로는 인스턴스 내부/정적 내부/지역 내부 크게 3가지로 구분이 가능하다
내부클래스 자체는 외부 클래스의 내부에 선언된다는 것 외에는 큰 차이점이 없다고 봐도 무방하다.
멤버 내부 클래스
앞에서 학습했던 변수와 비슷하게 인스턴스/정적 내부 클래스들을 묶어서 멤버 내부 클래스라고 통칭하고 있다.
자바 사용에 있어서 내부 클래스에 대한 사용은 핵심적인 문법의 요소 특징이라 할 수 있다.
class Worldclass { //외부 클래스
private int world = 1; //외부 클래스 인스턴스 변수
private static int nWorld = 2; // 외부 클래스 정적 변수
private InWorld inworld; // 내부 클래스 자료형 변수 선언
public Worldclass() {
inWorld = new InWorld(); //외부 클래스 생성자
}
class InWorld { //인스턴스 내부 클래스
int inWorld = 10; //내부 클래스의 인스턴스 변수
void Test() {
System.out.println("Worldclass world = " + world + "(외부 클래스의 인스턴스 변수)");
System.out.println("Worldclass nWorld = " + nWorld + "(외부 클래스의 정적 변수)");
}
}
public void testClass() {
inWorld.Test();
}
}
public class Main {
public static void main(String[] args) {
Worldclass worlds = new Worldclass();
System.out.println("외부 클래스 사용하여 내부 클래스 기능 호출");
worlds.testClass(); // 내부 클래스 기능 호출
}
}
// 출력값
외부 클래스 사용하여 내부 클래스 기능 호출
Worldclass world = 1(외부 클래스의 인스턴스 변수)
Worldclass nWorld = 2(외부 클래스의 정적 변수)
위의 예제를 통해서 대략적인 흐름을 우리는 예상하고 파악이 가능하다.
중요한 점은 인스턴스 내부 클래스가 외부 클래스에 위치하고 있지만 static으로 정의된 변수들을
문제없이 불러와서 사용할 수 있다는 점에 있다. 하지만 유의할 점은 내부 클래스는 외부 클래스를 생성하고 난 후에 사용되야 한다
정적 내부 클래스
내부클래스는 기본적으로 외부 클래스에게 의존하고 있다는 점을 알게 된다 하지만 외부 클래스에 의존되지 않은 사용을 한다면
우리는 정적 내부 클래스를 통하여 사용이 가능하다. 인스턴스 내부 클래스와는 다른 점은 static 키워드의 사용 유무이다.
class World { //외부 클래스
private int worldNum = 3; //내부 클래스의 인스턴스 변수
private static int worldWnum = 4;
void getPrint() {
System.out.println("인스턴스 메서드");
}
static void getPrintStatic() {
System.out.println("스태틱 메서드");
}
static class WorldInClass { // 정적 내부 클래스
void test() {
System.out.println("World worldNum = " +worldWnum + "(외부 클래스의 정적 변수)");
getPrintStatic();
// worldNum 과 getPrint() 는 정적 멤버가 아니라 사용 불가.
}
}
}
public class Main {
public static void main(String[] args) {
World.WorldInClass a = new World.WorldInClass(); //정적 이너 클래스의 객체 생성
a.test();
}
}
//출력값
World worldNum = 4(외부 클래스의 정적 변수)
스태틱 메서드
지역 내부 클래스
멤버가 아닌 메서드 내부에서 정의하여 사용하는 클래스로 메서드 내부에서 객체를 바로 생성하여 사용한다.
class World { //외부 클래스
int num = 5;
void test() {
int num2 = 6;
class LocalInClass { //지역 내부 클래스
void getPrint() {
System.out.println(num);
System.out.println(num2);
}
}
LocalInClass localInClass = new LocalInClass();
localInClass.getPrint();
}
}
public class Main {
public static void main(String[] args) {
World world = new World();
world.test();
}
}
//출력값
5
6
지역 내부 클래스를 메서드 내부에서 선언시키고 생성 후 메인에서 호출하여 외부 클래스의 변수를 출력하고 있다.