05 클래스와 객체 II

목차

    05 클래스와 객체 II

    5.1 객체의 생성과 소멸

    참조 변수와 대입 연산

    • 기초 변수의 값을 다른 변수에 대입하게 되면 변수의 값이 복사된다.
      (오른쪽 변수의 값이 복사되어서 왼쪽 변수로 대입)
    • 참조 변수의 경우
    Television tv1 = new Television();
    Television tv2 = tv1;
    • 언뜻 보기에 서로 다른 객체를 참조하는 것 같지만, 실제로는 동일한 객체를 참조

    객체의 소멸과 가비지 컬렉션

    • 자바에는 객체를 생성하는 연산자는 있지만, 객체를 삭제하는 연산자는 없다.
    • 자바에서 객체들은 new 연산자에 의하여 히프 메모리에서 할당된다.
    • 메모리는 무한 하지 않기 때문에 JVM에서 사용되지 않는 객체들을 삭제하여 메모리를 확보하는 것이 필요하다.
    • 가비지 컬렉션 : 자동 메모리 삭제 시스템

    가비지 컬렉션

    • 가비지 컬레터는 히트 메모리에서 더 이상 필요 없는 객체를 찾아 지우는 작업을 한다.
    • 가비지 컬렉터가 수행되면 가비지 컬렉터를 제외한 나머지 자바 애플리케이션은 모든 동작을 멈춘다.
    • 가바지 컬렉터가 작업을 완료한 이후에 중단한 작업을 다시 시작한다.
    • 이때, 사용자는 잠시 컴퓨터 서비스가 중단되는 것처럼 느낄 수 있음
    • 가비지 컬렉터는 JVM의 중요한 부분이고, JVM 중 가장 대표적인 것은 오라클사의 HotSpot이다.
    • HotSpot은 많은 가비지 컬렉션 옵션을 제공하고, 모든 가비지 컬렉터는 동일한 기본 프로세스를 따른다.
    • 첫 번째 단계에서 참조되지 않은 객체가 식별되고 가비지 수집 준비가 된것으로 표시됨
    • 두 번째 단계에서는 표시된 객체 삭제, 이후 압축할 수 있다.

    가비지 컬렉션 요청

    • 개발자는 System 객체의 gc() 메소드를 호출하여서 가비지 컬렉션을 요청할 수 있다.
    • 하지만, 가비지 컬렉터가 수행되면 모든 다른 애플리케이션이 멈추기 때문에
      가비지 컬렉터의 실행 여부는 전적으로 JVM이 판단한다.
    • 따라서 비결정적이며 런타임에 가비지 수집이 발생할 때를 예측할 방법이 없다.
    • 가비지 컬렉션을 조정하는 가장 좋은 방법은 JVM을 실행할 때 플래그를 설정 하는 것.

    5.2 인수 전달 방법

    메소드를 호출할 때, 인수가 전달되는 방법은 기본적으로 "값에 의한 호출(call-by-value)"이다.
    이때는 인수의 값이 복사되어 매개 변수로 전달된다.
    하지만 전달되는 인수가 객체인 경우에 주의할 점이 있다.

    기초형 값이 전달되는 경우

    • 전달하는 인수가 기초형 변수인 경우에는 호출자가 전달하는 인수의 값이 매개 변수로 복사된다.
    • 인수의 복사본이 만들어지고 매개 변수를 변경하여도 메소드 외부에 있는 인수에 영향을 주지 않는다.

    객체가 전달되는 경우

    • 객체를 메소드로 전달하게 되면 객체의 참조값만 복사되어서 전달되어
      메소드의 매개 변수도 동일한 객체를 참조하게 된다.
    • 매개 변수를 통하여 객체의 내용을 변경하게 되면 인수가 가리키는 객체도 변경된다.

    배열이 전달되는 경우

    • 배열 원소가 메소드로 전달된다면 값이 복사되어 전달
    • 배열 전체가 전달된다면 참조값이 복사되어 전달

    5.3 정적 멤버

    여러 개의 객체가 하나의 변수를 공유해야 되는 경우가 있는데, 이러한 멤버를 정적 멤버(static memer) 또는 클래스 멤버(class member)라고 한다.
    필드를 정의할 때 앞에 static을 붙이면 정적 멤버, 메소드 앞에 static을 붙이면 정적 메소드가 된다.
    이외에도  static 키워드는 자바에서 다양하게 사용된다.

    정적 멤버

    • 객체마다 별도로 소유하는 멤버는 인스턴스 멤버
    • 다른 객체와 공유하는 멤버는 정적 멤버

    인스턴스 멤버 vs 정적 멤버

    • 정적 변수는 클래스당 하나만 생성되는 변수
    • 정적 변수는 객체 없이도 사용이 가능하다.

    정적 변수의 생성 시기

    • 클래스가 자바 가상 기계에 적재되는 순간 생성된다.
    • 정적 변수는 프로그램이 종료되어야 비로소 소멸된다.

    정적 메소드

    • 객체를 생성할 필요 없이 메소드를 사용할 수 있다.

    예제 (정적 메소드 활용하기)

    nk값을 계산하는 power() 메소드와 절대값 메소드를 제공하는 MyMath 클래스 만들기

    package practice;
    
    public class MyMath {
    
    	public static int power(int n, int k) {
    		int result = 1;
    		for (int i = 0; i < k; i++) {
    			result *= n;
    		}
    		return result;
    	}
    
    	public static int abs(int n) {
    		return n < 0 ? -n : n;
    	}
    }
    package practice;
    
    public class MyMathTest {
    
    	public static void main(String[] args) {
    
    		System.out.println("10의 3승은 : " + MyMath.power(10, 3));
    	}
    
    }

    정적 변수의 활용

    • 정적 변수는 상수를 정의하는 용도로 사용된다.
    • 정적 메소드는 정적 멤버만 사용할 수 있다. 인스턴스 멤버 사용 X
    • 정적 메소드에서 정적 메소드를 호출하는 것은 가능하다.
    • 정적 메소드는 this를 사용할 수 없다.

    final 키워드

    • 상수를 정의할 때 static과 final 수식어를 동시에 사용하는 경우가 많다.

    정적 블록

    • 클래스가 메모리에 로드될 때 한번만 실행되는 문장들의 집합.
    • 일반적으로 정적 변수들을 초기화하는 용도로 많이 사용된다.

    LAB

    싱글톤 패턴

    객체 중에는 전체 시스템을 통틀어서 딱 하나만 존재하여야 하는 것들이 있다. 이럴 때 사용하는 것이 싱글톤 패턴.
    즉, 하나의 프로그램 내에서 하나의 인스턴스만을 생성.
    이러한 경우에은 객체를 생성 할 때 new를 사용하지 않고 정적 메소드 getInstance()를 호출
    package practice;
    
    class Single{
    	private static Single instance = new Single();
    	private Single() {}
    	
    	public static Single getInstance() {
    		return instance;
    	}
    }
    
    public class SingleTest {
    
    	public static void main(String[] args) {
    		Single obj1 = Single.getInstance();
    		Single obj2 = Single.getInstance();
    		System.out.println(obj1);
    		System.out.println(obj2);
    	}
    
    }

    practice.Single@4e50df2e

    practice.Single@4e50df2e

    getInstance() 호출이 반복적으로 이뤄져도 처음 생성된 객체의 참조값을 계속하여 반환해준다.
    생성자의 접근 제어가 private으로 되어 있어 외부에서 생성자 호출을 통한 객체 생성도 불가능

    5.4 객체 배열

    • 객체 배열에는 객체에 대한 참조값이 저장되어 있음

    동적 객체 배열

    • 자바 표준 배열은 크기가 결저오디면 변경하기가 힘들어서 동적 배열을 많이 사용함
    • ArrayList 클래스를 이용하고 객체 배열이라고 해서 딱히 다를건 없음
    • 참조변수명.add(new 생성자);


    Summary

    • 객체가 자신을 가리키는 참조값을 잃으면 사용이 불가능해진다. 이런 객체들은 정리 대상으로 표시되며 나중에 가용 메모리가 부족해지면 가비지 컬렉터에 의하여 삭제된다.
    • 메소드로 기초형의 값을 전달하면 복사되지만, 메소드에 객체를 전달하면 객체의 참조값이 전달된다. 메소드 안에서는 객체 참조값을 이용하여 객체 원본을 변경할 수 있다.
    • 인스턴스 멤버란 객체마다 하나씩 생성되는 멤버이다.
    • 정적 멤버는 모든 객체를 통틀어서 하나만 생성된다. 객체를 생성하지 않고 클래스 이름만으로 접근할 수 있다.
    • 정적 멤버를 선언하려면 앞에 static을 붙인다.
    • 정적 메소드에서는 정적 멤버에만 접근이 가능하다. 인스턴스 멤버는 사용할 수 없다.
    • 객체 배열은 객체를 저장하는 배열이다. 배열 자체도 객체이므로 먼저 배열 객체를 생성하고 배열 요소에 객체를 생성하여 채워야 한다.
    • 동적 객체 배열을 생성하려면 ArrayList를 사용한다.

    Programming

    ATM 구현 계좌는 Account 클래스 다음과 같은 사용자 인터페이스

    PIN을 입력하시오 : 1234
    
    1. 현금 입금
    2. 현금 인출
    3. 계좌 이체
    4. 종료
    번호를 선택하세요 : 1
    입금액 : 1000000
    현재 잔액은 1000000입니다.
    ------------------------------
    번호를 선택하세요 : 3
    이체액 : 30000
    이체 계좌 번호 : 1010000
    이체되었습니다.
    ------------------------------

     

    Account 클래스

    package basic.practice;
    
    public class Account {
    	private static int accountNumberCount = 101000;
    	private String name;
    	private String pinNumber;
    	private int accountNumber;
    	private int balance;
    	
    	public Account(String name, String pinNumber) {
    		this.name = name;
    		this.pinNumber = pinNumber;
    		this.accountNumber = ++accountNumberCount;
    	}
    	
    	// getter
    	public static int getAccountNumberCount() {
    		return accountNumberCount;
    	}
    
    	public String getPinNumber() {
    		return pinNumber;
    	}
    
    	public int getAccountNumber() {
    		return accountNumber;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public int getBalance() {
    		return balance;
    	}
    	// 0. pinNumber 확인
    	public static Account checkPinNumber(String name, String pinNumber, Account[] account) {
    		for (int i = 0; i < account.length; i++) {
    			if (account[i].getName().equals(name) && account[i].getPinNumber().equals(pinNumber)) {
    				System.out.println("확인되었습니다.");
    				return account[i];
    			}
    		}
    		System.out.println("잘못된 입력입니다.");
    		return null;
    	}
    	
    	// 1. 현금 입금
    	public void deposit(Account account, int money) {
    		balance += money;
    		
    	}
    	// 2. 현금 인출
    	public void withdraw(Account account, int money) {
    		if (balance < money) {
    			System.out.println("잔액이 부족합니다.");
    			return;
    		}
    		balance -= money;
    		
    	}
    	// 3. 계좌 이체
    	public void transfer(Account toAccount, int money) {
    		if (this.balance < money) {
    			System.out.println("잔액이 부족합니다.");
    			return;
    		}
    		this.withdraw(this , money);
    		toAccount.deposit(toAccount, money);
    		System.out.println("이체되었습니다.");
    	}
    	// 계좌 번호로 계좌 찾기
    	public Account findAccount(int accountNumber, Account[] account) {
    		for (int i = 0; i < account.length; i++) {
    			if (account[i] != null && accountNumber == account[i].getAccountNumber()) {
    				return account[i];
    			}
    		}
    		System.out.println("해당하는 계좌가 없습니다.");
    		return null;
    	}
    }

     

    ATM 클래스

    package basic.practice;
    
    import java.util.Scanner;
    
    public class ATM {
    
    	public static void main(String[] args) {
    		Account[] accountList = new Account[100];
    		accountList[0] = new Account("홍길동", "1234");
    		accountList[1] = new Account("이순신", "1111");
    		accountList[2] = new Account("강감찬", "1212");
    		
    		final int DEPOSITE = 1;
    		final int WITHDRAW = 2;
    		final int TRASNFER = 3;
    		final int END = 4;
    		boolean flag = true;
    		
    		Scanner sc = new Scanner(System.in);
    		System.out.println("이름을 입력하세요");
    		String name = sc.nextLine();
    		System.out.println("pin을 입력하세요");
    		String pin = sc.nextLine();
    		Account user = Account.checkPinNumber(name, pin, accountList);
    		if (user != null) {
    			while (flag) {
    				System.out.println("\n1. 현금 입금");
    				System.out.println("2. 현금 출금");
    				System.out.println("3. 계좌 이체");
    				System.out.println("4. 종료");
    				System.out.println("번호를 선택하세요 : ");
    				int choice = sc.nextInt();
    				sc.nextLine();
    				if (choice == DEPOSITE) {
    					System.out.print("입금액 : ");
    					int money = sc.nextInt();
    					sc.nextLine();
    					user.deposit(user, money);
    					System.out.println("현재 잔액은 " + user.getBalance() + "입니다.");
    					System.out.println("------------------------------");
    				} else if (choice == WITHDRAW) {
    					System.out.println("출금액 : ");
    					int money = sc.nextInt();
    					sc.nextLine();
    					user.withdraw(user, money);
    					System.out.println("현재 잔액은 " + user.getBalance() + "입니다.");
    					System.out.println("------------------------------");
    				} else if (choice == TRASNFER) {
    					System.out.println("이체액 : ");
    					int money = sc.nextInt();
    					sc.nextLine();
    					System.out.println("이체 계좌 번호 : ");
    					int accountNumber = sc.nextInt();
    					sc.nextLine();
    					Account toAccunt = user.findAccount(accountNumber, accountList);
    					user.transfer(toAccunt, money);
    				} else if (choice == END) {
    					System.out.println("프로그램을 종료합니다.");
    					flag = false;
    				} else {
    					System.out.println("잘못된 입력입니다.");
    				}
    			}
    			
    			
    			
    		} else {
    			System.out.println("프로그램을 종료합니다.");
    		}
    		sc.close();
    	}
    
    }

     

    결과

    이름을 입력하세요
    홍길동
    pin을 입력하세요
    1234
    확인되었습니다.
    
    1. 현금 입금
    2. 현금 출금
    3. 계좌 이체
    4. 종료
    번호를 선택하세요 : 
    1
    입금액 : 10000
    현재 잔액은 10000입니다.
    ------------------------------
    
    1. 현금 입금
    2. 현금 출금
    3. 계좌 이체
    4. 종료
    번호를 선택하세요 : 
    5000
    잘못된 입력입니다.
    
    1. 현금 입금
    2. 현금 출금
    3. 계좌 이체
    4. 종료
    번호를 선택하세요 : 
    2
    출금액 : 
    5000
    현재 잔액은 5000입니다.
    ------------------------------
    
    1. 현금 입금
    2. 현금 출금
    3. 계좌 이체
    4. 종료
    번호를 선택하세요 : 
    2
    출금액 : 
    1000
    현재 잔액은 4000입니다.
    ------------------------------
    
    1. 현금 입금
    2. 현금 출금
    3. 계좌 이체
    4. 종료
    번호를 선택하세요 : 
    3
    이체액 : 
    1000
    이체 계좌 번호 : 
    101002
    이체되었습니다.
    
    1. 현금 입금
    2. 현금 출금
    3. 계좌 이체
    4. 종료
    번호를 선택하세요 : 
    4
    프로그램을 종료합니다.

    Java 목차로 돌아가기

    'Java > 교재 정리' 카테고리의 다른 글

    07 추상 클래스, 인터페이스, 중첩 클래스  (1) 2024.04.26
    06 상속  (1) 2024.04.23
    04 클래스와 객체 I  (0) 2024.04.15
    03 조건문, 반복문, 배열  (0) 2024.04.14
    02 자바 프로그래밍 기초  (0) 2024.04.12