이번에 자바 기초를 다시 랩업 하는 차원에서 20년만에 자바책을 하나를 끝냈다.
제목은 카이호스트만의 "Core Java SE 9 for the Impatient"라는 책이다. 한국에는 "가장 빨리 만나는 코어 자바 9"라는 제목으로 번역이 되었다.
저자는 자바 마스터라는 칭호답게 자바의 기초를 포함하여 자바 프레임워크에 대해 은연 중에 설명을 한다.
(아참 책에 대한 리뷰를 쓰려던 것이 아니지..)
각설하고, Java 를 한지 20년이 되었는데 처음으로 break 레이블 형태를 알게되었다.
처음 봤을 때는 C언어의 GOTO 문이 생각났다.
반복문이 여러 레벨로 중첩이 되어 있어야 하는데 이럴 때 탈출을 쉽게 도와주는 방식이다.
자바 프레임워크에서도 실제 사용한 케이스가 있어서 글을 쓰게 되어싿.
아마 자바를 처음 배우게 되면 사용하게 될 Scanner라는 클래스 내부에서 사용하는 클래스 중에 FloatingDecimal이라는 클래스에서 readJavaFormatString 메서드 내에서 사용되고 있다.
static ASCIIToBinaryConverter readJavaFormatString( String in ) throws NumberFormatException {
boolean isNegative = false;
boolean signSeen = false;
int decExp;
char c;
parseNumber:
try{
in = in.trim(); // don't fool around with white space.
// throws NullPointerException if null
int len = in.length();
if ( len == 0 ) {
throw new NumberFormatException("empty String");
}
int i = 0;
switch (in.charAt(i)){
case '-':
isNegative = true;
//FALLTHROUGH
case '+':
i++;
signSeen = true;
}
c = in.charAt(i);
if(c == 'N') { // Check for NaN
if((len-i)==NAN_LENGTH && in.indexOf(NAN_REP,i)==i) {
return A2BC_NOT_A_NUMBER;
}
// something went wrong, throw exception
break parseNumber;
} else if(c == 'I') { // Check for Infinity strings
if((len-i)==INFINITY_LENGTH && in.indexOf(INFINITY_REP,i)==i) {
return isNegative? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
}
// something went wrong, throw exception
break parseNumber;
} else if (c == '0') { // check for hexadecimal floating-point number
if (len > i+1 ) {
char ch = in.charAt(i+1);
if (ch == 'x' || ch == 'X' ) { // possible hex string
return parseHexString(in);
}
}
} // look for and process decimal floating-point string
char[] digits = new char[ len ];
int nDigits= 0;
boolean decSeen = false;
int decPt = 0;
int nLeadZero = 0;
int nTrailZero= 0;
skipLeadingZerosLoop:
while (i < len) {
c = in.charAt(i);
if (c == '0') {
nLeadZero++;
} else if (c == '.') {
if (decSeen) {
// already saw one ., this is the 2nd.
throw new NumberFormatException("multiple points");
}
decPt = i;
if (signSeen) {
decPt -= 1;
}
decSeen = true;
} else {
break skipLeadingZerosLoop;
}
i++;
}
digitLoop:
while (i < len) {
c = in.charAt(i);
if (c >= '1' && c <= '9') {
digits[nDigits++] = c;
nTrailZero = 0;
} else if (c == '0') {
digits[nDigits++] = c;
nTrailZero++;
} else if (c == '.') {
if (decSeen) {
// already saw one ., this is the 2nd.
throw new NumberFormatException("multiple points");
}
decPt = i;
if (signSeen) {
decPt -= 1;
}
decSeen = true;
} else {
break digitLoop;
}
i++;
}
nDigits -=nTrailZero;
//
// At this point, we've scanned all the digits and decimal
// point we're going to see. Trim off leading and trailing
// zeros, which will just confuse us later, and adjust
// our initial decimal exponent accordingly.
// To review:
// we have seen i total characters.
// nLeadZero of them were zeros before any other digits.
// nTrailZero of them were zeros after any other digits.
// if ( decSeen ), then a . was seen after decPt characters
// ( including leading zeros which have been discarded )
// nDigits characters were neither lead nor trailing
// zeros, nor point
//
//
// special hack: if we saw no non-zero digits, then the
// answer is zero!
// Unfortunately, we feel honor-bound to keep parsing!
//
boolean isZero = (nDigits == 0);
if ( isZero && nLeadZero == 0 ){
// we saw NO DIGITS AT ALL,
// not even a crummy 0!
// this is not allowed.
break parseNumber; // go throw exception
}
//
// Our initial exponent is decPt, adjusted by the number of
// discarded zeros. Or, if there was no decPt,
// then its just nDigits adjusted by discarded trailing zeros.
//
if ( decSeen ){
decExp = decPt - nLeadZero;
} else {
decExp = nDigits + nTrailZero;
}
//
// Look for 'e' or 'E' and an optionally signed integer.
//
if ( (i < len) && (((c = in.charAt(i) )=='e') || (c == 'E') ) ){
int expSign = 1;
int expVal = 0;
int reallyBig = Integer.MAX_VALUE / 10;
boolean expOverflow = false;
switch( in.charAt(++i) ){
case '-':
expSign = -1;
//FALLTHROUGH
case '+':
i++;
}
int expAt = i;
expLoop:
while ( i < len ){
if ( expVal >= reallyBig ){
// the next character will cause integer
// overflow.
expOverflow = true;
}
c = in.charAt(i++);
if(c>='0' && c<='9') {
expVal = expVal*10 + ( (int)c - (int)'0' );
} else {
i--; // back up.
break expLoop; // stop parsing exponent.
}
}
int expLimit = BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
if (expOverflow || (expVal > expLimit)) {
// There is still a chance that the exponent will be safe to
// use: if it would eventually decrease due to a negative
// decExp, and that number is below the limit. We check for
// that here.
if (!expOverflow && (expSign == 1 && decExp < 0)
&& (expVal + decExp) < expLimit) {
// Cannot overflow: adding a positive and negative number.
decExp += expVal;
} else {
//
// The intent here is to end up with
// infinity or zero, as appropriate.
// The reason for yielding such a small decExponent,
// rather than something intuitive such as
// expSign*Integer.MAX_VALUE, is that this value
// is subject to further manipulation in
// doubleValue() and floatValue(), and I don't want
// it to be able to cause overflow there!
// (The only way we can get into trouble here is for
// really outrageous nDigits+nTrailZero, such as 2
// billion.)
//
decExp = expSign * expLimit;
}
} else {
// this should not overflow, since we tested
// for expVal > (MAX+N), where N >= abs(decExp)
decExp = decExp + expSign*expVal;
}
// if we saw something not a digit ( or end of string )
// after the [Ee][+-], without seeing any digits at all
// this is certainly an error. If we saw some digits,
// but then some trailing garbage, that might be ok.
// so we just fall through in that case.
// HUMBUG
if ( i == expAt ) {
break parseNumber; // certainly bad
}
}
//
// We parsed everything we could.
// If there are leftovers, then this is not good input!
//
if ( i < len &&
((i != len - 1) ||
(in.charAt(i) != 'f' &&
in.charAt(i) != 'F' &&
in.charAt(i) != 'd' &&
in.charAt(i) != 'D'))) {
break parseNumber; // go throw exception
}
if(isZero) {
return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
}
return new ASCIIToBinaryBuffer(isNegative, decExp, digits, nDigits);
} catch ( StringIndexOutOfBoundsException e ){ }
throw new NumberFormatException("For input string: \"" + in + "\"");
}
'Programing > JVM(Java, Kotlin)' 카테고리의 다른 글
[Java] switch와 String 그리고 바이트코드 (0) | 2019.03.22 |
---|---|
[JVM] currentTimeMillis vs nanoTime (0) | 2019.03.15 |
[Java] Generic in depth (0) | 2019.01.31 |
Spock Framework 프로젝트에 추가하기 (0) | 2018.10.02 |
[Java8] Optional.of 쓸 때 주의할 점... (0) | 2017.08.07 |