[JAVA] 클래스

JUJU·2024년 1월 15일
0

✏️ 클래스란

클래스(class)는 객체 지향 프로그래밍(OOP)에서 특정 객체를 생성하기 위해 변수와 메소드를 정의하는 일종의 틀(template)이다.

  • 클래스에는 객체를 생성하기 위한 필드와 메소드가 정의되어 있다.
  • 클래스로 만들어진 객체를 인스턴스라고 한다.
  • ⚠️ public 클래스 이름과 소스파일 이름은 같아야 한다. (하나의 파일에 여러 클래스를 선언 가능하지만, pulic 클래스는 단 하나만 존재한다)

클래스의 용도

용도목적개수
라이브러리 클래스다른 클래스에서 이용하기 위함n-1개
실행 클래스main 메소드 제공1개


✏️ 클래스 변수

앞선 포스트에서 설명했던 참조 변수의 종류 중, 클래스 변수라는 것이 있다.

// 클래스의 첫 글자는 대문자로
MyClass mine = new MyClass();

mine은 클래스 변수로, new 연산자로 생성된 객체의 주소(실제 주소는 아님)를 갖고있다.
mine은 new 연산자로 생성된 객체(인스턴스)를 참조한다.
인스턴스는 힙 영역에 생성된다.


✏️ 클래스의 구성 멤버

1. 필드

: 객체의 데이터를 저장하는 역할을 함

2. 생성자

: 객체 생성 시 초기화하는 역할을 함

3. 메소드

: 객체의 동작에 해당하는 실행 블록 (객체 간 데이터 전달 수단)



✏️ 필드

필드는 객체의 데이터를 저장하는 클래스 멤버다.

1. 선언

  • 필드 선언은 클래스 중괄호 블록 어디서든 존재할 수 있다.
// 선언 방법: 
// 타입 필드 [= 초기값]

public class MyClass{
  int field1;
  double field2 = 4.5;
  String field3 = "hi";
}
  • 초기값이 지정되지 않은 필드는 객체 생성 시 자동 초기화 된다.
  • 타입초기값
    byte0
    char\u0000 (공백)
    short0
    int0
    long0L
    float0.0F
    double0.0
    booleanfalse
    참조형null

2. 사용

  • 필드를 사용하기 위해서는 인스턴스를 생성해야 한다. (예외: static)
  • 필드에 접근하기 위해서 도트(.) 연산자를 사용한다.
public class UseMyClass{
  public static void main(String[] args){
  	MyClass mine = new MyClass();
    
    mine.field1 = 10; // 도트 연산자를 사용하여 객체에 접근
  }
}


✏️ 생성자

생성자는 객체의 초기화를 담당하는 클래스 멤버이다.

  • 모든 클래스에는 생성자가 반드시 존재한다.
  • 생성자는 하나 이상 가질 수 있다.

1. 기본 생성자

  • 클래스 내부에 생성자 선언을 생략한 경우, 컴파일러가 기본 생성자를 자동으로 추가한다.
  • 클래스가 public class로 선언되면 생성자에도 public이 붙고, public class가 아니면 생성자에도 public이 붙지 않음
// 소스 파일
public class MyClass{
	
}
// 바이트 코드 파일
public class MyClass{
	MyClass(){ } // 자동 생성됨
}
  • 클래스 내부에 생성자가 하나라도 존재하면, 컴파일러는 기본 생성자를 추가하지 않음

2. 선언

  • 생성자는 리턴 타입이 없고, 클래스 이름과 동일하다.
// 생성자 선언: 
// 클래스(매개변수, ...) { }
public class MyClass{
	int field1;
	double field2 = 4.5;
	String field3 = "hi";

	MyClass(int field1, double field2){
    	// 필드와 매개변수 이름이 동일한 경우, 매개변수의 우선순위가 높다.
        // 따라서, this를 사용한다.
        // this는 객체 자기 자신의 참조이다.
    	this.field1 = field1;
        this.field2 = field2;
    } 
}
  • 위의 예시로 보면, 생성자가 매개변수를 필요로하므로 기본 생성자를 사용할 수 없다.

3. 생성자 오버로딩

여러개의 생성자를 구현한 경우 다양한 매개변수로 객체를 생성할 수 있는데, 이를 생성자 오버로딩이라 한다.

  • 매개변수의 타입, 개수, 선언된 순서 중 하나만 달라도 다른 생성자로 간주한다.
  • 단, 매개변수의 이름만 다른 것은 오버로딩이 아니다.

public class MyClass{
	MyClass(int field1, double field2){ }
    MyClass(double field2, int field1){ }
    MyClass(String field3){ }
    // My Class(int field2, double field1)은 오버로딩이 아님
}

4. this()

  • this()는 생성자에서 다른 생성자를 호출할 때 사용한다.
  • this()는 반드시 생성자의 첫 줄에서만 사용한다.
  • this()를 사용하면, 생성자 코드의 중복을 줄일 수 있다.
public class MyClass{
	MyClass(){ }
    
	MyClass(String model){ 
    	this(model,"은색",100);
    }
    
    MyClass(String model, String color){ 
    	this(model, color, 100);
    }
    
    MyClass(String model, String color, int speed){ 
    	this.model = model;
        this.color = color;
        this.speed = speed;
    }
}


✏️ 메소드

메소드는 객체의 기능을 담당하는 클래스 멤버이다.

1. 선언

  • 메소드의 선언은 선언부(리턴 타입, 메소드 이름, 매개변수 선언)와 실행 블록으로 이루어진다.
  • 리턴하는 값이 없으면 void로 선언한다. 리턴하는 값이 있으면 반드시 return문을 포함해야 한다.

2. 매개변수

  • 매개변수는 없을 수 있다.
  • 매개변수의 개수를 모르는 경우는 배열 또는 ... 을 사용한다.
public class MyClass{
	// 방법1: 배열 사용
	int sum1(int[] values){
    	int sum = 0;
        
		for(int i=0;i<values.length;i++) {
			sum += values[i];
		}
		return sum;
    }
    
    // 방법2: ... 사용
 	int sum2(int ... values){
    	int sum = 0;
        
		for(int i=0;i<values.length;i++) {
			sum += values[i];
		}
		return sum;
    }
}
public class UseMyClass{
  public static void main(String[] args){
  	MyClass mine = new MyClass();
    
    int result1 = mine.sum1(new int[1,2,3,4,5]); // 인수는 반드시 배열로
    int result2 = mine.sum2(1,2,3,4,5) // ... 사용시 배열은 자동 생성됨
  }
}

3. 호출

  • 클래스 내부에서 호출: 메소드(매개변수);
  • 클래스 외부에서 호출: 인스턴스.메소드(매개변수);

4. 메소드 오버로딩

클래스 내에 같은 이름의 메소드를 여러개 선언하는 것을 메소드 오버로딩이라 한다.

  • 매개변수의 타입, 개수, 순서 중 하나가 달라야 한다.
  • 단, 매개변수의 이름만 다른 것은 오버로딩이 아니다.


✏️ 인스턴스 멤버와 정적 멤버

인스턴스 멤버: 객체마다 가지고 있는 멤버
정적 멤버: 모든 객체가 공유하는 멤버

1. 인스턴스 멤버

  • 인스턴스 멤버는 인스턴스를 생성한 후 사용할 수 있는 필드와 메소드다.
public class MyClass{
	int field1;

	int getField(){
    	return field1;
    }
}

위의 코드에서, 인스턴스 필드 field1은 객체마다 존재하고, 인스턴스 메소드는 메소드 영역에 저장되고 공유된다.

❓인스턴스 메소드는 왜 메소드 영역에 저장되고 공유되지??

메소드는 '코드 블록'이므로, 객체마다 똑같은 코드 블록을 갖고 있는 것은 비효율 적이다.
인스턴스 메소드라 부르는 이유는, 메소드 안에 인스턴스 필드가 포함될 수 있기 때문이다.

2. 정적 멤버

  • 정적 멤버는 클래스에 고정된 멤버로서 인스턴스 생성 없이 사용할 수 있는 필드와 메소드이다.
  • 정적 필드와 정적 메소드를 선언하려면, static 키워드를 붙여야 한다.
  • 정적 필드는 메소드 영역에 저장되며, 클래스의 인스턴스들이 하나의 정적 필드를 공유한다.
  • 정적 메소드는 인스턴스 필드, 인스턴스 메소드, this 키워드 사용이 불가능하다
public class MyClass{
	int field1; // 인스턴스 필드
	static double field2 = 1.1; // 정적 필드
    
	int getField(){
    	return field1; // 인스턴스 필드를 사용하므로, 인스턴스 메소드로 선언
    }
    
    static int plus(int x){
    	return x + 1; // 정적 메소드
    }
}

정적 메소드는 클래스이름.메소드()로 사용한다.

public class UseMyClass{
  public static void main(String[] args){
	int x = 1;
    int result = MyClass.plus(x); // 인스턴스 생성 없이 바로 사용 가능
  }
}


✏️ 싱글톤

싱글톤이란, 전체 프로그램에서 단 하나만 생성하도록 보장된 객체이다.

싱글톤을 만드려면, 다음과 같은 과정을 거쳐야 한다.

1. 클래스 외부에서 생성자를 호출할 수 없게 막아야한다.

--> 생성자를 private로 제한한다.

2. 클래스 내부에서 자신의 인스턴스를 정적 필드로 생성한다.
왜 정적 필드여야 하는가?

--> 인스턴스 필드인 경우, 반드시 해당 객체의 인스턴스를 생성해야만 사용 가능하기 때문이다. 클래스 외부에서는 생성자를 호출할 수 없으므로, 인스턴스 생성도 불가능하다.

3. 정적 메소드인 getInsgance()를 선언한다.

--> 해당 객체를 얻기위한 유일한 수단이다.

public class Singleton{
	private static Singleton singleton = new Singleton();
    
    private Singleton(){ }
    
    static Singleton getInstance(){
    	return singleton;
    }
}


✏️ final 필드와 상수

final 필드는, 초기값이 저장되면 변경 불가능한 필드이다.
final 필드의 초기값을 설정하는 방법은 2가지 밖에 없다.
1. 선언과 동시에 설정
2. 생성자로 설정

❓그럼 상수랑 뭐가 다르지??

다음의 예시를 한번 보자

public class MyClass{
	final String nation = "Korea";
    final String name;
    
    MyClass(String name){
    	this.name = name;
    }
}
public static void main(String[] args){
	MyClass mine1 = new MyClass("ju");
	MyClass mine2 = new MyClass("kim");
}

상수는 "불변"의 값이다.
하지만, final 필드는 각 인스턴스마다 값이 다를 수 있다.

상수를 만들기 위해서는 static final 필드를 사용해야 한다.
static final 필드는 객체마다 존재하지 않고 클래스에만 존재하며, 초기값 지정 시 변경 불가능하다.

static final double PI = 3.141592;

✏️ 패키지

패키지는 클래스 전용 파일 시스템이라 생각하면 된다.
클래스 이름이 동일하더라도, 패키지가 다르면 다른 클래스로 인식한다.

패키지 선언

  • 클래스를 작성할 때, 해당 클래스가 어떤 패키지에 속하는지 선언해줘야 한다.
package 상위패키지.하위패키지;
public class MyClass{}
  • 사용하고자 하는 클래스가 다른 패키지에 소속되어 있는 경우, import문을 사용하여 컴파일러에게 해당 패키지를 사용할 것임을 알려야 한다.
import 상위패키지.하위패키지.클래스이름; // 상위패키지를 import 한다고 하위패키지까지 import 되는건 아님
import 상위패키지.하위패키지.*; // 패키지에 있는 전체 클래스 사용 가능
public class MyClass{}


✏️ 접근 제한자

접근 제한자는 클래스 및 인터페이스 그리고 이들이 갖고 있는 멤버들에게 접근하는 것을 제한한다.

1. public
외부 클래스가 자유롭게 접근 가능하다

2. protected
같은 패키지 또는 자식 클래스에서만 접근 가능하다

3. private
외부에서 사용 불가능하다

default
접근 제한자가 적용되지 않는 경우, 같은 패키지에서만 접근 가능하다

요소사용가능한 접근 제한자
classpublic, default
생성자public, protected, default, private
필드public, protected, default, private
메소드public, protected, default, private
  • 일반적으로 객체 지향 프로그래밍에서는 객체의 필드를 외부에서 접근하는 것을 막는다.
    필드는 private, 메소드는 public 접근 제한자를 적용하여 메소드를 통해 필드에 접근하도록 한다.

  • 필드의 값을 리턴하는 메소드를 Getter, 필드를 변경하는 메소드를 Setter라고 한다.

REFERENCE

혼자 공부하는 자바

profile
개발자 지망생

0개의 댓글

관련 채용 정보