[Java] API 개념 복습
toString() 메소드 오버라이딩 목적을 이해하고 개발에 적용할 수 있다.
java.lang.Object 클래스의 toString() 메소드는 인스턴스가 생성될 때 사용한
full class name과 @ 그리고 16진수 해쉬코드가 문자열로 반환된다.
16진수 해쉬코드는 인스턴스의 주소를 가키리는 값으로 인스턴스마다 모두 다르게 반환된다.
Book book1 = new Book(1, "홍길동전", "허균", 50000);
System.out.println("book1.toString() : " + book1.toString());
toString() 메소드로 출력한 결과
book1.toString() : com.greedy.section01.object.book.Book@77cf73f0
레퍼런스 변수만 출력하는 경우도 동일한 결과가 나온다.
이 경우 자동으로 toString() 메소드를 호출해준다. 이를 이용하여 toString() 메소드를 재정의하여 사용한다.
toString() 메소드 오버라이딩
System.out.println("book1 : " + book1);// 자동으로 toString()호출@Override
public String toString() {
return "Book [number=" + number + ", title=" + title + "
, author=" + author + ", price=" + price + "]";
}
toString()메소드를 오버라이딩 한 후 출력한 결과
book1.toString() : Book [number=1, title=홍길동전, author=허균, price=50000]
equals() 메소드 오버라이딩 목적을 이해하고 개발에 적용할 수 있다.
java.lang.Object의 equals는 매개변수로 전달 받은 인스턴스와 == 연산하여 true or false를 반환한다.
즉 동일한 인스턴스인지 비교하는 기능을 한다.
equals() 메소드의 기본 기능은 동일객체 판단을 한다고 볼 수 있다.
동등 객체인지 확인해야 하는 경우 equals() 메소드를 오버라이딩하여
각각의 필드가 동일한 값을 가지는지 확인하고 맞으면 true, 아닌 경우 false를 반환하도록 작성 한다.
동등 객체인지 == 와 equals() 메소드로 비교
Book book1 = new Book(1, "홍길동전", "허균", 50000);
Book book2 = new Book(1, "홍길동전", "허균", 50000);
System.out.println("두 인스턴스의 == 연산 비교 : " + (book1 == book2)); //false
System.out.println("두 인스턴스의 equals() 비교 : " + (book1.equals(book2))); //true
book1과 book2이 동일한 값을 가진 동등 객체이지만 == 연산을 해보면
서로 다른 인스턴스 주소값을 가지고 있기 때문에 false가 출력된다.
이럴 때 equals() 메소드를 오버라이딩 하여 검사해보면 true가 나오는 것을 확인할 수 있다.
equals() 메소드 오버라이딩
@Override
public boolean equals(Object obj) {
/* 두 인스턴스의 주소가 같으면 이후 다른 내용을 비교할 것 없이 true 반환 */
if (this == obj)
return true;
/* this는 인스턴스가 생성되면 주소값이 저장 된다. null일 수 없다.
* 따라서 전달 받은 레퍼런스 변수에 null 값이 저장 되어 있다면
* 비교하려는 두 개의 인스턴스는 서로 다른 인스턴스이다.
* */
if (obj == null)
return false;
/* this와 obj의 클래스가 같지 않다면 필드값을 비교할 필요가 없다. */
if (getClass() != obj.getClass())
return false;
/* 전달 받은 레퍼런스 변수를 Book 타입으로 형변환하여 각 필드별로 비교를 시작한다. */
Book other = (Book) obj;
/* String 타입의 경우 null 여부 먼저 확인 */
if (author == null) {
if (other.author != null)
return false; // this는 null other는 not null 이므로 false
/* this가 null이 아닌 경우 String 클래스의 equals를 호출해서 값 비교 */
} else if (!author.equals(other.author))
return false;
/* 숫자 값은 바로 값 비교 가능 */
if (number != other.number)
return false;
if (price != other.price)
return false;
/* title도 String 타입이므로 author와 동일 */
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
/* 모든 조건을 통과하면 두 인스턴스의 모든 필드는 같은 값을 가지므로 true 반환 */
return true;
}
동일객체와 동등객체의 차이점을 설명할 수 있다.
동일객체 : 주소가 동일한 인스턴스를 동일객체라고 한다.
동등객체 : 주소는 다르더라도 필드 값이 동일한 객체를 동등객체라고 한다.
hashCode() 메소드 오버라이딩 목적을 이해하고 개발에 적용할 수 있다.
Object 클래스의 명세에 작성 된 일반 규약에 따르면equals() 메소드를 재정의 하는 경우
반드시 hashCode() 메소드도 재정의 하도록 되어 있다.
만약 hashCode()를 재정의 하지 않으면 같은 값을 가지는 동등 객체는
같은 해시코드 값을 가져야 한다는 규약에 위반되게 된다. (강제성은 없지만 규약대로 작성하는 것이 좋음)
hashCode() 메소드 오버라이딩
@Override
public int hashCode() {
/* 필드마다 곱해줄 소수값 선언
* 31은 소수이기 때문에 연산 시 동일한 hashCode 값이 나오지 않을 확률을 증가시킨다.
* 31이 통상적인 관례이며 String 클래스의 hashCode에도 사용한 값이다.
* */
final int prime = 31;
/* 곱셈 연산을 누적시킨 값 선언 (0으로 선언시에는 곱할 수 없음) */
int result = 1;
/* String 클래스의 hashCode 메소드는 이미 재정의 되어 있다.
* 같은 값을 가지는 문자열은 동일한 hashCode 값을 반환한다.
* */
result = prime * result + ((author == null) ? 0 : author.hashCode());
result = prime * result + number;
result = prime * result + price;
result = prime * result + ((title == null) ? 0 : title.hashCode());
/* 동등 객체는 동일한 hashCode 값을 반환하게 된다. */
return result;
}
String 클래스의 자주 사용하는 메소드를 이용하여 개발에 적용할 수 있다.
charAt() : 해당 문자열의 특정 인덱스에 해당하는 문자 반환
compareTo() : 두 문자열이 같은지 다른지에 따라 int 값을 반환
compareToIgnoreCase() : 대소문자를 구분하지 않고 비교 후 int 값을 반환
concat() : 문자열에 인자로 전달 된 문자열을 합침
indexOf() : 문자열을 앞에서부터 탐색하여 일치하는 위치의 인덱스를 int 값으로 반환
lastIndexOf() : 문자열을 뒤에서부터 탐색하여 일치하는 위치의 인덱스를 int 값으로 반환
trim() : 문자열의 앞 뒤 공백을 제거 후 문자열을 반환
toLowerCase() : 모든 문자를 소문자로 변환 후 문자열을 반환
toUpperCase() : 모든 문자를 대문자로 변환 후 문자열을 반환
문자열 비교 시 ==과 equals() 메소드의 비교 방식을 이해하고 개발에 적용할 수 있다.
" " 리터럴 형태 : 동일한 값을 가지는 인스턴스를 단일 인스턴스로 관리한다.
(singleton)new String("문자열") : 매번 새로운 인스턴스를 생성한다.
리터럴 형태로 만든 문자열 인스턴스는 동일한 값을 가지는 인스턴스는 하나의 인스턴스로 관리한다.
따라서 주소 값을 비교하는 == 연산으로 비교 시 서로 동일한 주소를 비교하여 true를 반환한다.
new로 새로운 인스턴스를 생성하게 되면 기존 인스턴스를 두고 새로운 인스턴스를 할당했기 때문에
== 연산으로 비교 시 서로 다른 주소값을 가지고 있기 때문에 false를 반환한다.
동일한 방식으로 인스턴스를 생성하고 값 또한 같더라도
새로운 인스턴스를 생성하는 방식은 서로 다른 주소를 가지기 때문에 false를 반환한다.
동일한 문자열은 동일한 hashCode 값을 가진다.
equals() : String 클래스의 equals() 메소드는 인스턴스 비교가 아닌 문자열값을 비교하여
동일한 값을 가지는 경우 true, 다른 값을 가지는 경우 false를 반환하도록 Object의 equals() 메소드를 재정의 해 두었다.
따라서 문자열 인스턴스 생성 방식과 상관 없이 동일한 문자열인지를 비교하기 위해
== 연산 대신 equals() 메소드를 써야 한다.
그냥 문자열은 다 equals()로 비교하는 것이 가장 좋은 방법이다.
문자열을 분리하는 방식을 이해하고 개발에 적용할 수 있다. ( split()과 StringTokenizer )
spilit() : 정규표현식을 이용하여 문자열을 분리하며 속도가 느림
String str1 = "변수/연산자/메소드/제어문";
String[] strArr = str1.split("/");
마지막 구분자 사이에 값이 존재하지 않는 경우 이후 값도 추출하고 싶을 때몇 개의 토큰으로 분리할 것인지 한계치를 두번째 인자로 넣어줄 수 있다.이 때 음수를 넣게 되면 마지막 구분자 뒤의 값이 존재하지 않는 경우 빈 문자열로 토큰을 생성한다.
String str1 = "변수/연산자/메소드/제어문/";
String[] strArr = str1.split("/", -1);
StringTokenizer : 문자열의 모든 문자들을 분리하며 split보다 속도가 빠름
구분자를 생략하는 경우 기본 구분자는 공백
String str2 = "red*orange#blue/yellow green";
// / * # 공백을 구분자로 사용하겠다는 의미
StringTokenizer st = new StringTokenizer(str2, "[/*# ]");
// nextToken()으로 토큰을 꺼내면 해당 StringTokenizer의 토큰을 재사용하는 것이 불가능하다.
while(st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
이스케이프 문자를 이해하고 개발에 적용할 수 있다.
/* 개행문자 */
System.out.println("안녕하세요.\\n저는 홍길동입니다.");
/* 탭 문자 */
System.out.println("안녕하세요.\\t저는 홍길동입니다.");
/* 홑 따옴표 문자 */
System.out.println("안녕하세요 저는 '홍길동'입니다.");
//이스케이프 문자 쓰지 않아도 됨//System.out.println(''');
//홑 따옴표 문자와 문자 리터럴 기호가 중복 됨
System.out.println('\\'');
//이스케이프 문자를 사용해서 구분해주어야 함
/* 쌍따옴표 문자 *///System.out.println("안녕하세요 저는 "홍길동"입니다.");
//문자열 리터럴과 기호가 중복 됨
System.out.println("안녕하세요 저는 \\"홍길동\\"입니다.");
//이스케이프 문자를 사용해서 구분함
/* 역슬래쉬 문자 *///System.out.println("안녕하세요 저는 \\홍길동\\입니다.");
//역슬래쉬를 표현하고 싶지만 에러 발생(이스케이프 문자 예약문자와 겹침)
System.out.println("안녕하세요 저는 \\\\홍길동\\\\입니다.");
//이스케이프 문자를 사용해서 구분함
- StringBuilder클래스를 이용하여 문자열 합치기를 할 수 있다.
StringBuilder : 문자열에 append() 메소드를 이용하여 합치기 하는 경우기존 인스턴스를 수정하기 때문에 새로운 인스턴스를 생성하지 않는다.따라서 잦은 문자열 변경이 일어나는 경우 String 보다 성능이 높다.
/* StringBuilder 인스턴스 생성 */
StringBuilder sb = new StringBuilder("java");
/* hashCode는 오버라이딩 되어 있지 않다.
* 즉, 동일한 값을 가지는 경우 같은 해쉬코드를 반환하는 것이 아닌,
* 인스턴스가 동일해야 같은 해쉬코드를 반환한다.
* */
System.out.println("sb의 hashCode : " + sb.hashCode());
/* 문자열 수정 */
sb.append("oracle");
System.out.println("sb : " + sb);
/* hashCode를 다시 출력하면 기존 인스턴스와 동일한 것을 확인할 수 있다.
* 즉, 문자열을 변경했다고 해서 새로운 인스턴스가 생성된 것은 아니다.
* */
System.out.println("sb의 hashCode : " + sb.hashCode());
출력된 결과
sb : java
sb의 hashCode : 1101288798
sb : javaoracle
sb의 hashCode : 1101288798
String과 StringBuilder의 차이점을 이해하고 설명할 수 있다.
String과 유사하지만 String이 불변이라면 StringBuilder나 StringBuffer는 가변
StringBuilder : 스레드 동기화 기능을 제공하지 않음
StringBuffer : 스레드 동기화 기능 제공
(스레드 동기화 유무의 차이 말고는 두 클래스가 의미하는 바가 동일함)
StringBuilder 클래스의 자주 사용하는 메소드를 이용하여 개발에 적용할 수 있다.
capacity() : 용량(현재 버퍼의 크기)을 int 값으로 반환 (문자열 길이 + 16이 기본 용량)
append() : 인자로 전달 된 값을 문자열로 변환 후 기존 문자열의 마지막에 추가
delete() : 시작 인덱스와 종료 인덱스를 이용해서 문자열에서 원하는 부분의 문자열 제거
insert() : 인자로 전달된 값을 문자열로 변환 후 지정한 인덱스 위치에 추가
reverse() : 문자열 인덱스 순번을 역순으로 재배열
부족했던 부분
1. 이전 게시글을 실수로 삭제함.
2. API의 래퍼클래스 이후 부분 정리는 추후에.. 컬렉션이 너무 급해서 API를 복습하는게 계속 미뤄졌다.