Chapter 09 java.lang 패키지와 유용한 클래스
Java.lang 패키지
- java.lang패키지는 자바 프로그래밍에서 가장 기본이 되는 클래스들을 포함하고 있음
- import문 없이 사용 가능
Object 클래스
- 모든 클래스의 최고 조상
- 멤버변수는 없고 오직 11개 메서드만 보유
- notify(), notifyAll(), wait() 등은 쓰레드와 관련된 메서드
- eqauls(), hashCode(), toString() 은 적절히 오버라이딩 해야함
- 오버라이딩할 때 접근제어자를 public으로 변경 해야함
equals(Object obj)
- 매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean 값으로 알려주는 역할
- 자신(this)의 객체와 obj가 같은 객체인지 알려줌
- 참조변수의 주소 값 비교
- 객체의 값을 비교하고 싶다면 오버라이딩 해야함
- 오버라이딩할 때 비교하고하는 멤버를 맞추기 위해 형변환 해야함. obj는 Object의 인스턴스이기 때문에 자신(this)의 객체의 멤버를 사용할 수 없음
- 형변환하기전에 instaceof로 형변환이 가능한지 체크 할 것. obj가 자신(this)의객체거나 또는 자신(this)의 객체의 조상일 경우 true 값 반환
- 형변환이 가능하다면 자신(this)의 객체로 형변환 함
- 그 후 형변환한 obj와 자신(thjis)의 객체의 멤버 값 비교
- static 변수는 객체마다 공통된 값이기 때문에 주로 인스턴스 변수 값을 비교함
- String, Date, File, Wrapper 클래스의 equals메서드는 이미 주소값이 아닌 내용을 비교하도록 오버라이딩 되어 있음
- StringBuffer 클래스는 오버라이딩 되어 있지 않음
예제1
class prac{
public static void main(String[] args){
Value v1 = new Value(10);
Value v2 = new Value(10);
if(v1.equals(v2)) System.out.println("v1과 v2는 같음");
else System.out.println("v1과 v2는 다름");
v1 = v2;
if(v1.equals(v2)) System.out.println("v1과 v2는 같음");
else System.out.println("v1과 v2는 다름");
}
}
class Value {
int value;
Value(int value) {this.value = value;}
}
/* 출력값
v1과 v2는 다름
v1과 v2는 같음
*/
예제2
class prac {
public static void main(String[] args){
Person p1 = new Person(8011081111222L);
Person p2 = new Person(8011081111222L);
if(p1 == p2) System.out.println("p1과 p2는 같은 사람");
else System.out.println("p1과 p2는 다른 사람");
if(p1.equals(p2)) System.out.println("p1과 p2는 같은 사람");
else System.out.println("p1과 p2는 다른 사람");
}
}
class Person{
long id;
Person(long id){
this.id = id;
}
public boolean equals(Object obj){
if(obj instanceof Person) { // 형변환 가능한지 체크
return this.id == ((Person)obj).id; // 형변환 후 멤버값 비교
}
else return false;
}
}
/* 출력값
p1과 p2는 다른 사람
p1과 p2는 같은 사람
*/
equals 메서드가 Person 인스턴스의 주소값이 아닌 멤버변수 id의 값을 비교하도록 오버라이딩함
hashCode()
- 객체의 해시코드(hash code)를 반환하는 메서드
- 해시코드란 해싱 알고리즘에 의해서 생성된 정수값 (음수 가능)
- 객체의 해시코드란 객체를 식별할 하나의 정수 값 (= 객체의 지문)
- Object 클래스의 hashCode()는 객체의 주소를 int로 변환해서 반환
- eqauls()를 오버라이딩하면, hashCode()도 오버라이딩 해야함
- eqauls()의 결과가 true인 두 객체의 해시코드는 같아야하기 때문
- System.identityHashCode(Object obj) 는 Object 클래스의 hashCode() 메서드와 기능 동일
- 객체마다 다른 해시코드 반환
- hashCode()는 오버라이딩이 가능하지만 System.identityHashCode(Object obj)는 오버라이딩 불가능
- hashCode()를 오버라이딩 했을 때 hashCode() 기능이 바뀌니깐 오버라이딩 이전의 hashCode() 기능이 필요할 때 System.identityHashCode(Object obj) 으로 대신 사용
- System.identityHashCode(Object obj) 의 결과값이 실행마다 달라질 수 있음
- 해싱기법을 사용하는 HashMap이나 HashSet과 같은 클래스에 저장할 객체라면 반드시 hashCode 메서드를 오버라이딩 할 것
hashCode()는 해싱(hashing) 기법에 사용되는 해시함수(hash function)을 구현한 것. 해싱은 데이터 관리기법 중 하나. 다량의 데이터를 저장하고 검색하는데 유용. 해시함수는 찾고자하는 값을 입력하면 그 값이 저장된 위치를 알려주는 해시코드(hash code)를 반환
일반적으로 해시코드가 같은 두 객체가 존재하는 것이 가능하지만, Object 클래스에 정의된 hashCod 메서드는 객체의 주소값으로 해시코드를 만들어 반환하기 때문에 32bit JVM에서는 서로 다른 두 객체는 같은 해시코드를 가질 수 없음. 단, 64 bit JVM에서는 8byte 주소값으로 해시코드(4byte)를 만들기 때문에 해시코드가 중복될 수 있음
예제1
class prac{
public static void main(String[] args){
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1.equals(str2));
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
System.out.println(System.identityHashCode(str1));
System.out.println(System.identityHashCode(str2));
}
}
/* 출력값
true
96354
96354
27134973
1284693
*/
String 클래스는 문자열이 같으면 동일한 해시코드를 반환하도록 이미 hashCode메서드가 오버라이딩 되어 있음
반면 System.identityHashCode(Object obj)는 오버라이딩이 불가능하기 때문에 Obejct 클래스의 hashCode 메서드처럼 객체의 주소값으로 해시코드를 생성하고 모든 객체에 대해 항상 다른 해시코드값을 반환함. 따라서 str1 과 str1의 해시코드는 같지만 서로 다른 객체라는 것을 알 수 있음
Objects.hash(Object... values) 메서드
- 매개 값으로 주어진 값들을 이용해서 해시 코드 생성
- 매개 값들로 배열을 생성하고 Arrays.hashCode(Object[]) 를 호출해서 해시코드 반환
- 이 메서드는 hashCode()를 오버라이딩할 때 리턴값으로 위해 사용됨
- 클래스의 멤버값을 매개변수로 삼기 때문에 동일한 멤버값을 갖는 객체는 동일한 해시코드를 가질 수 있음
public int hashCode(){
return Objects.hash(field1, field2, field3...); // 가변인자를 받기 때문에 매개변수 개수 제한 없음
}
Objects.hashCode(Object obj) 메서드
- 매개값으로 주어진 객체의 해시코드 반환
- obj.hashCode() 와 기능 동일. 단, obj.hashCode() 는 obj 값이 null일 경우 에러 발생. Objects.hashCode(Object obj)는 매개변수인 obj 값이 null 일 경우 0 반환
- import java.util.Objects; 적어야함
- Objects 클래스는 객체와 관련된 유용한 메서드를 제공하는 유틸 클래스
class prac {
public static void main(String[] args){
Card c1 = null;
System.out.println(Objects.hashCode(c1)); // 0
}
}
class Card{}
예제1
import java.util.Objects;
class Card{
int num;
String kind;
Card(String kind, int num){
this.num = num;
this.kind = kind;
}
Card(){this("Spade", 1);}
public boolean equals(Object obj){
if(!(obj instanceof Card)) return false;
Card c = (Card)obj;
return this.kind.equals(c.kind) && this.num == c.num;
}
public int hashCode(){
return Objects.hash(kind, num);
}
}
class prac{
public static void main(String[] args){
Card c1 = new Card("Heart", 5);
Card c2 = new Card("Heart", 5);
System.out.println(c1.equals(c2)); // true
System.out.println(c1.hashCode()); // -2137388960
System.out.println(c2.hashCode()); // -2137388960
System.out.println(System.identityHashCode(c1)); // 2713497
System.out.println(System.identityHashCode(c2)); // 1284693
}
}
해시코드가 음수일 수도 있음
toString()
- 인스턴스에 대한 정보를 문자열(String)로 제공할 목적
- 객체를 문자열(String)으로 변환하기 위한 메서드
- 주로 오버라이딩하여 인스턴스나 클래스에 대한 정보 또는 인스턴스 변수들의 값을 문자열로 반환
- 오버라이딩을 하지 않은채, toString()을 호출하면 클래스이름에 16진수의 해시코드 반환
예제1
class Card{
String kind;
int num;
Card(String kind, int num){
this.kind = kind;
this.num = num;
}
Card(){this("Spade", 1);}
}
class prac{
public static void main(String[] args){
Card c1 = new Card();
Card c2 = new Card();
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
/* 출력값
Card@19e0bfd
Card@139a55
*/
toString() 메서드를 오버라이딩하지 않았기 때문에 클래스이름에 16진수의 해시코드 값을 반환
예제2
import java.util.Date;
class prac{
public static void main(String[] args){
String str = new String("KOREA");
Date today = new Date();
System.out.println(str);
System.out.println(str.toString());
System.out.println(today); // 현재 시각 출력
System.out.println(today.toString()); // 현재 시각 출력
}
}
/* 출력값
KOREA
KOREA
Sat Oct 30 14:18:52 KST 2021
Sat Oct 30 14:18:52 KST 2021
*/
String 클래스와 Date 클래스는 toString() 메서드가 이미 오버라이딩이 되어있음
String 클래스의 toString()은 String 인스턴스가 갖고 잇는 문자열을 반환하도록 오버라이딩 되어있음
Date 클래스의 toString()은 Date 인스턴스가 갖고 있는 날짜와 시간을 문자열로 반환하도록 오버라이딩 되어 있음
예제3
class Card{
String kind;
int num;
Card(String kind, int num){
this.kind = kind;
this.num = num;
}
Card(){this("Spade", 1);}
public String toString(){
return kind + ", " + num;
}
}
class prac{
public static void main(String[] args){
Card c1 = new Card();
Card c2 = new Card("Heart", 10);
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
/* 출력값
Spade, 1
Heart, 10
*/
clone()
- 자신을 복제하여 새로운 인스턴스 생성
- 어떤 인스턴스에 대해 작업할 때, 원래의 인스턴스는 보존하고 clone() 메서드를 이용해서 새로운 인스턴스를 생성하여 작업을 하면 작업 이전의 값이 보존되므로 작업이 실패해서 원래의 상태로 되돌리거나 변경되기 전의 값을 참고할 수 있음
- Cloneable 인터페이스를 구현한 클래스에서만 clone() 호출 가능. Cloneable 인터페이스를 구현하지 않는 클래스에서 clone() 호출을 사용하면 예외 발생
- Cloneable 인터페이스를 구현한 클래스에서도 clone()을 사용할 때 반드시 예외 처리를 해줘야함
- 예외처리할 때 Exception 클래스들의 자손인 CloneNotSupportedException 클래스로 예외처리를 해야함
- 단순히 인스턴스변수 값만 복사하기 때문에 참조변수타입의 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제가 이뤄지지 않음
- 예를들어 배열의 경우, 복제된 인스턴스도 같은 배열의 주소를 갖기 때문에 복제된 인스턴스의 작업이 원래의 인스턴스에 영향을 미침
- 이런 경우 clone() 메서드를 오버라이딩해서 새로운 배열을 생성하고 배열의 내용을 복사할 것
- clone()으로 복제가 가능한 클래스인지 확인하려면 Java API에서 Cloneable을 구현했는지 확인할 것
- java.util 패키지의 배열, Vector, ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, Calendar, Date와 같은 클래스들은 clone() 을 통해서 복제 가능
예제1
class Point implements Cloneable{ // Clonable 인터페이스를 구현한 클래스
int x,y;
Point(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return x + ", " + y;
}
public Point clone(){
Object obj = null;
try{
obj = super.clone();
} catch(CloneNotSupportedException e){}
return (Point)obj;
}
}
class prac{
public static void main(String[] args){
Point original = new Point(3, 5);
Point copy = original.clone();
System.out.println(original); // 3, 5
System.out.println(copy); // 3, 5
}
}
예제2
import java.util.Arrays;
class prac{
public static void main(String[] args){
int[] arr = {1, 2, 3, 4, 5};
int[] arrClone = arr.clone();
arrClone[0] = 6;
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5]
System.out.println(Arrays.toString(arrClone)); // [6, 2, 3, 4, 5]
}
}
배열도 객체이기 때문에 Object 클래스를 상속 받으며, 동시에 Clonable 인터페이스와 Serializable 인터페이스가 구현되어 있음. 그래서 Object 클래스에서 protected로 정의되어있는 clone() 메서드를 배열에서는 이미 public으로 오버라이딩 되어 있기에 직접 호출 가능
배열을 복사할 때 같은 길이의 새로운 배열을 생성한 다음에 System.arraycopy()를 이용해서 내용을 복사할 수 있지만 clone()을 이용해서 간단하게 복사할 수 있다는것
얕은 복사 (shallow copy)
- 객체 안에 있는 참조형 멤버를 복사할 때 주소값을 복사. 그래서 원본 수정시 복사본도 영향이 감
깊은 복사 (deep copy)
- 객체 안에 있는 참조형 멤버를 복사할 때 새로운 메모리 공간에 값을 복사하기 때문에 원본을 수정해도 복사본에 영향이 없음
예제1
class Point{
int x, y;
Point(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "(" + x + ", " + y + ")";
}
}
class Circle implements Cloneable{
Point p;
double r;
Circle(Point p, double r){
this.p = p;
this.r = r;
}
public String toString(){
return "[p = " + p + ", r = " + r + "]";
}
public Circle shallowCopy(){
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e){}
return (Circle)obj;
}
public Circle deepCopy(){
Object obj = null;
try{
obj = super.clone();
} catch(CloneNotSupportedException e){}
Circle c = (Circle)obj;
c.p = new Point(this.p.x, this.p.y);
c.r = this.r;
return c;
}
}
class prac{
public static void main(String[] args){
Circle c1 = new Circle(new Point(1,1), 2.0);
Circle c2 = c1.shallowCopy();
Circle c3 = c1.deepCopy();
System.out.println(c1); // [p = (1, 1), r = 2.0]
System.out.println(c2); // [p = (1, 1), r = 2.0
System.out.println(c3); // [p = (1, 1), r = 2.0]
// 원본 수정
c1.p.x = 9;
c1.p.y = 9;
System.out.println(c1); // [p = (9, 9), r = 2.0]
System.out.println(c2); // [p = (9, 9), r = 2.0]
System.out.println(c3); // [p = (1, 1), r = 2.0]
}
}
얕은 복사를 한 c2는 원본 수정시 c2의 값도 바뀜
깊은 복사를 한 c3는 원본을 수정해도 영향이 없음
getClass()
- 자신이 속한 클래스의 Class 객체를 반환하는 메서드
- 현재 참조하고 있는 클래스를 확인할 수 있는 메서드
- 반환 타입 : Class
- getClass() 메서드는 해당 클래스로 인스턴스를 생성했을때만 사용 가능
- 먼저 기존에 생성된 클래스 객체가 메모리에 존재하는지 확인하고, 있으면 객체의 참조를 반환, 없으면 클래스 패스(classpath)에 지정된 경로를 따라서 클래스 파일을 찾음. 못 찾을시 ClassNotFoundException 발생. 찾으면 해당 클래스 파일을 읽어서 Class 객체로 변환
- 클래스의 정보를 얻는 메서드
- getClass().getName() : 해당 객체의 이름 반환 (패키지명 + 클래스 명 반환)
- getClass().getSimpleName() : 해당 객체의 이름 반환 (오직 클래스 이름만 반환)
- getClass().getSupperclass() : 해당 객체의 상위 클래스 이름 반환
- getClass().getDeclaredFields()[int index] : 해당 객체의 선언된 필드 정보를 배열 형태로 반환
class A{}
class B{}
class prac{
public static void main(String[] args){
A a1 = new A();
B b1 = new B();
System.out.println(a1.getClass()); // class A
System.out.println(b1.getClass()); // class B
}
}
class A{
int x = 5;
int y = 6;
String str = "하하";
}
class prac{
public static void main(String[] args){
A a1 = new A();
System.out.println(a1.getClass()); // class A
System.out.println(a1.getClass().getName()); // A
System.out.println(a1.getClass().getSuperclass()); // class java.lang.Object
System.out.println(a1.getClass().getDeclaredFields()[0]); // int A.x
System.out.println(a1.getClass().getDeclaredFields()[1]); // int A.y
System.out.println(a1.getClass().getDeclaredFields()[2]); // java.lang.String A.str
}
}
Class 객체
- Class 객체는 이름이 'Class'인 클래스의 객체
- Class 객체는 클래스의 모든 정보를 담고 있으며 클래스 당 1개만 존재
- 클래스파일이 '클래스 로더(ClassLoader)'에 의해서 메모리에 올라갈 때, 자동 생성
- 클래스 로더는 클래스 파일을 메모리에 로드하고 변환하는 일을 함
Class 객체 얻는방법
/* Class 객체 얻는 방법 3가지 */
// ex) Card 클래스의 Class객체를 얻는 방법 3가지
Class cObj = new Card().getClass(); // 생성된 객체로 부터 얻는 방법
Class cObj = Card.class; // 클래스 리터럴(*.class)로 부터 얻는 방법
Class cObj = Class.forName("Card"); // 클래스 이름으로 부터 얻는 방법
- forName()은 특정 클래스파일, 예를 들어 데이터베이스 드라이버를 메모리에 올릴 때 주로 사용
- Class 객체를 이용하면 클래스에 정의된 멤버의 이름이나 개수 등, 클래스에 대한 모든 정보를 얻을 수 있기 때문에 Class객체를 통해서 객체를 생성하고 메서드를 호출하는 등 동적인 코드 작성 가능
- 동적으로 객체를 생성하고 메서드를 호출하는 방법에 대해 더 알고싶다면, '리플렉션API(reflection API)' 검색 할 것
Class 객체를 이용한 객체 생성 2가지 방법
Card c = new Card(); // new연산자를 이용해서 객체 생성
// newInstance()는 InstantiationException이 발생할 수 있으므로 예외처리 필요
Card c= Card.class.newInstance(); // Class 객체를 이용해서 객체 생성
예제1
final class Card{
String kind;
int num;
Card(String kind, int num){
this.kind = kind;
this. num = num;
}
Card(){this("Spade", 1);}
public String toString(){
return kind + ", " + num;
}
}
class prac{
public static void main(String[] args) throws Exception{
Card c1 = new Card("Heart", 3); // new 연산자로 객체 생성
Card c2 = Card.class.newInstance(); // Class객체로 객체 생성
Class cObj = c2.getClass();
System.out.println(c1); // Heart, 3
System.out.println(c2); // Spade, 1
System.out.println(cObj.getName()); // Card
System.out.println(cObj.toGenericString()); // final class Card
System.out.println(cObj.toString()); // class Card
}
}
String 클래스
- String 클래스 = 데이터( char[] ) + 메서드(문자열 관련)
- 여기서 데이터란 문자형 배열을 의미함
- 문자열을 다루기 위한 클래스
- 내용을 변경할 수 없는 불변(immutable) 클래스
- 문자열 리터럴은 프로그램 실행시 자동으로 생성됨(JVM 내부의 constant pool에 저장됨)
- 프로그램에 있는 모든 상수는 상수 저장소(constant pool)에 저장됨
- String 클래스는 앞에 fianl이 붙어있으므로 다른 클래스의 조상이 될 수 없음
- 항번 생성된 String 인스턴스의 문자열 값은 읽어올 수 있고 변경할 수 없음. 만약에 값을 재할당하게 되면 기존의 주소값에 문자열 값이 바뀌는게 아니라 새로운 주소값에 새 할당된 값이 들어감
문자열 비교
문자열 만드는 방법 2가지
① 문자열 리터럴 지정
문자열 리터럴은 값을 만들기전에 이미 존재하는 값이 있는지 확인한 후 없으면 새로 만들고, 있으면 기존의 값의 주소를 사용함
문자열 리터럴은 클래스가 메모리에 로드 될 때 자동 생성
ex) String str = "abc";
② String 클래스의 생성자 사용
new 연산자에 의해서 메모리 할당이 이뤄지기 때문에 이미 존재하는 값인지 아닌지 따지지 않고 항상 새로운 String인스턴스가 생성
ex) String str = new String("abc");
문자열 생성시 ① 방식을 선호할 것
String 클래스는 불변 클래스이므로 문자열을 결합할때마다 새로운 객체가 만들어짐. 그러므로 덧셈 연산자를 이용한 문자열 결합은 성능이 떨어짐. 문자열의 결합이나 변경이 잦다면, 내용을 변경 가능한 StringBuffer를 사용할 것
String str1 = "abc"; // 문자열 리터럴 "abc"의 주소가 str1에 저장됨
String str2 = "abc"; // 문자열 리터럴 "abc"의 주소가 str1에 저장됨
String str3 = new String("abc"); // 새로운 String 인스턴스 생성
String str4 = new String("abc"); // 새로운 String 인스턴스 생성
System.out.println(str1 == str2); // true
System.out.println(str3 == str4); // false
System.out.println(str1.equals(str2)); // true
System.out.println(str3.equals(str4)); // true
문자열 리터럴로 String 클래스를 생성할 때, 문자열 리터럴 값이 동일하면 같은 주소값을 참조하고 있음. 그러나 new 생성자로 객체를 생성할 때는 문자열 리터럴 값이 일치하더라도 다른 주소값을 참조하고 있음
빈문자열 (empty string)
- 내용이 없는 문자열
- 크기가 0인 char형 배열을 저장하는 문자열
- 크기가 0인 배열을 생성하는 것은 어느 타입이나 가능
- String을 참조형의 기본값인 null보다는 빈 문자열로 초기화할 것
- char형은 기본값인 \u0000 보다 공백으로 초기화 할 것
String 클래스의 생성자와 메서드
- String 클래스의 메서드에서 String replace(Charsequence old, Charsequence new)이 있는데 여기서 CharSequence 타입은 인터페이스임 CharSequence 인터페이스를 통해 이 인터페이스를 구현한 CharBuff, Segment, String, StringBuffer, StringBuilder 클래스들을 하나로 묶어서 다형성을 통해 참조할 수 있음
기본형 → 문자열
- String 클래스의 ValueOf() 메서드는 지정된 값을 문자열로 변환하여 반환함. 그러나 빈 문자열("")의 덧셈연사자를 통해서도 지정된 값을 문자열로 변환할 수 있음. ValueOf() 메서드가 빈 문자열 덧셈보다 빠르지만 코드 가독성은 빈문자열의 덧셈이 더 뛰어나기 때문에 빈 문자열 덧셈연사자를 쓸 것을 추천 (성능 < 가독성)
- 속도를 더 중요시할 때만 valuseOf() 메서드를 쓸 것
문자열 → 기본형
- 기본형 → 문자열로 형변환하거나 문자열 → 기본형 형변환 할 때 모두 valueOf() 메서드를 사용할 것
- parseInt(), parseLong, getBoolean() 메서드처럼 문자열 → 기본형으로 하는 메서드들의 이름이 다 제각각이라서 문자열 → 기본형으로 형변환 할 때도 래퍼클래스타입.valueOf(String s) 로 통합적으로 쓸 것 (데이터 타입마다 오버라이딩 되어있음)
- 참고로 래퍼클래스타입.valueOf(String s) 의 반환타입은 래퍼 클래스로서 기본형값이 아닌 참조형임. 그러나 기본형 타입으로 autoBoxing으로 자동 형변환도 해줌
- 문자열 → 기본형 으로 형변환할 때 문자열에 공백 또는 문자가 포함되어 있는 경우 변환 시 예외(NumberFormatException)가 발생함. 양 끝공백 제거는 형변환전에 trim() 메서드를 이용할 것
- Wrapper 클래스
- Boolean
- Byte
- Short
- Integer
- Long
- Float
- Double
참고) Integer클래스의 parseInt(String s, int radix) 메서드를 사용하면 16진수 값으로 표현된 문자열 변환 가능
class prac{
public static void main(String[] args){
int result1 = Integer.parseInt("a", 16);
int result2 = Integer.parseInt("b", 16);
System.out.println(result1); // 10
System.out.println(result2); // 11
}
}
대소문자 구별없이 a, b, c, d, e, f 사용 가능
join()
- join()은 static 메서드이며 여러 문자열 사이에 구분자를 넣어서 결합
- 덧셈 연산자로 문자열을 결합하는것보다 join() 메서드를 통해 문자열을 재조합하는것이 더 빠름
예제1
import java.util.Arrays;
class prac{
public static void main(String[] args){
String animals = "dog, cat, bear";
String[] arr = animals.split(", ");
System.out.println(Arrays.toString(arr)); // [dog, cat, bear]
String str = String.join("-", arr);
System.out.println(str); // dog-cat-bear
}
}
StringJoiner 클래스
- import.javja.util.StringJoiner; 추가할 것
- 생성자
- new StringJoiner(CharSequence delimiter)
- new StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
- CharSequence delimiter : 구분자
- CharSequence prefix : 접두사
- CharSequence suffix : 접미사
- 접두사와 접미사는 생략 가능. 단, 생략시 둘 다 동시에 생략 해야함
- 메서드
접근제어자 반환타입 메서드 | 설명 |
public StringJoiner add(CharSequence newElement) | newElement값을 StringJoiner인스턴스의 값의 다음 요소로 추가 |
public int length() | StringJoiner인스턴스의 문자열 표현 길이 반환 |
public StringJoiner merge(StringJoiner other) | other 값을 다음 요소로 추가 other의 접미사, 접두사 값은 추가안됨 other 값이 null 인경우 NullPointException 예외 발생 |
public StringJoiner setEmptyValue(CharSequence emptyValue) | StringJoiner인스턴스의 값 요소가 없을 때 해당 인스턴스를 값을 출력할시 대신 출력할 문자열 |
public String toString() | 문자열 출력 |
예시
import java.util.StringJoiner;
class prac{
public static void main(String[] args){
// add()
StringJoiner sj1 = new StringJoiner("^^");
StringJoiner sj2 = new StringJoiner("^^", "첫" , "끝");
String[] arr1 = {"aa", "bb", "cc", "dd", "ee"};
for(String s : arr1) {
sj1.add(s);
sj2.add(s);
}
System.out.println(sj1); // aa^^bb^^cc^^dd^^ee
System.out.println(sj2); // 첫aa^^bb^^cc^^dd^^ee끝
System.out.println();
// merge()
StringJoiner sj3 = new StringJoiner("^^", "첫" , "끝");
StringJoiner sj4 = new StringJoiner("**", "[" , "]");
String[] arr2 = {"aa", "bb"};
String[] arr3 = {"AA", "BB"};
for(String s : arr2) {
sj3.add(s);
}
for(String s : arr3) {
sj4.add(s);
}
System.out.println(sj3); // 첫aa^^bb끝
System.out.println(sj4); // [AA**BB]
System.out.println(sj4.merge(sj3)); // [AA**BB**aa^^bb]
System.out.println();
// length()
StringJoiner sj5 = new StringJoiner("^^", "첫" , "끝");
String[] arr4 = {"aa", "bb"};
for(String s : arr4) {
sj5.add(s);
}
System.out.println(sj5); // 첫aa^^bb끝
System.out.println(sj5.length()); // 8
System.out.println();
// setEmptyValue()
StringJoiner sj6 = new StringJoiner("^^", "첫" , "끝");
sj6.setEmptyValue("비어있음");
System.out.println(sj6); // 비어있음
System.out.println();
// toString
StringJoiner sj7 = new StringJoiner("^^", "[" , "]");
String[] arr5 = {"aa", "bb"};
for(String s : arr5) {
sj7.add(s);
}
System.out.println(sj7); // [aa^^bb]
System.out.println(sj7.toString()); // [aa^^bb]
}
}
문자 인코딩 변환
- getBytes() 메서드를 통해서 문자열의 문자 인코딩을 다른 인코딩으로 변경 가능
- byte[] getBytes()
- byte[] getBytes(String charsetName)
- UnsupportedEncodingException 예외 처리 필요
- import java.io.UnsupportedEncodingException; 쓸 것
- byte[] getBytes(Charset charset)
- UnsupportedEncodingException 예외 처리 필요
- import java.io.UnsupportedEncodingException; 쓸 것
- 자바는 UTF-16 을 사용함, 문자열 리터럴에 포함되는 문자는 OS의 인코딩 사용. 한글 윈도우즈는 CP949 인코딩 사용
- 사용가능한 문자 인코딩 목록은 System.out.println(java.nio.charset.Charset.availableCharsets()); 을 출력하면 알수 있음
- 서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때는 적절한 인코딩 필요
예제1
import java.io.UnsupportedEncodingException;
import java.util.StringJoiner;
class prac{
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "가";
byte[] bArr1 = str.getBytes();
byte[] bArr2 = str.getBytes("UTF-8");
byte[] bArr3 = str.getBytes("CP949");
// 인코딩
System.out.println(joinByteArr(bArr1)); // [B0:A1]
System.out.println(joinByteArr(bArr2)); // [EA:B0:80]
System.out.println(joinByteArr(bArr3)); // [B0:A1]
// 디코딩
System.out.println(new String(bArr1)); // 가
System.out.println(new String(bArr2, "UTF-8")); // 가
System.out.println(new String(bArr3, "CP949")); // 가
}
static String joinByteArr(byte[] bArr){
StringJoiner sj = new StringJoiner(":", "[", "]");
for(byte b : bArr)
sj.add(String.format("%02X", b)); // 2자리 16진수로 표현
return sj.toString();
}
}
String.format()
- 형식화된 문자열을 만들어내는 메서드
- printf() 와 사용법 일치
substring()
- 문자열 자르기
- substring(int index)
- index 지점부터 끝까지의 문자열 반환
- substring(int beginIndex, int endIndex)
- beginIndex 지점부터 endIndex 지점까지 문자열 반환 (단, endIndex 지점 문자열은 포함 안됨)
예제
class prac{
public static void main(String[] args){
String fileName = "Hello.java";
// 문자열 자르기 substring() 이용
int index = fileName.indexOf('.');
System.out.println(fileName.substring(0, index)); // Hello
System.out.println(fileName.substring(index+1)); // java
// 문자열 자르기 split() 이용
System.out.println( fileName.split("[.]")[0] ); // Hello
System.out.println( fileName.split("[.]")[1] ); // java
}
}
split() 메서드 사용시 분리자를 .(점)으로 하려면 정규표현식인 [.] 으로 표현해야함
StringBuffer 클래스
- 문자열을 저장 & 다루기 위한 클래스
- String 클래스처럼 문자열 배열( char[] )을 내부적으로 가지고 있음
- String 클래스와 달리 내용을 변경할 수 있음
- 기본적으로 String으로 문자열을 다루지만 문자열의 조작이 많이 필요할 경우 StringBuffer를 사용할 것
- StringBuffer의 크기를 지정하지 않으면 기본 값은 16. 웬만하면 크기를 지정할 것
- Sting 클래스와 달리 equals()를 오버라이딩 하지 않음
- StringBuffer를 String으로 변환 후에 equals()로 비교할 것
String 같은 경우에는 내용 변경이 안되서 값을 재할당할 시 새로운 참조값의 주소와 새로운 메모리가 필요로 했는데 StringBuffer는 내용 수정이 가능하다보니 값 재할당시 기존의 참조값의 주소와 기존의 할당된 메모리를 그대로 재사용
StringBuffer 메서드
예제1
class prac{
public static void main(String[] args){
StringBuffer sb1 = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println(sb1 == sb2); // false
System.out.println(sb1.equals(sb2)); // false
// StringBuffer 내용을 String으로 변환
String s1 = sb1.toString(); // String s1 = new String(sb1); 와 일치
String s2 = sb2.toString();
System.out.println(s1.equals(s2)); // true
}
}
StringBuffer 클래스는 equals() 메서드가 오버라이딩이 되어있지 않아서 등가비교연산자(==) 와 같은 결과를 얻음
반면에 toString()은 오버라이딩 되어 문자열을 String으로 반환
예제2
class prac{
public static void main(String[] args){
StringBuffer sb1 = new StringBuffer("01");
StringBuffer sb2 = sb1.append(23);
sb1.append('4').append("56");
StringBuffer sb3 = sb1.append(78);
sb3.append(9.0);
System.out.println(sb1); // 0123456789.0
System.out.println(sb2); // 0123456789.0
System.out.println(sb3); // 0123456789.0
System.out.println(sb1.deleteCharAt(10)); // 01234567890
System.out.println(sb1.delete(3,6)); // 01267890
System.out.println(sb1.insert(3, "abc")); // 012abc67890
System.out.println(sb1.replace(6, sb1.length(), "END")); // 012abcEND
System.out.println(sb1.capacity()); // 18
System.out.println(sb1.length()); // 9
sb1.setLength(20);
System.out.println(sb1.capacity()); // 38
}
}
StringBuilder
- StringBuffer와 동일한 클래스
- StringBuffer와 차이점은 StringBuffer는 동기화 되어있고, StringBuilder는 동기화 안되어있음
- 동기화란 멀티 쓰레드를 안전(thread-safe)하게 하는 것
- 동기화는 성능을 떨어뜨림
- 싱글 쓰레드 : 한번에 1개 작업
- 멀티 쓰레드 : 한번에 여러개 작업
- 멀티 쓰레드 프로그램이 아닌경우 동기화는 불필요한 성능저하를 일으키므로 싱글 쓰레드일 때는 StringBuffer 보단 StringBuilder를 쓸 것
Math 클래스
- 수학관련 static 메서드의 집합
- Math 클래스의 생성자는 접근제어자가 private이기 때문에 다른 클래스에서 생성 불가능하고 클래스 내 인스턴스 변수가 없으므로 할 이유도 없음
- Math 클래스의 상수는 오직 2개
public static final double E = 2.7182818284590452354; // 자연로그의 밑
public static final double PI = 3.1459265358979323846; // 원주율
올림, 버림, 반올림
- round()
- 항상 소수점 첫째 자리에서 반올림해서 정수값(long)으로 반환
- 원하는 자리수에서 반올림된 값을 얻기 위해서는 10의 n제곱한 후 round()를 사용하고 다시 10의 n승을 나누면 됨
예제
class prac{
public static void main(String[] args){
double d1 = 90.7552;
d1 *= 100; // 9075.52
d1 = Math.round(d1); // 9076
d1 /= 100.0;
System.out.println(d1); // 90.76
}
}
소수 셋째 자리에서 반올림하기
예제2
class prac{
public static void main(String[] args){
// 올림
System.out.println(Math.ceil(1.1)); // 2.0
System.out.println(Math.ceil(-1.1)); // -1.0
// 내림
System.out.println(Math.floor(1.5)); // 1.0
System.out.println(Math.floor(-1.5)); // -2.0
// round 반올림
System.out.println(Math.round(1.5)); // 2
System.out.println(Math.round(-1.5)); // -1
// rint 반올림
System.out.println(Math.rint(1.5)); // 2.0
System.out.println(Math.rint(-1.5)); // -2.0
System.out.println(Math.rint(2.5)); // 2.0
System.out.println(Math.rint(-2.5)); // -2.0
}
}
rint()는 round()처럼 소수 첫 째자리에서 반올림하지만 반환값이 double임. 그리고 rint()는 두 정수의 정 가운데에 있는 값은 가장 가까운 짝수 정수 반환
예제3
class prac{
public static void main(String[] args){
double sum = 0, sum1=0, sum2=0;
for(double d = 1.5; d<=10.5; d++) {
double d1 = Math.round(d);
double d2 = Math.rint(d);
System.out.printf("%4.1f %4.1f %4.1f%n", d, d1, d2);
sum += d;
sum1 += d1;
sum2 += d2;
}
System.out.println("----------------");
System.out.printf("%4.1f %4.1f %4.1f%n", sum, sum1, sum2);
}
}
/* 출력값
1.5 2.0 2.0
2.5 3.0 2.0
3.5 4.0 4.0
4.5 5.0 4.0
5.5 6.0 6.0
6.5 7.0 6.0
7.5 8.0 8.0
8.5 9.0 8.0
9.5 10.0 10.0
10.5 11.0 10.0
----------------
60.0 65.0 60.0
*/
round()와 rint() 비교
예외를 발생시키는 연산 메서드
- 정수형의 연산에서 발생할 수 있는 오버플로우(overflow)를 감지하기 위한 것
- 오버플로우가 발생하면 예외(ArithmeticException) 발생
- 부호 연산식 '-a' 는 '~a+1' 와 일치
- Math 클래스에 포함된 메서드
반환타입 메서드 | 설명 |
int addExact(int x, int y) int addExact(long x, long y) |
x + y |
int subtractExcat(int x, int y) int subtractExcat(long x, long y) |
x - y |
int multiplyExact(int x, int y) int multiplyExact(long x, long y) |
x * y |
int incrementExact(int a) int incrementExact(long a) |
a++ |
int decrementExact(int a) int decrementExact(long a) |
a-- |
int negateExact(int a) int negateExact(long a) |
-a |
int toIntExact(long value) | (int)value int로 형변환 |
예제1
class prac{
public static void main(String[] args){
int i = Integer.MIN_VALUE; // int타입의 최소값
System.out.println(i); // -2147483648
System.out.println(-i); // -2147483648
try{
System.out.println(Math.negateExact(10)); // -10
System.out.println(Math.negateExact(-10)); // 10
System.out.println(Math.negateExact(i)); // 예외발생
} catch(ArithmeticException e){
// i 를 long타입으로 형변환 다음에 negateExact(long a) 호출
System.out.println( Math.negateExact((long)i) ); // 2147483648
}
}
}
- System.out.println(-i); 에서 부호가 바뀌지 않고 값이 그대로인 이유는 -i 는 ~i+1 로 계산하는데 여기서오버플로우가 발생했기 때문임
- 오버플로우가 발생하면 i 를 long 타입으로 형변환하여 negateExact 메서드 호출
삼각함수와 지수, 로그
반환타입 메서드 | 설명 |
double pow(double a, double b) | a^b (거듭제곱) |
double sqrt(double a) | a의 양의 제곱근 |
double atan2(double y, double x) | arc tan 값 반환(0,0)과의 좌표(x, y)의 각도(라디안)로 반환 (길이를 각으로 반환) 라디안을 각도로 변환하고 싶으면 라디안 * 180/Math.PI 또는 Math.toDegrees(라디안) 으로 계산 atan보다 주로 atan2가 자주 쓰임 반환 값 범위 : -π ~ π 까지라서 360° 범위 전체 구할 수 있음 |
double atan(double a) | arc tan 값 반환 (길이를 각으로 반환) 반환 값 범위 : -π/2 ~ π/2 |
double tan(double a) | 라디안 a의 탄제트 값 (각을 길이로 반환) |
double cos(double a) | 라디안 a의 코사인값 (각을 길이로 반환) |
double acos(double a) | arc cos값 반환 (길이를 각으로 반환) 반환 값 범위 : 0 ~ π a 인자가 NaN 이거나 1보다 크면 NaN 결과 반환 |
double sin(double a) | 라디안 a의 사인값 (각을 길이로 반환) |
double asin(double a) | arc sin값 반환 (길이를 각으로 반환) 반환 값 범위 : -π/2 ~ π/2 a 인자가 NaN 이거나 1보다 크면 NaN 결과 반환 a 인자가 0이면 0 결과 반환 |
double log(double a) | 로그값 |
double toDegrees(double angrad) | 호도법의 라디안 값을 육십분법의 각도 값으로 변환 |
double toRadians(double angdeg) | 육십분법의 각도 값을 호도법의 라디안 값으로 변환 |
예제1
class prac{
public static void main(String[] args){
int x1 = 1, y1 = 1; // (1, 1)
int x2 = 2, y2 = 2; // (2, 2)
double c = Math.sqrt(Math.pow(x2-x1, 2)+ Math.pow(y2-y1, 2)); // (1,1), (2,2) 사이 거리
double a = c * Math.sin(Math.PI/4);
double b = c * Math.sin(Math.PI/4);
System.out.println(a); // 1.0
System.out.println(b); // 1.0
System.out.println(c); // 1.414214
System.out.println(Math.atan2(a, b)); // 0.7853981
System.out.println(Math.atan2(a, b) * 180/Math.PI); // 45.0
System.out.println(Math.log10(2)); // 0.3010299956639812
}
}
StrictMath 클래스
- Math 클래스는 최대한 성능을 얻기 위해 JVM이 설치된 OS 메서드 사용
- OS에 의존적이라 부동소수점 계산의 경우, 반올림 처리 방법이 OS 마다 다를 수 있기에 컴퓨터마다 결과가 다를 수 있음
- 이러한 차이를 없애기 위해서 성능은 다소 포기하는 대신 어떤 OS에서 실행되어도 항상 같은 결과를 얻도록 Math 클래스를 새로 작성한 것이 StrictMath 클래스
래퍼(wrapper) 클래스
- 8개의 기본형을 객체화 시킨 클래스
- 8개의 기본형을 객체로 다뤄야할 때 사용하는 클래스
- 기본형은 객체가 아님. 기본형이 존재하는 이유는 성능 때문임. 기본형이 참조형보다 더 빠름
- 기본형 변수를 객체로 다뤄야 하는 경우가 있음. 매개변수로 객체를 요구할 때, 기본형 값이 아닌 객체로 저장해야 할 때. 객체간의 비교가 필요할 때, 등등 기본형 값들을 객체로 변환하여 작업 수행 해야함. 이때, 사용 되는 것이 래퍼(wraaper) 클래스
- 래퍼클래스의 생성자는 매개변수로 문자열이나 각 자료형의 값들을 인자로 받음
- 래퍼 클래스들은 모두 equals() 메서드가 오버라이딩 되어 있어 주소값이 아닌 개체가 가지고 있는 값을 비교가능하고 compareTo() 메서드로도 비교 가능
- 래퍼 클래스에 toString() 메서드가 오버라이딩 되어 있어 객체가 가지고 있는 값을 문자열로 변환하여 반환
- 래퍼 클래스는 MAX_VALUE, MIN_VALUE, SIZE, BYTES, TYPE 등 static 상수를 공통적으로 가지고 있음
예제1
class prac{
public static void main(String[] args){
Integer i = new Integer(100);
Integer i2 = new Integer(100);
System.out.println(i == i2); // false
System.out.println(i.equals(i2)); // true
System.out.println(i.compareTo(i2)); // 0
System.out.println(Integer.MIN_VALUE); // -2147483648
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.SIZE); // 32
System.out.println(Integer.BYTES); // 4
System.out.println(Integer.TYPE); // int
}
}
Number 클래스
- 모든 숫자 래퍼 클래스의 조상이며 추상 클래스임
- 기본형 중에서 숫자와 관련된 래퍼 클래스들은 모두 Number 클래스의 자손
- BigInteger : 아주 큰 정수. long으로 다룰 수 없는 큰 범위 정수를 처리하기 위한 것
- BigDecimal : 아주 큰 실수. double로도 다룰 수 없는 큰 범위 실수를 처리하기 위한 것
문자열을 숫자로 변환하기
문자열 → 기본형 | 문자열 → 래퍼 클래스 |
byte b = Byte.parseByte("100"); | Byte b = Byte.valueOf("100"); |
short s = Short.parseShort("100"); | Short s = Short.valueOf("100"); |
int i = Integer.parseInt("100"); | Integer i = Integer.valueOf("100"); |
long l = Long.parseLong("100"); | Long l = Long.valueOf("100"); |
float f = Float.parseFloat("3.14"); | Float f = Float.valueOf("3.14"); |
double d = Double.parseDouble("3.14"); | Double d = Double.valueOf("3.14"); |
- 타입.parse타입(String s) 메서드의 반환값은 기본형(primitive type)
- 타입.valueOf() 메서드는의 반환값은 래퍼 클래스 타입
- 그냥 구별없이 valueOf() 메서드를 써도 알아서 자동형변환을 해줘서 구별없이 valueOf() 메서드를 사용해도 상관 없음. 그러나 반환타입이 안맞는 경우에는 성능이 조금 떨어질 수 있음
문자열이 10진수가 아닌 다른 진법(radix)의 숫자일 때도 변환 가능
// 메서드 선언부
static int parseInt(String s, int radix) // 뭔자열 s를 radix 진법으로 인식
static Integer valueOf(String s, int radix)
예제
class prac{
public static void main(String[] args){
int i = Integer.parseInt("100", 2); // 100(2) → 4
int i2 = Integer.parseInt("100", 8); // 100(8) → 64
int i3 = Integer.parseInt("100", 16); // 100(16) → 256
int i4 = Integer.parseInt("FF", 16); // FF(16) → 255
// int i5 = Integer.parseInt("FF"); // NumberFormatException 예외 발생.
// 진법을 명시 안할 시 기본값인 10진수로 표현하는데 10진수에서는 F를 허용 안함
System.out.println(i); // 4
System.out.println(i2); // 64
System.out.println(i3); // 256
System.out.println(i4); // 255
}
}
오토박싱 & 언박싱 (atuoboxing & unboxing)
- 기본형과 참조형간의 자동형변환
- 오토박싱 : 기본형 → 래퍼 클래스의 객체로 자동 변환
- ex) int → Integer
- 언박싱 : 래퍼 클래스 객체 → 기본형으로 자동 변환
- ex) Integer → int
- 컴파일러가 자동으로 변환해줌
언박싱 메서드 (래퍼 클래스 객체 → 기본형) | 오토박싱 메서드 (기본형 → 래퍼 클래스의 객체) |
int intValue() | new Integer(int i) |
long longValue() | new Long(long l) |
float floatValue() | new Float(float f) |
double doubleValue() | new Double(double d) |
byte byteValue() | new Byte(byte b) |
short shortValue() | new Short(short s) |
컴파일러가 저 메서드들을 이용해서 자동으로 형변환해줌
예제1
class prac{
public static void main(String[] args){
int i = 10;
System.out.println(i); // 10
Integer intg = i; // Integer intg = Integer.valueOf(i);
System.out.println(intg); // 10
Object obj = i; // Object obj = (Object)Integer.valueOf(i);
System.out.println(obj); // 10
Long lng = 100L; // Long lng = new Long(100L);
System.out.println(lng); // 100
int i2 = intg + 10; // 참조형과 기본형간의 연산 가능
System.out.println(i2); // 20
long l = intg + lng; // 참조형간의 연산 가능
System.out.println(l); // 110
Integer intg2 = new Integer(20);
System.out.println(intg2); // 20
int i3 = intg2; // 참조형을 기본형으로 형변환
System.out.println(i3); // 20
Integer intg3 = intg2 + i3;
System.out.println(intg3); // 40
}
}
java.util.Objects 클래스
- import java.util.Objects; 써줘야함
- Object클래스의 보조 클래스로 Math클래스처럼 모든 메서드가 static
- 객체의 비교나 널 체크(null check)에 유용
Objects 클래스 메서드
- Objects클래스의 requireNonNull()은 해당 객체가 null이 아니어야 하는 경우에 사용. 해당 메서드를 사용할 시 해당 객체의 null인지 아닌지 유효성 검사 가능
- Objects클래스의 equals() 메서드와 deepEquals() 메서드는 Object 클래스의 equals()와 달리 null 검사를 안해도 됨
- Objects클래스의 equals() 메서드 내부에서 null 검사를 하기 때문에 null 검사를 위한 조건식을 따로 넣지 않아도 됨
- Objects클래스의 toString() 메서드는 Object클래스의 toString() 메서드와 달리 null 검사를 진행함
- 보통은 클래스에 선언된 인스턴스의 변수들의 hashCode()를 조합해서 반환하도록 hashCode()를 오버라이딩하는데, 그 대신 Objects클래스의 hash() 메서드를 사용하면 더 편리함
requireNonNull()
void setName(String name){
if (name == null){
throw new NullPointerException("비어있음");
}
this.name = name;
}
▼ 매개변수의 유효성 검사 없이 쓸 수 있음▼
import java.util.Objects;
void setName(String name){
this.name = Objects.requireNonNull(name, "비어있음");
}
예제1
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
class prac{
public static void main(String[] args){
String[][] str2D = new String[][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
for(String[] tmp : str2D) {
System.out.print(Arrays.toString(tmp));
} // [aaa, bbb][AAA, BBB]
System.out.println();
String[][] str2D_2 = new String[][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
for(String[] tmp : str2D_2) {
System.out.print(Arrays.toString(tmp));
} // [aaa, bbb][AAA, BBB]
System.out.println();
System.out.println(Objects.equals(str2D, str2D_2)); // false
System.out.println(Objects.deepEquals(str2D, str2D_2)); // true
System.out.println(Objects.isNull(null)); // true
System.out.println(Objects.nonNull(null)); // false
System.out.println(Objects.hashCode(null)); // 0
System.out.println(Objects.toString(null)); // null
System.out.println(Objects.toString(null, "비어있음")); // 비어있음
Comparator c = String.CASE_INSENSITIVE_ORDER;
System.out.println(Objects.compare("aa", "bb", c)); // -1
System.out.println(Objects.compare("bb", "aa", c)); // 1
System.out.println(Objects.compare("ab", "AB", c)); // 0
}
}
java.util.Random 클래스
- 난수(임의의 수)를 얻을 때 사용
- import java.util.Random; 쓸 것
- Math.random()은 Random 클래스의 인스턴스를 생성해서 사용하는 것
- Math.random() 와 Randmom 클래스의 차이는 Random클래스는 종자값(seed)을 설정할 수 있다는 것
- 컴퓨터는 기본적으로 난수를 만들 수 없음. 그래서 난수 생성 알고리즘을 통해 난수를 생성함. 이 때 난수 알고리즘으로 쓰이는것이 종자값(seed)
- 종자값이 같은 Random 인스턴스들은 항상 같은 난수를 같은 순서로 같은 결과 반환
- 종자값이 같은 Random 인스턴스들끼리는 결과가 같음
// 두 문장은 서로 동일한 문장
double number = Math.random();
double number = new Random().nextDouble();
int num = (int)(Math.random() * 6) +1;
Random 클래스 생성자
public Random(){
this(System.currentTimeMillis()); // Random(long seed) 호출
}
public Random(long seed){}
System.currentTimeMillis() 는 현재시간을 천분의 1초 단위로 변환해서 반환
메서드
반환타입 메서드 | 설명 |
boolean nextBoolean() | boolean 타입의 난수 반환 |
void nextBytes(byte[] bytes) | bytes배열에 byte 타입 난수를 채워서 반환 |
double nextDouble() | double 타입 난수 반환 (0.0 ≤ 반환값 < 1.0) |
float nextFloat() | float 타입의 난수 반환 (0.0 ≤ 반환값 < 1.0) |
double nextGaussian() | 평균은 0.0이고 표준편차는 1.0인 가우시안(Gaussian) 분포에 따른 double형 난수 반환 |
int nextInt() | int 타입 난수 반환 (반환값 범위는 int 타입 범위) |
int nextInt(int n) | 0 ≤ 반환값 < n 까지의 범위에 있는 int 값 반환 |
long nextLong() | long 타입의 난수 반환 (반환값 범위는 long 타입 범위) |
void setSeed(long seed) | 종자값 변경 |
예제1
import java.util.Random;
import java.util.StringJoiner;
class prac{
public static void main(String[] args){
Random rand = new Random(1);
Random rand2 = new Random(1);
StringJoiner sj = new StringJoiner(", ", "[", "]");
StringJoiner sj2 = new StringJoiner(", ", "[", "]");
for(int i=0; i<5; i++) {
sj.add(rand.nextInt() +"");
sj2.add(rand2.nextInt() +"");
}
System.out.println(sj); // [-1155869325, 431529176, 1761283695, 1749940626, 892128508]
System.out.println(sj2); // [-1155869325, 431529176, 1761283695, 1749940626, 892128508]
}
}
종자값이 같으므로 결과값도 같음
예제2
import java.util.Random;
class prac{
public static void main(String[] args){
final int num = 100;
int[] counter = new int[10];
for(int i=0; i<num; i++){
counter[new Random().nextInt(10)]++;
}
for(int i=0; i<counter.length; i++){
String str = printGraph('#', counter[i]);
System.out.printf("%d의 개수 : %s %d%n", i, str, counter[i]);
}
}
static String printGraph(char ch, int value){
char[] bar = new char[value];
for(int i=0; i<value; i++){
bar[i] = ch;
}
return new String(bar);
}
}
/* 출력값
0의 개수 : ##### 5
1의 개수 : ########### 11
2의 개수 : ############# 13
3의 개수 : ############## 14
4의 개수 : ###### 6
5의 개수 : ############ 12
6의 개수 : ########### 11
7의 개수 : ####### 7
8의 개수 : ########### 11
9의 개수 : ########## 10
*/
난수의 배열을 출력하기 때문에 출력할 때 마다 값이 달라짐
응용 메서드
- 자주 사용되는 메서드이므로 만들어놓으면 유용
// form과 to 범위의 정수(int)값 반환. from과 to 모두 범위에 포함
public static int getRand(int from, int to){
return (int)(Math.random() * (Math.abs(to-from)+1)) + Math.min(from, to);
}
// 배열 arr을 from과 to 범위의 값들로 채워서 반환
public static int[] fillRand(int[] arr, int from, int to){
for(int i=0; i<arr.length; i++) {
arr[i] = getRand(from, to);
}
return arr;
}
// 배열 arr을 배열 data에 있는 값들로 채워서 반환
public static int[] fillRand(int[] arr, int[] data){
for(int i=0; i<arr.length; i++){
arr[i] = data[getRand(0, data.length-1)];
}
return arr;
}
예제1
import java.util.Arrays;
class prac{
public static void main(String[] args){
for(int i=0; i<10; i++)
System.out.print(getRand(5, 10) + ", ");
System.out.println();
int[] result = fillRand(new int[10], new int[]{2, 3, 7, 5});
System.out.println(Arrays.toString(result));
}
public static int getRand(int from, int to){
return (int)(Math.random() * (Math.abs(from - to)+1)+ Math.min(from, to));
}
public static int[] fillRand(int[] arr, int from, int to){
for(int i=0; i<arr.length; i++)
arr[i] = getRand(from, to);
return arr;
}
public static int[] fillRand(int[]arr, int[]data){
for(int i=0; i<arr.length; i++)
arr[i] = data[getRand(0, data.length-1)];
return arr;
}
}
/* 출력값 (출력할 때마다 달라짐)
8, 10, 10, 8, 5, 9, 9, 5, 8, 10,
[2, 5, 7, 5, 7, 5, 7, 5, 5, 3]
*/
예제2
import java.util.Arrays;
class prac{
final static int RECORD_NUM = 10;
final static String TABLE_NAME = "TEST_TABLE";
final static String[] CODE1 = {"010", "011", "017", "018", "019"};
final static String[] CODE2 = {"남자", "여자"};
final static String[] CODE3 = {"10대", "20대", "30대", "40대", "50대"};
public static void main(String[] args){
for(int i=0; i<RECORD_NUM; i++) {
System.out.printf("%s %s %s %d%n", getArr(CODE1), getArr(CODE2)
, getArr(CODE3), getRand(100, 200));
}
}
static public int getRand(int from, int to) {
return (int)(Math.random() * (Math.abs(to-from)+1)) + Math.min(from, to);
}
static public int getRand(int n) {return getRand(0, n);}
static public String getArr(String[] arr) {
return arr[getRand(arr.length-1)];
}
}
/* 출력값 (출력할 때마다 달라짐)
011 남자 40대 110
018 남자 20대 127
010 남자 10대 106
019 남자 40대 165
011 남자 30대 182
011 여자 20대 189
010 남자 10대 143
017 남자 10대 115
011 남자 50대 198
010 여자 30대 105
*/
불연속적인 값을 배열에 저장한 후, 배열의 index를 임의로 얻어서 배열에 저장된 값을 읽어옴
정규식 (Regular Expression)
- java.util.regex 패키지
- 정규식이란 텍스트 데이터 중에서 원하는 조건(패턴, pattern)과 일치하는 문자열을 찾아내기 위해 사용하는 것
- import java.util.regex.*; 쓸 것
- Java API문서에 java.util.regex.Pattern을 보면 정규식 다 나와있음
- Pattern 클래스는 정규식을 정의하는데 사용
- Matcher 클래스는 정규식(패턴)을 데이터와 비교하는 역할
Pattern 클래스
반환타입 메서드 | 설명 |
static Pattern complie(String regex) static Pattern complie(String regex, int flags) |
주어진 정규표현식으로부터 패턴 생성 정규표현식 패턴을 생성할 때 flags로 조건 추가 가능 |
int flags() | Pattern 인스턴스의 compile 메서드의 2번째 매개변수인 flags 값 반환 (없으면 0 반환) |
Matcher matcher(CharSequence input) | 패턴에 입력할 문자열을 입력해 Matcher 생성 |
static boolean matches(String regex, CharSequence input) | regex 값인 정규식을 input에 비교. 일치하면 true |
String pattern() | 컴파일된 정규표현식을 String형태로 반환 |
String[] split(CharSequence input) String[] split(CharSequence input, int limit) |
패턴이 일치하는 항목을 중심으로 input 문자열 쪼개기 limit 값은 쪼개는 횟수를 의미 (= String배열 요소 개수) |
String toString() | 패턴을 문자열로 반환 |
Pattern flags
- static 변수이므로 사용시 Pattern.변수명 으로 사용 할 것
- flags란 정규식을 매칭하는 방식에 비트 마스크를 적용한 것
flags변수 | 설명 |
static int CASE_INSENSITIVE | 대소문자 구별 안함 |
static int COMMENTS | 패턴의 공백 및 주석 허용 |
Matcher 클래스 메서드
반환타입 메서드 | 설명 |
Pattern pattern | matcher가 해석한 패턴 반환 |
Matcher usePattern(Pattern newPattern) | matcher가 사용할 Pattern 변경 |
Matcher reset() Matcher reset(CharSequence input) |
matcher 설정 재설정 matcher가 분석할 문자열 변경 |
boolean matches() | 전체 문자열과 패턴이 일치하는 경우 true 반환 |
boolean find() boolean find(int start) |
패턴이 일치하는 문자열을 찾음. 찾으면 true 반환하고 그 위치로 이동 start 값 지정시 start값 위치 이후부터 매칭검색 수행 |
int start() int start(int group) int start(String name) |
매칭되는 문자열 시작 위치 반환 매칭 문자열 중 group번째 문자열의 시작 인덱스 값 반환 ( start(0) = start() ) 매칭 문자열 중 name을 지정한 그룹의 시작 인덱스 값 반환 |
int end() int end(int group) int end(String name) |
매칭되는 문자열 끝 다음 문자 인덱스 값 반환 지정된 그룹이 매칭되는 끝 다음 문자 인덱스 값 반환 ( end(0) = end() ) 매칭 문자열 중 name을 지정한 그룹의 마지막 문자열 끝 다음 문자 인덱스값 반환 |
String group() String group(int group) String group(String name) |
전체 패턴에서 그룹핑된 패턴에 일치하는 모든 문자열을 가져옴 매칭된 부분중 group번째 그룹만 문자열로 반환 ( group(0) = group() ) 매칭되는 문자열 중 해당 name을 지정한 그룹의 문자열 반환 |
int groupCount() | 패턴내 그룹핑한 개수 반환 (패턴에 있는 괄호 개수) |
String replaceAll(String replacement) | 패턴과 일치하는 모든 문자열을 지정된 replacement 문자열로 변경 |
Matcher appendReplacement(StringBuffer sb, String replacement) | 먼저 전체 문자열에서 find()함수로 매칭문자열을 찾아야하는 선행이 필요함. 그 후 매칭된 문자열은 replacement 문자열로 바뀌며 문자열을 찾기 시작한 위치부터 매칭된 문자열까지의 문자열을 sb에 넣음 |
StringBuffer appendTail(StringBuffer sb) | appendReplacement() 메서드를 통해 원하는 문자열로 치환된 후 그 다음 문자열부터 마지막 문자열까지를 sb에 삽입 |
String toString() | 매처를 문자열로 반환 |
정규표현식
정규식 | 설명 |
^ | 문자열 시작 |
$ | 문자열 종료 |
. | 임의의 문자 |
.* | 모든 문자열 |
[ ] | 한 개의 문자 [ ] 안에 ^ 가 선행하여 존재하면 not 의미 |
{ } | 횟수 또는 범위 표현 |
( ) | 괄호안에 문자를 하나의 문자로 인식. 그룹핑 |
| | 패턴을 or 연산할 때 사용 |
(?i) | 앞 부분에 (?i) 를 적어주면 대소문자 구분 안함 |
수량 제어
* | 앞 문자가 0개 또는 1개 이상의 개수 {0,} |
+ | 앞 문자가 1개 이상의 개수 {1,} |
? | 앞 문자가 없거나 또는 1개 {0,1} |
{n} | 정확히 n개 (n은 숫자) |
{n,} | 최소한 n개 (n은 숫자) |
{n, m} | n개에서부터 m개까지 (n과 m은 숫자) |
*? | 0개 |
+? | 1개 |
?? | 0개 |
정규식 | 설명 |
\s | 공백문자 |
\S | 공백문자가 아닌 나머지 문자 [^ ] |
\w | 알파벳 또는 숫자 또는 _(언더바) [a-zA-Z_0-9] |
\W | 알파벳이나 숫자를 제외한 문자 [^a-zA-Z_0-9] |
\d | 한개의 숫자 [0-9] |
\D | 숫자를 제외한 모든 문자 [^0-9] |
정규식 | 설명 |
[문자1문자2문자3] | 문자1, 문자2, 문자3 중 하나의 문자 |
[^문자1문자2문자3] | 문자1, 문자2, 문자3이 아닌 하나의 문자 |
[a-z] | a~z 중 하나의 소문자 글자 |
[a-zA-Z] | a~z 또는 A~Z 중 하나의 문자. 즉 영문자 의미 (대소문자 구별 안함) |
문자. ex) a. b. |
해당 문자로 시작하는 두자리 문자열 ex) a로 시작하는 두 자리 문자열 b로 시작하는 두 자리 문자열 |
문자.* ex) a.* b.* |
해당 문자로 시작하는 모든 문자열(기호 포함) ex) a로 시작하는 모든 문자열 (기호 포함) b로 시작하는 모든 문자열 (기호 포함) |
문자1.*문자2 ex) c.*t a.*g |
문자1로 시작하고 문자2로 끝나는 모든 문자열 ex) c로 시작하고 t로 끝나는 모든 문자열 a로 시작하고 g로 끝나는 모든 문자열 |
[문자1 | 문자2].* [문자1문자2].* [문자1-문자2].* ex) [a|b].* [cf].* [d-g].* |
문자1 또는 문자2로 시작하는 문자열 ex) a 또는 b로 시작하는 문자열 c 또는 f로 시작하는 문자열 d 또는 g로 시작하는 문자열 |
[^문자1 | 문자2].* [^문자1문자2].* [^문자1-문자2].* ex) [a|b].* [cf].* |
문자1 또는 문자2로 시작하지 않는 문자열 ex) a 또는 b로 시작하지 않는 문자열 c또는 f로 시작하지 않는 문자열 |
문자\. ex) a\. b\. |
문자. 과 일치하는 문자열 문자열 .(점)은 패턴작성에 사용하는 문자이므로 escape문자인 \를 앞에 써줘야함 ex) a. 과 일치하는 문자열 b. 과 일치하는 문자열 |
[가-힣] | 한글 |
정규 표현식 Java API
https://docs.oracle.com/javase/7/docs/api/
Java Platform SE 7
docs.oracle.com
예제1
import java.util.regex.*;
class prac{
public static void main(String[] args){
String[] data = {
"bat", "baby", "bonus", "cA", "ca", "co", "c.", "c.A", "c.0", "c0",
"car", "combat", "count", "data", "disc", "d_"
};
Pattern p = Pattern.compile("c[a-z]*"); // c로 시작하는 소문자 영단어
for(int i=0; i<data.length; i++) {
Matcher m = p.matcher(data[i]);
if(m.matches()) {
System.out.println(data[i]);
}
}
}
}
/* 출력값
ca
co
car
combat
count
*/
정규식 정의후 데이터 비교
① 정규식을 매개변수로 Pattern 클래스의 static 메서드인 Pattern compile(String regex)을 호출하여 Pattern 인스턴스 생성
ex) Pattern p = Pattern.compile("c[a-z]*");
② 정규식으로 비교할 대상을 매개변수로 Pattern 클래스의 Matcher matcher(CharSequence input)를 호출해서 Matcher인스턴스 생성
ex) Matcher m = p.matcher(data[i]);
③ Matcher인스턴스에 boolean matches()를 호출해서 정규식에 부합하는지 확인
ex) if( m. matches() ) {}
예제2
import java.util.regex.*;
class prac{
public static void main(String[] args){
String[] data = {
"bat", "baby", "bonus", "cA", "ca", "co", "c.", "c.A", "c.0", "c0",
"car", "combat", "count", "data", "disc", "d_"
};
String[] pattern = {".*", "c[a-z]", "c[a-z]", "c[a-zA-Z]",
"c[a-zA-Z0-9]", "c.", "c.*", "c\\.", "c\\w", "c\\d", "c.*t",
"[b|c].*", ".*a.*", ".*a.+", "[b|c].{2}"
};
for(int x = 0; x<pattern.length; x++) {
Pattern p = Pattern.compile(pattern[x]);
System.out.print("Pattern : " + pattern[x] + " 결과 : ");
for(int i=0; i<data.length; i++) {
Matcher m = p.matcher(data[i]);
if(m.matches())
System.out.print(data[i] + ", ");
}
System.out.println();
}
}
}
/* 출력값
Pattern : .* 결과 : bat, baby, bonus, cA, ca, co, c., c.A, c.0, c0, car, combat, count, data, disc, d_,
Pattern : c[a-z] 결과 : ca, co,
Pattern : c[a-z] 결과 : ca, co,
Pattern : c[a-zA-Z] 결과 : cA, ca, co,
Pattern : c[a-zA-Z0-9] 결과 : cA, ca, co, c0,
Pattern : c. 결과 : cA, ca, co, c., c0,
Pattern : c.* 결과 : cA, ca, co, c., c.A, c.0, c0, car, combat, count,
Pattern : c\. 결과 : c.,
Pattern : c\w 결과 : cA, ca, co, c0,
Pattern : c\d 결과 : c0,
Pattern : c.*t 결과 : combat, count,
Pattern : [b|c].* 결과 : bat, baby, bonus, cA, ca, co, c., c.A, c.0, c0, car, combat, count,
Pattern : .*a.* 결과 : bat, baby, ca, car, combat, data,
Pattern : .*a.+ 결과 : bat, baby, car, combat, data,
Pattern : [b|c].{2} 결과 : bat, c.A, c.0, car,
*/
예제3
import java.util.regex.*;
class prac{
public static void main(String[] args){
String source = "HP: 011-1111-1111, HOME: 02-999-9999";
String pattern = "(0\\d{1,2})-(\\d{3,4})-(\\d{4})";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(source);
int i=0;
while(m.find())
System.out.println(++i +", "+ m.group() +", "+ m.group(1)+
", " + m.group(2) +", "+ m.group(3));
}
}
/* 출력값
1, 011-1111-1111, 011, 1111, 1111
2, 02-999-9999, 02, 999, 9999
*/
group() 메서드를 통해 전체 패턴에서 그룹핑된 패턴에 일치하는 모든 문자열을 가져옴
group(int i) 메서드를 통해 그룹핑한 패턴에서 i번쨰에 해당하는 그룹의 패턴에 매치되는 문자열을 가져옴
예제4
import java.util.regex.*;
class prac{
public static void main (String[] args){
String source = "A broken hand works, but not a broken heart.";
String pattern = "broken";
StringBuffer sb = new StringBuffer();
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(source);
int i =0;
while(m.find()) {
++i;
m.appendReplacement(sb, "##");
System.out.println(
i+ "번째 매칭, " + sb.toString() +", " + m.start() + " ~ " + m.end()
);
}
m.appendTail(sb);
System.out.println();
System.out.println(source);
System.out.println(sb.toString());
}
}
/* 출력값
1번째 매칭, A ##, 2 ~ 8
2번째 매칭, A ## hand works, but not a ##, 31 ~ 37
A broken hand works, but not a broken heart.
A ## hand works, but not a ## heart.
*/
Matcher의 find()로 패턴과 일치하는 문자열을 찾으면, 그 문자열의 위치를 start() 메서드와 end() 메서드로 알아 낼 수 있음
appendReplacement(StringBuffer sb, String replacement)를 이용해서 원하는 문자열(replacement)로 치환 가능
이 때 치환된 결과는 StringBuffer인 sb에 저장
① 문자열 source에서 "broken"을 m.find()로 찾음. 처음으로 m.appendReplacement(sb, "##"); 가 호출되면 source의 시작부터 "broken"을 찾는 위치까지의 내용에 "##"을 더해서 저장
- sb에 저장된 내용 : "A ##"
② m.find()는 첫 번째로 발견된 위치의 끝에서부터 다시 검색을 시작하여 두 번째 "broken"을 찾게됨. 다시 m.appendReplacement(sb, "##"); 가 호출
- sb에 저장된 내용 : "A ## hand works, but not a ##"
③ m.appendTail(sb); 이 호출되면 마지막에 치환된 이후의 부분을 sb에 덧붙임
- sb에 저장된 내용 : "A ## hand works, but not a ## heart."
java util.Scanner 클래스
- 화면, 파일, 문자열과 같은 입력 소스로부터 문자 데이터를 읽어오는데 도움을 줌
- import java.util.Scanner; 쓸 것
- 인스턴스 생성 시 new Scanner(System.in) 에서 System.in 부분은 console 창에서 입력하는 데이터를 의미
- Scanner 클래스 생성자를 통해 다양한 입력소스로부터 데이터를 읽을 수 있음
- 정규식 표현(Regular expression)을 이용한 라인 단위의 검색 가능하며, 구분자(delimiter)에도 정규식 표현을 사용할 수 있어서 복잡한 형태의 구분자도 처리 가능
Scanner useDelimiter(Pattern pattern)
Scanner useDelimiter(String pattern)
참고) 화면 입출력만 전문적으로 담당하는 java.io.Console 클래스가 있으나 이클립스와 같은 IDE에서 잘 동작하지 않음. Scanner와 성능이 비슷하니 Scanner 써도 무방
Scanner 클래스 생성자
Scanner(String source)
Scanner(File source)
Scanner(InputStream source)
Scanner(Readable source)
Scanner(ReadableByteChannel source)
Scanner(Path source)
Scanner 메서드
반환타입 메서드 | 설명 |
void close() | 스캐너 프로그램 종료 |
boolean nextBoolean() | 입력값을 boolean 타입으로 반환 |
byte nextByte() | 입력값을 byte 타입으로 출력 |
short nextShort() | 입력값을 short 타입으로 출력 |
int nextInt() | 입력값을 int 타입으로 출력 |
long nextLong() | 입력값을 long 타입으로 출력 |
double nextDouble() | 입력값을 double 타입으로 출력 |
float nextFloat() | 입력값을 float 타입으로 출력 |
String nextLine() | 입력값을 string 타입으로 출력 공백 허용 |
String next() String next(Pattern pattern) String next(String pattern) |
입력값을 string 타입으로 출력 공백 허용 안함 |
boolean hasNext() boolean hasNext(Pattern pattern) boolean hasNext(String pattern) |
입력값이 존재하면 true 아니면 false 입력값이 패턴과 일치하면 true 아니면 fasle |
boolean hasNextByte() boolean hasNextByte(int regex) |
입력값이 byte 타입으로 호환되는지 안되는지 판단. 호환되면 true 값 반환 정규식을 패턴에 맞는 byte 타입으로 호환 되는 값이 존재하면 true |
boolean has NextShort() boolean has NextShort(int regex) |
입력값이 short 타입으로 호환되는지 안되는지 판단. 호환되면 true 값 반환 정규식을 패턴에 맞는 short 타입으로 호환 되는 값이 존재하면 true |
boolean hasNextInt() boolean hasNextInt(int regex) |
입력값이 int 타입으로 호환 되는지 안되는지 판단. 호환되면 true 값 반환 정규식을 패턴에 맞는 int 타입으로 호환 되는 값이 존재하면 true |
boolean hasNextLong() boolean hasNextLong(int radix) |
입력값이 long 타입으로 호환되는지 안되는지 판단. 호환되면 true 값 반환 정규식을 패턴에 맞는 long 타입으로 호환 되는 값이 존재하면 true |
boolean hasNextFloat() | 입력값이 float 타입으로 호환 되는지 안되는지 판단. 호환되면 true 값 반환 |
boolean hasNextDouble() | 입력값이 double 타입으로 호환 되는지 안되는지 판단. 호환되면 true 값 반환 |
boolean hasNextLine() | 입력값이 존재하면 true 아니면 fasle |
Scanner useDelimiter(Pattern pattern) Scanner useDelimiter(String pattern) |
입력값의 구분자를 기본값인 공백이 아닌 pattern 값으로 변경 |
Pattern delimiter() | 현재 사용중인 구분 기호의 패턴 반환 |
입력된 데이터의 형식에 맞지 않는 메서드를 사용할 경우 InputMismatchException 발생
예제1
import java.util.Arrays;
import java.util.Scanner;
class prac{
public static void main (String[] args){
Scanner s = new Scanner(System.in);
String[] arr = null;
while(true) {
System.out.print(">>");
String input = s.nextLine().trim(); // 입력 받은값의 양쪽 공백제거
input = input.toLowerCase(); // 소문자로 변환
arr = input.split(" +"); // 불핑요한 공백 제거 (정규표현식 사용)
if(input.equals("q")) {
System.exit(0);
} else if (input.equals("")) {
continue;
} else {
System.out.println(Arrays.toString(arr));
}
} // end of while
}
}
/* 출력값
>>hello java
[hello, java]
>>hi hello 1256899
[hi, hello, 1256899]
>>
>>
>>q
*/
System.exit(0) : 프로그램 정상 종료
System.exit(1) : 프로그램 비정상 종료
예제2 - txt 파일을 불러와서 데이터 합 계산하기
/* input.txt */
170
580 30
200
40 60 80
import java.io.File;
import java.util.Scanner;
class prac{
public static void main (String[] args) throws Exception{
int sum=0, cnt = 0;
Scanner s = new Scanner(new File("input.txt"));
while(s.hasNextInt()) {
sum += s.nextInt();
cnt ++;
}
System.out.printf("%d, %d", cnt, sum); // 7, 1160
}
}
데이터 파일 만드는 법 : 소스파일 폴더 → 우클릭 후 new 클릭 → file 클릭후 파일 생성
파일을 불러올 때는 무조건 예외처리를 해줘야함 안그러면 FileNotFoundException 예외 발생
데이터 파일에서는 데이터를 입력할 때 공백이나 줄바꿈을 통해서 데이터들을 구분할 것
만약 데이터 파일이 소스파일과 다른 디렉토리에 존재하면 파일명에 경로도 함께 적을 것
데이터 파일을 프로젝트의 최상위 경로에 위치 시킬것을 권장
예제3 - txt 파일에서 데이터의 구분자 패턴을 변경하여 읽기
/* input.txt */
100,150,50
200,250,70
300,300,300
400,400,400
500,500,500
import java.io.File;
import java.util.Scanner;
class prac{
public static void main (String[] args)throws Exception {
Scanner sc = new Scanner(new File("input.txt"));
int cnt=0, sum=0, total=0;
while(sc.hasNextLine()) {
String line = sc.nextLine();
Scanner sc2 = new Scanner(line).useDelimiter(","); // 구분자 패턴 변경
cnt++;
System.out.print(cnt+"줄 : ");
while(sc2.hasNextInt()) {
int num = sc2.nextInt();
sum += num;
System.out.print(num + ", ");
}
System.out.println("sum : "+sum);
total += sum;
}
System.out.print(total);
}
}
/* 출력값
1줄 100, 1줄 150, 1줄 50, sum : 300
2줄 200, 2줄 250, 2줄 70, sum : 820
3줄 300, 3줄 300, 3줄 300, sum : 1720
4줄 400, 4줄 400, 4줄 400, sum : 2920
5줄 500, 5줄 500, 5줄 500, sum : 4420
10180
*/
java.util.StringTokenizer 클래스
- 문자열을 우리가 지정한 구분자로 문자열을 쪼개주는 클래스. 그렇게 쪼개진 문자열을 토큰(token)이라고 부름
- import java.util.StringTokenizer; 쓸 것
- 긴 문자열을 지정된 구분자(delimiter)를 기준으로 토큰(token)이라는 여러개의 문자열을 잘라낼 때 사용
- String클래스의 split(String regex) 메서드나 Scanner클래스의 useDelimiter(String pattern) 메서드를 사용할 수 있지만 이 두가지 방법은 정규 표현식을 사용해야하므로 정규표현식이 익숙하지 않은 경우 StringTokenizer을 사용하는 것이 간단하면서도 명확한 결과를 얻을 수 있음
- StringTokenizer는 구분자로 단 하나의 문자 밖에 사용하지 못하기 때문에 보다 복잡한 형태의 구분자로 문자열을 나누어야할 때는 정규식을 사용하는 다른 클래스의 메서드를 사용할 것
- StringTokenizer(String str, String delim)에서 delim인 구분자에 여러 문자로 구성된 문자열 타입으로 값을 넣어도 전체 문자열이 하나의 구분자가 아니라 char 타입처럼 문자 한개 한개씩을 구분자로 인식함
- 두 문자 이상으로 합쳐진 구분자를 사용하고 싶다면 String 클래스의 split() 메서드나 Scanner 클래스의 useDelimiter메서드를 이용 할 것
StringTokenizer클래스 생성자
생성자 | 설명 |
StringTokenizer(String str) | 문자열 str을 공백으로 분리 |
StringTokenizer(String str, String delim) | 문자열 str을 구분자 delim 기준으로 분리 |
StringTokenizer(String str, String delim, boolean returnDelims) | 문자열 str을 구분자 delim 기준으로 분리할 때 구분자도 토큰으로 넣을거면 true, 안넣을거면 false 기본값은 false |
StringTokenizer클래스 메서드
반환타입 메서드 | 설명 |
int countTokens() | 전체 토큰의 수 반환 |
boolean hasMoreTokens() | 토큰이 남아있는지 알려줌. 남은 토큰 있으면 true, 없으면 false |
String nextToken() String nextToken(String delim) |
다음 토큰 반환 delim 기준으로 다음 토큰 반환 |
boolean hasMoreElements() | hasMoreTokens()와 기능 동일 hasMoreTokens() 메서드를 더 자주 사용함 |
Object nextElements() | nextToken() 메서드와 동일한 값을 반환하지만 반환 타입이 문자열이 아닌 객체 |
예제1
import java.util.StringTokenizer;
class prac{
public static void main (String[] args) {
String source = "100,200,300,400";
StringTokenizer st = new StringTokenizer(source, ",");
while(st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
}
}
/*
100
200
300
400
*/
예제2
import java.util.StringTokenizer;
class prac{
public static void main (String[] args) {
String expression = "x=100*(200+300)/2";
StringTokenizer st = new StringTokenizer(expression, "+-*/=()", true);
while(st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
}
}
/* 출력값
x
=
100
*
(
200
+
300
)
/
2
*/
StringTokenizer클래스는 단 한 문자의 구분자만 사용할 수 있기에 st의 구분자인 +-*/=() 는 전체가 하나의 구분자가 아니라 각각의 문자가 모두 구분자임
StringTokenizer 클래스의 생성자의 세번째 매개변수에 true 값을 넣으면 두번쨰 인자인 구분자값들도 토큰으로 인식
예제3
import java.util.StringTokenizer;
class prac{
public static void main (String[] args) {
String source =
"1,김천재,100,100,100|"
+ "2,박수재,95,80,90|"
+ "3,이자바,80,90,90";
StringTokenizer st = new StringTokenizer(source, "|");
while(st.hasMoreTokens()) {
StringTokenizer st2 = new StringTokenizer(st.nextToken(), ",");
while(st2.hasMoreTokens()) {
System.out.println(st2.nextToken());
}
System.out.println("-----");
}
}
}
/* 출력값
1
김천재
100
100
100
-----
2
박수재
95
80
90
-----
3
이자바
80
90
90
-----
*/
두 가지 종류의 구분자로 나뉘어져 있을 때 두 개의 StringTokenizer와 이중 반복문 사용해서 처리하는 방법
예제4 - 한글로된 숫자를 아라비아 숫자로 변환
import java.util.StringTokenizer;
class prac{
public static void main (String[] args){
String input ="삼십만삼천백십오";
System.out.println(input);
System.out.println(hangultonum(input));
}
public static long hangultonum(String input) {
long result = 0;
long tmpResult = 0; // 십백천 단위 저장
long num = 0;
final String NUMBER = "영일이삼사오육칠팔구";
final String UNIT = "십백천만억조";
final long[] UNIT_NUM = {10,100,1000,10000,(long)1e8, (long)1e12};
StringTokenizer st = new StringTokenizer(input, UNIT, true);
while(st.hasMoreTokens()) {
String token = st.nextToken();
if(UNIT.indexOf(token) == -1) { // 단위가 아닐 떄
num = NUMBER.indexOf(token);
} else { // 단위일 때
if ("만억조".indexOf(token) == -1) { // 만억조 단위가 아닐 때 (= 십백천 단위일 때)
tmpResult += (num!=0 ? num : 1) * UNIT_NUM[UNIT.indexOf(token)];
} else { // 만억조 단위일 때
tmpResult += num;
result += (tmpResult!=0 ? tmpResult : 1) * UNIT_NUM[UNIT.indexOf(token)];
tmpResult = 0;
}
num = 0;
}
} // end of while
return result + tmpResult + num;
}
}
/* 출력값
삼십만삼천백십오
303115
*/
예제5
import java.util.StringTokenizer;
class prac{
public static void main (String[] args){
String data = "100,,,200,300";
String[] result = data.split(",");
for(int i=0; i<result.length; i++) {
System.out.print(result[i] + "|");
}
System.out.println(" 개수 : "+ result.length);
int i=0;
StringTokenizer st = new StringTokenizer(data,",");
for(;st.hasMoreTokens();i++) {
System.out.print(st.nextToken() + "|");
}
System.out.println(" 개수 : " + i);
}
}
/* 출력값
100|||200|300| 개수 : 5
100|200|300| 개수 : 3
*/
구분자를 ,(콤마)로 하는 문자열 데이터를 String클래스의 split()메서드와 StringTokenizer로 잘라낸 결과를 비교.
String 클래스의 split() 메서드는 빈 문자열도 토큰으로 인식
StringTokenizer클래스는 빈 문자열을 토큰으로 인식하지 않음
빈 문자열을 토큰으로 인식하냐 안하냐에 따라서 토큰 개수 차이가 남
String 클래스의 split() 메서드는 데이터를 토큰으로 잘라낸 결과를 배열에 담아서 반환하기 때문에 데이터를 토큰으로 바로바로 잘라서 반환하는 StringTokenizer 보다 성능이 떨어짐. 그러나 데이터 양이 많은 경우가 아니라면 큰 차이가 없음
java.mathBigInteger 클래스
- long 타입보다 큰 정수타입
- long으로 표현할 수 있는 10진수로 19자리 수
- import java.math.BigInteger; 쓸 것
- 내부적으로 int 배열을 사용해서 값을 다룸. 그래서 long 타입보다 훨 씬 큰 값을 다룰 수 있음. 대신 성능은 long 타입보다 떨어짐
- 큰 수를 다루기 때문에 성능 향상을 위해 비트단위 연산을 수행하는 메서드들이 많이 있음
- Number 클래스를 상속 받음
- int, long, float, double기본형으로 자동 변환 (auto-boxing)가능
- String 클래스처럼 불변(immutable) → 값 바꿀시 기존 메모리에 값이 바뀌는것이 아니라 새 메모리에 할당됨
- BigInteger는 불변이므로 BigInteger 메서드의 반환타입이 BigInteger 라면 새로운 인스턴스를 반환하는것
- 모든 정수형이 그렇듯이 BigInteger 역시 값을 2의 보수의 형태로 표현
final int signum; // 부호. 1(양수), 0 , -1(음수)
final int[] mag; // 값(magnitude)
- signum의 값은 부호를 나타냄.
- signum의 값이 만약에 -1인 음수인 경우 2의 보수법에 맞게 mag의 값을 변환해서 처리. 그래서 부호만 다른 두 값의 mag는 같고 signum만 다름
BigInteger 생성자
- 주로 문자열로 숫자를 표현함
생성자 | 설명 |
BigInteger(String val) BigInteger(String val, int radix) |
문자열을 숫자로 표현 radix는 진법를 의미 |
// ex.1
BigInteger val = new BigInteger("100000000"); // 문자열로 생성
// ex.2
BigInteger val = new BigInteger("FFFF", 16); // 16진법의 문자열로 생성
// ex.3
BigInteger val = BigInteger.valueOf(1234567890L); // 숫자로 생성
메서드
반환타입 메서드 | 설명 |
byte byteValueExact() | byte 타입으로 형변환 ArithmeticException 예외 처리 필수 |
int intValueExact() | int 타입으로 형변환 ArithmeticException 예외 처리 필수 |
long longValueExact() | long 타입으로 형변환 ArithmeticException 예외 처리 필수 |
short shortValueExact() | short 타입으로 형변환 ArithmeticException 예외 처리 필수 |
BigInteger add(BigInteger val) | 덧셈 (this + val) |
BigInteger subtract(BigInteger val) | 뺄셈 (this + val) |
BigInteger multiply(BigInteger val) | 곱셈 (this + val) |
BigInteger divide(BigInteger val) | 나눗셈 (this / val) |
BigInteger remainder(BigInteger val) | 나머지 (this % val) |
int bitCount() | 2진수로 표현했을때, 1의 개수(음수는 0개) 반환 |
int bitLength() | 2진수로 표현했을때, 값을 표현하는데 필요한 bit 개수 |
boolean testBit(int n) | 우측에서 n+1번째 비트가 1이면 true, 0이면 false |
BigInteger setBit(int n) | 우측에서 n+1번째 비트를 1로 변경 |
BigInteger clearBit(int n) | 우측에서 n+1번째 비트를 0으로 변경 |
BigInteger flipBit(int n) | 우측에서 n+1번째 비트를 전환 (1 → 0, 0 → 1) |
boolean equals(Obejct x) | 값이 같은지 비교. 같으면 true. 다르면 false |
String toString() String toString(int radix) |
문자열로 출력 radix 진법의 문자열로 출력 |
상수
제어자 타입 변수 | 설명 |
static BigInteger ONE | BigInteger 타입의 상수 1 |
static BigInteger TEN | BigInteger 타입의 상수 10 |
static BigInteger ZERO | BigInteger 타입의 상수 0 |
예시 - 짝수인지 아닌지 계산
BigInteger bi = new BigInteger("4");
bi.remainder(new BigInteger("2")).equals(BigInteger.ZERO)
▼ 비트 연산으로 바꾸기 ▼
BigInteger bi = new BigInteger("4");
bi.testBIt(0)
짝수는 제일 오른쪽 비트가 0이므로 testBit(0) 메서드로 마지막 비트를 확인하는것이 더 효율적
가능하면 산술연산 대신 비트연산으로 처리하도록 노력할 것
예제1
import java.math.BigInteger;
class prac{
public static void main (String[] args) throws Exception {
for(int i =1; i<10; i++) {
System.out.printf("%d! = %s%n", i, calcFactorial(i));
Thread.sleep(300); // 0.3초 지연. 예외처리 필수
}
}
static String calcFactorial(int n) {
return factorial(BigInteger.valueOf(n)).toString();
}
static BigInteger factorial(BigInteger n) {
if(n.equals(BigInteger.ZERO))
return BigInteger.ONE;
else {
return n.multiply(factorial(n.subtract(BigInteger.ONE)));
}
}
}
/* 출력값
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
*/
long 타입으로는 20!까지 계산할 수 밖에 없지만 BigInteger로의 최대값은 ±2의 Integer.MAX_VALUE 제곱인 10의 6억 제곱임
java.math.BigDecimal
- BigDecimal은 실수형과 달리 정수를 이용해서 실수를 표현
- 실수를 정수와 10의 제곱의 곱으로 표현 → 정수 * 10^-scale
- 정수를 이용할 때 BigInteger 사용
- scale 범위 : 0 ~ Integer.MAX_VALUE
- 실수의 오차는 10진 실수를 2진 실수로 정확히 변환할 수 없는 경우가 있기에 발생하는것인데 오차가 없는 2진 정수로 변환하여 다루기에 오차가 없음
- BigInteger클래스처럼 불변(immutable)
- 값이 바뀌면 기존 메모리가 아닌 새로운 메모리에 할당됨
- 반환타입이 BigDecimal 인 경우 새로운 인스턴스가 반환
- Number 클래스를 상속 받음
- int, long, float, double 기본형으로 자동 변환(auto-box) 가능
- BigDecimal의 연산시 연산결과의 정수, 지수, 정밀도가 달라짐
- 곱센 : 두 피연산자의 scale을 더함
- 나눗셈 : 두 피연산자의 scale을 뺌
- 덧셈, 뺼 셈 : 두 피연산자 중 더 큰 scale
생성자
- 문자열로 숫자를 표현하는 것이 일반적. 기본형 리터럴로는 표현할 수 있는 값의 한계가 있기 때문
- double 타입의 값을 매개변수로 갖는 생성자를 사용하면 오차가 발생할 수 있음
생성자 | 설명 |
BigDecimal(String val) | 문자열을 BigDecimal로 변환 |
BigDecimal(double val) | double 타입을 BigDecimal로 변환 |
BigDecimal(int val) | int 타입을 BigDecimal로 변환로 변환 |
BigDecimal(long val) | long 타입을 BigDecimal로 변환로 변환 |
BigDecimal(BigInteger val) | BigInteger를 BigDecimal로 변환 |
// ex.1
BigDecimal val = new BigDecimal("123.4567890");
// ex.2
BigDecimal val = new BigDecimal(123.456);
// ex.3
BigDecimal val = new BigDecimal(123456);
// ex.4
BigDecimal val = BigDecimal.valueOf(123.456); // 생성자 대신 valueOf(double) 사용
// ex.5
BigDecimal val = BigDecimal.valueOf(123456); // 생성자 대신 valueOf(int) 사용
상수
제어자 타입 변수 | 설명 |
static BigDecimal ZERO | BigDeicaml 타입의 0 |
static BigDecimal ONE | BigDeicaml 타입의 1 |
static BigDecimal TEN | BigDeicaml 타입의 10 |
static int ROUND_CEILING | 올림 |
static int ROUND_FLOOR | 내림 |
static int ROUND_UP | 양수일 때 올림, 음수일 때는 내림 |
static int ROUND_DOWN | 양수일 때는 내림, 음수일 때는 올림 |
static int ROUND_HALF_UP | 반올림 (5이상 올림, 5미만 버림) |
static int ROUND__HALF_EVEN | 반올림 (반올림자리의 값이 짝수면 ROUND_HALF_DOWN, 홀수면 ROUND_HALF_UP) |
static int ROUND_HALF_DOWN | 반올림 (6이상 올림, 6미만 버림) |
static int ROUND_UNNECESSARY | 나눗셈의 결과가 딱 떨어지는 수가 아니면, ArithmeticException 예외 발생 |
- Round가 붙은 상수들은 반올림 처리 방법으로 divide() 메서드에서 매개변수로 쓰임
메서드
반환타입 메서드 | 설명 |
String toPlanString() | 문자열로 변환. 어떤 경우에도 다른 기호 없이 숫자로만 표현 |
String toString() | 문자열로 변환. 필요하면 지수형태로 표현 |
byte byteValueExact() | byte 타입으로 형변환 ArithmeticException 예외 처리 필수 |
short shortValueExact() | short 타입으로 형변환 ArithmeticException 예외 처리 필수 |
int intValueExact() | int 타입으로 형변환 ArithmeticException 예외 발생 |
long longValueExact() | long 타입으로 형변환 ArithmeticException 예외 처리 필수 |
BigInteger toBigIntegerExact() | BigInteger 타입으로 형변환 ArithmeticException 예외 처리 필수 |
BigDecimal add(BigDecimal augend) | 덧셈 (this + augend) |
BigDecimal subtract(BigDecimal subtrahend) | 빨셈 (this - subtrahend) |
BigDecimal multiply(BigDecimal multiplicand) | 곱셈 (this * multiplicand) |
BigDecimal divide(BigDecimal divisor) BigDecimal divide(BigDecimal divisor, int roundingMode) BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode) BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) BigDecimal divide(BigDecimal divisor, MathContext mc) |
나눗셈 (this / divisor) roundingMode는 반올림 처리 방법 |
BigDecimal remainder(BigDecimal divisor) | 나머지 (this % divisor) |
BigInteger unscaledValue() | BigInteger 타입으로 반환 (= 정수 반환) |
int scale | 지수 반환 (= 소수점 이하 자리수) |
int precision | 정밀도 반환 (= 정수의 전체 자리수) |
BigDecimal setScale(int newScale) BigDecimal setScale(int newScale, int roundingMode) BigDecimal setScale(int newScale, RoundingMode mode) |
scale 값 변경 |
BigDecimal 표현
private final BigInteger intVal; // 정수 (unscaled value)
private final int scale; // 지수 (scale)
private transient int precision; // 정밀도 (precision) → 정수의 자릿수
import java.math.BigDecimal;
class prac{
public static void main (String[] args) throws Exception {
BigDecimal val = new BigDecimal("123.45");
System.out.println(val.unscaledValue()); // 12345
System.out.println(val.scale()); // 2
System.out.println(val.precision()); // 5
}
}
'123.45' 를 '12345 * (10^-2)' 로 표현 가능. 이 때 intVal 값은 12345, scale 값은 2, precision 값은 5
문자열 변환
import java.math.BigDecimal;
class prac{
public static void main (String[] args) throws Exception {
BigDecimal val = new BigDecimal(1.0e-22);
System.out.println(val.toPlainString());
System.out.println(val.toString());
}
}
/* 출력값
0.000000000000000000000100000000000000004859677432....
1.00000000000000004859677432.....68141937255859375E-22
*/
toPlainString() 메서드와 toString()메서드의 반환값은 같지만 반환값의 표현 방식이 다름
toPlainString() 메서드는 순수하게 숫자로만 표현됨
toString() 메서드는 필요시 지수형태를 사용해서 표현됨
생성자에 따른 오차발생
import java.math.BigDecimal;
class prac{
public static void main (String[] args) throws Exception {
System.out.println(new BigDecimal(0.1));
System.out.println(new BigDecimal("0.1"));
}
}
/* 출력값
0.1000000000000000055511151231257827021181583404541015625
0.1
*/
double 타입의 값을 매개변수로 갖는 생성자를 사용하면 오차발생 가능
예제1
import java.math.BigDecimal;
import java.math.RoundingMode;
class prac{
public static void main (String[] args) throws Exception {
BigDecimal big = new BigDecimal("1.0");
BigDecimal big2 = new BigDecimal("3.0");
// System.out.println(big.divide(big2)); // ArithmeticException 발생
System.out.println(big.divide(big2, 5, RoundingMode.HALF_UP)); // 0.33333
}
}
RoundMode는 BigDecimal의 roundingMode 상수들을 열거형으로 정의한 것
가능하면 열거형 RoundingMode를 사용할 것
java.math.MathContext
- 반올림 모드와 정밀도(precision)을 하나로 묶어 놓은 것
- divide() 메서드에서 scale이 소수점 이하의 자리수를 의미하는데, MathContext에서는 precision이 정수와 소수점 이하를 모두 포함한 자리수 의미
- import java.math.MathContext; 쓸 것
scale 변경
- BigDecimal을 10으로 곱하거나 나누는 대신 scale의 값을 변경함으로써 같은 결과 표현
- scale 값을 변경하려면 BigDecimal 클래스의 setScale() 메서드 사용할 것
- setScale() 메서드로 scale의 값을 줄이는 것은 10의 n 제곱으로 나누는것과 같아서 divide()를 호출할 대 처럼 오차가 발생할 수도 있고, 반올림 모드를 지정해줘야함
예제
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.math.MathContext;
class prac{
public static void main (String[] args) throws Exception {
BigDecimal bd1 = new BigDecimal("123.456");
BigDecimal bd2 = new BigDecimal("1.0");
System.out.print("bd1 = "+ bd1);
System.out.print(",\tvalue = " + bd1.unscaledValue());
System.out.print(",\tscale = " + bd1.scale());
System.out.print(",\tprecision = " + bd1.precision());
System.out.println();
System.out.print("bd2 = "+ bd2);
System.out.print(",\tvalue = " + bd2.unscaledValue());
System.out.print(",\tscale = " + bd2.scale());
System.out.print(",\tprecision = " + bd2.precision());
System.out.println();
BigDecimal bd3 = bd1.multiply(bd2);
System.out.print("bd3 = "+ bd3);
System.out.print(",\tvalue = " + bd3.unscaledValue());
System.out.print(",\tscale = " + bd3.scale());
System.out.print(",\tprecision = " + bd3.precision());
System.out.println();
System.out.println(bd1.divide(bd2, 2, RoundingMode.HALF_UP));
System.out.println(bd1.setScale(2, RoundingMode.HALF_UP));
System.out.println(bd1.divide(bd2, new MathContext(2, RoundingMode.HALF_UP)));
}
}
/* 출력값
bd1 = 123.456, value = 123456, scale = 3, precision = 6
bd2 = 1.0, value = 10, scale = 1, precision = 2
bd3 = 123.4560, value = 1234560, scale = 4, precision = 7
123.46
123.46
1.2E+2
*/