• 여기서부터는 Java에 specific한 부분이 많아서 자세하게 다룰 예정입니다.

  • 위 package는 Java에서 기본적으로 사용되는 애들이 모인 패키지다.

Object class

  • 이전에 OOP에서 나왔던 모든 class의 ancestor에 해당하는 class다.

  • 여기서는 notify(), notifyAll(), wait()를 제외한 모든 method들에 대해서 설명한다. 저 셋은 동시성 관련 내용을 할 때 다루겠다.

protected Object clone()

  • 객체 복사를 한다.

  • Cloneable interface를 implement한 class가 overriding 가능. 참고로 저 interface에는 아무것도 제공된게 없는데, 굳이 이걸 implement해야 clone()을 쓸 수 있게 한 이유는 허락되지 않은 복제가 막 이루어지지 못하도록 하기 위해서다.

  • 기본적으로 shallow copy이며 deep copy를 할거면 overriding을 통해 구현해야 한다. 이때 보통 super로 superclass의 clone을 호출할텐데 CloneNotSupportedException을 throw하기 때문에 override하는 함수도 이걸 throw한다고 지정하거나 try-catch 문으로 이걸 처리해야 한다.

  • protected라서 계승관계가 없고 java.lang에 속하지 않는 곳에서 사용하는게 불가능하며, 이를 가능하게 하려면 overriding을 통해 접근 범위를 public으로 바꿔야 한다.

equals(Object obj)

  • 같은 객체인지를 비교한다.

  • 기본 정의는 reference 비교,즉 ==을 사용한다.

public boolean equals(Object obj) {
	return (this == obj)
}
  • 참고로 String의 경우, 내용물은 같지만 서로 다른 reference를 가지는 두 객체에 대해 equals()를 호출하면 true가 나옴을 볼 수 있는데 이는 String에서 해당 method를 override해서 그렇다. Object에서는 저 정의가 맞다. 이처럼 overriding을 통해 두 객체가 같은지, 안 같은지를 판단하는 기준을 우리가 바꿀 수 있다.

protected void finalize()

  • 더이상 해당 객체에 접근할 thread가 없다고 JVM이 판별시 호출한다. 정확히는 garbage collector이 호출한다.

  • 객체 소멸 전에 해야 하는 일이 있으면 이 함수를 overriding해서 설정을 하면 된다. 가장 흔한 경우는 객체랑 연관된 I/O connection 해제와 같이 garbage collector로 인해 자동으로 미할당이 되지 않는 자원들의 미할당을 해야 할때 여기서 정의된다.

  • 기본 정의에선 아무것도 정의된게 없다. 그저 비어있는 method. Object class에서 이걸 정의한 이유는 세밀한 메모리 미할당 조절을 위해서다. superclass에서의 메모리 미할당을 본인이 선택적으로 호출하거나 안 할 수 있는 권리를 주는 것이기 때문. 참고로 이를 통해 대충 눈치챘겠지만 constructor의 경우 아무것도 입력을 안해도 parameter이 없는 superclass constructor을 호출하는것과 다르게 finalize는 superclass의 finalize를 explicit하게 코드하지 않는 이상 절대로 호출하지 않는다. 참고.

  • 이 method 호출 직후에 해당 객체가 완전 소멸된다는 것은 아니라는 점도 유의. method 호출 후 JVM이 다시 진짜 소멸 판단 후에 garbage collector이 관련 자원을 수거한다.

  • exception throwing이 가능하며, 만약 내부에서 발생한 exception이 catch되지 않고 밖으로 튀어나올 경우 무시되며, 그냥 그 순간 해당 finalize가 종료된다.

  • Java의 메모리 관리랑 관련된 내용이 많으며, 깊이 알고 싶으면 The Java Language Specification, Java SE 8 Edition12.6단원을 참고하길 바란다. 여담으로 Java를 깊이 이해하는데 도움이 되는 책이니 북마크 해두는걸 추천.

public Class getClass()

  • 객체의 runtime class 정보를 담는 Class instance를 반환.

class instance

  • Class instance에 관한 모든 내용은 다음 documentation을 확인하시길

  • Class instance는 저 방법 말고도 2가지가 더 있다. 예를들어 Pokemon class의 class instance를 얻고 싶다면 다음 세가지 중 하나를 하면 된다.

Class cObj = new Pokemon().getClass();
Class cObj = Pokemon.class;
Class cObj = Class.forName("Pokemon");
  • Class instance로 객체 생성을 하는것도 가능하다.
Pokemon p1 = new Pokemon(); //original
Pokemon p2 = Pokemon.class.newInstance(); //another method

public int hashCode()

  • Java의 hashing에 사용되는 hashing function을 구현한 method다.

  • 해당 method는 Java 내에 구현된 기본 자료 구조들 중 hash value를 활용하는 자료 구조들 (HashMap, HashSet, HashTable)에서 활용이 된다.

  • hash의 특징을 안다면 당연한 소리인데, 같은 객체로 판별되는 녀석은 hashCode에서의 결과물도 같이 나와야 한다.

  • equals를 override 했을 경우 hashCode도 달라진 equals에 맞게 적절하게 override를 해야 한다. 안 그러면 위의 자료구조들처럼 이를 활용하는 구현들에서 문제를 일으킬 수 있으니 매우 조심.

  • Object에서의 기본 정의는 System.identityHashCode (Object x)랑 정의가 같으며, 객체의 주소값의 hash된 value를 반환한다.

public String toString()

  • instance를 문자열로 나타낼 때 어떻게 나타낼지를 정의하는 함수.

  • 기본 정의는 다음과 같다. getName()은 '보통' Class instance의 method로 runtime class의 이름을 반환한다. 자세한건 이거 참고

public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • Java 내 기본 class들의 경우 toString을 override한 경우가 많다. 대표적인건 StringDate.

Object class 활용법 : covariant return type

  • 어떤 class의 특정 return type을 가지는 method를 subclass가 override할 때, return type을 superclass에서 지정한 return type의 subclass로 바꾸는 것을 허용하는 문법이다.

  • 논리적으로는 크게 문제가 없다. 어쨌든 superclass에서 지정된 type에 포함되는 녀석을 return하는 것이기 때문이다.

  • 이것의 좋은 점은 불필요한 형변환을 많이 방지할 수 있다는 것이다. 밑의 첫번째는 covariatnt return type을 활용 안한 것이고 그 밑은 활용한 것이다.

Point copied = (Point) original.clone(); //original
Point copied2 = original.clone(); //used covariant return type, where return type was declared as Point

String class

  • 기본적으론 그냥 문자열(string)을 저장하는 class이다만, 몇가지 재미있는 특징들이 있다.

immutable class

  • String의 문자열 내용물은 value라는 attribute에 저장되어 있는데, 이 값은 private 선언으로 외부에서 접근이 불가능하다.

  • 이 때문에 어떤 String class instance에 다른 String을 저장하면 memory를 또 allocate 해서 거기에다가 저장을 한다. 단순히 내용물을 복사하는 것에 비해 시스템적으로 훨씬 시간이 오래 걸리는 작업이다.

String a = "hi";
String b = "Mom";
a = a + b; //new string allocation
  • 이 때문에 문자열을 자주 합하거나 추출해야 하는 경우 String보다는 StringBuffer을 사용하는 것이 좋다. String이랑 비슷한데 내용물 변동이 가능한 녀석이라고 생각하면 된다. 그래서 새로운 memory allocation이 매번 이루어지지 않는다.

Details of StringBuffer

  • 유동적인 길이를 저장하는 방법은 C++의 vector class처럼 공간이 부족해지면 늘리는 형식으로 이루어진다.

  • StringBuffer서 작업 후 반환되는 값은 본인의 주소값이다. String이 보통 새로 할당된 메모리 공간을 반환한다는 것과 차이가 있다는 점에 유의

  • equalsString과 다르게 내용물을 비교하도록 overriding이 되지 않아서 같은 내용물을 담은 서로 다른 StringBuffer도 다른 것으로 취급한다.

  • toString()은 내용물을 String으로 변환하도록 overriding이 되어 있다.

  • 관련 함수들은 이 글 참고

StringBuilder

  • 위의 StringBuffer doc의 첫부분을 보면 알겠지만 StringBuffer은 thread-safe다. 여기서의 thread-safe는 C, C++의 thread-safe와 완전히 동일하며, 동기화(synchornization)을 통해 race condition으로 인한 data race가 해당 resource에 관해서 발생하지 않는다는 것을 의미한다. 자세한건 이걸 참고. 이 시리즈에선 앞으로도 이것에 대해 잘 알고 있다고 가정하기에 잘 모르면 이참에 링크들을 보거나 검색해보는걸 추천한다.

  • C나 C++에서 위를 보장하는 함수들을 구현해보려 해봤으면 알겠지만 어느 구현 방식을 사용하든 overhead가 어느정도 발생해가지고 성능이 좀 떨어질 수 있다. thread-safe 함수들은 이 overhead로 인해 속도가 좀 느린 대신 multi-thread program을 만들 때 data race를 신경쓰지 않아도 되도록 프로그래머를 돕는데, 문제는 multi-threading을 하지 않는 프로그램을 만드는 경우 장점 없는 overhead만 있는 class가 된다는 것이다.

  • 그래서 동기화를 위한 구현 부분만 제거하고 나머지는 StringBuffer과 같은 class를 만들었는데 이를 StringBuilder이라고 한다. 멀티쓰레드 프로그램을 쓰지 않는 경우라면 꼭 참고.

String comparison

  • String 생성은 따옴표, 즉 literal을 사용하거나 new와 함께 String 생성자를 사용하는 방법 2가지가 있다.
String s1 = "hi mom";
String s2 = new String("hi mom");
  • literal로 형성된 문자열들은 같은 내용물을 가질 경우 같은 메모리 공간을 가리키며 생성자로 형성된 문자열들은 같은 내용물을 가져도 다른 메모리 공간을 나타낸다.
String s1 = "hi mom";
String s2 = "hi mom"; //same memory space compared to s1
String s3 = new String("hi mom") //different memory space compared to s1 / s2
String s4 = new String("hi mom") //different memory space compared to s1 / s2 / s3

System.out.println(s1 == s2); //true
System.out.println(s2 == s3); //false
System.out.println(s3 == s4); //false
  • Java는 literal들이 컴파일 될 때 class file들에 전부 목록 형태로 저장된 후, 이것이 메모리에 load 될 때 constant pool이라는 공간에 다시 저장이 되고 literal을 저장한 변수들은 거기 내의 공간을 전부 가리키는 것이다. C나 C++에서 initialize된 static variable이 bss section에 저장되는 느낌과 비슷하다. 비슷하다는거지 둘이 같다는 것은 아니라는 점 유의... 그래도 이해해 어렵진 않을 것이라고 생각한다.

zero-length string

methods and constructors

  • 모든 documentation은 이거 참고.

  • String(String s) : s의 내용물을 가지는 새로운 String instance 생성.

  • String(char[] value) : value라는 array의 내용물을 가지고 String의 instance 생성.

  • String(StringBuffer buf) : buf라는 StringBuffer의 내용물을 가지고 String instance 생성

  • char charAt(int index) : Stringindex위치 char값 반환. (index는 0부터)

  • int compareTo(String str) : C의 strcmp, strncmp와 동일하다.

  • String concat(String str) : 문자열 뒤에 str을 덧붙인다. 새로운 memory allocation 발생 유의

  • boolean contains(CharSequence s) : s가 포함되어있는지를 확인. CharSequence는 interface의 일종으로 뭔지는 이 글 참고 하필 이걸 사용한 이유는 String이외의 녀석들도 받아들일 수 있게 하기 위해서라고 한다.

  • boolean endsWith(String suffix) : suffix로 끝나는지를 확인.

  • boolean equals(Object obj) : objString이면서 비교 대상과 내용물이 같은지를 확인.

  • boolean equalsIgnoreCase(String str) : str이 비교 대상과 내용물이 대소문자 상관없이 같은지 확인

  • int indexOf(int ch) : ch가 존재할 시 그 위치를 반환. 없으면 -1.

  • int indexOf(int ch, int pos) : chpos 이후에 존재할시 그 위치를 반환. 없으면 -1.

  • int indexOf(String str) : str이 존재할 시 그 시작 위치를 반환. 없으면 -1.

  • String intern() : new로 생성한 String을 constant pool에 등록. 만일 해당 문자열이 이미 constant pool에 등록되어 있을 경우 그 주소값을 반환. 없으면 저장된 위치를 반환.

  • int lastIndexOf(int ch) : chString상에 존재시 맨 마지막에 등장하는 위치를 반환. 없으면 -1.

  • int lastIndexOf(String str) : strString상에 존재시 맨 마지막에 등장하는 위치를 반환. 없으면 -1.

  • int length() : 길이 반환

  • String replace(char old, char nw) : Stringold를 전부 nw로 바꾼 후 그 문자열을 반환.

  • String replace(CharSequence old, CharSequence nw) : 위와 동일하나, char이 아닌 CharSequence.

  • String replaceAll(String regex, String replacement) : 위와 동일하나, CharSequence이 아닌 String. 정규 표현식 사용이 가능하다는 점이 특징.

  • String replaceFirst(String regex, String replacement) : String에서 regex에 해당하는 첫번째 녀석만 replacement로 바꾼다.

  • String[] split(String regex) : Stringregex를 기준으로 나누어서 배열에 집어넣는다. C의 strtok와 역할이 비슷하다만 더 편리하다고 볼 수 있음.

  • String[] split(String regex, int limit) : Stringregex를 기준으로 나누어 배열에 저장하나, 최대 limit 개수만큼만 나누는 것이 가능하다.

  • boolean startsWith(String prefix) : Stringprefix로 시작한다면 true, 아니면 false 반환.

  • String substring(int begin) : begin부터 String 끝까지를 내용물로 가지는 String 반환. C의 substr과 유사하다.

  • String substring(int begin, int end) : begin부터 end 전까지의 문자열을 내용물로 가지는 String 반환.

  • String toLowerCase() : String 내용물을 전부 소문자로 변환 후 반환.

  • String toString() : String 내용물을 String으로 반환. ObjecttoString의 override 형태다.

  • String toUpperCase() : String 내용물을 전부 대문자로 변환 후 반환.

  • String trim() : String의 양측 공백을 제거 후 반환.

  • static String valueOf(...) : primitive나 Object 값들을 String으로 변환 후 반환한다. Object가 parameter인 경우 toString에서의 반환 값을 반환한다.

join(), StringJoiner

  • join은 첫번째 변수로 CharSequence를, 두번째로는 CharSequence나 이로 이루어진 array같은 Iterable을 받는 함수인데 첫번재 CharSequence로 두번째로 들어온 여러 CharSequence들을 연결하는 함수다.
String family[3] = {"mom", "dad", "son"};
String str = String.join("-", family);
System.out.println(str); //mom-dad-son
  • 이와 관련된 StringJoiner class도 있다.
StringJoiner sj = new StringJoiner(",", "[", "]");
String family[3] = {"mom", "dad", "son"};

for (String s : family)
	sj.add(s.toUpperCase());

System.out.println(sj.toString()); // [mom,dad,son]

Casting String to primitive value

  • parse...()을 사용한다. 예를들면 parseInt(String s).

  • primitive type의 wrapper class에서 호출하면 된다. 예를들어 int의 경우 Ineger.parseInt(String s)

  • 공백이나 문자열이 포함되어 있는 String 변환시 문제가 발생한다. 단 parseFloat에서 f같은게 붙은것은 괜찮다. 물론 이것도 parseInt같은것에서는 문제가 된다.

Math Class

  • 전체 doc은 다음 링크 참고.

  • C/C++의 math.h와 비슷한 위치다.

  • 완전 동일하진 않다. 예를들어 random()이 Java에선 여기에 해당되는데, C에는 stdlib.h에 이와 비슷한 함수가 포함되어 있다. 보통 안쓰지만 여튼

  • constructor이 private 선언인데, 이유는 instance를 만들 이유가 없기 때문이다.

  • 상수 2개가 정의되어 있다. EPI로 각각 자연상수 e랑 원주율 pi를 의미한다.

  • 삼각함수, 지수함수, 로그함수들도 다 지원해준다.

  • 삼각함수 관련해서 toRadianstoDegrees함수라고 각각 각도를 radian으로, radian을 각도로 바꿔주는 함수가 있다는 점 참고.

rounding

  • Java엔 round()라고 소수점 첫째자리 기준으로 반올림 하는것이 있다.

  • 원하는 소수점자리에서 반올림을 하고 싶으면 그만큼의 10의 거듭제곱을 곱하고 round()호출 후 다시 실수 형태로 나누면 된다.

double a = 90.7552;
a *= 100; //9075.52
int b = Math.round(a); //9076
System.out.println(b / 100.0); // 90.76
  • 이 기법으로 특정 자릿수까지의 값만 얻는 것 등이 가능하기 때문에 잘 활용하도록 하자.

그 외의 rounding관련 함수들은 ceil(), floor(), rint() 등이 있다. 처음 2개는 C, C++을 했으면 이미 알거고, 마지막은 round()와 동일하나 반환값이 double형태이며, round()가 rounding away from zero(commercial rounding, 우리가 흔히 아는 반올림)인것과 다르게 round-to-even이다. 둘의 차이를 모르면 이 위키 글을 참고.

exception making methods

  • C/C++의 overflow는 기본적으로 프로그램을 중단시키진 않는다. (대신 flag 형태로 발생했음을 알려주긴 하나 이는 설명하기 복잡하니 넘어가자.)

  • Java도 기본적으로 마찬가지이나, JDK 1.8부터 overflow시 exception을 일으키는 method들을 만들었다. 'Exact'가 포함되어 있다.

trigonometric function, exponentials and logarithm

  • 삼각함수, 지수함수, 로그함수들도 다 지원해준다.

  • 유의할만한 것은 toRadianstoDegrees함수로 각각 각도를 radian으로, radian을 각도로 바꿔주는 함수다.

StrictMath

  • 이 stackoverflow 글을 보면 알겠지만 StrictMathMath 대비 성능을 포기하는 대신 고정된 알고리즘을 활용해 언제나 일정한 결과물을 내도록 보장하는 class다. 그것 말고는 Math와 동일.

  • 다른말로 기본 MathOS에 따라 결과물이 달라질 수 있다. 이는 JVM측에서 OS 상의 구현을 적극 활용해가지고 성능상 좋은 method 구현을 만들려고 하기 때문. 웃긴게 '기본' 정의는 StrictMathMath가 동일하다. 다만 JVM에서 OS에 있는 더 최적화된 구현을 사용하고 싶을 때 그 정의를 바꾸는 것일 뿐.

  • 결론은 성능은 좀 더 안좋지만 결과물이 OS 상관없이 동일하길 원할 때 이것을 사용하면 된다.

Wrapper Class

  • primitive type을 wrapping하는 class.

  • 대체로 객체들을 활용하는 연산이 많은 OOP 특성상, primitive type 자체를 객체로 생각해야 할 때가 있다.

  • Kotlin의 경우 primitive type을 기본적으로 wrapper로 감싼 객체로 생각하나 Java의 경우에는 그렇지 않다...만 그래도 객체로 활용해야할 때가 있으면 이 wrapper class를 사용한다.

  • 이들은 전부 equals()가 primtive type 때의 comparison과 동일하게 작동하도록 overriding이 되어 있다.

  • toString()도 primitive type 때의 예상되는 String값을 반환하도록 overriding이 되어 있다.

  • static constant로 MAX_VALUE, MIN_VALUE, SIZE, BYTES, TYPE을 가지고 있다는 것도 특징.

Number Class

  • Wrapper은 Boolean, Character, Number로 크게 나눠지며 NumberByte, Short, Integer, Long, Float, Double이 있다.

  • 하지만 Number에는 이거 말고도 BigIntegerBigDecimal이라고 한계가 없는 정수/실수 값을 저장하는 type도 있다.

  • 구현 방식은 이 글 참고

String to numeric

  • 문자열을 primitive로 바꿀려면 parse...를, wrapper로 바꿀려면 valueOf를 사용해야 하는데 JDK 1.5부터 autoboxing 이라고 primtive가 객체형태로 사용되어야 하면 컴파일러가 알아서 바꾸기 때문에 덕분에 둘의 차이가 없어졌다. 다만 valueOf이 약간 더 느리다는 점은 참고.

  • 이때 문자열의 진법이 10진법이 아닌 경우 radix parameter을 활용해가지고 변환하면 된다.

java.util.Objects

  • 객체 nullity check 지원 (isNull, nonNull).

  • 객체 대소 비교 지원 (compare(), 단 Comparator interface 구현 필요)

  • null 확인이 필요 없는 equals(), toString(), hashCode() 제공

  • 재귀적 비교를 통해 multi dimensional array 비교를 가능하게 해주는 deepEquals() 제공.

java.util.Random

  • 난수 형성. Math.Random도 사실 이걸 활용한다.

  • seed를 기반으로 생성. seed가 동일하면 같은 난수들이 나온다.

  • 보통은 seed를 현재 시스템 시간으로 설정한다.

java.util.regex

  • text에서 원하는 pattern과 일치하는 String을 찾아내는데 사용되는 녀석들을 regular expression이라고 하며 이와 관련된 package가 Java에선 위와 같다.

  • 데이터 추출에 유용하다. 문법이 많이 복잡하며 이를 전부 담은 doc은 다음 참고. Pattern class라고 따로 있다.

  • Pattern이 pattern 지정 용도라면 이를 실제 data와 비교하는 녀석도 필요할텐데 이 녀석을 Matcher이라는 class에서 정의한다. matches()라는 method를 활용.

  • 자세한 문법 활용은 생략. 이미 많은 언어에서 유사한 문법을 적용하고 있기에 (심지어 VSC도.) 이미 어느정도 익숙하다고 가정하겠다.

java.util.Scanner

  • JDK 1.5에서 추가

  • IO source로부터 문자 데이터를 읽는데 사용된다.

  • regular expression, delimeter 지원.

  • JDK 1.6에 standard input/output을 담당하는 java.io.Console도 등장했다.

  • 많이 어렵진 않아서, 자세한 내용은 doc 참고

java.util.Stringtokenizer

  • 실질적으로 C의 strtok과 대응되는 녀석이다.

  • delimeter을 기준으로 문자열을 여러 token으로 나눈다.

  • split, ScanneruseDelimeter(String pattern)과의 차이점은 정규식을 꼭 활용하지 않아도 된다는 것이다.

  • C랑 문법이 매우 유사해서 doc만 남기고 내용은 생략.

profile
안 흔하고 싶은 개발자. 관심 분야 : 임베디드/컴퓨터 시스템 및 아키텍처/웹/AI

0개의 댓글

Powered by GraphCDN, the GraphQL CDN