본문 바로가기

Backend/Java

[JAVA] 클래스(Class) 생성과 사용

 

 

 


 

클래스 (Class) 생성과 사용 알아보기

국비지원수업 12일차

 


 

 

 

1. 클래스(class)란?


클래스 (Class)

프로그램으로 처리하고자 하는 대상의 요소값들을 자료화하여 하나의 자료형으로 정의한 사용자 정의 자료형

- 하나 이상의 서로 다른 자료형의 자료를 하나로 저장할 수 있는 복합 자료형

- 클래스는 ' 정의 영역 ' -> 실제 사용은 ' 객체 ' 라는 이름으로 사용됨

- 그동안 클래스-객체인지 모르고 사용해오던 클래스 : Scanner

 

 


1-1. 클래스 구성

- 필드 (Field)

- 생성자 (Constructor)

- 메소드 (Method)

class First{
    // 필드(Field)
    int n1; // 멤버 변수
    int n2;

    // 생성자(Constructor)
    First(){

    }

    // 메소드(Method)
    public void method1() {

    }
}

 


1-2. 클래스 선언

main 외부에 class(클래스이름){ } 선언

class Student{
	int number;
	String name;
	int kor, eng, mat, tot;
	double avg;
}

 

중괄호 안에 필요한 변수들을 생성하고 사용 (필드)

* 생성자와 메소드는 추후 포스트로 다룸

 


1-3. 클래스 사용

class First{
    int n1;
    int n2;

    public void method1() { }
}
public static void main(String[] args) {

    First f1 = new First();
    First f2 = new First();

    f1.n1 = 100;
    f1.n2 = 200;
    f2.n1 = 300;
    f2.n2 = 400;

    System.out.println(f1.n1);
    System.out.println(f1.n2);
    System.out.println(f2.n1);
    System.out.println(f2.n2);

    f1.method1();
    f1.method2();
}

 

(class 이름) (변수명) = new (class 이름)();

배열과 마찬가지로 new 명령을 이용하여 First 클래스 객체 생성

1. Heap 영역에 n1, n2를 위한 공간(클래스가 정의된 크기만큼)을 확보 후

2. 그 주소를 f1 변수에 저장하여 사용

 

(변수명).(클래스변수)

' . ' 을 이용하여 클래스 내 변수, 생성자, 메소드 참조 가능

 

 

배열과 차이점

int[] a1 = new int[10]; // 정수 10개의 저장공간을 생성 후 주소를 a1에 저장
First f1 = new First(); // n1과 n2를 위한 저장공간 생성 후 주소를 f1에 저장

 

- 저장되는 실제 공간의 모양이 다름

배열 : 여러 공간 (int[10] -> 10개의 공간)

클래스 : 선언한 여러 자료형의 크기만큼 저장(하나의 자료)

 

 

 

2. 멤버 메소드


멤버 메소드

- 클래스에 소속되어 클래스 객체에 종속된 메소드

- 클래스 내부에 정의되는 메소드

- 멤버 메소드는 특별한 경우를 제외하곤 static을 사용하지 않음

 

class Animal{
	String name;
	int age;
	String address;
	
	public void inputInfo() {
		Scanner sc = new Scanner(System.in);
		
		System.out.print("이름을 입력하세요 : ");
		name = sc.nextLine();
		System.out.print("나이를 입력하세요 : ");
		age = Integer.parseInt(sc.nextLine());
		System.out.print("주소를 입력하세요 : ");
		address = sc.nextLine();
	}
	
	public void prnInfo() {
		System.out.println("이름 : " + name);
		System.out.println("나이 : " + age);
		System.out.println("주소 : " + address);
	}
}

 

- Animal 클래스 내부에 값을 입력 받을 메소드 inputInfo() 와 입력받은 값을 출력할 prnInfo() 메소드 작성

 

public static void main(String[] args) {
    Animal a1 = new Animal();
    Animal a2 = new Animal();

    // 멤버메소드를 이용해서 멤버변수에 값을 저장
    a1.inputInfo();
    a2.inputInfo();

    // 멤버메소드를 이용해서 멤버변수값을 출력하는 경우
    a1.prnInfo();
    a2.prnInfo();
}

 

- Animal 클래스 객체를 main 에서 생성한 뒤, (변수명).(메소드명) 을 통해 메소드 호출

 

전달인수와 매개변수가 없어도 a1, a2 같은 메소드를 호출한 주체(호출객체)는 현재 멤버 메소드에 전달되어져 있음

( == 누가 inputInfo 또는 prnInfo를 호출했는지 알고 있는 상태 )

따라서 멤버변수 앞에 객체의 이름을 쓰지 않아도 해당 객체의 멤버변수에 접근이 가능

 

 

 


2-1. 전달인수가 존재하는 멤버 메소드

public void inputInfoWithParam(String n, int a, String ad) {
    name = n;
    age = a;
    address = ad;
}

Animal 클래스

// 전달인수와 멤버메소드를 이용해서 값을 저장
Animal a3 = new Animal();
a3.inputInfoWithParam("냥냥이", 7, "철수네집");

Scanner sc = new Scanner(System.in);

Animal a4 = new Animal();
System.out.print("이름을 입력하세요 : ");
String name = sc.nextLine();
System.out.print("나이를 입력하세요 : ");
int age = Integer.parseInt(sc.nextLine());
System.out.print("주소를 입력하세요 : ");
String address = sc.nextLine();
a4.inputInfoWithParam(name, age, address);

main 클래스

 

 

기존에 main 클래스 안에 적용했던 메소드와 사용 방법이 유사함

1. 클래스 내의 ' 메소드(괄호) ' 안에 전달 받을 매개변수를 작성하고

2. 호출 시 ' 객체.메소드(괄호) ' 안에 전달인수를 작성해 넣음

 

 


2-2. 값을 저장하고 출력하는 get, set 메소드

클래스 내의 메소드에 직접 접근이 불가능한 경우 (밑에서 설명할 4. 접근 지정자 에 의함)

// 멤버 변수인 name을 가져옴
public String getName() {
    return name;
}

public int getAge() {
    return age; 
}

public String getAddress() {
    return address;
}

public void setName(String n) {
    name = n;
}

public void setAge(int a) {
    age = a;
}

public void setAddress(String ad) {
    address = ad;
}

public void prnInfo() {
    System.out.println("이름 : " + name);
    System.out.println("나이 : " + age);
    System.out.println("주소 : " + address);
}

Amimal 클래스

Animal a3 = new Animal();
a3.inputInfoWithParam("냥냥이", 7, "철수네집");

// 멤버메소드의 리턴값을 이용해서 멤버변수 값을 출력
name = a3.getName();
age = a3.getAge();
address = a3.getAddress();

System.out.println("이름 : " + name);
System.out.println("나이 : " + age);
System.out.println("주소 : " + address);

a3.prnInfo();

main 클래스

 

반드시 get, set 메소드는 모두 public 으로 설정해야 함

외부 클래스인 main에서도 해당 메소드를 참조할 수 있어야 하기 때문인데..

자세한 이유는 바로 밑 접근 지정자에서 설명.

 

 

직접 타이핑하지 않고 이클립스 창에서

우클릭 -> Source -> Generate Getters and Setters -> Select All -> Generate 로 손쉽게 메소드를 만들 수 있음

 

 

 

 

 

3. 접근 지정자 (private, public, protected, default)


접근 지정자

- 자료의 은닉, 캡슐화의 목적

- 모든 클래스의 멤버들은 현실에 존재하는 대상이 정보로 지정되며, 이들은 직접 접근이 허용되는 것과 허용되면 안 되는 것으로 구분함

-> 이러한 정보들을 접근 지정자로 제어함

 

private

- 자신이 속한 클래스 내부에서만 사용 가능한 멤버를 정의하는 키워드

 

public

- 접근에 제한이 없는 멤버를 정의하는 키워드

 

protected

- child class에서만 자유롭게 접근 가능 (상속 단원에서 자세히 학습)

 

default

- 접근 지정자를 지정하지 않으면 자동으로 갖게 되는 접근 지정자

- 같은 패키지에서는 public처럼 자유롭게 접근 가능하지만 외부 패키지에서는 private나 마찬가지로 접근을 제한함

 

class GetSetTest{
    private int intVar;
    private double doubleVar;
    private String stringVar;
    private boolean booleanVar;

    public void setIntVar(int n) {
        intVar = n;
    }
    
    public int getIntVar() {
        return intVar;
    }
}

GetSetTest 클래스

GetSetTest a = new GetSetTest();

// private로 인한 접근 제한 -> public 으로 된 멤버 메소드 이용
a.setIntVar(100);
System.out.println(a.getIntVar());

main 클래스

 

GetSetTest 클래스에서 변수를 private로 선언했기에 main에서 직접적으로 접근할 수 없다.

따라서 get, set 메소드를 통해 변수를 선언한 클래스 내에서 접근해야 한다.

 

 


3-1. 활용 예시 1

class Account{ // 은행 계좌 클래스

    private double balance; // 계좌 잔액을 저장하는 변수
    
    // 초기 계좌값
    void initBalance(int i) {
        balance = i; // 용량이 작은 int형 자료는 용량이 큰 double형 변수에 별도의 조치없이 저장 가능
    }
	
    // 잔액 보기
    void display() {
        System.out.printf("현재 잔액은 %.1f 입니다\n", balance);
    }
    
    // 입금
    public void deposit(int i) {
        balance += i;
        System.out.printf("%d원 입금완료\n", i);
    }
	
    // 출금
    public void withraw(int i) {
        balance -= i;
        System.out.printf("%d원 출금완료\n", i);
    }
}

Account 계좌 클래스

 

1. 무분별한 접근을 막기 위해 계좌 잔액을 저장하는 멤버변수 balance에 private 지정

2. 멤버 메소드의 접근 지정자 public은 특별한 경우를 제외하곤 생략이 가능

이때, 생략해도 public으로 유지되는 것이 아닌 default로 지정되며, 같은 패키지내에서 public 사용 가능

3. double과 비교했을 때 용량이 작은 int형 자료는 용량이 큰 double형 변수에 별도의 조치없이 저장 가능하다

 

 

Account a = new Account();

a.initBalance(10000);
a.display();
a.deposit(20000); // 입금
a.display();
a.withraw(5000); // 출금
a.display();

main 클래스

 

 


3-2. 활용 예시 2

사용자로부터 메뉴얼을 입력 받아 입금, 출금, 잔액 조회, 시스템 종료를 관리하는 코드

class AccountWithPermission{
    Scanner sc = new Scanner(System.in); // 클래스내에서 자유롭게 쓸 수 있도록 선언

    private double balance;

    public void initBalance(int money) {
        balance = money;
    }

    // 입금
    public void deposit() {
        System.out.print("입금할 금액을 입력하세요 : ");
        int money = Integer.parseInt(sc.nextLine());

        if(money < 0) {
            System.out.println("입금액 오류. 관리자에게 문의하세요");
            // void의 return은 되돌림 값 없이 메소드를 종료하라는 뜻
            return; // 값이 없는 리턴으로 deposit 메소드 종료
        }

        balance += money;
        System.out.println(money + "원 입금 완료");
        display(); // 멤버 메소드 간의 자유로운 호출이 가능
    }

    // 출금
    public void withdraw() {
        System.out.print("출금할 금액을 입력하세요 : ");
        int money = Integer.parseInt(sc.nextLine());

        System.out.print("비밀번호를 입력하세요 : ");
        String pass = sc.nextLine();
        if(!pass.equals("1234")) {
            System.out.println("비밀번호가 맞지 않습니다.");
            return;
        }

        if(money > balance) {
            System.out.println("잔액이 부족합니다.");
            return;
        }

        balance -= money;
        System.out.println(money + "원 출금 완료");
        display();
    }

    // 잔액 조회
    public void display() {
        System.out.printf("현재 잔액은 %.2f원 입니다.\n", balance);
    }
}

AccountWithPermission 클래스

AccountWithPermission a = new AccountWithPermission();
a.initBalance(50000); // 잔액 초기화

Scanner sc = new Scanner(System.in);
int selectMenu;
while(true) {
    System.out.print("메뉴 선택 : 1.입금 2.출금 3.잔액확인 4.종료 -> ");
    selectMenu = Integer.parseInt(sc.nextLine());

    if(selectMenu == 4) break;

    switch(selectMenu) {
        case 1:
            a.deposit(); break;
        case 2:
            a.withdraw(); break;
        case 3:
            a.display(); break;
    }
}

System.out.println("프로그램이 종료됩니다");

main 클래스

 

 

 

4. 사용자 지정 클래스의 객체 복사와 참조 변수


class Tiger{
    String name;
    int age;
    String phone;

    public void input(String name, int age, String phone) {
        this.name = name;
        this.age = age;
        this.phone = phone;
    }

    public void output() {
        System.out.println("저의 이름은 " + name + ", 나이는 " + age + "살입니다");
    }
}

Tiger 클래스

Tiger t1 = new Tiger();
Tiger t2 = new Tiger();
t1.input("야옹이", 10, "010-2222-1111");
t2.input("어흥이", 11, "010-3333-2222");
t1.output();
t2.output();

main 클래스

 

 

출력이 위와 같이 이루어지는 객체 t1, t2가 있다고 가정하자.

만약 t2 객체를 t3에 복사하고 싶다면 어떻게 해야할까?

 

막연하게 생각해서.. 새로운 Tiger 객체 t3를 선언하고 t1 혹은 t2를 t3에 대입하면?

 

// t2 객체를 t3에 복사하고 싶을 때
Tiger t3;

// 실제 new Tiger로 만들어진 레퍼런스의 주소 2개(t1, t2) 
t3 = t2; // t3는 t2의 참조값 복사 (t2의 주소를 공유할 뿐)
t2.output();
t3.output();

 

t3에 t2를 대입해서 결과를 보면 둘은 동일하게 뜬다.

이는 값이 복사되었다고 착각할 수 있지만, 사실 대입 연산자(=)를 통해 t2의 주소값을 t3에도 공유한 것 뿐이다

따라서 t2 혹은 t3 객체의 요소 하나를 바꾸게 되면...

 

t3.input("어흥이", 5, "010-8888-9999");
t2.output();
t3.output();

 

위와 같이 두 값 모두 동일하게 변해버린다.

 

그렇다면 '값'을 복사하기 위해서는 어떻게 해야할까?

 


4-1. 전달인수가 클래스 객체 

첫번째는 값의 복사가 이뤄지는 메소드를 따로 만드는 방법이다.

 

// 전달인수 : t2, 매개변수 t
public void copy(Tiger t) {
    // t4.name에 t2.name 값 저장
    this.name = t.name;
    this.age = t.age;
    this.phone = t.phone;
}

Tiger 클래스

Tiger t2 = new Tiger();
t2.input("어흥이", 11, "010-3333-2222");

Tiger t4 = new Tiger();
t4.copy(t2); // 전달인수가 Tiger
t2.input("야옹이", 10, "010-2222-1111");
t2.output();
t4.output();

main 클래스

 

1. 새로운 객체 t4를 생성하고

2. 복사할 t2를 Tiger 클래스에 새로 작성한 copy 로 전달한다.

3. 이후 copy에서 t2의 name, age, phone 값을 this.(멤버변수) 를 통해 복사한다.

* this 키워드는 추후 작성할 포스트인 클래스의 생성자에서 자세히 다룬다. 대강 매개변수와 멤버변수 이름이 동일한 경우를 대비해 구분짓기 위함이라고 보면 된다.

4. 복사가 되었는지 확인하기 위해 t2의 값을 바꾼다.

 

 

 

 


4-2. 리턴값이 클래스 객체

두번째 방법은 값을 복사하는 메소드이나 리턴값이 Tiger인 경우이다.

 

public Tiger copy2() {
    Tiger temp = new Tiger();
    temp.name = this.name;
    temp.age = this.age;
    temp.phone = this.phone;

    return temp; // 카피 후 카피한 Tiger 객체(temp)의 주소 리턴
}

Tiger 클래스

Tiger t2 = new Tiger();
t2.input("어흥이", 11, "010-3333-2222");

Tiger t5 = t2.copy2(); // 리턴값이 Tiger
t2.input("냥냥이", 1, "010-777-9999");
t2.output();
t5.output();

main 클래스

 

앞서 전달인수로 Tiger 객체를 보내고, 해당 객체를 전달받아 Tiger 클래스의 멤버 메소드에서 값을 직접 복사해 대입하는 방식으로 만들었다면 이번 방법은...

1. 전달인수 없이 Tiger 멤버 메소드 내부에서 임의의 Tiger 객체 temp를 만들고

2. temp에 name, age, phone값을 복사해

3. temp를 반환한다.

 

 

 

 

new 명령에 의해 새로운 주소값을 할당 받은 객체들.

이러한 객체들을 전달인수로, 리턴값으로 받는 등의 주소를 전달하는 과정을 call by reference라 하며,

배열의 주소를 전달한 것과 같은 맥락이다.

// call by reference로 배열의 주소를 전달한 것과 같음
int[] a = {1, 2, 3, 4, 5};
int[] b = a;
b[0] = 100;
System.out.println(a[0] + " " + b[0]);

 

 

 

 

'Backend > Java' 카테고리의 다른 글

[JAVA] static과 인스턴스(instance)  (0) 2024.10.14
[JAVA] 클래스 생성자(Constructor)  (0) 2024.10.14
[JAVA] 메소드 (Method)  (0) 2024.09.27
[JAVA] 배열 (Array), 2차원 배열  (0) 2024.09.27
[JAVA] 연산자 (Operator)  (0) 2024.09.24