이것이 자바다 정리 #8 기본 API 클래스 2 (Objects, System, Class, Reflection, String, Tokenizer, Builder)
이것이 자바다 책을 참고하였습니다.
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
}
.hashCode() 메소드를 재정의할 때 리턴 값을 생성하기 위한 용도로 사용하면 좋다.@Override
public int hashCode() {
return Objects.hash(number, name);
}
위와 같이 이를테면 Student 클래스에 number와 name이라는 필드가 있을 때, .hashCode() 메소드를 위와 같이 오버라이드할 수 있다.
그러면 번호와 이름이 같은 객체에 대해 같은 해시코드를 리턴하게 된다.
isNull(): null인 경우, truenonNull(): null이 아닌 경우, truerequireNonNull(): null인 경우, NullPointerExceptionrequireNonNull(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, () -> "널이라고!");
}

Objects.toString() 메소드를 이용해 내장된 .toString() 메소드를 끌어오거나, null에 대한 처리를 할 수도 있다.
toString(Object o): 기본으로 .toString()을 끌어옴. null이면 "null" 문자열 반환toString(Object o, String nullDefault): null이면 nullDefault로 받은 문자열을 반환java.lang 패키지에 속해있어 따로 import하지 않아도 이용할 수 있는 클래스이다. 운영체제의 기능을 이용할 때 사용된다.
위와 같은 기능이 있다.
모든 필드와 메소드는 정적(static) 필드와 정적 메소드로 구성되어 있다.
.exit()는 현재 실행중인 프로세스를 강제 종료시킨다.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: 비정상
}
}

예제 코드를 실행시키면 위와 같은 결과가 나타난다.
System.gc()를 실행하면 가비지 콜렉터 수행 시점을 조금 앞당길 수 있다..finalize() 메소드가 실행된다.currentTimeMillis(): 현재 시간을 밀리 세컨드(1/1000초) 단위로 출력nanoTime(): 현재 시간을 나노 세컨드(1/10^9초) 단위로 출력long이다.JVM이 시작할 때 자동으로 갖게 되는 시스템의 속성 값을 반환한다.
java.version: 자바 버전java.home: JRE의 설치 경로os.name: 운영체제 이름file.separator: 파일 구분자 (윈도우는 \)user.name: 사용자 이름user.home: 사용자의 홈디렉토리user.dir: 사용자가 현재 작업중인 디렉토리 경로
System.getProperties()메소드를 이용해 프로퍼티 정보를 받으면 위에 나열된 값 이외에도 많은 프로퍼티를 한번에 볼 수 있다.
운영체제에 설정된 환경변수를 가져올 수 있다.
System.getenv()메소드를 이용하면 모든 환경변수가 들어있는Map형태의 값이 반환된다.
클래스, 인터페이스의 메타데이터를 얻을 수 있다.
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);
}
}
}

new 연산자를 사용하지 않고 객체를 생성하는 방법이다.Constructor 객체를 얻어야 한다.InstantiationException 예외가 발생한다.IllegalAccessException 예외가 발생한다.Object라서 강제 형변환을 해야 하는데, 보통 인터페이스를 이용한 다형성으로 처리한다.java.lang 패키지에 속해있다.String 객체로 생성된다.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();
}
}
}
char / 특정 위치의 문자를 반환한다.boolean / 두 문자열을 비교한다.byte[] / 문자열을 바이트 배열로 반환한다.UTF-8 같은 캐릭터셋 유형을 주면, 해당 캐릭터셋에 맞게 반환해준다.
EUC-KR문자셋을 이용하여 인코딩하면 영어 1바이트 한글 2바이트가 된다.
UTF-8문자셋을 이용하여 인코딩하면 영어 1바이트 한글 3바이트가 된다.
기본 값은 시스템의 기본 문자셋을 이용한다.
존재하지 않는 문자셋을 입력하면java.io.UnsupportedEncodingException예외가 발생한다.
target과 replacement를 받아서 target이 replacement로 대체된 결과 문자열을 반환한다..equalsIgnoreCase() 메소드를 이용하면 대소문자 무시 비교를 할 수 있다.주로 문자열이 특정한 구분자(delimiter)로 연결된 구조일 때, 특정한 구분자를 기준으로 문자열을 분리할 때 사용된다.
String 클래스에도 .split() 메소드가 있어서 특정 구분자를 기준으로 문자열을 나눌 수 있지만, 정규표현식을 이용해야 한다. StringTokenizer 클래스가 제공하는 .split()은 단순히 문자 delimeter만 구분해서 나누는 방식이다.
public class StringTokenizerTest {
String exampleString = "김철수,이미영-김문수";
@Test
public void stringSplitTest() {
String[] split = exampleString.split(",|-");
for (String s : split) {
System.out.println("s = " + s);
}
}
}

위와 같이 정규표현식을 이용하여 구분할 수 있다. 예제 문자열은 이름이 , 혹은 -로 구분되어 있는데, 두 문자를 모두 구분자로 이용하기 위한 정규표현식이 ",|-"라는 문자열로 표현되었다.
문자열이 한 종류의 구분자로 되어있을 때 용이하며, 말 그대로 문자열을 토큰화한다. 토큰화한 문자열을 다루기 위해 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);
}
}

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()메소드는 다양한 타입으로 오버로딩되어 있어서 다양한 타입을 문자로 추가, 삽입할 수 있다.