Welcome! Everything is fine.

[Java/Study] 김영한의 실전 자바 중급 1편 - 스터디 7회차 본문

Java

[Java/Study] 김영한의 실전 자바 중급 1편 - 스터디 7회차

개발곰발 2024. 11. 30.
728x90

 

인프런 강의 <김영한의 실전 자바 - 중급 1편>을 보고 정리한 내용입니다.

매주 모여 각자 정리한 내용을 기반으로 발표하고 질문 공유하는 스터디입니다.


📘String 클래스

String 클래스란?

  • String 클래스 : 문자열을 편리하게 다룰 수 있는 클래스. 직접 char[]에 문자를 하나하나 넣어서 문자를 다루는 것은 불편하기 때문에 String 클래스를 제공해 더 편리하게 문자열을 사용할 수 있다.

String 클래스에서 문자열을 생성하는 방법에 1) 쌍따옴표를 사용하거나, 2) 객체를 생성하는 방법이 있다. String은 클래스, 즉 참조형이기 때문에 참조값만 들어갈 수 있다. 그러나 문자열은 매우 자주 사용되기 때문에 자바에서는 1)번 방법을 사용하더라도 2)번으로 변경해준다. 같은 이유로, 참조형이지만 편의상 + 연산을 사용할 수 있다.

String str1 = "hello"; 
String str1 = new String("hello");

String 클래스 구조

String 클래스의 구조는 대략 다음과 같다. char[] 혹은 byte[]에 실제 문자열 값이 보관되고, 그런 문자열을 다룰 수 있는 다양한 기능을 제공한다.

public final class String {
    //문자열 보관
    private final char[] value;// 자바 9 이전
    private final byte[] value;// 자바 9 이후
    
    //여러 메서드
    public String concat(String str) {...}
    public int length() {...}
    ...
}

 

✔️ String 클래스 주요 메서드

  • length() : 문자열의 길이를 반환한다.
  • charAt(int index) : 특정 인덱스의 문자를 반환한다.
  • substring(int beginIndex, int endIndex) : 문자열의 부분 문자열을 반환한다.
  • indexOf(String str) : 특정 문자열이 시작되는 인덱스를 반환한다.
  • toLowerCase() , toUpperCase() : 문자열을 소문자 또는 대문자로 변환한다.
  • trim() : 문자열 양 끝의 공백을 제거한다.
  • concat(String str) : 문자열을 더한다.

동일성과 동등성

다음과 같이 str1와 str2 두 변수가 각각 new string()을 사용해 인스턴스를 생성했다고 할 때, 두 변수는 같을까?

String str1 = new String("hello");
String str2 = new String("hello");

 

str1과 str2는 동일성 비교(==)에는 실패하고, 동등성 비교(equals())에는 성공한다고 말할 수 있다. 동일성과 동등성이란 다음과 같이 설명할 수 있다.

  • 동일성(Identity): == 연산자를 사용해서 두 객체의 참조가 동일한 객체를 가리키고 있는지 확인(물리적으로 같은지)
  • 동등성(Equality): equals() 메서드를 사용하여 두 객체가 논리적으로 같은지 확인

즉, str1과 str2는 서로 다른 인스턴스이므로(다른 참조값을 가리키고 있으므로) 동일성 비교에 실패하는 것이다. 대신 둘 다 내부에 "hello"라는 값을 가지고 있어 논리적으로 같기 때문에 동등성 비교에 성공하는 것이다. 참고로 String 클래스는 내부 문자열 값을 비교하도록 equals() 메서드를 재정의해두었다. 이 부분은 지난 주에 정리를 하면서 스스로 궁금증이 생겨 포스팅한 내용이다. String 클래스의 equals() 메서드는 단순히 == 으로 비교하지 않고, 보다 복잡하게 비교한다.

 

그렇다면 다음과 같이 둘 다 문자열 리터럴을 사용하는 경우에는 어떨까? 이때는 == 비교와 equals() 비교 모두 true가 된다.

String str3 = "hello";
String str4 = "hello";

 

이렇게 문자열 리터럴을 사용할 때는 자바가 메모리 효율성과 성능 최적화를 위해 문자열 풀을 사용한다.

 

✔️ 문자열 풀?

  • 필요한 String 인스턴스를 미리 만들어두는 곳
  • 여러 곳에서 재사용함으로써 성능과 메모리 최적화
  • 힙 영역을 사용
  • 해시 알고리즘을 사용해 빠르게 String 인스턴스를 찾을 수 있음

str3과 str4를 비교하려고 할 때, 자바가 실행되는 시점에 다음과 같은 일이 일어난다.

 

1) 클래스에 문자열 리터럴이 있을 경우 문자열 풀에 String 인스턴스 생성

2) 문자열 풀에서 "hello"라는 문자를 가진 String 인스턴스 탐색 후 찾은 인스턴스의 참조 반환

3) str4 역시 같은 문자열이므로 str3과 같은 참조 사용

 

이 과정을 거쳐 str3과 str4는 같은 문자를 사용하기 때문에 성능을 최적화할 수 있다. 또한 같은 참조값을 가지므로 equals() 비교뿐만 아니라 == 비교도 성공한다.

 

하지만, 개발을 할 때 만나는 수많은 String 인스턴스가 new String()으로 만들어진 것인지, 문자열 리터럴로 만들어진 것인지 하나하나 확인하기는 어렵다. 개발자가 여러명이라면 더 그렇다. 

 

⭐ 따라서, 문자열 비교는 항상 equals() 비교를 사용해서 동등성 비교를 해야한다!

불변 객체

String은 불변 객체이므로, 변경이 필요하면 기존 값을 변경하는 대신 새로운 결과를 만들어 반환한다.

String 클래스 주요 메서드

✔️ 문자열 정보 조회

  • length() : 문자열의 길이를 반환한다.
  • isEmpty() : 문자열이 비어 있는지 확인한다. (길이가 0)
  • isBlank() : 문자열이 비어 있는지 확인한다. (길이가 0이거나 공백(Whitespace)만 있는 경우), 자바 11
  • charAt(int index) : 지정된 인덱스에 있는 문자를 반환한다.

✔️  문자열 비교

  • equals(Object anObject) : 두 문자열이 동일한지 비교한다.
  • equalsIgnoreCase(String anotherString) : 두 문자열을 대소문자 구분 없이 비교한다.
  • compareTo(String anotherString) : 두 문자열을 사전 순으로 비교한다.
  • compareToIgnoreCase(String str) : 두 문자열을 대소문자 구분 없이 사전적으로 비교한다.
  • startsWith(String prefix) : 문자열이 특정 접두사로 시작하는지 확인한다.
  • endsWith(String suffix) : 문자열이 특정 접미사로 끝나는지 확인한다.

✔️  문자열 검색

  • contains(CharSequence s) : 문자열이 특정 문자열을 포함하고 있는지 확인한다.
  • indexOf(String ch) / indexOf(String ch, int fromIndex) : 문자열이 처음 등장하는 위치를 반환한다.
  • lastIndexOf(String ch) : 문자열이 마지막으로 등장하는 위치를 반환한다.

✔️  문자열 조작 및 변환

  • substring(int beginIndex) / substring(int beginIndex, int endIndex) : 문자열의 부분 문자열을 반환한다.
  • concat(String str) : 문자열의 끝에 다른 문자열을 붙인다.
  • replace(CharSequence target, CharSequence replacement) : 특정 문자열을 새 문자열로 대체한다.
  • replaceAll(String regex, String replacement) : 문자열에서 정규 표현식과 일치하는 부분을 새 문자열로 대체한다.
  • replaceFirst(String regex, String replacement) : 문자열에서 정규 표현식과 일치하는 첫 번째 부분을 새 문자열로 대체한다.
  • toLowerCase() / toUpperCase() : 문자열을 소문자나 대문자로 변환한다.
  • trim() : 문자열 양쪽 끝의 공백을 제거한다. 단순 Whitespace 만 제거할 수 있다.
  • strip() : Whitespace 와 유니코드 공백을 포함해서 제거한다.

✔️  문자열 분할 및 조합

  • split(String regex) : 문자열을 정규 표현식을 기준으로 분할한다.
  • join(CharSequence delimiter, CharSequence... elements) : 주어진 구분자로 여러 문자열을 결합한다.

✔️  기타 유틸리티

  • valueOf(Object obj) : 다양한 타입을 문자열로 변환한다.
  • toCharArray() : 문자열을 문자 배열로 변환한다.
  • format(String format, Object... args) : 형식 문자열과 인자를 사용하여 새로운 문자열을 생성한다.
  • matches(String regex) : 문자열이 주어진 정규 표현식과 일치하는지 확인한다.

StringBuilder

  • StringBuilder : 자바에서 제공하는 가변 String으로, 내부에 final이 아닌 변경할 수 있는 byte[]을 가지고 있다.

불변인 String은 내부 값을 변경할 수 없기 때문에 문자를 변경하려고 할 때마다 계속 새로운 객체를 생성해야한다는 단점이 있다. 새로운 객체를 만들면 그만큼 메모리를 더 많이 사용하게 된다. 이런 문제를 해결하기 위해서 자바는 성능과 메모리 사용면에서 더 효율적인 StringBuilder를 제공한다. StringBuilder 는 보통 문자열을 변경하는 동안만 사용하다가 문자열 변경이 끝나면 안전한(불변) String으로 변환하는 것이 좋다.

String 최적화

자바 컴파일러는 다음과 같이 문자열 리터럴을 더하는 부분을 자동으로 합쳐준다.

String helloWorld = "Hello, " + "World!"; // 컴파일 전
String helloWorld = "Hello, World!"; // 컴파일 후

 

문자열 변수의 경우, 자바 버전에 따라 다른 방식으로 최적화를 수행한다.

String result = str1 + str2;

 

다음과 같이 StringBuilder를 사용할 수도 있고, 자바 9부터는 StringConcatFactory를 사용해 최적화를 한다.

String result = new StringBuilder().append(str1).append(str2).toString();

 

하지만 반복문에서 문자열 연산이 일어날 경우, 컴파일러는 얼마나 많은 반복이 일어날지 알 수 없다. 따라서 이런 상황에서는 직접 StringBuilder를 사용하면 된다.

 

✔️StringBuilder를 직접 사용하는 것이 더 좋은 경우

  • 반복문에서 반복해서 문자를 연결할 때
  • 조건문을 통해 동적으로 문자열을 조합할 때
  • 복잡한 문자열의 특정 부분을 변경해야 할 때
  • 매우 긴 대용량 문자열을 다룰 때

메서드 체이닝

  • 메서드 체이닝(Method Chaining) : 여러 메서드를 하나의 연속된 줄로 호출할 수 있도록 메서드가 객체 자신을 반환하는 방식

✔️ 메서드 체이닝의 동작 원리

  • 메서드는 객체 자신(this)을 반환
  • 반환된 객체에서 다시 다른 메서드 호출 가능
  •  결과적으로 여러 메서드 호출을 한 줄로 이어서 쓰기 가능

다음과 같은 클래스가 있다고 해보자. 여기서 add() 함수는 value에 addValue를 더한 값 자체를 반환하는 것이 아니라, 객체 자신을 반환하고 있다.

public class ValueAdder {
    private int value;

    public ValueAdder add(int addValue) {
        value += addValue;
        return this; // 객체 자신 반환
    }

    public int getValue() {
        return value;
    }
}

 

그리고나서 다음과 같이 메서드를 계속 연결해서 사용하는 것이 가능하다. 메서드를 호출하고 반환된 참조값을 즉시 사용해서 바로 메서드를 호출할 수 있는 것이다. 메서드 체이닝 기법은 코드를 간결하고 읽기 쉽게 만들어준다.

public static void main(String[] args) {
    ValueAdder adder = new ValueAdder();
    int result = adder.add(1).add(2).add(3).getValue();

    System.out.println("result = " + result);
}

📘래퍼, Class 클래스

기본형의 한계

기본형은 한계가 있다.

  • 객체가 아니므로 객체 프로그래밍의 장점을 살릴 수 없다.
  • null 값을 가질 수 없다. 

기본형인 int는 그저 데이터 조각이기 때문에 그 자체에서 다양한 기능을 사용할 수는 없다. 그래서 아래 코드와 같이 int를 클래스로 만들어 더 유용하게 사용할 수 있다. 이렇게 특정 기본형을 감싸서 만드는 클래스를 래퍼 클래스라 한다.

public class MyInteger {
    private final int value;

    public MyInteger(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public int compareTo(int target) { // 인스턴스에 소속된 메서드로 만듦
        if (value < target) {
            return -1;
        } else if (value > target) {
            return 1;
        } else {
            return 0;
        }
    }

    @Override
    public String toString() {
        return String.valueOf(value);
    }
}

 

MyInteger 클래스는 기본형 변수인 int value를 갖고, value를 편리하게 사용하기 위한 다양한 메서드를 제공한다. 이 중에서도 compareTo() 메서드는 들어오는 숫자와 자기 자신을 비교할 수 있다. 이런식으로 기본형을 클래스로 감싸면서 기본형을 객체로 다룰 수 있게 되었다.

 

우리가 '데이터가 없음'이라는 값을 표현하기 위해서 null 이라는 값이 필요할 때가 있다. 하지마 기본형은 항상 값이 있어야하기 때문에 보통 -1 또는 0을 사용한다. 하지만 상황에 따라 -1이 정말 -1을 뜻하는지, 데이터가 없다는걸 뜻하는지 명확하지 않다. 따라서 다음과 같이 래퍼 클래스를 통해 null 값을 표현할 수 있다.

public class MyIntegerNullMain1 {
    public static void main(String[] args) {
        MyInteger[] intArr = {new MyInteger(-1), new MyInteger(0), new MyInteger(1)};
        System.out.println(findValue(intArr, -1)); // -1
        System.out.println(findValue(intArr, 0));
        System.out.println(findValue(intArr, 1));
        System.out.println(findValue(intArr, 100)); // null
    }

    private static MyInteger findValue(MyInteger[] intArr, int target) {
        for (MyInteger myInteger : intArr) {
            if (myInteger.getValue() == target) {
                return myInteger;
            }
        }
        return null;
    }
}

래퍼 클래스

자바는 다음과 같이 기본형에 대응하는 래퍼 클래스를 기본으로 제공한다. 래퍼 클래스는 기본형의 객체 버전이라고 할 수 있다. 래퍼 클래스는 불변이며, 객체이기 때문에 equals()로 비교해야 한다.

  • byte → Byte  
  • short → Short  
  • int → Integer  
  • long → Long  
  • float → Float  
  • double → Double  
  • char → Character  
  • boolean → Boolean  

래퍼 클래스의 사용법은 대략 다음과 같다.

public static void main(String[] args) {
    Integer newInteger = new Integer(10); // 미래에 삭제 예정, 대신에 valueOf() 사용
    Integer integerObj = Integer.valueOf(10); // -128 ~ 127 자주 사용하는 숫자 값 재사용, 불변
    Long longObj = Long.valueOf(100);
    Double doubleObj = Double.valueOf(10.5);
    System.out.println("newInteger = " + newInteger);
    System.out.println("integerObj = " + integerObj);
    System.out.println("longObj = " + longObj);
    System.out.println("doubleObj = " + doubleObj);

    System.out.println("내부 값 읽기");
    int intValue = integerObj.intValue();
    System.out.println("intValue = " + intValue);
    long longValue = longObj.longValue();
    System.out.println("longValue = " + longValue);

    System.out.println("비교");
    System.out.println("==: " + (newInteger == integerObj));
    System.out.println("equals:" + (newInteger.equals(integerObj)));
}

 

✔️ 박싱(Boxing)과 언박싱(Unboxing)

  • 박싱(Boxing) : 기본형을 래퍼 클래스로 변경하는 것 - valueOf()
  • 언박싱(Unboxing) : 래퍼 클래스에 들어있는 기본형 값을 다시 꺼내는 것 - xxxValue()

오토 박싱 /  오토 언박싱

개발자들이 박싱/언박싱을 많이 사용하다보니, 이 기능을 더 편리하게 사용할 수 있도록 자바 1.5부터 오토 박싱과 오토 언박싱을 지원한다. 말 그대로 컴파일러가 자동으로 박싱/언박싱을 해줘서 기본형과 래퍼형을 서로 편리하게 변환할 수 있다. 즉, 컴파일러가 valueOf(), xxxValue() 등의 코드를 추가해준다.

public static void main(String[] args) {
    // Primitive -> Wrapper
    int value = 7;
    // Integer boxedValue = Integer.valueOf(value);
    Integer boxedValue = value; // 오토 박싱(Auto-boxing)

    // Wrapper -> Primitive
    // int unboxedValue = boxedValue.intValue();
    int unboxedValue = boxedValue; // 오토 언박싱(Auto-unboxing)

    System.out.println("boxedValue = " + boxedValue);
    System.out.println("unboxedValue = " + unboxedValue);
}

래퍼 클래스 주요 메서드와 성능

✔️ 래퍼 클래스 주요 메서드

  • valueOf() : 래퍼 타입을 반환한다. 숫자, 문자열을 모두 지원한다.
  • parseInt() : 문자열을 기본형으로 변환한다. 각 타입에 parseXxx() 가 존재한다.
  • compareTo() : 내 값과 인수로 넘어온 값을 비교한다. 내 값이 크면 1 , 같으면 0 , 내 값이 작으면 -1 을 반환한다.
  • Integer.sum() , Integer.min() , Integer.max() : static 메서드이다. 간단한 덧셈, 작은 값, 큰 값 연산을 수행한다.

✔️ 기본형 vs 래퍼형 성능 차이

⭐ 결론부터 말하자면, 기본형 연산이 래퍼 클래스보다 빠르다!

 

다음과 같이 기본형 long에 더할 때와 래퍼형 Long에 더할 때를 각각 나누어 테스트 하면, 기본형 연산이 더 빠르다는 것을 알 수 있다. 래퍼 클래스는 자바에서 객체 자체를 다루는데 필요한 객체 메타데이터를 포함하기 때문에 더 많은 메모리를 사용한다.

public static void main(String[] args) {
    int iterations = 1_000_000_000; // 반복 횟수 설정, 10억
    long startTime, endTime;

    // 기본형 long 사용
    long sumPrimitive = 0;
    startTime = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
        sumPrimitive += i;
    }
    endTime = System.currentTimeMillis();
    System.out.println("sumPrimitive = " + sumPrimitive);
    System.out.println("기본 자료형 long 실행 시간: " + (endTime - startTime) + "ms");

    // 래퍼 클래스 Long 사용
    Long sumWrapper = 0L;
    startTime = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
        sumWrapper += i; // 오토 박싱 발생
    }
    endTime = System.currentTimeMillis();
    System.out.println("sumWrapper = " + sumWrapper);
    System.out.println("래퍼 클래스 Long 실행 시간: " + (endTime - startTime) + "ms");
}

실행 결과

 

하지만 CPU 연산이 매우 많은 경우가 아닌 일반적인 경우라면 유지보수하기에 더 나은 것을 선택하는 것이 좋다. 강의에서는 유지보수와 최적화 중, 유지보수하기 좋은 코드를 먼저 고민해야 한다고 말하고 있다. 개발 이후 성능 테스트를 해보고 문제가 되는 부분을 찾아 최적화를 하는 것이 권장된다.

Class 클래스

  • Class 클래스 : 클래스의 정보(메타데이터)를 다루는데 사용되는 클래스
    • 타입 정보 얻기: 클래스의 이름, 슈퍼클래스, 인터페이스, 접근 제한자 등과 같은 정보 조회
    • 리플렉션: 클래스에 정의된 메서드, 필드, 생성자 등 조회, 이들을 통해 객체 인스턴스를 생성하거나 메서드를 호출하는 등의 작업
    • 동적 로딩과 생성: Class.forName() 메서드를 사용하여 클래스를 동적으로 로드, newInstance() 메서드를 통해 새로운 인스턴스 생성
    • 애노테이션 처리: 클래스에 적용된 애노테이션(annotation) 조회 및 처리 기능 제공
    public static void main(String[] args) throws Exception {
        // Class 조회
        Class clazz = String.class; // 1. 클래스에서 조회
       // Class clazz1 = new String().getClass(); // 2.인스턴스에서 조회
       // Class clazz2 = Class.forName("java.lang.String"); // 3. 문자열로 조회
        
        // 모든 필드 출력
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("field = " + field.getType() + " " + field.getName());
        }

        // 모든 메서드 출력
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("method = " + method);
        }

        // 상위 클래스 정보 출력
        System.out.println("Superclass: " + clazz.getSuperclass().getName());
        
        // 인터페이스 정보 출력
        Class[] interfaces = clazz.getInterfaces();
        for (Class i : interfaces) {
            System.out.println("Interface: " + i.getName());
        }
    }

 

예시에서 사용된 주요 메서드는 다음과 같다.

  • getDeclaredFields() : 클래스의 모든 필드를 조회한다.
  • getDeclaredMethods() : 클래스의 모든 메서드를 조회한다.
  • getSuperclass() : 클래스의 부모 클래스를 조회한다.
  • getInterfaces() : 클래스의 인터페이스들을 조회한다.

또한, Class 클래스에는 클래스의 모든 정보가 들어있어서 이 정보를 바탕으로 인스턴스를 생성하거나 메서드를 호출하는 등의 작업을 할 수 있다.

public static void main(String[] args) throws Exception {
    Class helloClass = Hello.class;

    // getDeclaredConstructor() - 생성자 선택
    // newInstance() - 선택한 생성자 기반으로 인스턴스 생성
    Hello hello = (Hello) helloClass.getDeclaredConstructor().newInstance();
    String result = hello.hello();
    System.out.println("result = " + result);
}

System 클래스

  • System 클래스 : 시스템과 관련된 기본 기능들을 제공하는 유틸리티 클래스 . 표준 입/출력, 오류 스트림 관리, 시스템 속성, 환경 변수, 시간 측정, 메모리 관리와 작업을 처리한다.
public static void main(String[] args) {
    // 현재 시간(밀리초)을 가져온다.
    long currentTimeMillis = System.currentTimeMillis();
    System.out.println("currentTimeMillis = " + currentTimeMillis);

    // 현재 시간(나노초)을 가져온다.
    long currentTimeNano = System.nanoTime();
    System.out.println("currentTimeNano = " + currentTimeNano);

    // 환경 변수를 읽는다.
    System.out.println("getenv = " + System.getenv());

    // 시스템 속성을 읽는다.
    System.out.println("propertise = " + System.getProperties());
    System.out.println("Java version: " + System.getProperty("java.version"));

    // 배열을 고속으로 복사한다.
    char[] originalArray = {'h', 'e', 'l', 'l', 'o'};
    char[] copiedArray = new char[5];
    System.arraycopy(originalArray, 0, copiedArray, 0, originalArray.length);
    // 배열 출력
    System.out.println("copiedArray = " + copiedArray);
    System.out.println("Arrays.toString = " + Arrays.toString(copiedArray));

    // 프로그램 종료 - 가급적이면 사용 권장하지 않음
    System.exit(0);
    System.out.println("hello"); // 위에서 프로그램 종료 -> 출력X
}

실행 결과

 

✔️ 표준 스트림

  • System.in : 표준 입력 (키보드 입력)
  • System.out : 표준 출력 (콘솔 출력)
  • System.err : 표준 에러 출력 (에러 메시지 출력)

✔️  시간 측정

  • System.currentTimeMillis() : 현재 시간을 밀리초 단위로 반환
  • System.nanoTime() : 현재 시간을 나노초 단위로 반환 

✔️   환경 변수

  • System.getenv() : 운영 체제의 환경 변수를 가져옴
  • 특정 환경 변수의 값을 얻으려면 키를 전달 (System.getenv("PATH"))

✔️   시스템 속성

  • System.getProperties() : 현재 JVM의 모든 시스템 속성을 반환
  • System.getProperty(String key) : 특정 시스템 속성을 가져옴
    • 예: os.name (운영 체제 이름), java.version (Java 버전)

✔️   프로그램 종료

  • System.exit(int status) : 프로그램을 종료하며 상태 코드를 반환.
    • 0 : 정상 종료
    • 0이 아님 : 비정상 종료 (에러 발생 등)

✔️   배열 복사

  • System.arraycopy : 고성능 배열 복사 메서드로, 직접 반복문을 사용하는 것보다 빠름
  • 형식: System.arraycopy(src, srcPos, dest, destPos, length)

Math 클래스

  • Math 클래스 : 수학 연산을 수행하기 위해 제공되는 유틸리티 클래스. 

✔️ 기본 연산 메서드

  • abs(x) : 절대값
  • max(a, b) : 최대값
  • min(a, b) : 최소값

✔️  지수 및 로그 연산 메서드

  • exp(x) : e^x 계산
  • log(x) : 자연 로그
  • log10(x) : 로그 10
  • pow(a, b) : a의 b 제곱

✔️  반올림 및 정밀도 메서드

  • ceil(x) : 올림
  • floor(x) : 내림
  • rint(x) : 가장 가까운 정수로 반올림
  • round(x) : 반올림

✔️  삼각 함수 메서드

  • sin(x) : 사인
  • cos(x) : 코사인
  • tan(x) : 탄젠트

✔️  기타 유용한 메서드

  • sqrt(x) : 제곱근
  • cbrt(x) : 세제곱근
  • random() : 0.0과 1.0 사이의 무작위 값 생성

Random 클래스

  • Random 클래스 : 난수를 생성하기 위해 제공되는 유틸리티 클래스. Math.random()도 내부에서는 Random 클래스를 사용한다. Random 클래스로 더 다양한 랜덤값을 구할 수 있다.
public static void main(String[] args) {
    Random random = new Random();
    // Random random = new Random(1); // seed가 같으면 Random의 결과가 같음

    int randomInt = random.nextInt();
    System.out.println("randomInt: " + randomInt);

    double randomDouble = random.nextDouble();// 0.0d ~ 1.0d
    System.out.println("randomDouble: " + randomDouble);

    boolean randomBoolean = random.nextBoolean();
    System.out.println("randomBoolean: " + randomBoolean);
        
    // 범위 조회
    int randomRange1 = random.nextInt(10); // 0 ~ 9까지 출력
    System.out.println("0 ~ 9: " + randomRange1);

    int randomRange2 = random.nextInt(10) + 1; // 1 ~ 10까지 출력
    System.out.println("1 ~ 10: " + randomRange2);
}

 

랜덤은 내부에서 씨드(Seed)값을 사용해서 랜덤 값을 구한다. 그런데 이 씨드 값이 같으면 항상 같은 결과가 출력된다.

  • new Random() : 생성자를 비워두면 내부에서 System.nanoTime() 에 여러가지 복잡한 알고리즘을 섞어서 씨드값을 생성한다. 따라서 반복 실행해도 결과가 항상 달라진다.