[1/2] Java 중급 Summary (w. Programmers)

차재현·2025년 1월 12일
0

[ Backend Study - Java ]

목록 보기
10/11
post-thumbnail

Section 1. 예외 처리

Chapter 1. Exception

  • catch

    • 발생한 예외가 “예외클래스”와 맞아야 실행되는 부분
  • finaly

    • 생략 가능
  • 예시 코드

    package exception1;
    
    public class Exception1 {
    	public static void main(String[] args) {
    		int i = 10;
    		int j = 0;
    
    		// int k = i/j; //ArithmeticException 발생, 예외 발생 시점부터 마지막 코드까지 실행 안하고 프로그램 중단 O
    		// System.out.println(k);
    
    		try{
    			System.out.println("예외처리 적용");
    			int k = i/j; //예외 발생 시점 부터 try의 나머지 코드는 실행하지 않는다 
    			System.out.println(k);
    		}catch(Exception e){ //try 문 내부에서 예외 발생 시, catch문 내용 실행 -> 프로그램 중단 X
    			System.out.println("오류 발생 : " + e.toString());
    		}finally {
    			System.out.println("오류 발생하든 안하든 실행되는 부분");
    		}
    		
    		System.out.println("main 종료");
    
    	}
    }
    
    /* 실행 결과
    예외처리 적용
    오류 발생 : java.lang.ArithmeticException: / by zero
    오류 발생하든 안하든 실행되는 부분
    main 종료
    */
    • catch의 “예외클래스” 부분에는 다양한 예외클래스가 들어갈 수 있다.
      • ArithemticException e 를 넣어도 되고, 예상 가능한 특정 예외클래스를 넣어도 된다.
      • Exception e 는 모든 예외를 포괄하는 예외클래스이다.
    • 예외클래스 변수의 메서드
      • try 부분에서 예외 발생 시, catch로 파라미터가 넘어옵니다.
      • .toString() 메서드를 사용하여 파라미터로 넘어온 예외의 내용을 확인할 수 있습니다.

Chapter 2. Throws

  • try문 내부가 아닌 호출한 메서드 내부에서 예외가 발생한다면 프로그램이 중단됩니다.
  • 이를 방지하기위해 thorws 를 사용하면, 메서드를 호출한 곳에서 예외를 처리하도록 예외를 전달합니다.
  • 예시 코드
    package thorws1;
    
    public class Throws1 {
    	public static void main(String[] args) {
    		int i = 10;
    		int j = 0;
    
    		try{
    			int k = divide(i, j); // 메서드 호출 과정에서 예외 발생 시, catch 문으로 예외 전달
    			System.out.println(k);
    		}catch (Exception e){
    			System.out.println(e.toString());
    		}
    
    		System.out.println("예외 처리를 잘 했군요!");
    	}
    
    	// 호출되는 메서드 내부에서 예외발생이 예상된다면 "throws 예외클래스"를 사용해 
    	// 프로그램이 중단되는 것이 아닌, 발생하는 예외를 전달시킬 수 있습니다.
    	public static int divide(int i, int j) throws Exception{
    		return i/j;
    	}
    }
    
    /* 실행 결과
    java.lang.ArithmeticException: / by zero
    예외 처리를 잘 했군요!
     */
    • 여기서도 Exception 이라는 “예외클래스” 부분에 다른 “예외클래스”들을 넣을 수 있습니다.

Chapter 3. Throw new “예외클래스” (Exception 발생시키기)

  • 이렇게 예외들을 처리하는 방법을 알게되었다.
  • 그런데 생각해보면 Chapter 2의 예시 코드 중에 divide 메서드의 목적을 생각하면 두번째 매개변수에는
    애초에 0이 들어오면 안된다.
  • 즉, 나누기 연산을 하기전에 먼저 들어온 인자값들을 확인하여 예외를 방지하면 좋지 않을까?

💡 우리가 예상되는 예외를 먼저 발생시켜서 확실하게 예외를 제어할 수 있다.

  • 예시 코드
    package throw1;
    
    public class Throw1 {
    	public static void main(String[] args) {
    		int i = 10;
    		int j = 0;
    
    		try{
    			int a = divde(i, j);
    			System.out.println(a);
    		}catch (IllegalArgumentException e) {
    			System.out.println(e.toString());
    		}catch (Exception e){ //catch 문은 예외에 따라 여러개 사용 가능
    			System.out.println("나머지 예외 처리 " + e.toString());
    		}
    
    		System.out.println("예외 처리를 잘 했군요!");
    	}
    
    	public static int divde(int i, int j) throws new IllegalArgumeneException{
    		if(j == 0){
    			throw new IllegalArgumentException("두번째 인자로 0이 들어올 수 없습니다.");
    		}
    		return i/j;
    	}
    }

Chapter 4. 사용자 정의 Exception

  • 지금까지는 예외를 처리하기 위해 Java에서 제공해주는 “예외클래스”를 사용해왔다.
  • 하지만 프로그램에서는 정말 다양한 예외들이 발생하며, “Java 제공 예외 클래스” 만으로는 어떤 예외인지 바로 확인하는데 한계가 있다.
  • 그래서 사용자 정의 Exception을 만들어서 직접 예외의 이름을 지어줄 수 있다. 💡 이를 통해 개발자는 더 빠르게 예외를 확인하고 처리할 수 있다!
  • Java에서 예외는 크게 두 가지로 나눌 수 있습니다

    • 체크 예외(Checked Exception)
      • Exception 클래스를 상속받는 예외 중에서 컴파일 타임에 처리해야 하는 예외입니다.
        • Checked - Compile
      • 특징
        • 메서드 선언부에 throws 키워드를 사용하여 해당 메서드가 발생시킬 수 있는 체크 예외를 명시해야만 합니다.
          → throw를 통해 발생시키는 예외보다 넓은 범위의 체크 예외를 명시해야 합니다.
        • 반드시 호출하는 쪽에서 예외를 처리해야 하며, 처리하지 않으면 컴파일 에러가 발생합니다
      • 예시
        • IOException, SQLException, ClassNotFoundException 등이 있습니다.
    • 언체크 예외(Unchecked Exception)
      • RuntimeException 클래스를 상속받는 예외로, 컴파일 타임에 처리할 필요가 없는 예외입니다.
        • Unchecked - Runtime
      • 특징
        • 메서드 선언부에 throws를 명시할 필요가 없으며, 호출하는 쪽에서 예외를 처리하지 않아도 컴파일 에러가 발생하지 않습니다.
        • 주로 프로그래밍 오류(예: 잘못된 인덱스 접근, null 참조 등)로 인해 발생합니다.
      • 예시
        • NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException 등이 있습니다.
    • 요약
      • 체크 예외는 컴파일 타임에 처리해야 하며, 반드시 throws를 명시해야 합니다.
      • 언체크 예외는 런타임에 발생하며, throws 처리하지 않아도 컴파일 에러가 발생하지 않습니다.
  • 예시 코드

    package bizexception;
    
    public class Bizexception extends RuntimeException{ //RuntimeException을 상속받는다
    	public Bizexception(String msg){
    		super(msg);
    	}
    }
    • RuntimeException 클래스를 상속받는 Bizexception 클래스 생성

    • 예외 역할을 하는 클래스의 경우 Exception 또는 RuntimeException 클래스를 상속받아야 한다.

    • 예외 클래스의 생성자의 매개변수
      - 기본 생성자 가능 (매개변수 없음)
      - 전송 메세지만 받기 가능
      - 예외클래스만 받기 가능
      - 전송 메세지, 예외클래스 모두 받기 가능

      package bizexception;
      
      public class Bizservice {
      	public void bizMethod(int a){
      		System.out.println("bizMethod 시작");
      		if(a < 0){
      			throw new Bizexception("매개변수 a는 0 보다 작을 수 없습니다");
      		}
      
      		System.out.println(a);
      		System.out.println("bizMethod 종료");
      	}
      }
    • Bizexception의 경우, RuntimeException의 자식 클래스이기에 bizMethod에서 throws 생략 가능

      package bizexception;
      
      public class Bizmain {
      	public static void main(String[] args) {
      		Bizservice bizservice = new Bizservice();
      		
      		try{
      			bizservice.bizMethod(10);
      			bizservice.bizMethod(-3);
      		}catch (Exception e){
      			System.out.println(e.toString());
      		}
      	}
      }
      
      /* 실행 결과
      bizMethod 시작
      10
      bizMethod 종료
      bizMethod 시작
      bizexception.Bizexception: 매개변수 a는 0 보다 작을 수 없습니다
      */

Section 2. Object 클래스

Chapter 1. Object 클래스와 메서드 오버라이딩

  • Object클래스는 모든 클래스의 최상위 클래스

  • 아무것도 상속받지 않으면 자동으로 Object를 상속

  • Object가 가지고 있는 메소드는 모든 클래스에서 다 사용할 수 있다는 것을 의미

  • 대표적인 Object 클래스의 부모 메서드

    • equals()
    • toString()
    • hashCode()

Section 3. java.lang 패키지

Chapter 1. java.lang 패키지와 Wrapper 클래스

  • java.lang 패키지에는 기본형타입을 객체로 변환시킬때 사용하는 Wrapper 클래스가 있다.
    • Boolean, Byte, Short, Integer, Long, Float, Double 클래스들을 묶어 Wrapper 클래스라 부른다.
  • Object 클래스
  • 문자열과 관련된 String, StringBuffer, StringBuilder
  • 화면에 값을 출력할때 사용했던 System클래스
  • 수학 관련 Math클래스
  • Thread 관련 클래스

Chapter 2. 오토박싱 / 오토언박싱

  • 과거에는 기본형과 Wrapper 클래스 사이의 형변환을 위해 메서드를 사용해야 했다.
  • 하지만 Java 언어가 발전하면서 이를 자동으로 처리해주고 있다.
  • 예시 코드
     public class WrapperExam {
          public static void main(String[] args) {
              int i = 5; 
              Integer i2 = new Integer(5); //과거 메서드를 통한 형변환
              Integer i3 = 5;     //오토박싱
              int i4 = i2.intValue(); //과거 메서드를 통한 형변환
              int i5 = i2;       //오토언박싱
              
              // 기본형 변수에서는 필드나 메서드를 가지고 있지 않기에 사용할 수 없다.
              //int j = 10;
              //int i6 = j.intValue();
              //long i7 = j.longValue();
          }
      }

Chapter 3. String, StringBuffer, StringBuilder

  • Java의 문자열 처리방식 3가지
  • 한번 값이 저장되면 불변 (immutable)
    • String
      • 한번 생성되면 할당된 메모리의 크기는 변하지 않는다.
      • 모든 문자열 내용 변경 시, 새로운 StringBuffer 객체 생성 & 문자열 내용 변경 & toString() 반환
        → String 내용 변경이 빈번하다면 메모리 낭비 유발
  • 하나의 객체에서 문자열 내용 변경 가능 (mutable) → 내부적으로 문자열을 배열로 관리하기에 가변 문자열로서 사용 가능
    • StringBuffer
      • 동기화(Synchronization) 지원 O → 멀티쓰레드 상황에서 효율적
    • StringBuilder
      • 동기화(Synchronization) 지원 X → 단일쓰레드 상황에서 효율적
    • StringBuffer와 StringBuilder의 주요 메서드
      • append(String str): 주어진 문자열을 현재 문자열의 끝에 추가합니다.
      • insert(int offset, String str): 지정된 위치에 문자열을 삽입합니다.
      • delete(int start, int end): 지정된 범위의 문자를 삭제합니다.
      • replace(int start, int end, String str): 지정된 범위의 문자를 주어진 문자열로 대체합니다.
      • reverse(): 현재 문자열을 반전시킵니다.
      • length(): 현재 문자열의 길이를 반환합니다.
      • toString(): StringBuffer 객체를 String으로 변환합니다.
  • String과 StringBuffer 속도 비교
    public class StringBufferPerformanceTest{
        public static void main(String[] args){
            // (1) String의 +연산을 이용해서 10,000개의 *를 이어붙입니다.
            //시작시간을 기록합니다.(millisecond단위)
            long startTime1 = System.currentTimeMillis();
            String str="";
            for(int i=0;i<10000;i++){
                str=str+"*";
            }
            //종료시간을 기록합니다.(millisecond단위)
            long endTime1 = System.currentTimeMillis();
            
            // (2) StringBuffer를 이용해서 10,000개의 *를 이어붙입니다.
            //시작시간을 기록합니다.(millisecond단위)                
            long startTime2 = System.currentTimeMillis();
            StringBuffer sb = new StringBuffer();
            for(int i=0;i<10000;i++){
                sb.append("*");
            }
            //종료시간을 기록합니다.(millisecond단위)
            long endTime2 = System.currentTimeMillis();
            
            // 방법(1)과 방법(2)가 걸린 시간을 비교합니다.
            long duration1 = endTime1-startTime1;
            long duration2 = endTime2-startTime2;
            
            System.out.println("String의 +연산을 이용한 경우 : "+ duration1);
            System.out.println("StringBuffer의 append()을 이용한 경우 : "+ duration2);
        }
    }
    /* 실행 결과
    String의 +연산을 이용한 경우 : 24
    StringBuffer의 append()을 이용한 경우 : 1
    */

🔥 앞으로 StringBuffer / StringBuilder 를 자주 사용하도록 하자!


Section 4. java.util 패키지

Chapter 1. java.util 패키지

  • Date, Calendar 등의 날짜와 관련된 클래스
  • 자료구조, 알고리즘에 많이 사용되는 컬렉션 프레임워크와 관련된 인터페이스 클래스가 있다.
    • List, Set, Collection, Map

Chapter 2. 컬렉션 프레임워크

  • Collection
    • 정보들의 중복 허용 | 순서 없음
    • Iterator : 정보들을 순서대로 꺼내기 위한 인터페이스
      • hasNext() : 꺼낼 값이 있는지 확인한다
      • next() : 다음 정보를 꺼낸다
  • List
    • 정보들의 중복 허용 | 순서 있음
    • get() : 특정 위치의 정보를 꺼낸다
  • Set
    • 정보들의 중복 금지 | 순서 없음
    • add()
      • 만약 기존에 존재하는 정보라면 false 반환
      • 없는 정보라면 정보 추가 후, true 반환
  • Map
    • 정보를 Key-Value 형태로 저장
    • 정보들의 순서 없음 | Key 값은 중복 금지
    • KeySet() : 모든 Key값을 Set 형태로 가져온다.
    • Put() : Key와 Value를 입력받아 저장한다.

Chapter 3. 제네릭

  • Generic을 사용함으로써 선언할때는 가상의 타입으로 선언하고, 사용시에는 구체적인 타입을 설정함으로써 다양한 타입의 클래스를 이용하는 클래스를 만들 수 있습니다.
  • Generic을 사용하는 대표적인 클래스는 컬렉션 프레임워크와 관련된 클래스입니다.
  • 예시 코드
    public class Box<E> { //클래스 선언부에 < > 를 활용
            private E obj; // 입력받은 제네릭으로 필드 타입 지정
            public void setObj(E obj){ // 입력받은 제네릭으로 매개변수 타입 지정
                this.obj = obj;
            }
    
            public E getObj(){ // 입력받은 제네릭으로 메서드 반환 타입 지정
                return obj;
            }
        }
    • 선언시의 제네릭 부분에는 명시되어있는 클래스가 들어갈 수 있다.

    • 가상클래스를 이용하고 싶다면 클래스로서 값을 가지지않는 제네렉명을 넣고
      - 변수 선언시에 제네릭을 지정해주면 된다.
      - 변수 선언시에 제네릭을 지정하지 않으면 자동으로 Object 클래스로 지정된다.

      public class BoxExam {
              public static void main(String[] args) {
                  Box<Object> box = new Box<>(); //필드 obj의 타입을 Object로 결정
                  box.setObj(new Object());
                  Object obj = box.getObj();
      
                  Box<String> box2 = new Box<>(); //필드 obj의 타입을 String으로 결정
                  box2.setObj("hello");
                  String str = box2.getObj();
                  System.out.println(str);
      
                  Box<Integer> box3 = new Box<>(); //필드 obj의 타입을 Integer로 결정
                  box3.setObj(1);
                  int value = (int)box3.getObj();
                  System.out.println(value);
                  
                  //제네릭 설정 안하면 자동으로 Object로 결정
                  Box box4 = new Box(); //필드 obj의 타입은 Object로 설정됨
                  box4.setObj("hello");
                  String str = box4.getObj();
                  System.out.println(str);
              }
          }

Chapter 4. Set

  • 예시 코드
    package collection0;
    
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
    
    public class Set0 {
    	public static void main(String[] args) {
    		Set<String> set = new HashSet<>();
    
    		set.add("cha");
    		set.add("jae");
    		set.add("hyeon");
    		//set.add("cha"); // 이미 set에 중복된 정보가 있기에 "cha"가 추가되지 않으며, false 반환
    
        System.out.println(set.size());
    
    		Iterator<String> itr = set.iterator();
    
    		while(itr.hasNext()){
    			System.out.println(itr.next());
    		}
    		// Iterator 변수에서 next()는 현재 값을 읽고, 다음으로 읽을값의 위치를 정합니다.
    		// Iterator 변수에서 hasNext()는 next()에 의해 결정된 다음으로 읽을값이 있는지를 확인합니다.
    
    		// while(set.iterator().hasNext()){ // 계속 새로운 Iterator 변수를 만들어내며, next()로 읽을 위치를 지정하지 않기에 무한 루프
    		// 	 System.out.println(itr.next()); // itr에서 다음에 읽을 수 있는 값이 없는데 읽으려고해서 NoSuchElementException 발생
    		// }
    
    		// while(itr.hasNext()){ // next()로 itr의 다음으로 읽을 값을 지정하지 않기에 무한 루프
    		// 	 System.out.println(set.iterator().next()); // 계속 새로운 Iterator 변수를 만들어내기에, 맨 처음 값만 출력
    		// }
    	}
    }
    • 위의 3가지 while문을 실험해본 결과, Set 타입의 변수의 정보를 읽어내기 위해서는
      • Iterator 타입 변수를 선언하여 사용해야 한다.
      • hasNext()로 다음으로 읽을 값이 있는지 확인해야 한다.
        • next()를 사용해야만 다음으로 읽을 값의 위치를 정할 수 있다.
  • 주요 메서드
    • add()
    • size()
    • iterator()
      • next()
      • hasNext()

Chapter 5. List

  • 예시 코드
    import java.util.ArrayList;
    import java.util.List;
    
    public class ListExam {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
    
            // list에 3개의 문자열을 저장합니다.
            list.add("kim");
            list.add("lee");
            list.add("kim");
    
            System.out.println(list.size()); //list에 저장된 자료의 수를 출력 (중복을 허용하므로 3 출력) 
            for(int i = 0; i < list.size(); i++){
                String str = list.get(i); //List는 순서를 가지고 있기에 특정 위치의 데이터 확인 가능
                System.out.println(str);
            }
        }   
    }
  • 주요 메서드
    • add()
    • size()
    • get()

Chapter 6. Map

  • 예시 코드
    import java.util.HashMap;
        import java.util.Iterator;
        import java.util.Map;
        import java.util.Set;   
        public class MapExam {  
            public static void main(String[] args) {
                // Key, Value가 모두 String 타입인 HashMap인스턴스를 만듭니다.
                Map<String, String> map = new HashMap<>();
    
                // key와 value값을 put으로 저장합니다.
                map.put("001", "kim");
                map.put("002", "lee");
                map.put("003", "choi");
                
                // 같은 key가 2개 있을 수 없습니다. 첫번째로 저장했던 001, kim은 001, kang으로 바뀐다.
                map.put("001", "kang");
    
                // map에 저장된 자료의 수를 출력합니다. 3이 출력됩니다.
                System.out.println(map.size());
    
                // 키가 001, 002, 003인 값을 꺼내 출력합니다.
                System.out.println(map.get("001"));
                System.out.println(map.get("002"));
                System.out.println(map.get("003"));
    
                // map에 저장된 모든 key들을 Set자료구조로 꺼냅니다.
                Set<String> keys = map.keySet(); //반환되는 key의 타입에 맞추어 Set의 제네릭 지정
                // Set자료구조에 있는 모든 key를 꺼내기 위하여 Iterator를 구합니다.
                Iterator<String> iter = keys.iterator();
                while (iter.hasNext()) {
                    // key를 꺼냅니다.
                    String key = iter.next();
                    // key에 해당하는 value를 꺼냅니다.
                    String value = map.get(key);
                    // key와 value를 출력합니다.
                    System.out.println(key + " : " + value);
                }
            }
        }
    • Map의 경우, 모든 key들을 확인하기 위해서는 다음 2가지 과정을 거쳐야 한다.
      • Set 자료구조로 한번 변환
      • Iterator로 Set의 정보 확인
  • 주요 메서드
    • put()
    • size()
    • get()
    • ketSet()
profile
Develop what? and why?

0개의 댓글