이것이 자바다 정리 #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
인 경우, 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, () -> "널이라고!");
}
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()
메소드는 다양한 타입으로 오버로딩되어 있어서 다양한 타입을 문자로 추가, 삽입할 수 있다.