0. 공식 문서

Paths (Java Platform SE 8 )

 

Java Platform SE 8

 

docs.oracle.com

 

public final class Paths
extends Object
  • public final class
  • final이 붙어서 다른 클래스가 상속 및 변경할 수 없는 class이다.
  • 유틸리티(Utility) 목적으로만 사용됨을 명확하게 한다
    • 여러 클래스에서 공통적으로 사용되는 메서드를 모아서 클래스로 만든 것
    • Helper Class라고도 한다.

 

1. 개요

이 클래스는 경로 문자열 또는 URI를 변환하여 경로를 반환하는 정적 메서드로만 구성된다.

즉 정적 유틸리티 클래스이다.

 

2. 주요 메소드

Path 인터페이스를 직접 구현하는 클래스는 아니며, 주로 Paths.get()를 통해 Path 객체를 반환하는 역할을 한다.

0. 공식 문서

Path (Java Platform SE 8 )

 

Java Platform SE 8

 

docs.oracle.com

 

1. Superinterfaces

All Superinterfaces:

Comparable<Path>, Iterable<Path>, Watchable

public interface Path
extends Comparable<Path>, Iterable<Path>, Watchable

 

즉, Comparable, Iterabe, Watchable안에 Path인터페이스가 있다

 

 

superinterface in java

What is super-interface is java? what is the purpose of super-interface?

stackoverflow.com

 

2. 개요

Path 인터페이스는 파일 시스템에서 파일을 찾는 데 사용할 수 있는 객체를 나타낸다.

  • Path는 계층적인 구조를 가지며, 디렉터리 및 파일명 요소가 나열된다.
  • 각 요소는 특정 구분자(예: / 또는 \) 로 구분된다.
  • 루트 경로(파일 시스템 계층 구조를 나타내는 부분)가 포함될 수도 있다.
  • 가장 마지막에 위치한 요소는 파일 또는 디렉터리의 이름을 의미한다.
  • 나머지 요소들은 디렉터리 이름을 나타낸다.
  • 비어 있는 경로(empty path) 는 하나의 빈 이름 요소만 포함하는 경로를 의미하며, 이 경우 기본(default) 디렉터리를 가리칸다.

 

3. 주요 메소드

3.1 요소 접근 메소드

  • getFileName() → 파일 이름 반환
  • getParent() → 부모 디렉터리 경로 반환
  • getRoot() → 루트 디렉터리 반환
  • subpath() → 경로의 특정 부분(하위 경로) 반환

 

3.2 경로 결합/비교 메소드

  • resolve() 및 resolveSibling() → 다른 경로와 결합 가능
  • relativize() → 두 경로 간의 상대 경로 생성
  • startsWith() 및 endsWith() → 특정 경로로 시작하는지 또는 끝나는지 비교 가능

 

3.3 Watchable 인터페이스 확장

  • Path는 Watchable 인터페이스를 확장하여 파일 변경 사항을 감지할 수 있다.
  • 특정 경로의 디렉터리를 WatchService에 등록하면 파일 변경 이벤트 감지 가능 (예: 파일 생성, 삭제, 수정 감지).

 

4. Accessing Files

Paths는 Files 클래스와 함께 사용하면 파일 및 디렉터리 작업을 쉽게 수행할 수 있다.
예제 코드 (UTF-8 인코딩된 access.log 파일을 읽는 방법):

 

 

5. Interoperability

  • 기본 생성자로부터 생성된 Paths 객체는 java.io.File 클래스와 호환된다
  • 하지만 다른 생성자로부터 생성된 Paths 객체는 java.io.File과 호환되지 않을 수 있다.
  • toPath() → java.io.File 객체에서 Path 객체를 얻음.
  • toFile() → Path 객체를 java.io.File 객체로 변환.

 

6. Interoperability

이 인터페이스의 구현체는 불변(immutable)이며, 다중 스레드 환경에서도 안전하게 사용할 수 있다.

1. 예외

1.1 try-catch

  • try : 예외 발생이 예측되는 부분을 넣는다.
  • catch : 예외를 처리한다. 
    • catch는 자바 가상 머신에 의해 호출 된다.
    • try에서 발생한 에러 상황에 대한 정보를 담은 객체를 인자로 전달
    • 이때 객체는 데이터 타입이 Exception 클래스이다

 

1.2 다양한 예외와 다중 캐치

  • try에서 발생한 문제가 무엇이냐에 따라 각 catch에서 실행될 수 있게 분기하는 역할을 하는 것. 다중 catch
  • Exception은 모든 예외를 포괄하는 예외이다.
package org.opentutorials.javatutorials.exception;

class A {
	private int[] arr = new int[3];

	A() {
		arr[0] = 0;
		arr[1] = 10;
		arr[2] = 20;
	}

	public void z(int first, int second) {
		try {
			System.out.println(arr[first] / arr[second]);
		}
		catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("ArrayIndexOutOfBoundsException");
		}
		catch (ArithmeticException e) {
			System.out.println("ArithmeticException");
		}
		catch (Exception e) {
			System.out.println("Exception");
		}
	}
}

public class ExceptionDemo {
	public static void main(String[] args) {
		A a = new A();
		a.z(10, 0);
		a.z(1, 0);
		a.z(2, 1);
	}
}

 

1.3 finally

  • try/catch와 함께 올 수 있다
  • 언제나 try와 catch 뒤에 나와야한다. 
  • try에서 아무런 문제가 발생하지 않은 경우에도 finally 영역의 로직이 실행된다. 
    • 즉 try/catch 문의 결과와는 무관하게 언제나 실행되도록 약속된 로직
package org.opentutorials.javatutorials.exception;

class A {
	private int[] arr = new int[3];

	A() {
		arr[0] = 0;
		arr[1] = 10;
		arr[2] = 20;
	}

	public void z(int first, int second) {
		try {
			System.out.println(arr[first] / arr[second]);
		}
		catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("ArrayIndexOutOfBoundsException");
		}
		catch (ArithmeticException e) {
			System.out.println("ArithmeticException");
		}
		catch (Exception e) {
			System.out.println("Exception");
		}
		finally {
			System.out.println("finally");
		}
	}
}

public class ExceptionDemo {
	public static void main(String[] args) {
		A a = new A();
		a.z(10, 0);
		a.z(1, 0);
		a.z(2, 1);
	}
}

// ===== output =====
// ArrayIndexOutOfBoundsException
// finally
// ArithmeticException
// finally
// 2
// finally

 

 

2. 예외 던지기 : throws

  • throws : 다음 사용자에게 예외 처리 책임을 넘긴다.
package org.opentutorials.javatutorials.exception;

import java.io.*;

class B{
	void run() {
		BufferedReader bReader = null;
		String input = null;
		try {
			bReader = new BufferedReader(new FileReader("out.txt"));
		}catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		try {
			input = bReader.readLine();
		}catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(input);
	}
}

class C {
	void run() {
		B b = new B();
		b.run();
	}
}

public class ThrowExceptionDemo {
	public static void main(String[] args) {
		C c = new C();
		c.run();
	}
}

 

B에서 예외를 직접 처리하는 대신 B 클래스를 사용하는 C 클래스에게 예외 처리를 넘길 수 있다

package org.opentutorials.javatutorials.exception;

import java.io.*;

class B{
	void run() throws FileNotFoundException, IOException{
		BufferedReader bReader = null;
		String input = null;
		bReader = new BufferedReader(new FileReader("out.txt"));
		input = bReader.readLine();
		System.out.println(input);
	}
}

class C {
	void run() {
		B b = new B();
		try {
			b.run();
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}
}

public class ThrowExceptionDemo {
	public static void main(String[] args) {
		C c = new C();
		c.run();
	}
}

 

 

3. 예외 만들기

  • throw를 사용하여 예외 발생시키기
if (right == 0) {
	throw new IllegalArgumentException("두 번째 인자의 값은 0이 될 수 없습니다.")
}

 

예외 사용해야 할 상황
IllegalArgumentException 매개변수가 의도하지 않은 상황을 유발할 때
IllegalStateException 메소드를 호출할 수 있는 상태가 아닐 때
NullPointerException 매개변수 값이 null 일 때
IndexOutOfBoundsException 인덱스 매개변수 값이 범위를 벗어날 때
ArithmeticException 산술적인 연산에 오류가 있을 때

 

3.1 Throwable 클래스

  • Error : 자바 가상 머신/하드 웨어에 문제가 발생해서 더 이상 프로그램을 실행할 수 없는 상태. try/catch처리 필요 없음
  • Exception
    • unchecked exception : 부모 클래스로 RuntimeException이 있음. 예외 처리할 필요 없다.
    • checked exception : 부모 클래스로 RuntimeException이 없음. 반드시 예외 처리해야 한다.
  • 참고하면 좋을 글 : ☕ 자바 에러(Error) 와 예외 클래스(Exception) 💯 이해하기
 

☕ 자바 에러(Error) 와 예외 클래스(Exception) 💯 이해하기

프로그래밍의 오류 종류 프로그램에서 오류가 발생하면 시스템 레벨에서 프로그램에 문제를 야기하여 원치 않는 버그를 일으키거나, 심각하면 실행 중인 프로그램을 강제로 종료시키도 한다.

inpa.tistory.com

 

3.2 사용자 정의 예외

  • 검사 예외 or 비검사 예외 결정
    • 검사 예외 : 예외 처리를 강제 → 사용자가 예외 상황에서 복구/개선할 수 있는 경우
    • 비검사 예외 : 예외 처리 강제하지 않음 → 사용자가 할 수 있는 일이 없거나 종료시키는 것이 바람직한 경우
  • 비검사 예외로 만들기 위해서는 RuntimeException을 상속해야 한다. 
  • Exception을 상속할 경우 try/catch 또는 throws를 통해 예외 처리를 한다.

 

  • [DivideException에서 RuntimeException을 상속]
package org.opentutorials.javatutorials.exception;

class DivideException extends RuntimeException {
    DivideException() {
       super();
    }

    DivideException(String message) {
       super(message);    // 부모 클래스의 생성자를 호출할 때 메시지 전달
    }
}

class Calculator {
    int left, right;

    public void setOperands(int left, int right) {
       this.left = left;
       this.right = right;
    }

    public void divide() {
       if (this.right == 0) {
          throw new DivideException("0으로 나누는 것은 허용되지 않습니다.");
       }
       System.out.print(this.left/this.right);
    }
}

public class CalculatorDemo {
    public static void main(String[] args) {
       Calculator c1 = new Calculator();
       c1.setOperands(10, 0);
       c1.divide();
    }
}

 

  • [DivideException에서 Exception을 상속 : try/catch로 검사 예외를 처리]
package org.opentutorials.javatutorials.exception;

class DivideException extends Exception {
    DivideException() {
       super();
    }

    DivideException(String message) {
       super(message);    // 부모 클래스의 생성자를 호출할 때 메시지 전달
    }
}

class Calculator {
    int left, right;

    public void setOperands(int left, int right) {
       this.left = left;
       this.right = right;
    }

    public void divide() {
       try {
          if (this.right == 0) {
             throw new DivideException("0으로 나누는 것은 허용되지 않습니다.");
          }
          System.out.print(this.left/this.right);
       }
       catch(DivideException e) {
          e.printStackTrace();
       }
    }
}

public class CalculatorDemo {
    public static void main(String[] args) {
       Calculator c1 = new Calculator();
       c1.setOperands(10, 0);
       c1.divide();
    }
}

 

  • [DivideException에서 Exception을 상속 : try/catch로 검사 예외를 처리]
package org.opentutorials.javatutorials.exception;

class DivideException extends Exception {
    DivideException() {
       super();
    }

    DivideException(String message) {
       super(message);    // 부모 클래스의 생성자를 호출할 때 메시지 전달
    }
}

class Calculator {
    int left, right;

    public void setOperands(int left, int right) {
       this.left = left;
       this.right = right;
    }

    public void divide() throws DivideException{
       if (this.right == 0) {
          throw new DivideException("0으로 나누는 것은 허용되지 않습니다.");
       }
       System.out.print(this.left/this.right);
    }
}

public class CalculatorDemo {
    public static void main(String[] args) {
       Calculator c1 = new Calculator();
       c1.setOperands(10, 0);
       try {
          c1.divide();
       }
       catch (DivideException e) {
          e.printStackTrace();
       }
    }
}

 

 

☕ Exception Handling - 자바 예외를 처리하는 3가지 기법

Exception Handling 3가지 기법 자바의 예외를 try - catch 블럭으로 잡았다고 해서 끝이 아니다. 예외가 발생하였으면 코드를 수정하여 문제점을 고쳐야 되는 것은 맞지만, 예상할 수 없는 예외인 경우

inpa.tistory.com

 

 

4. Object 클래스

 

☕ 자바 Object 클래스와 메서드 오버라이딩

Object 클래스 모든 자바의 클래스는 Object 클래스로 부터 시작된다. 즉, Object 클래스가 모든 클래스의 조상 혹은 base 라고 할 수 있다. 사실 우리가 클래스 파일을 만들어 클래스명을 작성하면 자

inpa.tistory.com

 

4.1 toString

  • 인스턴스에 대한 정보를 문자열로 제공
  • 기본적으로 해당 인스턴스에 대한 정보와 주소(해시코드)를 문자열로 반환한다.
  • 오버라이딩을 통해 toString()의 출력 내용을 바꿀 수 있다
  • 인스턴스가 담긴 변수만 println() 메소드의 인자로 전달하면 내부적으로 toString() 메소드를 호출한다.
  • 실제로 toString() 메서드 내부를 본다면 다음과 같이 구현되어있다
    • return getClass().getName() + "@" + Integer.toHexString(hashCode());

 

4.2 equals

  • 어떤 두 개의 인스턴스가 서로 같은 인스턴스인지 비교
  • 객체 간의 동일성을 비교하고 싶을 때는 ==보다 equals() 메소드를 이용하자
  • equals() 메소드를 직접 구현해야 한다면 hashCode() 메소드도 함께 구현해야 한다.
  • 자식이 부모가 될 때는 자동으로 형변환 된다.
    • Object obj = s2
  • 부모가 자식이 될 때는 명시적으로 형변환해야 한다.
    • Student s = (Student)obj;

 

4.3 finalize

  • 객체가 소멸될 때 호출되기로 약속한 메소드
  • 사용할 일이 거의 없는 메소드이다. 사용을 권장하지 않는다.

 

4.4 clone

  • 객체를 복제한다.
  • 복제 가능한 객체라는 사실을 자바 가상 머신에 알려줘야 한다. 따라서 Cloneable이라는 인터페이스를 구현한다.
  • 그러나 Cloneable은 비어있는 인터페이스로 클래스가 복제 가능함을 자바 가상머신에게 알려주기 위한 구분자에 불과하다.
  •  clone() 메소드의 접근 제어자는 protected이기 때문에 반드시 메소드를 public 접근 제어자로 오버라이딩해야 한다.
  • clone() 메소드는 Exception을 상속해서 예외 처리가 필수적이다.

 

 

5. enum

  • enum은 열거형이라고도 한다.
  • 열거형은 서로 연관된 상수의 집합이다.
package org.opentutorials.javatutorials.constant;

enum Fruit {
    APPLE, PEACH, BANANA;
}

enum Company {
    GOOGLE, APPLE, ORACLE;
}

public class ConstantDemo {
    public static void main(String[] args) {
       Fruit type = Fruit.APPLE;
       switch (type) {
          case APPLE:
             System.out.println(57 + " kcal");
             break;
          case PEACH:
             System.out.println(34 + " kcal");
             break;
          case BANANA:
             System.out.println(93 + " kcal");
             break;
       }
    }
}

 

  • enum은 사실상 클래스이므로 생성자를 가질 수 있다.
  • 열거형에 포함된 상수를 values() 메소드를 통해 상수가 담긴 배열을 가져올 수 있다.

 

 

6. 참조

  • 데이터 타입을 생성할 때 new를 통해 생성하는 것들은 기본 데이터 타입이 아니고 참조형 또는 참조 데이터 타입이다. 
package org.opentutorials.javatutorials.reference;

class A {
    public int id;

    A(int id) {
       this.id     = id;
    }
}

public class ReferenceParameterDemo {
    static void _value(int b) {
       b = 2;
    }

    public static void runValue() {
       int a = 1;
       _value(a);
       System.out.println("runvalue, " + a);
    }

    static void _reference1(A b) {
       b = new A(2);
    }

    public static void runReference1() {
       A a = new A(1);
       _reference1(a);
       System.out.println("runReference1, " + a.id);
    }

    static void _reference2(A b) {
       b.id = 2;
    }

    public static void runReference2() {
       A a = new A(1);
       _reference2(a);
       System.out.println("runReference2, " + a.id);
    }

    public static void main(String[] args) {
       runValue();    // runValue, 1
       runReference1();   // runReference1, 1
       runReference2();   // runReference2, 2
    }
}

 

 

7. 제네릭

  • 정의 : 클래스 내부에서 사용할 데이터 타입을 인스턴스를 생성할 때 확정하는 것
  • 제네릭에는 참조형 데이터 타입만 가능하다.
  • 기본 데이터 타입은 제네릭을 사용하려면 래퍼(wrapper) 클래스를 사용한다
 

☕ 자바 Wrapper 클래스와 Boxing & UnBoxing 총정리

래퍼 클래스 (Wrapper Class) 이전 강의글에서 우리는 자바의 자료형은 기본 타입(primitive type)과 참조 타입(reference type) 으로 나누어지고 각 특징에 대해 알아보았다. 프로그래밍을 하다 보면 기본 타

inpa.tistory.com

package org.opentutorials.javatutorials.generic;

import java.net.Inet4Address;

class EmployeeInfo {
    public int rank;

    EmployeeInfo(int rank) {
       this.rank = rank;
    }
}

class Person<T, S> {
    public T info;
    public S id;

    Person(T info, S id) {
       this.info = info;
       this.id = id;
    }
}

public class GenericDemo {
    public static void main(String[] args) {
       EmployeeInfo e = new EmployeeInfo(1);
       Integer i = 10;

       Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);
       
       // 제네릭을 명시적으로 언급하지 않고 생략할 수 있다.
       // Person<EmployeeInfo, Integer> p1 = new (e, i);
    }
}

 

7.1 제네릭의 제한

  • extends 키워드를 이용해 특정 데이터 타입만 오도록 할 수 있다.
package org.opentutorials.javatutorials.generic;

abstract class Info {
    public abstract int getLevel();
}

class EmployeeInfo extends Info {
    public int rank;

    EmployeeInfo(int rank) {
       this.rank = rank;
    }

    public int getLevel() {
       return this.rank;
    }
}

class Person<T extends Info> {  // Info 클래스의 자식만 T로 올 수 있다
    public T info;

    Person(T info) {
       this.info = info;
    }
}

public class GenericDemo {
    public static void main(String[] args) {
       Person p1 = new Person(new EmployeeInfo(1));

       // String은 Info의 자식이 아니기 때문에 에러 발생
       // Person<String> p2 = new Person<String>("부장");
    }
}

 

 

8. 컬렉션 프레임워크

 

🧱 Java Collections Framework 종류 💯 총정리

Java Collection Framework 자바 새내기분들은 컬렉션 프레임워크라는 단어에 뭔가 거창하고 어려운 느낌이 들수 있겠지만, 그냥 자료 구조(Data Structure) 종류의 형태들을 자바 클래스로 구현한 모음집

inpa.tistory.com

 

8.1 ArrayList

  • 배열의 문제점 : 몇 개의 값을 가질 수 있는지 지정하게 되어 있다.
  • 해결책 : ArrayList를 사용한다.
    • 몇 개의 값을 담을 수 있는지 지정할 필요가 없다.
    •  java.util.ArrayList를 import해야 한다.
    • add() 메소드를 호출해서 데이터를 추가할 수 있다.
    • get() 메소드를 호출해서 값을 가져올 수 있다.
    • add() 메소드의 매개변수 타입이 Object이다. 따라서 변수에 담으려고 할 때 원하는 데이터 타입으로 강제 형변환 또는 제네릭을 통해 지정해야 한다.

 

8.2 컬렉션 프레임워크 구성

출처 : 🧱 Java Collections Framework 종류 💯 총정리

 

8.3 List / Set

  • List : 추가하는 모든 값이 들어간다.
    • Set과 달리 순서가 존재하기 때문에 인덱스와 관련된 API를 가지고 있다. 
  • Set : 중복되는 값은 들어가지 않고 고유한 값만 저장된다.
    • Collection 인터페이스를 상속받고 있지만 내용이 없다. 즉 Collection 인터페이스와 Set 인터페이스는 동일한 API를 제공한다.
    • Set의 특징은 순서에 상관없이 값이 지정되어 있다는 것이다. 따라서 순서를 기반으로 작업이 실행되는 메소드는 없다.

 

8.4 Iterator

  • 반복자 : 컨테이너에 담긴 값을 하나씩 꺼내서 처리할 수 있게 한다.
  • iterator() 메소드는 Iterator 타입의 객체를 반환한다. 이때 인스턴스에 담긴 값은 참조 값에 불과해서 원본 데이터까지 삭제되지 않는다. 
  • hasNext(), next(), remove() 3개의 메소드를 포함한다.

 

8.5 Map

  • key 값은 중복될 수 없지만 value의 값은 중복될 수 있다.
package org.opentutorials.javatutorials.collection;

import java.util.*;

public class MapDemo {
    public static void main(String[] args) {
       HashMap<String , Integer> a = new HashMap<String, Integer>();
       a.put("one", 1);
       a.put("two", 2);
       a.put("three", 3);
       a.put("four", 4);

       System.out.println(a.get("one"));
       System.out.println(a.get("two"));
       System.out.println(a.get("three"));

       iteratorUsingForEach(a);
       iteratorUsingIterator(a);
    }

	// 방법 1
    static void    iteratorUsingForEach(HashMap map) {
       Set<Map.Entry<String, Integer>> entries = map.entrySet();
       for (Map.Entry<String, Integer> entry : entries) {
          System.out.println(entry.getKey() + " : " + entry.getValue());
       }
    }

	// 방법 2 
    static void    iteratorUsingIterator(HashMap map) {
       Set<Map.Entry<String, Integer>> entries = map.entrySet();
       Iterator<Map.Entry<String, Integer>> i = entries.iterator();
       while (i.hasNext()) {
          Map.Entry<String, Integer> entry = i.next();
          System.out.println(entry.getKey() + " : " + entry.getValue());
       }
    }
}

1. 클래스패스

  • 클래스패스 : 클래스가 위치하는 경로를 지정해 자바가 필요로 하는 어떤 클래스를 로드할 수 있는 방법을 지정하는 것
  • 환경 변수 : 자바 애플리케이션을 실행할 때 사용하는 클래스가 컴퓨터 상에서 어디에 위치하는가를 지정하는 것

클래스패스(ClassPath). 자바 개발자라면 반드시 알아야 할 자바 클래스패스 개념과 사용법 | by Ageofsys | Medium

 

클래스패스(ClassPath)

자바 개발자라면 반드시 알아야 할 자바 클래스패스 개념과 사용법

medium.com

 

  • -classpath의 옵션을 사용하지 않을 경우/환경변수 설정이 되어있지 않을 경우 현재 디렉토리가 기본 클래스 패스로 설정된다.
  • -classpath 옵션 사용 시 마침표(".")현재 디렉토리를 추가한다.
  • 세미콜론(";")을 통해 클래스패스 경로를 구분한다.

 

 

2. 패키지

  • 클래스를 모아둔 자바의 디렉토리
  • 패키지는 클래스의 이름 충돌 문제를 해결한다.
    • 서로 다른 패키지에 들어있는 클래스들은 이름 충돌 문제가 발생하지 않는다.
  • 자바는 다른 패키지에 존재하는 클래스를 로드할 수 없다.
  • 따라서 이때 import 키워드 사용하여 명시적으로 로드를 한다. 
  • *(와일드카드) : 모든 것을 의미한다. 즉 모든 클래스를 로드한다는 기호

 

 

3. API와 API 문서 보는 법

  • 인터페이스 : 시스템과 시스템을 제어하는 사람이나 주체 사이의 접점에 해당하는 일종의 조작 방법
  • 계층적인 관계이 접점이 되는 부분

 

3.1 API 문서 보는 법

 

Overview (Java SE 22 & JDK 22)

JavaScript is disabled on your browser. This document is divided into two sections: Java SE The Java Platform, Standard Edition (Java SE) APIs define the core Java platform for general-purpose computing. These APIs are in modules whose names start with jav

docs.oracle.com

 

 

4. 접근 제어자

  • 정의 : 객체에 속한 멤버를 사용하는 입장에서 누가 이것을 사용할 수 있는가에 대한 권한을 부여하는 역할
  • public : 언제나 접근 가능
  • private : 같은 클래스에서만 접근 가능
  • 예시 코드
package org.opentutorials.javatutorials.accessmodifier;

class A{
	public String y(){
		return "public void y()";
	}

	private String z() {
		return "public void z()";
	}

	public String x() {
		return z();	// 같은 클래스 내부에서는 접근 가능
	}
}

public class AccessDemo {
	public static void main(String[] args) {
		A a = new A();

		System.out.println(a.y());
		// System.out.println(a.z());
		System.out.println(a.x());
	}
}

 

4.1 세밀한 제어

  public protected default private
같은 패키지, 같은 클래스 접근 가능 접근 가능 접근 가능 접근 가능
같은 패키지, 상속 관계 접근 가능 접근 가능 접근 가능 접근 불가
같은 패키지, 상속 관계 아님 접근 가능 접근 가능 접근 가능 접근 불가
다른 패키지, 상속 클래스 접근 가능 접근 가능 접근 불가 접근 불가
다른 패키지, 상속 관계 아님 접근 가능 접근 불가 접근 불가 접근 불가
  • public : 모든 클래스에서 접근 가능
  • private : 같은 클래스에서만 접근 가능
  • protected : 같은 패키지 내에서/다른 패키지에서 상속 시 접근 가능
  • default : 같은 패키지 내에서 접근 가능

 

4.2 클래스 접근 제어자

  • default : 같은 패키지에서만 접근 가능
  • public : 모든 패키지에서 접근 가능
    • public 으로 지정된 클래스와 소스코드의 이름이 일치해야 한다. 
    • 하나의 소스코드 안에는 public 클래스가 단 하나만 있을 수 있다. 

 

4.3 public vs static

 

[JAVA]static vs public

자바 static과 public 선언자를 어떤 경우에 쓰는 지 알 수 있었다.

velog.io

 

 

5. Abstract

  • abstract클래스를 상속해서 사용하도록 강제하는 키워드이다. 
  • 주의!
    1. 추상 클래스는 반드시 그것을 상속받아 사용해야 한다. 즉 main메소드에서 직접 접근이 불가능하다.
    2. 추상 메소드를 사용하려면 메소드를 사용하는 쪽에서 반드시 오버라이딩하여 구체적인 로직을 정의해야 한다.
    3. abstract가 지정된 메소드는 로직을 가지고 있으면 안된다.
    4. 클래스의 멤버 중 하나라도 abstract 키워드가 지정되어 있으면 해당 멤버를 담고 있는 클래스도 추상클래스가 된다.
abstract class A {
	// 추상 메소드
    public abstract int b();
    
    // 본체가 있는 메소드는 abstract 키워드를 가질 수 없다.
    public abstract int c(){System.out.println("Hello");}
    
    // 추상 클래스 내에서는 추상 메소드가 아닌 메소드가 존재할 수 있다.
    public void d(){
    	System.out.println("world");
    }
}

 

 

6. final

  • final을 지정하면 바꿀 수 없는 상수가 된다.

 

 

7. 인터페이스

7.1 역할 및 사용 이유

  • 역할 : 특정 클래스에서 특정한 인터페이스를 사용한다면 그 클래스가 반드시 해당 인터페이스에 포함된 메소드를 구현하도록 강제한다
  • 사용 이유 : 어떤 클래스가 어떤 멤버(메소드)를 가지고 있는지 명세서와 같은 역할을 한다. 인터페이스에 명시된 대로 구현을 해야하기 때문에 커뮤니케이션의 착오를 방지한다.
// 인터페이스
interface I() {
	public void z();
}

// A 클래스는 I 인터페이스를 '구현한다'
class A implements I {
	public void z() {
    	// 구현이 되어 있지 않기 때문에 컴파일 에러
    }
}

 

7.2 특징

  • 인터페이스를 선언할 때 인터페이스에 정의되는 멤버의 접근 제어자반드시 public이어야 한다. 
  • 인터페이스는 한 번에 여러 개를 상속할 수 있다.
  • 인터페이스끼리도 상속이 되고 상속된 인터페이스는 부모 인터페이스가 가진 기능그대로 자식들이 갖게 된다.
interface I1 {
	public void x();
}

interface I2 {
	public void z();
}

// 한 번에 여러 개의 인터페이스를 상속받을 수 있다.
class A implements I1, I2 {
	public void x() {
    }
    
    public void z() {
    }
}

interface I3 {
	public void x();
}

// 인터페이스도 상속이 된다 
interface I4 extends I3 {
	public void z();
}

// 부모 인터페이스가 가진 기능도 구현해야 한다
class B implements I4 {
	public void x() {
    }
    
    public void z() {
    }
}

 

 

8. 다형성

  • 정의 : 한 메소드나 클래스가 다양한 방식으로 동작하는 것

 

8.1 클래스와 다형성

  • B 클래스를 인스턴스화한 obj 변수가 현재 A 클래스 행세를 한다.
    • A obj = new B();
  • 자식 클래스에서 오버라이딩한 메소드의 내용이 실행된다
    • A 클래스에서 구현된 내용("A.x")이 출력되지 않는다
  • 부모 클래스에는 없지만 자식 클래스에 있는 메소드를 실행하려고 하면 부모 클래스에서 정의된 바가 없기존재하지 않는 메소드가 된다.
    • y() 메소드는 실행되지 않는다
package org.opentutorials.javatutorials.polymorphism;

class A {
	public String x(){
		return "A.x";
	}
}

class B extends A {
	public String x(){
		return "B.x";
	}

	public String y(){
		return "y";
	}
}

class B2 extends A {
	public String x() {
		return "B2.x";
	}
}

public class PolymorphismDemo {
	public static void main(String[] args) {
		A obj = new B();
		A obj2 = new B2();
		System.out.println(obj.x());	// B.x
		System.out.println(obj2.x());	// B2.x
		// System.out.println(obj.y()); -> error!
	}
}

 

8.2 인터페이스와 다형성

  • 어떤 클래스가 어떤 인터페이스를 구현하고 있다면 그 클래스의 인스턴스는 그 인터페이스일 수 있다.
  • 어떤 클래스의 데이터 타입이 특정한 인터페이스일 경우 해당 클래스에서 특정 인터페이스에서 정의한 멤버만을 가진 클래스인 것처럼 사용할 수 있다.
package org.opentutorials.javatutorials.polymorphism;

interface I2{
	public String A();
}

interface I3{
	public String B();
}

class D  implements I2, I3 {
	public String A() {
		return "A";
	}

	public String B() {
		return "B";
	}
}

public class PolymorphismDemo {
	public static void main(String[] args) {
		D obj = new D();
		I2 objI2 = new D();
		I3 objI3 =  new D();
		
		obj.A();
		obj.B();
		
		objI2.A();
		// objI2.B(); error!

		// objI3.A(); error!
		objI3.B();
	}
}

1. 객체 지향 프로그래밍(OOP)

  • 객체 : 서로 연관된 기능별로 그룹화된 단위
  • 추상화 : 해결해야 할 문제 또는 반영해야 할 현실을 소프트웨어 방식으로 단순화하는 행위
  • 소프트웨어 설계 : 복잡한 현실을 추상화하는 과정

 

  • 부품화 : 좋은 객체를 만든다는 것은 로직을 재활용할 수 있게 부품화 하는 것
  • 은닉화, 캡슐화 : 내부의 동작방법을 객체에 숨기고 사용자에게는 그 부품의 사용법만 노출하는 것을 정보 은닉화/캡슐화
  • 인터페이스 : 서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면

 

2. 클래스와 인스턴스, 그리고 객체

  • 클래스 : 객체를 만들기 위한 일종의 설계도 
    • static 키워드가 붙음
  • 인스턴스 : 그 설계도에 따라서 만든 구체적인 제품
    • static 이 붙지 않음

 

  • 인스턴스 메소드 클래스 멤버 접근 가능
    • 인스턴스 메소드는 인스턴스에 속한 메소들로서 static이라는 키워드가 지정되지 않은 메소드이다.
    • 인스턴스 메소드 안에서 클래스 변수와 클래스 메소드에 접근할 수 있다
  • 클래스 메소드 인스턴스 멤버 접근 불가능
    • 클래스라는 것은 언제나 메소드보다 먼저 존재한다. 왜냐하면 설계도를 만들고 이 설계도에 따라 만들어진 구체적인 제품인 인스턴스를 나중에 만들기 때문.
    • 클래스 메소드는 그 클래스를 기반으로 만들어진 인스턴스에 접근하는 것이기 때문에 아직 생성되지 않은 인스턴스에 접근하는 것이다. 
    • 어떤 변수에 인스턴스가 담겨 있는지 조차도 클래스 안에서는 알 수 없다.

 

3. 유효 범위

package org.opentutorials.javatutorials.scope;

class C{
    int v = 10;
    void m() {
       int v = 20;
       System.out.println(v);
       System.out.println(this.v);
    }
}

public class ScopeDemo {
    public static void main(String[] args) {
       C c1 = new C();
       c1.m();
    }
}

// 출력 결과
// 20
// 10
  • this : 인스턴스의 인스턴스 자신을 의미하는 값

 

4. 상속과 생성자

  • 상속받은 하위 클래스의 인스턴스가 생성될 때 상속한 상위 클래스의 기본 생성자를 자동으로 호출한다. 
  • 만약 부모 클래스에서 매개변수가 있는 생성자를 선언하면 자동으로 기본생성자가 생성되지 않아서 오류 발생

 

4.1 super

  • super : 부모 클래스 의미
  • super() : 부모 클래스생성자
  • 주의!  : 하위 클래스의 초기화 코드를 super보다 먼저 등장시키면 안된다. 

 

5. 오버라이딩

  • 상속은 부모 클래스에 어떠한 기능을 추가만 하는 경우
  • 오버라이딩은 부모 클래스의 메소드를 그대로 쓰지 않고 자식 클래스의 필요에 따라 메소드를 재정의. 이때 부모 클래스의 메소드는 무시된다. 

 

5.1 오버라이딩 주의사항

  • 부모 클래스의 메소드 형식과 자식 클래스의 메소드 형식이 불일치하는 경우 오버라이딩할 수 없다. 즉 오버라딩하기 위해서는 부모 클래스와 자식 클래스의 반환 타입이 일치해야한다. 
  • 메소드의 이름이 같아야한다
  • 매개변수 개수와 반환 타입이 같아야한다

 

5.2 오버라이딩 조건 (메소드의 서명=signature) 

  1. 메소드 이름
  2. 메소드 매개변수의 개수와 데이터 타입, 순서
  3. 메소드의 반환 타입
  • Tip! 자식 메소드에서 부모 메소드를 호출할 때는 super키워드를 사용한다.

 

6. 오버로딩

  • 정의 : 클래스에 메소드를 정의할 때 이름이 같지만 서로 다른 매개변수 형식을 지닌 메소드를 여러 개 정의할 수 있는 방법
  • 규칙 : 메소드의 이름과 반환값은 같아야하 하지만 매개변수는 달라야한다. 

1. 상수의 데이터 타입

  • double a = 2.2 (O)
  • float a = 2.2  -> Error!
  • float a = 2.2F (O)

결론 : 실수는 double 형의 데이터 타입을 기본적으로 채택한다. double이 아닌 float으로 바꾸고 싶다면 상수에 F를 붙인다. 

 

마찬가지로 int형의 범위를 넘어가는 숫자를 자료형 long을 통해서 나타내고 싶다면...

  • int a = 2147483648 -> Error! : int 자료형의 범위를 넘어간다
  • long a = 2147483648 -> Error! : long 으로 명시적으로 바꿔주지 않았다.
  • long a = 2147483648L (O) : 상수에 L을 붙인다

 

2. 형 변환

정의 : 어떤 데이터의 형식을 바꾸다. 

 

☕ JAVA 타입 형변환 원리 & 방법 - 완벽 정리

타입 변환 (Type Conversion) 하나의 타입을 다른 타입으로 바꾸는 것을 타입 변환 혹은 형변환 이라고 한다. 예를 들어 byte 타입을 int 타입으로 변환하거나 반대로 int 타입을 byte 타입으로 변환하는

inpa.tistory.com

 

 

2.1 자동 형 변환

  • double a = 3.0F
    • 좌변 a : double형
    • 우변 3.0 : F를 통해 float으로 변환

 

Q. 양 변의 데이터 타입이 다른데 에러가 발생하지 않는 이유는?

A. 3.0F라는 상수를 double형으로 자동으로 형 변환

  • float과 double 중 double형이 더 많은 정보를 표현/수용할 수 있다.
  • 따라서 float 형에서 double형으로 형 변환이 일어나더라도 데이터 손실이 없다. 

 

Q. 그렇다면 반대로 double을 float으로 자동으로 형 변환할 수 있을까?

A. 불가능하다

  • float a = 3.0 -> Error!
  • float 보다 double이 더 많은 데이터를 표현할 수 있기 때문에 데이터가 유실될 가능성이 있다. 

 

결론 : 자동 형 변환의 원칙표현 범위가 좁은 데이터 타입에서 넓은 데이터 타입으로만 허용한다. 

2.1.1 예시

		int a = 3;
		float b = 1.0F;
		double c = a + b;

		System.out.println(c);
  1. a+b를 진행한다. int 와 float의 덧셈이므로 우선 형을 같도록 형 변환을 한다. 즉 int가  float형으로 변환된다. 따라서 a+b = 4.0F
  2. 4.0F를 c에 대입한다. c의 데이터 타입은 double형이고 double형에 float형을 대입하면 float형은 double형으로 형 변환이 일어난다.

 

2.2 명시적 형 변환

정의 : 수동으로 직접 형 변환

  • float a = (float)100.0;
  • int b = (int)100.0F;

 

3. 연산자

3.1 연산자의 우선순위

우선순위 연산자 결합 방향
1 [ ]  ()  . ->
2 ++  --  +(양수)  -(음수)  ~  !  (type)  new <-
3 *  /  % ->
4 +(더하기)  -(빼기)  +(문자열 결합 연산자) ->
5 <<  >>  >>> ->
6 <  <=  >  >=  instanceof ->
7 ==  != ->
8 &  & ->
9 ^  ^ ->
10 |  | ->
11 && ->
12 || ->
13 ?  : <-
14 =  *=  /=  +=  -=  %=  <<=  >>= >>>=  &=  ^=  != <-

 

3.2 비교와 불린

  • 주의! 문자열을 비교할 때는 ==(동등 비교 연산자)를 사용하는 것이 아니라 .equals()라는 메소드를 사용해야 한다. 

 

4. 입력과 출력

package org.opentutorials.javatutorials.io;

public class InputDemo {
	public static void main(String[] args) {
		System.out.println(args.length);
	}
}
  • String[] : 문자열로 구선된 배열을 의미
  • args : 문자열 데이터 타입을 담을 수 있는 배열

 

4.1 시작할 때 입력

Run Configuration 클릭
Input 작성

 

4.2 실행 중에 입력

:자바에서 기본적으로 제공하는 라이브러리 가운데 Scanner라는 라이브러리를 이용해 실행중에 입력을 받는다.

package org.opentutorials.javatutorials.io;

// Scanner를 사용하려면 애플리케이션에 로드해야 한다.
// java.util 안에 있는 Scanner라는 객체를 사용하겠다
import java.util.Scanner;

public class InputDemo {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int i = sc.nextInt();	// 실행을 멈추고 입력을 기다리는 상태가 된다
		System.out.println(i*1000);
		sc.close();
	}
}

글로벌 시대에 당연히 알고리즘의 영어 이름도 알아야겠다고 생각해서 검색해보았다. 그런데, 한국어로 검색했을 때는 대부분 트리 지름, 트리의 지름으로 키워드가 검색되었는데 영어로 검색했을 때는 Diameter of Binary Tree 였다. 트리의 지름 유형에서는 어쩌면 당연한 이야기일지도?

트리의 지름이란?

트리의 지름이란, 트리에서 임의의 두 점 사이의 거리 중 가장 긴 것을 말한다. 

그런데 왜 지름이라고 하는 거지? 가장 긴 트리 사이의 경로라던가

백준 1967번 문제의 설명을 보면 좀 더 직관적으로 받아들일 수 있다.

친절한 설명과 시각 자료..

 

풀이: 탐색 2번

1. 이진 트리가 있다.
2. 임의의 노드 p에서 DFS를 실행하여 p로부터 가장 먼 거리의 노드 a를 구한다. 
3. 노드 a에서 DFS를 다시 실행하여 a로부터 가장 먼 거리의 노드 b를 구한다.
4.  a→b 의 거리가 트리의 지름이다.

 

증명

(가정) 트리에서 $dis(u, v)$의 경로가 트리의 지름이다. 임의의 노드 x에서 가장 먼 노드는 y이다.

 

이때 케이스를 나눠보자. 

$i.$ x가 u 또는 v인 경우
$ii.$ y가 u 또는 v인 경우
$iii.$ x, y, u, v가 서로 다른 경우

 

Case1. x가 u 또는 v인 경우

가정에 따라 x에서 가장 먼 노드인 y까지의 거리는 트리의 지름이다. 

왜냐하면 x가 이미 트리 지름의 한 쪽 끝이기 때문에 dfs를 돌릴 경우 다른 트리의 끝 점으로 가는 것이 당연하다. 

 

Case2. y가 u 또는 v인 경우

가정에 따라 x에서 가장 먼 노드인 y까지의 거리는 트리의 지름이다.

 

Case3. x, y, u, v가 서로 다른 경우

이 경우 다시 케이스를 두 가지로 나눌 수 있다.

 

$dis(u, v)$의 경로와 $dis(x, y)$의 경로가 한 점 이상 공유하는 경우

t는 여러 노드가 합쳐진 경로일 수 있지만 편의상 압축하여 한 노드로 표현

 

x에서 DFS를 수행할 경우 가정에 따라 가장 먼 노드는 y이다. 

가장 먼 노드가 y라는 것은 $dis(x, t)$값은 고정되어 있으므로 d1, d2, d3를 비교하였을 때 최대값이 d3라는 의미이다.

 

이때, $d1 \neq d2$를 가정해보자.

$i.$ $d1 > d2$ 인 경우

우리는 위에서 $d3$가 최대임을 알아냈으므로 $d3 \geq d1 > d2$가 성립한다. 

그런데 가정에 따라 $dis(u, v)$가 트리 지름이므로 $d1 + d2 > d1 + d3$임을 알 수 있다.

양변의 $d1$을 소거할 경우 $d2 > d3$이고 이는 위의 $d3 \geq d1 > d2$와 모순이다. 

 

$ii.$ $d1 < d2$ 인 경우

마찬가지로 우리는 위에서 $d3$가 최대임을 알아냈으므로 $d3 \geq d2 > d1$가 성립한다. 

그런데 가정에 따라 $dis(v, u)$가 트리 지름이므로 $d2 + d1 > d2 + d3$임을 알 수 있다.

양변의 $d2$을 소거할 경우 $d1 > d3$이고 이는 위의 $d3 \geq d2 > d1$와 모순이다. 

 

따라서 귀류법을 통해 $d1 = d2 = d3$임을 알 수 있다.

이미 방문한 노드 x는 고려하지 않는다

따라서 이후 y에서 두 번째 dfs를 수행할 경우 y에서 가장 먼 정점까지의 거리를 구하면 2d로 트리의 지름과 같아지게 된다.

즉즉 트리의 지름을 구할 수 있게 된다. 

 

$dis(u, v)$의 경로와 $dis(x, y)$의 경로가 한 점 이상 공유하지 않는 경우

x에서 DFS를 수행한다. 이때 가장 먼 노드가 y이려면

$dis(x, a)$가 고정이므로 $dis(a, y) \geq dis(a, b) + dis(b, v)$여야 한다. 

 

그럼 노드 u에서 가장 먼 정점을 찾아보자.

$dis(u, b)$가 고정이므로 $dis(b, v) \geq dis(b, a) + dis(a, y)$여야 한다. (by. 가정)

 

이때 두 부등식을 더해보면

$dis(a, y) + dis(b, v) \geq dis(a, b) + dis(b, v) + dis(b, a) + dis(a, y)$

 

양변을 소거해보면

$0 \ge dis(a, b) + dis(b, a)$

이므로 모순이다.

 

Case3의 결론 :$dis(u, v)$의 경로와 $dis(x, y)$의 경로가 최소 한 점 이상 공유한다. 그리고 이때 귀류법에 의해 겹치는 경로에서부터 y, u, v까지의 거리가 모두 같고 이때 두 번째 dfs를 통해 트리의 지름을 구할 수 있다. 

 

백준(BOJ) 문제

[백준 1967 | C++] 트리의 지름 :: 트리의 지름

 

[백준 1967 | C++] 트리의 지름 :: 트리의 지름

문제 링크 : 1967번: 트리의 지름관련 태그 : 트리, 그래프, 깊이우선탐색참고 자료 : 2025.01.05 - [STUDY/Algorithm] - [Algorithm] Diameter of Binary Tree : 트리의 지름 [알고리즘] Diameter of Binary Tree : 트리의 지

yeonee911.tistory.com

 

2025.01.06 - [BOJ] - [백준 1167 | C++] 트리의 지름 :: 트리의 지름

 

[백준 1167 | C++] 트리의 지름 :: 트리의 지름

문제 링크 : 1167번: 트리의 지름관련 태그 : 그래프 이론, 그래프 탐색, 트리, 깊이 우선 탐색참고 자료 : 2025.01.05 - [STUDY/Algorithm] - [알고리즘] Diameter of Tree : 트리의 지름 [알고리즘] Diameter of Binary

yeonee911.tistory.com

 

참고 자료 및 사이트

3.2 좋은 코드를 짜기 위한 원칙

1. 간결한 코드를 작성하기

2. 적극적으로 코드 재사용

  • 같은 코드가 반복된다면 함수나 클래스로 분리

3. 표준 라이브러리 공부하기

4. 항상 같은 형태로 프로그램을 작성하기

5. 일관적이고 명료한 명명법 사용하기

  • 변수명, 함수명
  • 네이밍 컨벤션

6. 모든 자료를 정규화해서 저장하기

  • 같은 자료를 두 가지 형태로 저장하지 않기
  • 예시) 유리수를 저장할 때 항상 약분해서 기약 분수의 형태로 저장하기.
    • 9/6을 표현하는 변수와 3/2를 표현하는 변수가 따로 존재하게 된다

7. 코드와 데이터를 분리하기

  • 예시) 직접 코드에 저장하는 대신 배열로 선언
  • 체스의 나이트가 움직일 수 있는 상대 좌표 여덟 가지를 배열로 선언하고 배열을 순회하기
const int knightDx[8] = {2, 2, -2, -2, 1, 1, -1, -1};
const int knightDy[8] = {1, -1,  1, -1, 2, 2, 2, -2};

-> 특히 좌표에서 상하좌우를 순서대로 검사할 때 많이 쓴 듯

 

3.3 자주 하는 실수

1. 산술 오버플로

2. 배열 범위 밖 원소에 접근

: 진짜 화남

 

3. 일관되지 않은 범위 표현 방식 사용하기

  • 반 열린구간을 많이 사용한다. == [lo, hi)
  • C++ STL에서는 반복자로 범위를 표현할 때 첫 원소를 가리키는 반복자와 마지막 원소 다음 위치를 가리키는 반복자를
    • 예시) begin() : 첫 번째 원소, end() : 마지막 원소 다음에 있는 가상의 원소를 가리킴

4. Off-by-one 오류

  • 계산의 큰 줄기는 맞지만 하나가 모자라거나 하나가 많아서 틀리는 코드
  • 방지하는 법 : 최소 입력이 주어졌을 때 코드가 어떻게 동작할지 되새겨 보면서 짜기

5. 컴파일러가 잡아주지 못하는 상수 오타

6. 스택 오버플로

  • 주로 재귀 호출 깊이가 너무 깊어져서 발생

7. 다차원 배열 인덱스 순서 바꿔 쓰기

: 진짜 화남. i랑 j랑 잘못 쓴 게 한 두번이 아님

 

8. 잘못된 비교 함수 작성

9. 최소, 최대 예외 잘못 다루기

  • 가능한 입력 중 최소 값과 최대 값이 예외가 되는 문제들이 많다
  • 예시) 소수판정의 1과 2

10. 연산자 우선순위 잘못 쓰기

  • 예시) if (b & 1 == 0)
    • 예상 작동 순서 : if ((b & 1) == 0) -> b의 최하 비트가 0일 때 조건문 참
    • 실제 작동 순서 : if (b & (1 == 0)) -> 항상 조건문이 거짓이 된다

11. 너무 느린 입출력 방식 선택

  •  cin 같은 고수준 입출력 방식은 저수준 방식보다 두 배 이상 느리다
  • cin_sync_with_stdio(false); 를 수행하여 의 표준 입출력 함수들과의 동기화를 끄면 훨씬 빨라짐

12. 변수 초기화 문제

 

3.5 변수 범위의 이해

산술 오버플로

  • 너무 큰 결과
    • 32bit자료형을 넘어가면 64bit 쓰기
    • 최종 결과 값은 64bit이나 중간 계산 값을 32bit 사용
  • 너무 큰 중간 값
    • 프로그램의 출력 값의 범위는 작지만 중간 과정에서 큰 값을 일시적으로 계산해야 하는 경우
  • 너무 큰 '무한대' 값
    • 최소 거리를 찾는 문제 중, 두 지점 사이의 길이 없는 경우 등고 같이 무한대에 가까운 큰 값을 이용하는게 이득일때가 있다.
    • 문제는 해당 자료형이 처리할 수 있는 가장 큰 값을 무한대 값으로 쓰는 경우. 예를 들어  $2^{31}-1$을 사용할 경우 2를 더하면 오버플로우 발생
    • 따라서 무한대 값을 선택할 때 이 무한대 값들이 서로 더해지거나 곱해지는 경우가 없는지 잘 살펴보고 오버플로가 나지 않을 크기의 값을 선택.
    • Tip! 987654321 애용. 

 

오버플로 피해가기

  1. 더 큰 자료형 쓰기
  2. 오버플로가 나지 않도록 연산의 순서 바꾸기

 

자료형의 프로모션

프로모션이란? 피연산자의 자료형이 다르거나 자료형의 범위가 너무 작은 경우 컴파일러가 같은 자료형으로 변환하여 계산

  1. 정수형&&실수형 : 정수형이 실수형으로 변한다.
  2. 정수형&&정수형 || 실수형&&실수형 : 보다 넓은 범위를 갖는 자료형으로 변환
  3. 양쪽 다 int형보다 작은 정수형인 경우 : 양쪽 다 int형으로 변환
  4. 부호 없는 정수형과 부호 있는 정수형이 섞여 있을 경우 : 부호 없는 정수형으로 변환

 

3.6 실수 자료형의 이해

실수를 다루게 되면 무한의 세계로 날아가게 됩니다.

실수 표기법

  • 이진수로 실수를 표기
  • 부동 소수점 표기법
  • 무한대, 비정규 수 등의 특수한 값이 존재

 

실수 비교하기

컴퓨터에서 실수 연산을 할 때 원하는 값이 나오지 않는 이유는 컴퓨터가 실수를 근사적으로 표현하기 때문이다.

두 가지 해결방법 : 비교할 실수의 크기들에 비례한 오차 한도를 정한다

1. 같다고 판단해야 할 큰 값 두 개를 비교할 경우

  • 오차 한도 값이 |a-b|보다 항상 커야한다.
  • 즉 |a-b|의 상한을 오차 한도 값으로 쓴다.
  • 이 변수들이 가질 수 있는 최대 값의 $\frac{1}{10^{10}} \sim  \frac{1}{10^{12}}$ 정도를 취한다.

 

2. 다르게 판단해야하는 작은 값 두 개를 비교할 경우

  • 오차 한도 값이 |a-b|보다 항상 작아야 한다.
  • 즉 |a-b|의 하한을 오차 한도 값으로 쓴다.
  • 그러나 객관적인 기준은 없다. -> 상대 오차를 이용한다. 

 

+ 상대 오차를 이용한다

비교하는 숫자들의 크기에 비례하여 오차를 정해야 한다.

상대 오차의 정의 : $relativeError(a, b) = \frac{|a-b|}{max(|a|,|b|)}$

  • 두 수의 상대 오차가 일정 범위 이하면 같은 수로 판정한다
  • 실질적인 오차 허용 범위는 a, b가 커지면 커질수록 커진다.
// 절대 오차와 상대 오차를 모두 이용해서 두 수가 같은지 판정
bool doubleEqual(double a, double b){
	double diff = fabs(a - b);
    // 절대 오차가 허용 범위 안일 경우 무조건 true 반환
    if(diff < 1e-10)	return true;
    // 이 외의 경우에는 상대 오차를 사용
    return diff <= 1e-8 * max(fabs(a), fabs(b));
}

절대 오차와 상대 오차를 모두 이용한 실수 비교

 

대소 비교

예시 상황) a=b가 되어야 할 두 수의 계산에서, 오차 때문에 a가 약간 더 작은 값으로 계산되었다. 그럼 a<b는 원래 거짓이어야했는데 참이 된다. 

 

결론 : 실수 연산 아예 하지 않기

실수 연산을 제대로 하는 가장 좋은 방법은 아예 실수 연산을 하지 않는 것이다. 

실수 연산이 필요해 보이면 문제에 변형을 가해 실수 연산을 없애자

  • 곱셈과 나눗셈 순서 바꾸기
  • 양변 제곱하기
  • 실수 좌표를 써야 하는 기하 문제에서 좌표계를 가로 세로로 정수배 늘리면 정수만을 이용할 수도 있다

 

'STUDY > 종만북 마스터' 카테고리의 다른 글

[종만북 마스터] 2 문제 해결 개관  (3) 2025.01.01

2.2 문제 해결 과정

1. 문제를 읽고 이해한다.

2. 문제를 익숙한 용어로 재정의한다. = 문제의 재정의와 추상화

3. 어떻게 해결할지 계획을 세운다.

4. 계획을 검증한다.

5. 프로그램으로 구현한다.

6. 어떻게 풀었는지 돌아보고, 개선할 방법이 있는지 찾아본다.

 

2.3 문제 해결 전략

체계적인 접근을 위한 질문들

1. 비슷한 문제를 풀어본 적이 있던가?

  • 여기서 말하는 '비슷하다' 라는 것은 문제의 목표 혹은 유형이 같다는 것이다. 이미 풀어본 경험이 있는 것이나 마찬가지
  • 동작 과정과 원리를 완전히 이해하고 있어야 한다.

읽자마자 떠오르는 유명한 말. 암기가 되어야 응용이 가능하다. 그리고 실제로 잘하는 사람들의 생각을 훔쳐보면, 처음에 문제를 풀기 전 딱 하는 말이, "아, 이거 --한 문제랑 비슷하네/똑같네." 이런식으로 말한다.

 

 

2. 단순한 방법에서 시작할 수 없을까?

  • 무식하게 풀 수 있을까? == 완전 탐색으로 가능한가?
  • 효과1 : 설령 단순한 방법으로 풀리지 않는다고 하더라도 좀 더 효율 적인 자료구조를 사용하거나, 계산과정을 줄이면서 최적화를 적용해서 알고리즘을 개선할 수 있다.
  • 효과2 : 알고리즘 효율성의 기준선을 정해주는 효과 있음. 새로운 알고리즘이 얼마나 개선되었는지 재는 용도로 유용

 

3. 내가 문제를 푸는 과정을 수식화할 수 있을까?

  • 손으로 여러 간단한 입력을 직접 해결해보자. 

 

4. 문제를 단순화할 수 없을까?

  • 문제의 제약 조건을 없애거나, 변수의 수를 줄이거나, 다차원의 문제를 1차원으로 줄이는 등
  • 예를 들어 2차원 위의 점을 찾는 문제를 x, y축으로 각각 1차원으로 나누어 두 부분 문제의 합으로 구한다. 

 

5. 그림으로 그려볼 수 있을까?

 

6. 수식으로 표현할 수 있을까?

 

7. 문제를 분해할 수 있을까?

  • (예시) 제약 조건을 분해
  • 한 개의 복잡한 조건보다 여러 개의 단순한 조건이 다루기 쉽다
  • and, or 연산자 등을 이용해서 제약 조건을 분해 (best[j] < worst[i])&&(best[i] < worst[j])

 

8. 뒤에서부터 생각해서 문제를 풀 수 있을까?

  • 문제에 내재된 순서를 바꾼다.
  • (예시) A에서 B를 가는 방법은 찾기 어렵지만 B에서 A로 가는 방법을 찾기는 쉬운 경우

 

9. 순서를 강제할 수 있을까?

  • 순서가 없는 문제에 순서를 강제해서 문제 풀기
  • (예시) 5*5 크기의 격자의 불을 켜는 문제
    • 첫 줄을 정한다면 두 번째 줄부터는 자연스럽게 불을 켜야할 지 안 켜야할 지 정해짐. 즉, 전체 25칸을 불을켜야할지 결정(2^25)하는게 아니라 첫째 줄 2^5의 경우의 수만 보면 됨.
    • 여기서 순서를 강제한 포인트는 가장 윗 줄의 왼쪽부터 순서대로 보기로 강제

 

10. 특정 형태의 답만을 고려할 수 있을까? == 정규화 기법

  • 정규화 : 우리가 고려해야 할 답들 중 형태가 다르지만 결과적으로는 똑같은 것들을 그룹으로 묶은 뒤, 각 그룹의 대표들만을 고려하는 방법

 

 


CCW의 활용편이라고 할 수 있는 선분 교차 판별

 

먼저 CCW의 개념 이해하기는 필수!!

[알고리즘] CCW(Counter Clock Wise) (tistory.com)

 

[알고리즘] CCW(Counter Clock Wise)

CCW는 3개의 점 A, B, C가 있을 때 이 세 개의 점을 이은 직선의 방향을 판별할 때 유용한 기하 알고리즘이다. 외적의 결과 값이 음수이면 시게 방향, 0이면 직선, 양수이면 반시계 방향이다. 11758번:

yeonee911.tistory.com

 

문제 상황 : 두 선분이 주어졌을 때 두 선분은 교차하는가?

 

벡터의 외적값은 선분의 점을 어떻게 잡느냐에 따라 부호가 바뀔 수 있다. 따라서 한 벡터의 부호, 예를 들어 벡터v0의 부호를 기억하는 것이 아니라 세트가 되는 두 벡터(v0과 v1, v2와 v3)의 곱의 결과에 따라 판별하는 것이 중요하다.   

 

보통 결론만 외우고 끝인 경우가 많은데, 그럼 금방 잊어버릴 것 같다!

 

해당 결론이 나오게 된 원리를 정확히 알아두면 오래오래 기억나고 좋을 듯.. 그리고 무엇보다 결론을 까먹었을 때 원리만 이해하고 있다면 결론을 스스로 도출해낼 수 있을 것이라고 생각한다. 

 

기하적 의미를 살펴보면 해당 알고리즘이 나오게 된 경위를 더 쉽게 이해할 수 있다. 

 

두 선분이 교차하지 않는 경우도 기하적 의미를 따져보면 금방 이해할 수 있다. 


17386번: 선분 교차 1 (acmicpc.net) (골3)
17387번: 선분 교차 2 (acmicpc.net) (골2)

27718번: 선분 교차 EX (acmicpc.net) (플5) 

+ Recent posts