자, 이제 실제로 유틸리티 클래스를 만들어 보자.
단순히 웹 어플리케이션 개발을 할 경우 실제적으로 많이 쓰이는 것은 문자열을 숫자형으로 변환하는 것이다.
문자열을 입력받아 자바 기본 숫자형으로 변환하는 클래스를 만들어보자
별로 쓸 일은 없겠지만 unsigned int 형을 다뤄보자. 자바에는 unsigned 형이 없다. 그래서 int형을 unsigned int 형 처럼 사용할려면 i & 0xfffffffL; 연산을 이용해서 long형에 담으면 부호없는 int 형 처럼 보인다.
테스트 케이스를 만들어보자.
0xFFFFFFFF는 int형일 경우 -1이다. 근데 잘 보면 비교값이 0xFFFFFFFFL 이다. 'L'이란 문자가 붙는다. 이 'L'은 long 타입의 숫자란 뜻이다. 그래서 0xFFFFFFFFL = 4294967295임으로 원하는 결과가 나오게 되는것이다.
이번에 비트(bit) 연산을 한번 해볼까.
'&'(AND) 연산은 두 값이 다 1일때만 1을, 아니면 0을 나타낸다.
예) 1011 & 1101 = 1001
'|'(OR) 연산은 두 값중 하나라도 1일때 1을, 둘다 0이면 면 0을 나타낸다.
예) 1011 & 1101 = 1111
shift 연산
'<<' 은 비트단위만큼 왼쪽으로 이동을 한다는 뜻이다. 오른쪽은 0으로 채운다.
예) 10111010 << 4 = 10100000
'>>' 은 비트단위만큼 오른쪽으로 이동을 한다는 뜻이다. 만일 최상위 비트가 1인 경우 왼쪽을 1로 채우고 아닐 경우 0으로 채운다.
예) 10111010 >> 4 = 11111011
00111010 >> 4 = 00000011
'>>>'은 비트단위만큼 오른쪽으로 이동을 한다는 뜻이다. 단, 무조건 왼쪽을 0으로 채운다.
예) 10111010 >>> 4 = 00001011
00111010 >>> 4 = 00000011
'~'은 보수로서 0을 1로, 1을 0으로 바꾼다.
자바에서는 shift 연산은 지원하는데, rotate 연산은 지원하지 않는다.(아마도..) 그래서 위의 연산을 가지고 roate를 구현해보자
덤으로 long형의 앞 워드(word)와 뒷 워드(word)를 추출하는것도 만들어보자.
단순히 웹 어플리케이션 개발을 할 경우 실제적으로 많이 쓰이는 것은 문자열을 숫자형으로 변환하는 것이다.
문자열을 입력받아 자바 기본 숫자형으로 변환하는 클래스를 만들어보자
/**
* 문자열을 숫자(short)형으로 변환한다.
*
* <pre>
* NumberUtils.toShort(null, 0) = 0
* NumberUtils.toShort("NaN", 0) = 0
* NumberUtils.toShort("1234", 0) = 1234
* </pre>
*
* @param value
* @param defaultValue 기본값
* @return
*/
public static short toShort(String value, short defaultValue) {
if (value == null) {
return defaultValue;
}
short result = defaultValue;
try {
result = Short.parseShort(value);
} catch (NumberFormatException e) {
// ignore
}
return result;
}
/**
* 문자열을 숫자(Short)형으로 변환한다.
*
* <pre>
* NumberUtils.toShortObject(null) = null
* NumberUtils.toShortObject("NaN") = null
* NumberUtils.toShortObject("1234") = 1234
* </pre>
*
* @param value
* @return
*/
public static Short toShortObject(String value) {
if (value == null) {
return null;
}
Short result = null;
try {
result = new Short(Short.parseShort(value));
} catch (NumberFormatException e) {
// ignore
}
return result;
}
/**
* 문자열을 숫자(Short)형으로 변환한다.
* 단, 문자열은 패턴(pattern)과 일치해야한다.
* 패턴과 일치하지 않을 경우에는 null을 반환한다.
*
* <pre>
* NumberUtils.toShortObject(null, *) = null
* NumberUtils.toShortObject("NaN", *) = null
* NumberUtils.toShortObject("1,234", "#,###") = 1234
* NumberUtils.toShortObject("1234", "#,###") = null
* NumberUtils.toShortObject("1234", null) = 1234
* </pre>
*
* @param value
* @param pattern 패턴
* @return
*/
public static Short toShortObject(String value, String pattern) {
if (value == null) {
return null;
}
Short result = null;
if (pattern != null) {
DecimalFormat dFormat = new DecimalFormat(pattern);
try {
result = new Short(dFormat.parse(value).shortValue());
if (value.equals(dFormat.format(result)) == false) {
result = null;
}
} catch (ParseException e) {
// ignore
}
} else {
try {
result = new Short(Short.parseShort(value));
} catch (NumberFormatException e) {
// ignore
}
}
return result;
}
/**
* 문자열을 숫자(int)형으로 변환한다.
*
* <pre>
* NumberUtils.toInteger(null, 0) = 0
* NumberUtils.toInteger("NaN", 0) = 0
* NumberUtils.toInteger("1234", 0) = 1234
* </pre>
*
* @param value
* @param defaultValue 기본값
* @return
*/
public static int toInteger(String value, int defaultValue) {
if (value == null) {
return defaultValue;
}
int result = defaultValue;
try {
result = Integer.parseInt(value);
} catch (NumberFormatException e) {
// ignore
}
return result;
}
/**
* 문자열을 숫자(Integer)형으로 변환한다.
*
* <pre>
* NumberUtils.toIntegerObject(null) = null
* NumberUtils.toIntegerObject("NaN") = null
* NumberUtils.toIntegerObject("1234") = 1234
* NumberUtils.toIntegerObject("-2147483649") = null
* NumberUtils.toIntegerObject("-2147483648") = -2147483648
* NumberUtils.toIntegerObject("2147483647") = 2147483647
* NumberUtils.toIntegerObject("2147483648") = null
* </pre>
*
* @param value
* @return
*/
public static Integer toIntegerObject(String value) {
if (value == null) {
return null;
}
Integer result = null;
try {
result = new Integer(Integer.parseInt(value));
} catch (NumberFormatException e) {
// ignore
}
return result;
}
/**
* 문자열을 숫자(Integer)형으로 변환한다.
* 단, 문자열은 패턴(pattern)과 일치해야한다.
* 패턴과 일치하지 않을 경우에는 null을 반환한다.
*
* <pre>
* NumberUtils.toIntegerObject(null, *) = null
* NumberUtils.toIntegerObject("NaN", *) = null
* NumberUtils.toIntegerObject("1,234", "#,###") = 1234
* NumberUtils.toIntegerObject("1234", "#,###") = null
* NumberUtils.toIntegerObject("1234", null) = 1234
* </pre>
*
* @param value
* @param pattern 패턴
* @return
*/
public static Integer toIntegerObject(String value, String pattern) {
if (value == null) {
return null;
}
Integer result = null;
if (pattern != null) {
DecimalFormat dFormat = new DecimalFormat(pattern);
try {
result = new Integer(dFormat.parse(value).intValue());
if (value.equals(dFormat.format(result)) == false) {
result = null;
}
} catch (ParseException e) {
// ignore
}
} else {
try {
result = new Integer(Integer.parseInt(value));
} catch (NumberFormatException e) {
// ignore
}
}
return result;
}
/**
* 문자열을 숫자(long)형으로 변환한다.
*
* <pre>
* NumberUtils.toLong(null, 0) = 0
* NumberUtils.toLong("NaN", 0) = 0
* NumberUtils.toLong("1234", 0) = 1234
* </pre>
*
* @param value
* @param defaultValue 기본값
* @return
*/
public static long toLong(String value, long defaultValue) {
long result = defaultValue;
try {
result = Long.parseLong(value);
} catch (NumberFormatException e) {
// ignore
}
return result;
}
/**
* 문자열을 숫자(Long)형으로 변환한다.
*
* <pre>
* NumberUtils.toLongObject(null) = null
* NumberUtils.toLongObject("NaN") = null
* NumberUtils.toLongObject("1234") = 1234
* </pre>
*
* @param value
* @return
*/
public static Long toLongObject(String value) {
Long result = null;
try {
result = new Long(Long.parseLong(value));
} catch (NumberFormatException e) {
// ignore
}
return result;
}
/**
* 문자열을 숫자(Long)형으로 변환한다.
* 단, 문자열은 패턴(pattern)과 일치해야한다.
* 패턴과 일치하지 않을 경우에는 null을 반환한다.
*
* <pre>
* NumberUtils.toLongObject(null, *) = null
* NumberUtils.toLongObject("NaN", *) = null
* NumberUtils.toLongObject("1,234", "#,###") = 1234
* NumberUtils.toLongObject("1234", "#,###") = null
* NumberUtils.toLongObject("1234", null) = 1234
* </pre>
*
* @param value
* @param pattern 패턴
* @return
*/
public static Long toLongObject(String value, String pattern) {
if (value == null) {
return null;
}
Long result = null;
if (pattern != null) {
DecimalFormat dFormat = new DecimalFormat(pattern);
try {
result = new Long(dFormat.parse(value).longValue());
if (value.equals(dFormat.format(result)) == false) {
result = null;
}
} catch (ParseException e) {
// ignore
}
} else {
try {
result = new Long(Long.parseLong(value));
} catch (NumberFormatException e) {
// ignore
}
}
return result;
}
/**
* 문자열을 숫자(float)형으로 변환한다.
*
* <pre>
* NumberUtils.toFloat(null, 0) = 0.0
* NumberUtils.toFloat("NaN", 0) = 0.0
* NumberUtils.toFloat("1234.5", 0) = 1234.5
* </pre>
*
* @param value
* @param defaultValue 기본값
* @return
*/
public static float toFloat(String value, float defaultValue) {
if (value == null) {
return defaultValue;
}
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
* 문자열을 숫자(Float)형으로 변환한다.
*
* <pre>
* NumberUtils.toFloatObject(null) = null
* NumberUtils.toFloatObject("AAA") = null
* NumberUtils.toFloatObject("NaN") = Float.NAN
* NumberUtils.toFloatObject("1234.5") = 1234.5
* </pre>
*
* @param value
* @return
*/
public static Float toFloatObject(String value) {
if (value == null) {
return null;
}
try {
return new Float(Float.parseFloat(value));
} catch (NumberFormatException e) {
return null;
}
}
/**
* 문자열을 숫자(Float)형으로 변환한다.
* 단, 문자열은 패턴(pattern)과 일치해야한다.
* 패턴과 일치하지 않을 경우에는 null을 반환한다.
*
* <pre>
* NumberUtils.toFloatObject(null, *) = null
* NumberUtils.toFloatObject("AAA", *) = null
* NumberUtils.toFloatObject("NaN", *) = Float.NAN
* NumberUtils.toFloatObject("1,234", "#,###.#") = 1234.0
* NumberUtils.toFloatObject("1,234.5", "#,###.#") = 1234.5
* NumberUtils.toFloatObject("1234.5", "#,###") = null
* NumberUtils.toFloatObject("1234.5", null) = 1234.5
* </pre>
*
* @param value
* @param pattern 패턴
* @return
*/
public static Float toFloatObject(String value, String pattern) {
if (value == null) {
return null;
}
Float result = null;
if (pattern != null) {
DecimalFormat dFormat = new DecimalFormat(pattern);
try {
result = new Float(dFormat.parse(value).floatValue());
if (value.equals(dFormat.format(result)) == false) {
result = null;
}
} catch (ParseException e) {
// ignore
}
} else {
try {
result = new Float(Float.parseFloat(value));
} catch (NumberFormatException e) {
// ignore
}
}
return result;
}
/**
* 문자열을 숫자(double)형으로 변환한다.
*
* <pre>
* NumberUtils.toDouble(null, 0) = 0.0
* NumberUtils.toDouble("AAA", 0) = 0.0
* NumberUtils.toDouble("NaN", 0) = 0.0
* NumberUtils.toDouble("1234.5", 0) = 1234.5
* </pre>
*
* @param value
* @param defaultValue 기본값
* @return
*/
public static double toDouble(String value, double defaultValue) {
if (value == null) {
return defaultValue;
}
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
* 문자열을 숫자(Double)형으로 변환한다.
*
* <pre>
* NumberUtils.toDoubleObject(null) = null
* NumberUtils.toDoubleObject("NaN") = null
* NumberUtils.toDoubleObject("1234.5") = 1234.5
* </pre>
*
* @param value
* @return
*/
public static Double toDoubleObject(String value) {
if (value == null) {
return null;
}
try {
return new Double(Double.parseDouble(value));
} catch (NumberFormatException e) {
return null;
}
}
/**
* 문자열을 숫자(Double)형으로 변환한다.
* 단, 문자열은 패턴(pattern)과 일치해야한다.
* 패턴과 일치하지 않을 경우에는 null을 반환한다.
*
* <pre>
* NumberUtils.toDoubleObject(null, *) = null
* NumberUtils.toDoubleObject("NaN", *) = null
* NumberUtils.toDoubleObject("1,234", "#,###.#") = 1234.0
* NumberUtils.toDoubleObject("1,234.5", "#,###.#") = 1234.5
* NumberUtils.toDoubleObject("1234.5", "#,###") = null
* NumberUtils.toDoubleObject("1234.5", null) = 1234.5
* </pre>
*
* @param value
* @param pattern 패턴
* @return
*/
public static Double toDoubleObject(String value, String pattern) {
if (value == null) {
return null;
}
Double result = null;
if (pattern != null) {
DecimalFormat dFormat = new DecimalFormat(pattern);
try {
result = new Double(dFormat.parse(value).doubleValue());
if (value.equals(dFormat.format(result)) == false) {
result = null;
}
} catch (ParseException e) {
// ignore
}
} else {
try {
result = new Double(Double.parseDouble(value));
} catch (NumberFormatException e) {
// ignore
}
}
return result;
}
단순히 null 체크나, 변환 실패할 경우만 체크하고 나머지는 자바에서 기본적으로 제공해주는 parserXXX를 사용해서 어려운 부분이 없으니, 설명은 생략하도록하겠다.별로 쓸 일은 없겠지만 unsigned int 형을 다뤄보자. 자바에는 unsigned 형이 없다. 그래서 int형을 unsigned int 형 처럼 사용할려면 i & 0xfffffffL; 연산을 이용해서 long형에 담으면 부호없는 int 형 처럼 보인다.
/**
* 입력한 i값을 unsigned int로 변환한다.
* 자바는 unsinged int가 없기때문에 long형으로 반환한다.
*
* <pre>
* NumberUtils.unsignedInt(1) = 1
* NumberUtils.unsignedInt(0) = 0
* NumberUtils.unsignedInt(-1) = 4294967295
* </pre>
*
* @param i signed int
* @return unsigned int
*/
public static long unsignedInt(int i) {
return i & 0xFFFFFFFFL;
}
테스트 케이스를 만들어보자.
@Test
public void testUnsignedInt() {
Assert.assertEquals(NumberUtils.unsignedInt(0xFFFFFFF0), 0xFFFFFFF0L);
Assert.assertEquals(NumberUtils.unsignedInt(0xFFFFFFFF), 0xFFFFFFFFL);
}
0xFFFFFFFF는 int형일 경우 -1이다. 근데 잘 보면 비교값이 0xFFFFFFFFL 이다. 'L'이란 문자가 붙는다. 이 'L'은 long 타입의 숫자란 뜻이다. 그래서 0xFFFFFFFFL = 4294967295임으로 원하는 결과가 나오게 되는것이다.
이번에 비트(bit) 연산을 한번 해볼까.
'&'(AND) 연산은 두 값이 다 1일때만 1을, 아니면 0을 나타낸다.
예) 1011 & 1101 = 1001
'|'(OR) 연산은 두 값중 하나라도 1일때 1을, 둘다 0이면 면 0을 나타낸다.
예) 1011 & 1101 = 1111
shift 연산
'<<' 은 비트단위만큼 왼쪽으로 이동을 한다는 뜻이다. 오른쪽은 0으로 채운다.
예) 10111010 << 4 = 10100000
'>>' 은 비트단위만큼 오른쪽으로 이동을 한다는 뜻이다. 만일 최상위 비트가 1인 경우 왼쪽을 1로 채우고 아닐 경우 0으로 채운다.
예) 10111010 >> 4 = 11111011
00111010 >> 4 = 00000011
'>>>'은 비트단위만큼 오른쪽으로 이동을 한다는 뜻이다. 단, 무조건 왼쪽을 0으로 채운다.
예) 10111010 >>> 4 = 00001011
00111010 >>> 4 = 00000011
'~'은 보수로서 0을 1로, 1을 0으로 바꾼다.
자바에서는 shift 연산은 지원하는데, rotate 연산은 지원하지 않는다.(아마도..) 그래서 위의 연산을 가지고 roate를 구현해보자
덤으로 long형의 앞 워드(word)와 뒷 워드(word)를 추출하는것도 만들어보자.
/**
* <p>
* long 형의 앞 워드(word=4byte)를 추출한다.
* </p>
*
* @param l
* @return
*/
public static int extractW0(long l) {
return (int) (l >> 32);
}
/**
* <p>
* long 형의 뒤 워드(word=4byte)를 추출한다.
* </p>
*
* @param l
* @return
*/
public static int extractW1(long l) {
return (int) l;
}
/**
* <p>
* 지정한 거리만큼 왼쪽으로 로테이트(rotate)한다.
* </p>
*
* @param i
* @param distance
* 로테이트 할 비트 수
* @return
*/
public static int rotateLeft(int i, int distance) {
return (i << distance) | (i >>> -distance);
}
/**
* <p>
* 지정한 거리만큼 오른쪽으로 로테이트(rotate)한다.
* </p>
*
* @param i
* @param distance
* 로테이트 할 비트 수
* @return
*/
public static int rotateRight(int i, int distance) {
return (i >>> distance) | (i << -distance);
}
/**
* <p>
* 지정한 거리만큼 왼쪽으로 로테이트(rotate)한다.
* </p>
*
*
* @param i
* @param distance
* 로테이트 할 비트 수
* @return
*/
public static long rotateLeft(long i, int distance) {
return (i << distance) | (i >>> -distance);
}
/**
* <p>
* 지정한 거리만큼 오른쪽으로 로테이트(rotate)한다.
* </p>
*
* @param i
* @param distance
* 로테이트 할 비트 수
* @return
*/
public static long rotateRight(long i, int distance) {
return (i >>> distance) | (i << -distance);
}
@Test
public void testExtractW0() {
Assert.assertEquals(NumberUtils.extractW0(0xFFFFFFFF00000000L), 0xFFFFFFFF);
Assert.assertEquals(NumberUtils.extractW0(0x00000001FFFFFFFFL), 0x00000001);
Assert.assertEquals(NumberUtils.extractW0(0x00000000FFFFFFFFL), 0x00000000);
}
@Test
public void testExtractW1() {
Assert.assertEquals(NumberUtils.extractW1(0x00000000FFFFFFFFL), 0xFFFFFFFF);
Assert.assertEquals(NumberUtils.extractW1(0xFFFFFFFF00000000L), 0x00000000);
Assert.assertEquals(NumberUtils.extractW1(0xFFFFFFFF00000001L), 0x00000001);
}
@Test
public void testRotateLeftIntInt() {
Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD, 4), 0x2AB34CDF);
Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD, 8), 0xAB34CDF2);
Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD, 16), 0x34CDF2AB);
Assert.assertEquals(NumberUtils.rotateLeft(0x02AB34CD, 4), 0x2AB34CD0);
}
@Test
public void testRotateRightIntInt() {
Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD, 4), 0xDF2AB34C);
Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD, 8), 0xCDF2AB34);
Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD, 16), 0x34CDF2AB);
Assert.assertEquals(NumberUtils.rotateRight(0x02AB34CD, 4), 0xD02AB34C);
}
@Test
public void testRotateLeftLongInt() {
Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD00000000L, 4), 0x2AB34CD00000000FL);
Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD00000000L, 8), 0xAB34CD00000000F2L);
Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD00000000L, 16), 0x34CD00000000F2ABL);
Assert.assertEquals(NumberUtils.rotateLeft(0x02AB34CD00000000L, 4), 0x2AB34CD000000000L);
}
@Test
public void testRotateRightLongInt() {
Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD00000000L, 4), 0x0F2AB34CD0000000L);
Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD00000000L, 8), 0x00F2AB34CD000000L);
Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD00000000L, 16), 0x0000F2AB34CD0000L);
Assert.assertEquals(NumberUtils.rotateRight(0x02AB34CD00000000L, 4), 0x002AB34CD0000000L);
}
NumberUtils.java