본문 바로가기
뒷북 정리 (국비 교육)/java

[Java] step13. Util Class

by 규글 2022. 4. 22.

step13. Util Class

- 01

		// String type  을 저장할수 있는 index 로 관리되는 목록(List) 객체 
		ArrayList<String> names=new ArrayList<String>();
        
		// String type 의 참조값 저장하기 
		names.add("김구라");
		names.add("해골");
		names.add("원숭이");
        
		// 배열의 방의 크기 얻어오기 
		int size=names.size();
        
		// 각각의 방에 저장된 값 불러오기
		String name1=names.get(0); // 0번방
		String name2=names.get(1); // 1번방
		String name3=names.get(2); // 2번방
		
		// 특정 방의 값 수정(덮어쓰기)
		names.set(0, "이정호"); // 0번방 수정
		
		// 특정 인덱스 삭제(방 자체를 제거하기)
		names.remove(1); // 1번방 삭제 
		
		// 모든 깨끗이 방 삭제
		names.clear();

 ArrayList는 일종의 배열이다. 이 array list에 어떤 type을 담을 것인지 객체를 만들 때 generic 으로 지정한다. 이렇게 만든 array list에는 여러 기능들이 있다. add( )는 새로운 내용을 객체에 추가하는 기능, size( )는 객체의 크기를 알 수 있는 기능, get( )은 객체의 해당 방에 있는 값을 얻어오는 기능, set(#, value)는 객체의 해당 방의 값을 수정하는 기능, remove( )는 객체의 해당 방 자체를 삭제하는 기능, clear( )는 객체의 모든 방을 삭제하는 기능이다.

 

 이 'ArrayList' 에 마우스를 올려서 browser를 띄우거나, 혹은 직접 oracle docs[각주:1] 에서 관련 내용들을 찾아볼 수 있다.

 보면 어떤 class를 extends 했는지, 어떤 interface를 implements 했는지 등등을 다 볼 수 있으며, 하단으로 조금 더 내려보면 사용할 수 있는 method 들을 살펴볼 수 있다.

 

- 02

		// 정수를 저장할수 있는 가변 배열 객체 생성
		ArrayList<Integer> nums=new ArrayList<Integer>();
		nums.add(10);
		nums.add(20);
		nums.add(30);
        
		// 배열의 크기 얻어오기 
		int size=nums.size();
        
		// 특정 인덱스 아이템 얻어오기 (참조)
		int num1=nums.get(0);
		Integer num2=nums.get(1);
		int num3=nums.get(2);
        
		// 특정 인덱스 수정
		nums.set(1, 40);
        
		// 특정 인덱스 삭제 
		nums.remove(1);
        
		// 배열 비우기 
		nums.clear();

 이번 예시는 String이 아닌 Integer이다. Generic이 들어갈 자리에 int는 불가능하다. 그러나 해당 값을 받을 때 int를 사용하는 것은 가능하다. 이것이 이전에 언급했던 나중에 배우겠다면 '가변 배열'이다.

 

- 03

		// String type  을 저장할 ArrayList 객체 생성하고 
		// 참조값을 List 인터페이스 type 지역변수 msgs 에 담기
		List<String> msgs=new ArrayList<String>();
		msgs.add("김구라");
		msgs.add("해골");
		msgs.add("원숭이");
		msgs.add("주뎅이");
		msgs.add("덩어리");
		
		// msgs 의 방의 크기만큼 반복문 돌면서
		for(int i=0; i<msgs.size(); i++) {
			// i번째 방에 저장된 아이템 불러오기
			String tmp=msgs.get(i);
			System.out.println(tmp);
		}
		System.out.println("------");
		
		for(String tmp:msgs) {
			System.out.println(tmp);
		}

 고정 배열에서 배열의 길이를 얻으려 할 때는 length를 사용했지만, 가변 배열에서 얻으려 할 때는 size( )를 사용한다.

 여기에서 짚고 넘어갈 부분은 for문의 다른 표현법이다. 예시를 해석하면 다음과 같다. String type의 data를 가지고 있는 msgs 배열에 대해서 반복문을 수행할 것이다. 그 각 내용을 tmp로 받아서 반복문의 작업에 이용할 것이다. 이를 확장 form 이라고 했다. 주의할 점은 type을 잘 확인해야 한다는 것이다. 만약에 String type data를 다루는데 int로 받으면 안된다.

 

- 04

		// 1. Car type 을 저장할수 있는 ArrayList 객체를 생성해서
		// 참조값을 List 인터페이스 type  지역변수 cars  에 담아 보세요.
		List<Car> cars=new ArrayList<>();
        
		// 2. Car 객체(3개)를 생성해서 List 객체에 저장해 보세요. 
		cars.add(new Car("소나타"));
		cars.add(new Car("프라이드"));
		Car c1=new Car("그랜저");
		cars.add(c1);
        
		// 3. 반복문 for 문을 이용해서 List 객체에 저장된 모든 Car 객체의 drive() 메소드를
		// 순서대로 호출해 보세요.
		for(int i=0; i<cars.size(); i++) {
			Car tmp=cars.get(i);
			tmp.drive();
		}
		System.out.println("확장 for 문을 이용하면");
		for(Car tmp:cars) {
			tmp.drive();
		}

 for문의 확장 form을 사용하는 또다른 예시이다.

 

- 05

public class Info {
	// 필드
	public int num;
	public String name;
	public String addr;
}

 잠시 '접근 지정자' 를 살펴보자. 이전에 접근 지정자라는 친구가 존재한다고 간단히 언급하고 넘어갔었다.[각주:2] 그 친구를 다시 한 번 볼 것이다. 접근 지정자는 다음의 네 종류가 있다고 했었다.

  1. public
  2. private
  3. protected
  4. x (default)

 먼저 public은 전체 공개를 의미한다. 누구나 어디서든 접근할 수 있는 친구가 된다. 다음 private는 반대로 비공개이다. 나만 사용하겠다는 의미이다. 이 private로 만든 친구는 같은 class나 같은 object 안에서만 접근할 수 있는 친구가 된다. 다음 protected는 같은 package나 상속 관계에서의 자식에서만 접근할 수 있는 친구가 된다. 마지막으로 아무 것도 작성하지 않는 default는 접근 가능 범위가 같은 package가 된다.

 이 중에서 class의 접근 지정자는 public과 default로만 한정된다. 나머지 private나 protected는 없다. 이 class에 접근한다는 것은 import가 가능한가, 불가능한가의 문제이다.

 

		// Info  클래스로 객체를 생성해서 참조값을 i1  이라는 지역 변수에 담기
		Info i1=new Info();
        
		// 필드는 public  으로 선언되어 있기 때문에 직접 접근해서 대입연산자로 값을 대입할수 있다.
		i1.num=1;
		i1.name="김구라";
		i1.addr="노량진";
		
		Info i2=new Info();
		i2.num=2;
		i2.name="해골";
		i2.addr="행신동";
		
		// 위의 Info type 객체의 참조값을 List 객체에 순서대로 담아 보세요.
		List<Info> list=new ArrayList<>();
		list.add(i1);
		list.add(i2);
        
		/*
		 *  반복문 for 를 이용해서 List 객체에 담긴 정보를 아래와 같은 형식으로 출력해 보세요.
		 *  
		 *  1 | 김구라 | 노량진
		 *  2 | 해골 | 행신동
		 *  .
		 *  .
		 */
		for(Info tmp: list) {
			// 출력할 문자열 구성하기
			String line=tmp.num+" | "+tmp.name+" | "+tmp.addr;
			System.out.println(line);
		}

 Info class는 public으로 되어 있기 때문에 다른 package에서도 해당 class에 접근해서 객체를 만들 수 있고, 해당 객체의 field도 public이기 때문에 접근해서 값을 대입할 수 있다. 만약에 Info class의 field 중에서 접근 지정자가 default인 것이 있다면 다른 package에서 해당 field에는 접근할 수 없게 된다.

 

- 06

public class MemberInfo {
	// 필드
	public int num;
	public String name;
	public String addr;
	
	// default 생성자
	public MemberInfo() {}
	
	// 생성자의 인자로 번호,이름,주소를 전달 받을수 있도록 한다.
	public MemberInfo(int num, String name, String addr) {
		// 생성자의 인자로 전달된 값을 필드에 저장한다.
		this.num=num;
		this.name=name;
		this.addr=addr;
	}
}
		// default 생성자를 호출해서 MemberInfo 객체 생성
		MemberInfo m1=new MemberInfo();
        
		// 필드에 값을 각각 대입하기
		m1.num=1;
		m1.name="김구라";
		m1.addr="노량진";
		
		// int, String, String type 을 인자로 전달하는 생성자를 호출해서 
		// MemberInfo 객체 생성
		MemberInfo m2=new MemberInfo(2, "해골", "행신동");

 첫 번째는 객체를 만들고, 해당 객체의 field에 접근해서 값을 넣어준 경우이고, 두 번째는 객체를 만듦과 동시에 해당 객체의 field에 값을 넣어준 경우이다. 여러 생성자를 만들면 이런 방식이 가능하다.

 그런데 여기에는 문제가 발생할 수 있는 여지가 있다. 생각해보자. 이전에 field는 method를 사용할 때 사용하는 일종의 부품이 된다고 했다. 그런데 이 부품이 공개되어 있는 것이다. 그렇다면 공개되어 있으니 수정도 자유롭게 되는데, 만약에 이 field에 잘못된 값이 전달되면 method가 잘못된 방향으로 동작할 가능성이 생긴다. 그렇기 때문에 특별한 이유가 아니라면 웬만하면 field는 private로 되어있는 경우가 많다고 한다.

 

- 07

		// default 생성자를 이용해서 객체를 생성한 경우 
		MemberDto m1=new MemberDto();
		m1.setNum(1);
		m1.setName("김구라");
		m1.setAddr("노량진");
		
		// 인자로 필드에 저장할 생성자를 이용해서 객체를 생성한 경우
		MemberDto m2=new MemberDto(2, "해골", "행신동");
		
		// 위에서 생성한 객체의 참조값을 List 객체에 담아 보세요.
		List<MemberDto> list=new ArrayList<>();
		list.add(m1);
		list.add(m2);
		
		/*
		 * for 문을 이용해서 List 객체에 담긴 MemberDto 객체를 순서대로 참조해서
		 * 아래와 같은 형식으로 출력해 보세요.
		 * 
		 * 1 | 김구라 | 노량진
		 * 2 | 해골 | 행신동
		 * .
		 * .
		 * 
		*/
		
		for(MemberDto tmp:list) {
			// 출력할 문자열 구성하고 
			String line=tmp.getNum()+" | "+tmp.getName()+" | "+tmp.getAddr();
			// 출력하기 
			System.out.println(line);
		}
		
		// 참조 테스트
		List<MemberDto> a = list;
		MemberDto b = list.get(0);
		int c = list.get(0).getNum();
		String d = list.get(0).getName();
		String e = list.get(0).getAddr();
		int f = list.get(0).getName().length();

 이번 예제를 보면 method를 통해서 값을 넣고 있다. 이전 예제의 경우, field에 직접 접근해서 값을 바꿔주었지만 이번에는 method를 통해서 넣고 있는 것이다. 이는 field가 private이기 때문에, field에 직접 접근하는 것이 불가능했기 때문이다. 다음을 보자.

 

public class MemberDto {
	// private 접근 지정자로 필요한 필드를 정의한다.
	private int num;
	private String name;
	private String addr;
    
	// default 생성자를 정의한다.
	public MemberDto() {}
    
	// 모든 필드의 값을 전달받아서 필드에 저장하는 생성자를 정의한다.
	public MemberDto(int num, String name, String addr) {
		super();
		this.num = num;
		this.name = name;
		this.addr = addr;
	}
    
	// 필드에 접근을 할수 있도록 getter, setter 메소드를 만들어 놓는다.
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	
}

 이번 예제를 위한 MemberDto class는 field가 private로 만들어졌고, set과 get으로 시작하는 method들이 있다. 이렇게 set과 get으로 시작하는 method를 setter/getter method라고 한다. 값을 가져올 때 사용하는 것은 getter, 값을 넣어줄 때 사용하는 것은 setter이다. 그리고 이 모양을 보면 field의 이름이 method의 이름을 정하게 된다. 이런 method를 제공해주는 class를 Data Transfer Object (DTO) 혹은 Value Object (VO) 라고 한다.(명확하게는 DTO와 VO는 다른 친구이다. VO는 읽기만 가능한, 단지 값만 가지고 있는 반면, DTO는 그 값을 변하게 할 수 있다.) 그리고 이런 class를 만드는 표준이 있다. 다음을 보자.

 

/*
 *   [ Data Transfer Object ]  or  [ Value Object ]
 *   
 *   객체를 생성할 클래스의 작성법
 *   
 *   1. private  접근 지정자로 필요한 필드를 data type 을 고려해서 만든다.
 *   2. default 생성자를 만든다.
 *   3. 모든 필드의 값을 생성자의 인자로 전달 받아서 필드에 저장하는 생성자를 generate 한다.
 *   4. 모든 필드의 접근 메소드 즉 setter, getter 메소드를 generate 한다.
 *   
 *   generate 하는 방법: 마우스 우클릭 => Source => Generate Constructor 
 *                                            Generate getters and setters
 */

 하지만 이런 방식을 아는데, field의 개수가 상당히 많은 class를 만들어야 한다고 상상해보자. 이 얼마나 반복되는 노가다의 상황인가? 하지만 걱정하지 않아도 된다. 이를 해결하는 아주 좋은 방법이 존재한다.

 

	public MemberDto(int num, String name, String addr) {
		super();
		this.num = num;
		this.name = name;
		this.addr = addr;
	}

 class 작업 중에 우클릭을 눌러 Source > Generate Constructor using Fields 를 누르면 우측의 창이 뜨는데, 원하는대로 커스텀해서 Generate 하면 위의 코드를 만들어준다. 여기에서의 super( );는 명시적으로 작성해주었으나, 자동으로도 만들어진다. 지역 변수의 이름과 field가 다르면 this를 쓰지 않아도 되지만 그런 패턴은 java에서 잘 쓰지 않는다. (예를 들면 변수에 under bar _ 를 붙인다든지)

 그리고 또 Source > Generate Getter and Setters 를 누르면 이전과 비슷한 창이 나오고 만들어지길 원하는 field에 대한 getter와 setter method를 자동으로 만들어준다. 굳이 'set/get + ctrl + space' 로 하나하나 만들지 않아도 된다.

 

- 08

 javascript에서 array와 java에서의 array를 비교해봤다. 그렇다면 javascript에서의 object와 java에서의 object는 어떻게 다를까?

 

		/*
		 *  HashMap<key 값의 type, value 값의 type> 
		 *  
		 *  key의 generic class는 String으로 지정하는 것이 일반적이다.
		 *  value의 generic class는 담을 data type을 지정하면 된다.
		 *  여러 개의 type을 섞어서 담을 것이라면 Object로 지정하면 된다.
		 */
		Map<String, Object> map1=new HashMap<>();
		map1.put("num", 1);
		map1.put("name", "김구라");
		map1.put("addr", "노량진");
        
		/*
		 *  value 의 Generic 클래스가 Object 로 지정 되어 있기때문에
		 *  리턴되는 Object type 을 원래 type 으로 casting 해야 한다. 
		 */
		int num=(int)map1.get("num");
		String name=(String)map1.get("name");
		String addr=(String)map1.get("addr");

 javascript에서의 object는 중괄호 { } 안에 key와 value를 콜론 : 으로 나눠서 작성했었다. java에서는 Map이라는 친구를 사용한다. Map을 만들면서 generic을 정하는데, key의 값은 보통 String으로 하는 것이 일반적이고, value에는 담고 싶은 type을 적으면 되는데 여러 type인 경우라면 Object를 적어준다. 만약 이렇게 Object로 정해줬다면, 다른 type의 변수로 받을 때 casting 해줘야 한다는 수고로움이 조금 있다.

 대부분은 수정하고 지울 일이 거의 없기 때문에 Map의 method 중에 객체에 값을 넣고 값을 얻어오는 put과 get만을 사용하게 될 것이라 했다.

 

- 09

		/*
		 *  3 명의 회원정보 (번호, 이름, 주소) 를 HashMap 객체에 담고
		 *  
		 *  HashMap 객체의 참조값을 ArrayList 객체에 순서대로 담아 보세요.
		 */
		Map<String, Object> map1=new HashMap<>();
		map1.put("num", 1);
		map1.put("name", "김구라");
		map1.put("addr", "노량진");
		
		Map<String, Object> map2=new HashMap<>();
		map2.put("num", 2);
		map2.put("name", "해골");
		map2.put("addr", "노량진");
		
		Map<String, Object> map3=new HashMap<>();
		map3.put("num", 3);
		map3.put("name", "원숭이");
		map3.put("addr", "동물원");
		
		List<Map<String, Object>> list=new ArrayList<>();
		list.add(map1);
		list.add(map2);
		list.add(map3);
		
		// 해보세요
		for(Map<String, Object> tmp: list) {
			
			int num=(int)tmp.get("num");
			String name=(String)tmp.get("name");
			String addr=(String)tmp.get("addr");
			
			System.out.println(num+" | "+name+" | "+addr);
		}
		
		// 참조 연습
		List<Map<String, Object>> a=list;
		Map<String, Object> b=list.get(0);
		Object c=list.get(0).get("num");
		Object d=list.get(0).get("name");
		Object e=list.get(0).get("addr");
		int f=(int)list.get(0).get("num");
		String g=(String)list.get(0).get("name");
		String h=(String)list.get(0).get("addr");
		
		int i=((String)list.get(0).get("addr")).length();

 한 예시이다.

 

- 10

		Map<String, Object> map1=new HashMap<>();
		map1.put("num", 1);
		map1.put("name", "김구라");
		map1.put("isMan", true);
		map1.put("phone", "010-1111-2222");
		
		// 이름을 참조 하려면?
		String name=(String)map1.get("name");
        
		// 전화 번호를 수정하려면?
		map1.put("phone", "010-3333-4444"); //동일한 key 값으로 다시 담는다.
        
		// "isMan" 이라는 키값이 존재 하는지 여부?
		boolean isExist=map1.containsKey("isMan");
        
		// "num" 이라는 키값으로 저장된값 삭제
		map1.remove("num");
        
		// 전체 삭제
		map1.clear();

 앞서 언급했듯 put은 객체에 저장하는 것, get은 저장된 내용을 얻어오는 method였다. 만약에 저장한 내용을 수정하려면 사용했던 동일한 key 값에 다른 value를 넣어주면 된다. 특정 key 값이 존재하는지 여부를 알려주는 contatinKey( ) method도 있고, 특정 key 값에 대한 내용을 삭제하는 remove("xxx") 도 있다. Map의 전체 내용을 삭제하고자 한다면 clear( ) method를 사용하면 된다.

 

 Debugging을 해보면 뭔가 순서대로 들어있는 것 같지만, 사실 순서가 있는 것은 아니라고 했다. 그리고 보면 List와 Map에 있는 data의 value를 보면 모두 reference data type으로 되어 있는 것을 볼 수 있다. 이들에는 primitive data type으로 data를 저장할 수 없다. 하지만 우리가 신경 쓸 필요는 없는데, 값을 자동으로 reference data type으로 바꿔서 넣어준다.

 

- 11

		Map<String, String> dic=new HashMap<>();
		dic.put("house", "집");
		dic.put("phone", "전화기");
		dic.put("car", "자동차");
		dic.put("pencil", "연필");
		dic.put("eraser", "지우게");
		/*
		 *  검색할 단어를 입력하세요: house
		 *  
		 *  house 의 뜻은 집입니다.
		 */
		Scanner scan=new Scanner(System.in);
		System.out.print("검색할 단어를 입력하세요: ");
		String inputWord=scan.nextLine(); // 문자열 입력 받기 
		// Map 객체에서 입력한 문자열로 저장된 값을 읽어와 보기
		String mean=dic.get(inputWord);
		if(mean == null) { // 존재 하지 않으면 
			System.out.println(inputWord+" 는 목록에 없습니다.");
		}else {
			// 출력할 문자열 구성하기
			String line=inputWord+" 의 뜻은 "+mean+" 입니다.";
			// 출력하기
			System.out.println(line);
		}

 Map이라는 친구는 일종의 사전 형식인 것이다. 특정 key 값을 찾아가면 그에 대응하는 어떤 value가 있는 것이다.

 

- 12

/*
 *   HashSet 은  Set 인터페이스를 구현한 클래스 이다.
 *   
 *   - 순서가 없다.
 *   - key 값도 없다.
 *   - 중복을 허용하지 않는다.
 *   - 어떤 data 를 묶음(집합) 으로 관리하고자 할때 사용한다.
 */

 HashSet은 Set interface를 구현(implements)한 class이다. Map과 다르게 그냥 단순한 묶음에 지나지 않는다. 순서도 key 값도 없으며 중복을 허용하지 않는, 그저 data를 묶음으로 관리하고자 할 때 사용한다. 만약에 같은 값이 들어간다고 하더라도 하나만 남게 된다고 했다. 다음은 그 예시이다.

 

		// 정수값을 저장할수 있는 HashSet 객체 
		Set<Integer> set1=new HashSet<>();
		set1.add(10);
		set1.add(20);
		set1.add(20);
		set1.add(30);
		set1.add(30);
        
		// 문자열을 저장할수 있는 HashSet 객체
		Set<String> set2=new HashSet<>();
		set2.add("kim");
		set2.add("lee");
		set2.add("park");
		set2.add("lee");
		set2.add("park");
        
		// Car 객체를 저장할수 있는 HashSet 객체
		Set<Car> set3=new HashSet<>();
		set3.add(new Car("프라이드"));
		set3.add(new Car("프라이드"));
		set3.add(new Car("소나타"));
		Car car1=new Car("그랜저");
		set3.add(car1);
		set3.add(car1);

 

- 13

		//정수를 담을수 있는 HashSet 객체를 생성해서 참조값을 Set type 지역 변수에 담기
		Set<Integer> nums=new HashSet<>();
		
		// 0~9 정수를 for 문을 이용해서 담기
		for(int i=0; i<10; i++) {
			nums.add(i);
		}
		
		// Set 에 저장된 item 의 개수?
		int size=nums.size();
		// Set 에 저장된 item 을 모두 불러오기?
		
		// Iterator 반복자 객체 얻어오기 
		Iterator<Integer> it = nums.iterator();
        
		// it.hasNext() 는 커서 다음에 읽어낼 데이터가 있는 지 확인해서 있으면 true  를 
		// 리턴하고 없으면 false 를 리턴한다.
		while(it.hasNext()) {
			// it.next() 는 커서를 한칸 움직여서 커서가 위치한 곳의 데이터를 읽어온다. 
			int tmp=it.next();
			System.out.println(tmp);
		}

 Set type의 method Iterator( )를 사용해서 받으면 되는 Iterator 객체가 있다. 이 객체의 hasNext( ) method는 다음에 읽어낼 객체가 있다면 true를, 없다면 false를 return해준다. 이를 while문을 돌릴 때 이용할 수 있다. While 문은 소괄호 ( ) 안이 true면 반복문을 계속해서 수행하게 하는데, Set 이었기 때문에 순서는 알 수 없지만 내용을 일렬로 세우고 cursor를 옮겨 가면서 실행 순서도 옮겨간다고 생각하면 되겠다.

'뒷북 정리 (국비 교육) > java' 카테고리의 다른 글

[Java] step15. Swing  (0) 2022.04.25
[Java] step14. Exception  (0) 2022.04.24
[Java] step12. Generic Class  (0) 2022.04.22
[Java] step11. Interface  (0) 2022.04.19
[Java] step10. Abstract Class  (0) 2022.04.19

댓글