본문 바로가기

카테고리 없음

[ Java ] static과 final, List타입 및 코딩 연습_학습한 바를 두서없이 기록

static과 final

 

static의 사전적 의미

static: (변화·움직임이 없이) 고정된[고정적인]; 물리(힘이) -- 정지상태의; (수신기의) 잡음; 물리 -- 정전기

정지하고 있는, 변화하지 않는; 정적인, 움직임이 없는 ↔ [반의어]  dynamic --> 동적인

물리 -- 정적인, 정압의(고요할 정, 누를 압(진정하다의 의미도 있음)); 컴퓨터 -- 재생하지 않아도 기억 내용이 유지되는

 

(수신기의) 잡음: unwanted noise caused in a radio or television receiver by electricity by conditions in the atmosphere.

변화 또는 움직임이 거의 없는: showing little or no change, action, or progress

 

자바에서 static의 의미

static 키워드는 주로 클래스 변수나 메서드에 사용되고, static 키워드가 붙은 클래스 변수 및 메서드는 클래스 로드 시에 메모리에 할당되어 프로그램 종료 시까지 유지된다. 클래스의 인스턴스 간에 공유되어야 하는 기능(메서드)을 정의할 때 사용된다.

static 변수는 모든 인스턴스에서 공유된다 --> 하나의 인스턴스에서 static 변수의 값을 변경하면 모든 인스턴스에 영향을 미침

 

자바에서 final의 의미

final 키워드는 변수, 메서드, 클래스에 사용될 수 있고,

변수, 메서드, 클래스가 final로 선언되면 그 대상은 변경이 불가하다.

변수에 사용될 경우 --> 변수에 대입된 값의 변경이 불가

메서드에 사용될 경우 ---> 오버라이딩이 불가 ( 메서드의 재정의가 불가)

클래스에 사용될 경우 ---> 상속이 불가하다.

 

static과 final 키워드를 함께 사용할 경우

static과 final로 선언된 변수는 클래스의 상수로써 모든 인스턴스에서 공유되며(static) 변경할 수 없는 값(final)이 된다 --> 프로그램 전체에서 일관된 값을 유지해야 할 때 static final 키워드를 함께 사용한다 ex. 수학에서의 파이 값, 환경 설정에서의 최대 파일 크기 등

프로그램 전체에서 공유되어야 하며 변경되지 않는 값에 static final 키워드를 사용한다.

 

public class ClubCoordinator {
	// 클래스 로드시 전체 프로그램에서 공유되며(static), 값을 변경할 수 없는(final) MIN_PAGE_SIZE
	public static final int MIN_PAGE_SIZE = 10;
    public static final int MAX_PAGE_SIZE = 20;

}

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

 

// String클래스에 선언된 int타입을 반환하는 length() 메서드는 문자열의 길이를 반환한다.
// Returns the length of string.

// 문자열의 길이가 0이면 true를 반환하는 isEmpty() 메서드
public boolean isEmpty() {
	return value.length == 0;
}

 

문자열의 길이가 0인 경우에 boolean타입의 값을 반환하는 isEmpty() 메서드, 문자열의 길이가 0인 경우 ---> 빈문자열("")

문자열이 비어 있거나(빈문자열 ---> "") 빈공백으로만 이루어져 있으면("            ") true를 반환하는 isBlank() 메서드

 

중요 포인트

isEmpty() 메서드와 isBlank() 메서드는 String이 null일 경우는 판단하지 못한다.

null일 경우 NullPointerException이 발생하므로 null 비교를 먼저 해주어야 한다.

 

리스트에서(ex. ArrayList) 요소의 수를 반환하는 size() 메서드

int size() --> Returns the number of elements in this list

리스트가 비어있는지의 여부를 확인하려면 isEmpty(), 리스트에 요소의 갯수가 몇 개 인지 확인할 때는 size() 메서드를 사용할 것

List 객체가 null일 경우 size() 메서드 또는 isEmpty() 메서드 호출시 NullPointerException이 발생하기 때문에 리스트가 null인지의 여부도 함께 확인해야 한다. List가 null 이라는 것은 해당 인스턴스가 생성되지 않았다는 것을 의미한다, isEmpty() 와 size() == 0 은 해당 인스턴스는 생성된 상태이지만 List 내부의 요소가 존재하지 않는다는 것을 의미한다.

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

public void modify(TravelClub club, Map<String, String> newValueMap) {
	// Map은 Key와 Value의 한 쌍으로 이루어진 데이터의 집합이다.
    // Key 값은 중복되지 않으나 Value값은 중복될 수 있다.
    // ----> Key 값으로 데이터를 식별하기 때문
    
    // 순서를 보장하지 않으며, 뛰어난 검색 속도를 갖는다.
    // 인덱스가 따로 존재하지 않으며 Iterator를 지원한다.









}

 

Map은 순서를 보장하지 않음 ---> 인덱스가 없음 ----> Iterator를 지원받는다.iterate: (계산·컴퓨터·처리절차 등을) 반복한다; ...을 되풀이하여 말하다, ...을 되풀이하여 하다. 반복하다 --> perform repeatedlyMap은 인터페이스, Interface Map<K, V>HashMap은 순서를 보장하지 않고 ---> 인덱스가 없다 ---> Iterator를 지원한다. key에 대한 중복이 없으며, value는 중복을 허용하고, key와 value의 값으로 null을 허용한다. 검색에 가장 뛰어난 성능을 가진다.Iterator는 인터페이스, Interface Iterator<E> --> E는 이 Iterator에 의해 반환되는 요소의 타입이다.Iterator<E>, 이 인터페이스는 자바 콜렉션 프레임워크(데이터를 저장하는 클래스들을 표준화한 설계이다)의 멤버다 --> 이터레이터는 자바 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법들을 표준화 한것이다. Iterator는 반복자로써 객체 지향적 프로그래밍에서 배열이나 이와 유사한 자료구조의 내부요소를 순회하는 객체다. Iterator는 컬렉션(Collection -- Set, List)과 Map을 구현한 구현체에 저장된 요소를 접근하는데 사용되는 인터페이스다.

Interface Collection<E>은 Iterable<E> 인터페이스를 상속한 인터페이스다 --> public interface Collection<E> extends Iterable<E>, 인터페이스 Collection은 collection 계층에서 최상위 인터페이스다. Collection 인터페이스를 상속한 Set, List 인터페이스 또한 Iterable<E> 인터페이스를 상속하게 된다 ---> Iterable<E> > Collection<E> > Set<E>, List<E> 

Collection(컬렉션)은 객체의 집합을 의미한다 ---> A collection represents a group of objects, known as its elements.

Iterable<E> 인터페이스를 상속한 Collection<E>을 상속한 Set과 List 인터페이스에 모두 Iterator<E>타입을 반환하는 iterator() 메서드가 있어 Set과 List를 구현한 구현체에서 iterator() 메서드를 사용하여 Iterator타입 객체를 반환받아 사용할 수 있다.Iterator타입 객체(Iterator)는 컬렉션 안에 있는 모든 요소를 하나씩 순서대로 꺼낼 수 있는 도구(반복자)다 ---> 컬렉션 안의 데이터를 처음부터 끝까지 하나씩 순회하며 꺼낼 수 있도록 해주는 기능을 제공한다. List와 Set 인터페이스를 구현한 구현체에서 Iterator() 메서드를 사용할 수 있고, Iterator() 메서드는 해당 구현체가 갖고 있는 요소들을 순회하며 꺼낼 수 있도록 해주는 기능을 갖고 있는 Iterator 객체를 반환한다. Map인터페이스는 Key와 Value의 한 쌍으로 구성된 데이터로 이루어져 있기 때문에 iterator() 메서드를 직접 호출할 수 없고, 그 대신 keySet() 이나 entrySet()과 같은 메서드를 통해 키(key)와 값(value)을 따로 Set의 형태로 얻어온 후 다시 iterator()를 호출해야 iterator객체를 얻어올 수 있다. iterator 객체를 얻은 후에 반복문(주로 while문)을 통해 컬렉션 클래스의 요소를 읽어온다. 

 

번호를 입력받아야 하는데 문자를 입력하면 예외가 발생한다: nextInt() ---> 사용자로부터 숫자를 입력받는 메서드, 사용자가 숫자를 입력해야 하는데 문자를 입력하게 되면 입력값이 맞지 않아 InputMisMatchException이라는 예외가 발생한다. 입력 값이 불일치해서 발생하는 예외인 InputMisMatchException

 

List타입 컬렉션 객체에 요소가 하나도 없을 때

List<E> extends Collection<E> {...} 컬렉션 인터페이스를 상속한 List인터페이스 설계도를 보면

boolean 타입의 isEmpty() 메서드가 있다. 이 메서드는 리스트가 하나의 요소도 포함하고 있지 않다면 ---> 리스트에 저장된 요소가 아예 없다면 true를 반환한다. Returns true if this list contains no elements.

int타입의 size() 메서드는 리스트에 저장된 요소의 개수를 정수로 반환한다. Returns the number of elements in this list.

 

리스트 타입의 컬렉션 객체에 요소가 한 개도 없을 때 size() 메서드를 호출했는데 0을 반환하는 것을 확인했다.

그리고 isEmpty() 메서드를 호출했을 때는 true가 반환되는 것을 확인했다.

---> List타입의 참조변수.size() != 0 --> 리스트 타입의 컬렉션 참조변수가 size() 메서드를 호출했을 때

0이 아니라면 리스트에는 요소가 있다는 것을 의미한다. 그리고 isEmpty() 메서드 호출시

true가 아니라면(!List타입의 참조변수.isEmpty()) 이것 역시 리스트에 요소가 있다는 것을 의미한다.

 

IllegalArgumentException 예외

java.lang.IllegalArgumentException은 메서드로 전달한 인자가 적합하지 않거나 적절하지 못할 경우에 발생하는 예외다.

---> IllegalArgumentException 예외가 발생했다면 예외 발생지점에서 메서드에 전달되는 인자가 잘 전달되고 있는지 확인할 것

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

 

개발연습

 

public class ClubMenu {
	private ClubWindow clubWindow; // ---> register(), find(), findOne(), modify(), remove() 메서드가 있음
    private MemberMenu memberMenu; // 어떤 클럽에서 회원을 추가/검색/수정/삭제 하는 기능을 제공하는 화면단
    private TravelClub currentClub;
    
    private Scanner scanner;
    private Narrator narrator;
    
    public ClubMenu() {
    	ClubCoordinator service = new ClubCoordinator();
        this.memberMenu = new MemberMenu(service);
        this.clubWindow = new ClubWindow(service);
        
        this.scanner = new Scanner(System.in);
        this.narrator = new Narrator(this, TalkingAt.LEFT);
    }
    
    public void show]() {
    	int inputNumber = 0;
        
        while(true) {
        	displayMenu();
            
            inputNumber = selectMenu();
            
            switch(inputNumber) {
            
            case 1:
            	clubWindow.register();
                break;
            case 2:
            	clubWindow.find();
                break;
            case 3:
            	clubWindow.modify();
                break;
            case 4:
            	clubWindow.remove();
                break;
            case 5:
            	memberMenu.show();
                break;
            case 0:
            	this.exitProgram();
            default: // selectMenu() 메서드의 반환 값이 -1이면
            	narrator.sayIn("숫자를 다시 입력해주세요!");
            }
        }
    }
    
    private void displayMenu() {
    	narrator.sayIn("");
        narrator.sayln("..............................");
        narrator.sayIn("여행 클럽 메뉴");
		narrator.sayIn("1. 새로운 여행 클럽 등록하기");
        narrator.sayIn("2. 여행 클럽 검색하기");
        narrator.sayIn("3. 여행 클럽 수정하기");
        narrator.sayIn("4. 여행 클럽 삭제하기");
        narrator.sayln("..............................");
        narrator.sayIn("5. 회원 메뉴");
        narrator.sayln("..............................");
        narrator.sayIn("0. 프로그램 종료하기");
        narrator.sayln("..............................");
    }
    
    private int selectMenu() {
    	System.out.print("이용하실 서비스 메뉴 번호를 입력하세요: ");
        
        try{
        	int menuNumber = scanner.nextInt();
            // Scanner의 nextInt() 메서드가 호출되면 사용자로부터 숫자 입력을 받아야 하는데
            // 사용자가 숫자가 아닌 글자를 입력하면 InputMisMatchException이 발생하기 때문에
            // try~catch문으로 감싸주는 것이다.
            
            // if the next token does not match the Integer --> InputMisMatchException 예외 발생
            // if this scanner is closed --> IllegalStateException 예외 발생
            
            if(menuNumber >= 0 && menuNumber <= 5) // 사용자로부터 입력받은 숫자가 0이상,
            // 그리고 5이하가 맞다면
            {
            	scanner.nextLine();
                // Scanner의 nextLine() 메서드는 Returns the line that was skipped
                // ---> 생략된[건너띈] 줄을 반환한다?? 뭔말이야
                
                // 만약 Scanner객체를 다 쓴 뒤에 자원 반납을 했는데(scanner.close();)
                // nextLine() 메서드를 호출한다면 IllegalStateException 예외가 발생한다.
                
                return menuNumber;
            
            } else {
            	narrator.sayIn("유효한 숫자가 아닙니다> " + menuNumber);
                return -1;      
            
            }
        
        
        
        } catch(InputMisMatchException e) {
        	System.out.println("숫자가 아닌 글자를 입력하셨습니다.");
            // e.printStackTrace();
            System.out.println(e.getMessage());
        } return -1; // catch블럭안의 내용을 수행하고, -1을 반환한다.    
    }
    
    private vodi exitProgram() {
    	narrator.sayIn("프로그램을 종료합니다.");
        scanner.close(); // 자원을(Scanner 객체) 반납한다.
    	System.exit(0);   
    }
}

 

public class ClubWindow {
	private ClubCoordinator clubCoordinator;
    
    private ConsoleUtil consoleUtil;
    
    private Narrator narrator;
    
    public ClubWindow(ClubCoordinator clubCoordinator) {
    	this.clubCoordinator = clubCoordinator;
        //-------------------------------------------------
        this.narrator = new Narrator(this, TalkingAt.LEFT);
        this.consoleUtil = new ConsoleUtil(narrator);
        //-------------------------------------------------
    }
    
    public TravelClub register() {
    	TravelClub newClub = null;
        
        while(true) {
        	String clubName = consoleUtil.getValueOf("\n 등록할 클럽이름을 입력해주세요(0. 클럽 메뉴로 돌아가기)");
            
            if(clubName.equals("0") || clubName.equals("")) break;
            // 숫자 0을 입력하거나 클럽 이름을 입력하지 않고, 엔터를 누른다면 반복문을 빠져나간다.
            
            if(clubCoordinator.exist(clubName)) {
            	narrator.sayIn("입력하신 클럽이름은 이미 존재합니다. "+ clubName);
                continue;
                
                // break는 반복문을 완전히 빠져나옴 ---> 제어흐름이 중단됨
                // continue는 제어흐름을 유지한 상태에서 코드의 실행만을 건너뛴다.
                // ---> continue문을 만나면 하기 코드의 실행문은 모두 실행되지 않고,
                // 반복문의 첫 번째 코드부터 다시 실행된다.
                
                // 즉 입력한 클럽이름이 이미 존재한다면 더 이상 클럽 등록을 진행하지 않고
                // 존재하지 않는 클럽이름을 다시 작성하여 클럽 등록이 이루어질 수 있도록
                // 반복문의 첫 번째 코드부터 다시 시작한다 ---> 클럽명을 입력해주세요 코드가 다시 실행됨
            }
            
            String intro = consoleUtil.getValueOf("클럽을 소개말을 작성해주세요(0. 클럽 메뉴 돌아가기)");
            
            if(intro.equals("0")) break;
			//-------------------------------------------------
            newClub = new TravelClub(clubName, intro);
            clubCoordinator.register(newClub);
            
            narrator.say(newClub.getName()+" 클럽이 등록되었습니다.");
            narrator.say(newClub.getName()+" 클럽 세부 정보는 "+newClub.toString());
            
        	return newClub;
        }
        
        // 0을 입력하지 않고는 무한 반복된는 클럽 찾기 메서드
        public TravelClub find() {
        	TravelClub clubFound = null;
            
            if(!clubCoordinator.hasClubs()) {
            	narrator.sayIn("등록되어 있는 클럽이 없습니다.");
                return null;
            }
            
            while(true) {
            	String clubName = consoleUtil.getValueOf("\n 조회할 클럽명을 입력해주세요(0. 클럽 메뉴로 돌아가기)");
                
                if(clubName.equals("0")) break;
                
                if(clubCoordinator=.exist(clubName)) {
                	clubFound = clubCoordinator.find(clubName);
                    narrator.sayIn("\t 조회한 클럽은 "+clubFound);
                    // break; 검색한 클럽을 찾았다면 정보를 출력하고
                    // break문을 만나 while문을 빠져나간다.
                    // 그리고 찾은 클럽을 반환한다.

                } else {
                	narrator.sayIn("검색한 클럽이 존재하지 않습니다.");
                }

            }
            return clubFound;
        }
        
        // find() 와 findOne() 메서드의 차이???
        public TravelClub findOne() {
        	TravelClub clubFound = null;
            
            if(!clubCoordinator.hasClubs()) {
            	narrator.sayIn("등록되어 있는 클럽이 없습니다.");
                return null;
            }
            
            while(true) {
            	String clubName = consoleUtil.getValueOf("검색할 클럽 이름을 입력해주세요(0. 클럽 메뉴로 돌아가기)");
                if(clubName.equals("0") break];
                
                if(clubCoordinator.exist(clubName)) {
                	clubFound = clubCoordinator.find(clubName);
                    narrator.sayIn("\t 검색한 클럽은: "+clubFound);
                    break;

                } else {
                	narrator.sayIn("검색하신 클럽은 등록되어 있지 않습니다."+clubName);
                
                }
                clubFound = null;            
            }
            return clubFound;
        }
        
        public TravelClub modify() {
        	TravelClub targetClub = findOne();
            
            if(targetClub == null ) {
            	return targetClub;
            }
            
            String newName = null;
            
            while(true) {
            	newName = consoleUtil.getValueOf("변경할 이름을 입력해주세요(0. 클럽 메뉴로 돌아가기, enter만 입력시 변경되지 않습니다.)");
                if(newName.equals("0")) break;
                
                
                // 엔터만 입력시 현재 클럽명을 바꾸지 않는다
                // 그런데 현재 클럽명을 새로운이름이라는 변수에 대입하고,
                if(newName.equals("")) {
                	newName = targetClub.getName();
                    break; // 여기서 break를 함으로써 하기 코드 진행은 하지 못하도록 해야하는 것이 아닌가??
                }
                
                // 기존에 있던 이름으로 현재 등록되어있는 클럽명과 대조한다면
                // 당연히 기존에 있는 이름이라는 문구가 뜨면서 계속 변경할 이름을 입력하라고 할 것이다.
                if(clubCoordinator.exist(newClubName)) {
                	narrator.sayIn("입력하신 클럽명은 이미 존재합니다> "+newClubName);
                    continue;
                }
            }
            
            String newIntro = consoleUtil.getValueOf("새로운 클럽 소개말을 작성해주세요(0. 클럽 메뉴로 돌아가기, enter만 입력시 변경되지 않습니다.)");
        	if(newIntro.equals("")) {
            	newIntro = targetClub.getIntro();
            }
            
            String foundedDate = consoleUtil.getValueOf("new foundedDate (0. Club Menu, Enter. no change)");
        	if(foundedDate.equals("0")) return targetClub; // 사용자로부터 입력받은 값이 0이면
            // 기존의 targetClub을 리턴한다 ---> 무엇을 의미하는가'
            
            if(foundedDate.equals("")) foundedDate = targetClub.getFoundedDate();
            // ------------------------------------------------------------------
            targetClub.setName(newName);
            targetClub.setIntro(newIntro);
            targetClub.setFoundedDate(foundedDate);
            
            narrator.sayIn("클럽이 변경되었습니다. 변경된 클럽정보는 "+targetClub.toString());
            // 이 사이에 IllegarArgumentException 이 발생할 수 있어 try~catch문으로
            // 감쌌는데, 어디서 발생하고 어떻게 예외를 처리해야 하는지 학습할 것 --->
            // ------------------------------------------------------------------
            }
        
        
        	public void remove() {
            	TravelClub targetClub = findOne();
                if(targetClub == null ) return;
                
                String confirmStr = consoleUtil.getValueOf("이 클럽을 삭제하시겠습니까? (Y:yes, N:no)");
                if(confirmStr.toLowerCase().equals("y") || confirmStr.toLowerCase().equals("yes"))
                {
                	narrator.sayIn("클럽을 삭제합니다."+targetClub.getName());
                    clubCoordinator.remove(targetClub);
                } else {
                	narrator.sayIn("삭제 취소되었습니다.");
                }
            }
        }