Objects 클래스 내장 메소드
equals()와 deepEquals()
- 공통점
- a와 b가 모두 null일 때 true를 리턴한다.
- 차이점
- 배열을 비교할 때, .equals()를 쓰면 배열의 주소 값이 똑같다면 true를 리턴한다.
- 배열을 비교할 때, .deepEquals()를 쓰면 배열 내 항목 값이 모두 같다면 true를 리턴한다.
@Test
public void equalsAndDeepEquals() {
Integer[] a = new Integer[]{1, 2};
Integer[] b = new Integer[]{1, 2};
boolean equals = Objects.equals(a, b);
System.out.println("equals = " + equals); // false
boolean deepEquals = Objects.deepEquals(a, b);
System.out.println("deepEquals = " + deepEquals); // true
}
해시코드 생성(hash(), hashCode())
- 매개변수로 주어진 값들을 이용해 해시코드를 생성한다.
- 클래스가 .hashCode() 메소드를 재정의할 때 리턴 값을 생성하기 위한 용도로 사용하면 좋다.
@Override
public int hashCode() {
return Objects.hash(number, name);
}
위와 같이 이를테면 Student 클래스에 number와 name이라는 필드가 있을 때, .hashCode() 메소드를 위와 같이 오버라이드할 수 있다.
그러면 번호와 이름이 같은 객체에 대해 같은 해시코드를 리턴하게 된다.
널 체크(isNull(), nonNull(), requireNonNull())
- isNull(): null인 경우, true
- nonNull(): null이 아닌 경우, true
- requireNonNull(): null인 경우, NullPointerException
- requireNonNull(T obj, String message): NullPointerException과 메세지
- requireNonNull(T obj, Supplier<String> msgSupplier): NullPointerException과 람다식 수행
requireNonNull()은 다음과 같이 다양한 인자를 받아 수행할 수 있다.
널체크 테스트 코드
@Test
public void nullCheck() {
Object object = null;
boolean isNull = Objects.isNull(object);
System.out.println("isNull = " + isNull);
boolean nonNull = Objects.nonNull(object);
System.out.println("nonNull = " + nonNull);
Objects.requireNonNull(object, () -> "널이라고!");
}
객체 문자 정보 (toString())
Objects.toString() 메소드를 이용해 내장된 .toString() 메소드를 끌어오거나, null에 대한 처리를 할 수도 있다.
- toString(Object o): 기본으로 .toString()을 끌어옴. null이면 "null" 문자열 반환
- toString(Object o, String nullDefault): null이면 nullDefault로 받은 문자열을 반환
System 클래스
java.lang 패키지에 속해있어 따로 import하지 않아도 이용할 수 있는 클래스이다. 운영체제의 기능을 이용할 때 사용된다.
- 프로그램 종료
- 키보드 입력
- 모니터 출력
- 메모리 정리
- 현재 시간 읽기
- 시스템 프로퍼티 읽기
- 환경 변수 읽기
위와 같은 기능이 있다.
모든 필드와 메소드는 정적(static) 필드와 정적 메소드로 구성되어 있다.
프로그램 종료(exit())
- .exit()는 현재 실행중인 프로세스를 강제 종료시킨다.
- 인자로 종료 상태값이라 불리는 int 값을 받는다.
- 0이면 정상종료
- 0이 아니면 비정상종료이다.
- 어떤 값을 줘도 종료는 된다.
- 인자로 종료 상태값이라 불리는 int 값을 받는다.
- System.exit()은 자동으로 SecurityManager.checkExit() 메소드를 호출시킨다.
- SecurityManager의 checkExit() 메소드를 오버라이드하여 종료 코드에 따른 예외처리가 가능하다.
예제 코드
public class Main {
public static void main(String[] args) {
SecurityManager securityManager = new SecurityManager() {
@Override
public void checkExit(int status) {
System.out.println("status = " + status);
if(status == 0) {
System.out.println("정상적으로 종료되었습니다.");
} else {
System.out.println("비정상적으로 종료되었습니다.");
throw new SecurityException("예외처리를 해주세요.");
}
}
};
System.setSecurityManager(securityManager);
// usualExit();
unusualExit();
}
public static void usualExit() {
System.out.println("정상적으로 프로그램을 종료시킵니다.");
System.exit(0); // 0: 정상
}
public static void unusualExit() {
System.out.println("비정상적으로 프로그램을 종료시킵니다.");
System.exit(1); // 1: 비정상
}
}
예제 코드를 실행시키면 위와 같은 결과가 나타난다.
가비지 콜렉터 실행(gc())
- 일반적으로 가비지 콜렉터는 CPU가 한가하거나 메모리가 부족할 때 JVM에서 자동으로 실행한다.
- System.gc()를 실행하면 가비지 콜렉터 수행 시점을 조금 앞당길 수 있다.
- 바로 실행되는 것은 아니고 빠른 시간 내에 실행시키기 위해 노력하는 것이다.
- 수집되는 순간 이전에 배웠던 .finalize() 메소드가 실행된다.
현재시각 읽기(currentTimeMillis(), nanoTime())
- currentTimeMillis(): 현재 시간을 밀리 세컨드(1/1000초) 단위로 출력
- nanoTime(): 현재 시간을 나노 세컨드(1/10^9초) 단위로 출력
- 프로그램 실행 소요시간 측정에 사용된다.
- 반환 타입은 long이다.
시스템 프로퍼티 읽기(getProperty())
JVM이 시작할 때 자동으로 갖게 되는 시스템의 속성 값을 반환한다.
- java.version: 자바 버전
- java.home: JRE의 설치 경로
- os.name: 운영체제 이름
- file.separator: 파일 구분자 (윈도우는 \)
- user.name: 사용자 이름
- user.home: 사용자의 홈디렉토리
- user.dir: 사용자가 현재 작업중인 디렉토리 경로
System.getProperties() 메소드를 이용해 프로퍼티 정보를 받으면 위에 나열된 값 이외에도 많은 프로퍼티를 한번에 볼 수 있다.
환경 변수 읽기(getEnv())
운영체제에 설정된 환경변수를 가져올 수 있다.
System.getenv() 메소드를 이용하면 모든 환경변수가 들어있는 Map 형태의 값이 반환된다.
Class 클래스
클래스, 인터페이스의 메타데이터를 얻을 수 있다.
- 클래스의 메타데이터란 클래스의 이름, 생성자 정보, 필드 정보, 메소드 정보 등을 말한다.
Class 객체를 얻는 방법
- object.getClass() 메소드를 수행하여 얻을 수 있다.
- Class.forName() 메소드를 수행하여 얻을 수 있다.
- 해당 클래스가 존재하지 않는 경우의 예외처리를 위해 ClassNotFoundException을 처리해주어야 한다.
리플렉션
- Class 객체를 이용해 메타데이터를 알아낼 때 이용된다.
- 클래스 이름
- 생성자 정보
- 필드 정보
- 메소드 정보
- Class 객체에서 리플렉션을 얻는 방법
- .getDeclaredConstructors(): Constructor 객체의 배열을 반환
- .getDeclaredFields(): Field 객체의 배열을 반환
- .getDeclaredMethods(): Method 객체의 배열을 반환
예제 코드
public class Myself {
String name = "Jake Seo";
int birthYear = 1992;
int koreanAge;
String[] favoriteFoods = {"김치", "밥"};
public Myself() {
koreanAge = LocalDateTime.now().getYear() - birthYear + 1;
}
public Myself(int thatYear) {
koreanAge = thatYear - birthYear + 1;
}
public String getName() {
return name;
}
public int getKoreanAge() {
return koreanAge;
}
public String[] getFavoriteFoods() {
return favoriteFoods;
}
}
public class Main {
public static void main(String[] args) {
Class myselfClass = Myself.class;
Field[] declaredFields = myselfClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("declaredField = " + declaredField);
}
Constructor[] declaredConstructors = myselfClass.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println("declaredConstructor = " + declaredConstructor);
}
Method[] declaredMethods = myselfClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("declaredMethod = " + declaredMethod);
}
}
}
동적 객체 생성(newInstance())
- new 연산자를 사용하지 않고 객체를 생성하는 방법이다.
- 클래스의 이름이 런타임 시에 결정되는 경우에 사용된다.
- 기본 생성자를 호출해서 객체를 생성하기 때문에, 반드시 기본 생성자가 있어야 한다.
- 매개변수가 있는 생성자를 호출하고 싶다면 리플렉션을 이용해서 Constructor 객체를 얻어야 한다.
- 추상 클래스나 인터페이스를 이용해 인스턴스를 만드려하면 InstantiationException 예외가 발생한다.
- 클래스나 생성자가 접근 제한자로 인해 접근할 수 없다면 IllegalAccessException 예외가 발생한다.
- 메소드의 반환 타입이 Object라서 강제 형변환을 해야 하는데, 보통 인터페이스를 이용한 다형성으로 처리한다.
String 클래스
- java.lang 패키지에 속해있다.
- 문자열 리터럴은 자동으로 String 객체로 생성된다.
- Deprecated된 생성자 외에도 13개의 생성자를 더 제공한다.
- 파일의 내용을 읽거나, 네트워크를 통해 받은 데이터는 보통 byte[] 이므로 이것을 문자열로 변환하기 위해 사용한다.
// 특정 인코딩으로 넘어온 바이트 배열을 문자열로 변경하는 생성자
String str = new String(byte[] bytes, String charsetName);
키보드에서 문자열을 입력받아 출력하는 생성자 예제 코드
public class Main {
public static void main(String[] args) {
byte[] bytes = new byte[100];
try {
System.out.print("문자열 입력: ");
int readByteLength = System.in.read(bytes);
System.out.println("\n읽어들인 문자의 수 = " + readByteLength);
System.out.println("입력된 내용 = " + new String(bytes, 0, readByteLength - 1));
} catch (IOException e) {
e.printStackTrace();
}
}
}
.charAt() 메소드
- char / 특정 위치의 문자를 반환한다.
.equals() 메소드
- boolean / 두 문자열을 비교한다.
.getBytes() 메소드
- byte[] / 문자열을 바이트 배열로 반환한다.
- 인자로 UTF-8 같은 캐릭터셋 유형을 주면, 해당 캐릭터셋에 맞게 반환해준다.
- 네트워크로 문자열을 전송하거나, 문자열을 암호화할 때 유용하다.
EUC-KR 문자셋을 이용하여 인코딩하면 영어 1바이트 한글 2바이트가 된다.
UTF-8 문자셋을 이용하여 인코딩하면 영어 1바이트 한글 3바이트가 된다.
기본 값은 시스템의 기본 문자셋을 이용한다.
존재하지 않는 문자셋을 입력하면 java.io.UnsupportedEncodingException 예외가 발생한다.
.indexOf() 메소드
- 문자열 내에서 주어진 문자열의 위치를 인덱스 기준의 숫자로 반환한다.
- 없으면 -1 반환
.length() 메소드
- 총 문자열의 길이를 반환한다.
.replace() 메소드
- 인자로 target과 replacement를 받아서 target이 replacement로 대체된 결과 문자열을 반환한다.
.substring()
- 지정된 위치에서 끝까지 잘라낸 새로운 문자열을 반환한다.
- 인자를 2개 주면 두번째 인자까지 잘라낸 새로운 문자열을 반환한다.
toLowerCase(), toUpperCase()
- 대문자 혹은 소문자로 변환한다.
- 대소문자 문자 비교 때는 .equalsIgnoreCase() 메소드를 이용하면 대소문자 무시 비교를 할 수 있다.
trim()
- 앞뒤 공백을 제거한 문자를 반환한다.
valueOf()
- 기본 타입값을 문자열로 반환한다.
StringTokenizer 클래스
주로 문자열이 특정한 구분자(delimiter)로 연결된 구조일 때, 특정한 구분자를 기준으로 문자열을 분리할 때 사용된다.
String 클래스에도 .split() 메소드가 있어서 특정 구분자를 기준으로 문자열을 나눌 수 있지만, 정규표현식을 이용해야 한다. StringTokenizer 클래스가 제공하는 .split()은 단순히 문자 delimeter만 구분해서 나누는 방식이다.
String.split()
public class StringTokenizerTest {
String exampleString = "김철수,이미영-김문수";
@Test
public void stringSplitTest() {
String[] split = exampleString.split(",|-");
for (String s : split) {
System.out.println("s = " + s);
}
}
}
위와 같이 정규표현식을 이용하여 구분할 수 있다. 예제 문자열은 이름이 , 혹은 -로 구분되어 있는데, 두 문자를 모두 구분자로 이용하기 위한 정규표현식이 ",|-"라는 문자열로 표현되었다.
StringTokenizer 생성자
문자열이 한 종류의 구분자로 되어있을 때 용이하며, 말 그대로 문자열을 토큰화한다. 토큰화한 문자열을 다루기 위해 3가지 내장 메소드를 제공한다.
- int countTokens(): 꺼내지 않고 남아있는 토큰의 수를 반환한다.
- boolean hasMoreTokens(): 남아 있는 토큰이 있는지 여부에 대해 반환한다.
- String nextToken(): 토큰을 하나씩 꺼내온다.
- 토큰이 남아있지 않다면, NoSuchElementException 예외를 발생시킨다.
@Test
public void stringTokenizerTest() {
String exampleString2 = "김철수,이미영,김문수";
StringTokenizer stringTokenizer = new StringTokenizer(exampleString2, ",");
int countTokens = stringTokenizer.countTokens();
System.out.println("countTokens = " + countTokens);
boolean hasMoreTokens = stringTokenizer.hasMoreTokens();
System.out.println("hasMoreTokens = " + hasMoreTokens);
while(stringTokenizer.hasMoreTokens()) {
String token = stringTokenizer.nextToken();
System.out.println("token = " + token);
}
}
StringBuffer, StringBuilder 클래스
필요성
String은 구조적으로는 내부의 문자열을 수정할 수 없다. 단, 새로운 문자열을 String 타입의 변수에 할당함으로써, 새로운 문자를 저장할 수 있다.
이를테면 String.replace()는 내부의 문자를 변경하는 것이 아니라, 변경된 새로운 문자열을 반환하는 것이다. String 타입끼리의 + 연산도 마찬가지이다.
이러한 방식의 단점은 아무래도 많은 문자열 변환 연산이 있을 때, 속도가 현저히 느려질 수 있다는 단점이 있다.
문자열 변경 작업이 많을 때, 그로 인한 속도 저하를 막기 위해 StringBuffer 혹은 StringBuilder 클래스가 이용되는 것이다.
위 두 클래스는 내부 버퍼(데이터를 임시로 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업을 할 수 있도록 설계되어 있다.
String타입처럼 새로운 문자열 객체를 만들어내고 다시 저장하는 방식이 아니라 진짜 기존의 문자열을 수정하는 방식이다.
두 클래스의 차이점
기본적으로 StringBuffer와 StringBuilder의 사용 방법은 동일하다.
- StringBuffer는 멀티 스레드 환경에서 사용할 수 있도록 동기화가 적용되어 있어 스레드에 안전하다.
- StringBuilder는 단일 스레드 환경에서만 사용하도록 설계되어 있다.
생성자
- StringBuilder(): 기본 생성자는 16개의 문자를 저장할 수 있는 초기 버퍼를 만든다.
- StringBuilder(int capacity): capacity 만큼의 초기 버퍼를 만든다.
사실 버퍼가 부족하면 버퍼 크기를 자동으로 늘리기 때문에 초기 버퍼 사이즈가 크게 중요하진 않다.
메소드들
- append(...): 문자열 끝에 매개값을 추가한다.
- insert(int offset, ...): offset으로 지정된 자리에 매개값을 추가한다.
- delete(int start, int end): 문자열의 일부분을 삭제한다.
- deleteCharAt(int index): 문자열에서 주어진 index의 문자를 삭제한다.
- replace(int start, int end, String str): 문자열의 일부를 다른 문자열로 변환한다.
- reverse(): 문자열의 순서를 뒤바꾼다.
- setCharAt(int index, char ch): 문자열에서 주어진 index의 문자를 다른 문자로 바꾼다.
append()와 insert() 메소드는 다양한 타입으로 오버로딩되어 있어서 다양한 타입을 문자로 추가, 삽입할 수 있다.
'👨🏫Study > JAVA' 카테고리의 다른 글
[JAVA] JavaFX (0) | 2022.03.25 |
---|---|
[JAVA] 11 - 3 기본 API 클래스(Pattern, Arrays, Wrapper, Math, Date, Calendar, Format , LocalDate, Instant, DateTimeFormatter) (0) | 2022.03.23 |
[JAVA] 08 - 1 인터페이스(2, 이것이 자바다) (0) | 2022.03.23 |
[JAVA] 06 - 7 애노테이션 (0) | 2022.03.23 |
[JAVA] 16 - 1 스트림 (0) | 2022.03.22 |
댓글