11.1 자바 API 도큐먼트
API는 라이브러리(library)라고 부르기도 하는데, 프로그램 개발에 자주 사용되는 클래스 및 인터페이스의 모음을 말한다.
우리가 사용해왔던 String 클래스와 System 클래스 모두 API에 속하는 클래스들이다.
이 API는 <JDK 설치경로> /jre/lib/rt.jar 라는 압축 파일에 저장되어 있다.
■ API 도큐먼트 (p.454~456)
- 쉽게 API 찾아 이용할 수 있도록 문서화한 것
- HTML 페이지로 작성되어 있어 웹 브라우저로 바로 볼 수 있음
http://docs.oracle.com/javase/8/docs/api/
Java Platform SE 8
docs.oracle.com
11.2 java.lang과 java.util 패키지
자바 애플리케이션을 개발할 때 공통적으로 가장 많이 사용하는 패키지는 java.lang과 java.util 패키지일 것이다.
이 패키지들에 포함되어 있는 클래스의 종류와 역할을 이번 장에서 알아보도록 하자.
11.2.1 java.lang 패키지
- 자바 프로그램의 기본적인 클래스를 담은 패키지
- 포함된 클래스와 인터페이스는 import 없이 사용
java.lang 패키지는 자바 프로그램의 기본적인 클래스를 담고 있는 패키지이다.
그렇기 떄문에 java.lang 패키지에 있는 클래스는 import 없이 사용 할 수 있다.
지금까지 사용한 String, System 클래스도 java.lang 패키지에 포함되어 있기 떄문에 import 하지 않고 사용했다.
11.2.2 java.util 패키지
java.util 패키지는 자바 프로그램 개발에 조미료 같은 역할을 하는 클래스를 담고 있따.
java.util 패키지는 컬렉션 클래스들이 대부분을 차지하고 있다.
클래스 | 용도 |
Arrays | 배열을 조작(비교, 복사, 정렬, 찾기) |
Calendar | 운영체제의 날짜와 시간을 얻을때 사용 |
Date | 날짜와 시간 정보를 저장하는 클래스 |
Objects | 객체 비교, 널(null) 여부 등을 조사할 때 사용 |
StringTokenizer | 특정 문자로 구분된 문자열을 뽑아낼때 사용 |
Random | 난수를 얻을 때 사용 |
11.3 Object 클래스
- 다른 클래스 상속하지 않으면 java.lang.Object 클래스 상속 암시
- Object의 메소드는 모든 클래스에서 사용 가능
여러분이 클래스를 선언할때 Extends 키워드로 다른 클래스를 상속하지 않으면 암시적으로 java.lang.Object 클래스를 상속하게 된다.
따라서 자바의 모든 클래스는 Object 클래스의 자식이거나 자손 클래스이다.
Object 클래스는 자바의 최상위 부모 클래스에 해당한다.
Object 클래스는 필드가 없고, 메소드들로 구성되어 있다.
이 메소드들은 모든 클래스가 Object를 상속하기 때문에 모든 클래스에서 사용이 가능하다.
11.3.1 객체 비교(equals())
public boolean equals(Object obj){......}
equals() 메소드의 매개타입은 Object인데, 이것은 모든 객체가 매개값으로 대입될 수 있음을 말한다.
그 이유는 Object가 최상위 타입이므로 모든 객체는 Object 타입으로 자동 타입 변환 될 수 있기 때문이다.
- 기본적으로 == 연산자와 동일한 결과 리턴 (번지 비교)
- 논리적 동등 위해 오버라이딩 필요
• 논리적 동등이란?
같은 객체이건 다른 객체이건 상관없이 객체 저장 데이터 동일
• Object의 equals() 메소드
재정의하여 논리적 동등 비교할 때 이용
equals() 메소드는 두 객체를 비교해서 논리적으로 동등하면 true를 리턴하고, 그렇치 않으면 false를 리턴한다.
논리적으로 동등하다는 것은 같은 객체이건 다른 객체이건 상관없이 저장하고 있는 데이터가 동일함을 뜻한다.
예를들어 String 객체의 equals() 메소드는 String 객체의 번지를 비교하는 것이 아니고, 문자열이 동등한지 조사해서 같다면 true를 리턴하고, 그렇지 않으면 false를 리턴한다.
이것이 가능한 이유는 String 클래스가 Object의 equals() 메소드를 재정의(오버라이딩)해서 번지 비교가 아닌 문자열 비교로 변경했기 때문이다.
Object의 equals() 메소드는 직접 사용되지 않고 하위 클래스에서 재정의하여 논리적으로 동등한 객체로 취급하고 싶을 경우 Object의 equals() 메소드를 재저으이해서 id 필드값이 같음을 비교하면 된다.
public class Member {
public String id;
public Member(String id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Member) { //매개값이 Member 타입이지 확인
Member member = (Member) obj;
if(id.equals(member.id)) { // Member 타입으로 강제 타입 변환하고 id 필드값이 동일한지 검사한 후
return true; // 동일하다면 true를 리턴
}
}
return false; //매개값이 Member 타입이 아니거나 id필드값이 다른경우 false를 리턴
}
}
11.3.2 객체 해시코드(hashCode())
- 객체 해시코드란?
• 객체 식별할 하나의 정수값
• 객체의 메모리 번지 이용해 해시코드 만들어 리턴
– 개별 객체는 해시코드가 모두 다름
객체 해시코드란 객체를 식별할 하나의 정수값을 말한다.
Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 떄문에 객체마다 다른 값을 갖는다.
논리적 동등 비교시 hashCode()를 오버라이딩 할 필요성이 있는데, 15장에서 배울 컬렉션 프레임워크에서 HashSet, HashMap, Hashtable은 다음과 같은 방법으로 두 객체가 동등한지 비교한다.
우선 HashCode() 메소드를 실행해서 리턴된 해시코드값이 같으면 equals() 메소드로 다시 비교한다.
그렇기 때문에 hashCode() 메소드가 true가 나와도 equals()의 리턴값이 다르면 다른 객체가 된다.
11.3.3 객체 문자 정보( toString( ) )
- 객체를 문자열로 표현한 값
- Object 클래스의toString() 메소드는 객체의 문자 정보 리턴
Object obj = new Object();
System.out.println(obj.toString());
//실행결과
java.lang.Object@de6ced
Object클래스의 toString() 메소드는 객체의 문자 정보를 리턴한다.
기본적으로 "클래스명@16진수 해시코드" 로 구성된 문자 정보를 리턴한다.
Object의 toString() 메소드의 리턴값은 자바 애플리케이션에서는 별 값어치가 없는 정보이므로 Object 하위 클래스는 toString() 메소드를 재정의(오버라이딩)하여 간결하고 유익한 정보를 리턴하도록 되어 있다.
예를들어 java.util 패키지의 Date 클래스는 toString() 메소드를 재정의하여 현재 시스템 날짜와 시간 정보를 리턴한다.
그리고 String 클래스는 toString() 메소드를 재정의해서 저장하고 있는 문자열을 리턴한다.
public class ToStringExample {
public static void main(String[] args) {
Object obj1 = new Object();
Date obj2 = new Date();
System.out.println(obj1.toString());
System.out.println(obj2.toString());
}
}
우리가 만드는 클래스도 toString() 메소드를 재정의해서 좀 더 유용한 정볼르 리턴하도록 할 수 있다.
SmartPhone 클래스에서 toString() 메소드를 오버라이딩하여 제작회사와 운영체제를 리턴하도록 만들어보자.
public class SmartPhone {
private String company;
private String os;
public SmartPhone(String company, String os) {
this.company = company;
this.os = os;
}
@Override
public String toString() {
return company + ", " + os;
}
}
- System.out.pritnln(Object) 메소드
• Object의 toString()의 리턴값 출력
우리가 지금까지 콘솔에 출력하기 위해 System.out.pritnln() 메소드를 사용해 왔다.
이 메소드의 매개값은 콘솔에 출력할 내용인데, 매개값이 기본타입(byte, short, int, long, float, double, boolean)일 경우, 해당 값을 그대로 출력한다.
만약 매개값으로 객체를 주면 객체의 toString()메소드를 호출해서 리턴값을 받아 호출하도록 되어 있다.
11.3.4 객체 복제(clone())
- 원본 객체의 필드 값과 동일한 값을 가지는 새로운 객체 생성하는 것
- 복제 종류
• 얕은 복제(thin clone) : 필드 값만 복제 (참조 타입 필드는 번지 공유)
• 깊은 복제(deep clone) : 참조하고 있는 객체도 복제
얕은 복제(thin clone)
단순히 필드값을 복사해서 객체를 복제하는 것을 말한다.
Object의 clone() 메소드는 자신과 동일한 필드값을 가진 얕은 복제된 객체를 리턴한다.
이 메소드로 객체를 복제하려면 원본 객체는 반드시 java.lang.Cloneable 인터페이스를 구현하고 있어야 한다.
(구현하지 않으면 CloneNotSupportedException 예외 발생)
public class Member implements Cloneable { //"Cloneable" 복제를 할 수 있다는 선언
public String id;
public String name;
public String password;
public int age;
public boolean adult;
public Member(String id, String name, String password, int age, boolean adult ) {
this.id = id;
this.name = name;
this.password = password;
this.age = age;
this.adult = adult;
}
public Member getMember() {
Member cloned = null;
try {
cloned = (Member) clone(); //clone() 메소드의 리턴 타입은 Object이므로
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cloned;
}
}
깊은 복제(deep clone)
참조하고 있는 객체도 복제하는 것을 말한다.
import java.util.Arrays;
public class Member implements Cloneable {
public String name;
public int age;
public int[] scores;
public Car car;
public Member(String name, int age, int[] scores, Car car) {
this.name = name;
this.age = age;
this.scores = scores;
this.car = car;
}
@Override
protected Object clone() throws CloneNotSupportedException {
//먼저 얕은 복사를 해서 name, age를 복제한다.
Member cloned = (Member) super.clone();
//scores를 복제한다.
cloned.scores = Arrays.copyOf(this.scores, this.scores.length);
//car를 복제한다.
cloned.car = new Car(this.car.model);
//깊은 복제된 Member 객체를 리턴
return cloned;
}
public Member getMember() {
Member cloned = null;
try {
cloned = (Member) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cloned;
}
}
public class Car {
public String model;
public Car(String model) {
this.model = model;
}
}
11.3.5 객체 소멸자(finalize())
참조하지 않는 배열이나 객체는 쓰레기 수집기(Garbage Collector)가 힙 영역에서 자동적으로 소멸시킨다.
쓰레기 수집기는 객체를 소멸하기 직전에 마지막으로 객체 소멸자(finalize())를 실행시킨다.
소멸자는 Object의 finalize() 메소드를 말하는데, 기본적으로 실행 내용이 없다.
객체가 소멸되기 전에 실행할 코드가 있다면 Object의 finalize()를 재정의 하면 된다.
- 될 수 있으면 소멸자는 사용하지 말 것
• GC는 메모리의 모든 쓰레기 객체를 소멸하지 않음
• GC의 구동 시점이 일정하지 않음
public class FinalizeExample {
public static void main(String[] args) {
Counter counter = null;
for(int i=1; i<=50; i++) {
counter = new Counter(i);
counter = null;
System.gc();
}
}
}
11.4 Objects 클래스
object와 유사한 이름을 가진 java.util.Objects 클래스는 객체 비교, 해시코드 생성, null 여부, 객체 문자열 리턴 등의 연산을 수행하는 정적 메소드들로 구성된 Object의 유틸리티 클래스이다.
11.4.1 객체 비교( compare(T a, T b, Comparator<T>c) )
- a, b 두 객체를 비교자(c)로 비교해 int값 리턴
a가 b보다 작으면 음수, 크면 양수를 리턴하도록 클래스를 만들어야 한다.
public interface Comparator<T>{
int compare(T a, T b);
}
public class CompareExample {
public static void main(String[] args) {
Student s1 = new Student(1);
Student s2 = new Student(1);
Student s3 = new Student(2);
int result = Objects.compare(s1, s2, new StudentComparator());
System.out.println(result);
result = Objects.compare(s1, s3, new StudentComparator());
System.out.println(result);
}
static class Student {
int sno;
Student(int sno) {
this.sno = sno;
}
}
static class StudentComparator implements Comparator<Student> {
@Override
public int compare(Student a, Student b) {
/*if(a.sno<b.sno) return -1;
else if(a.sno == b.sno) return 0;
else return 1;*/
return Integer.compare(a.sno, b.sno);
}
}
}
11.4.2 동등 비교( equals()와 deepEquals())
Objects.equals(Object a, Object b)
deepEquals(Object a, Object b)
- 비교할 객체가 배열일 경우 항목 값까지도 비교
11.4.3 해시코드 생성(hash(), hashCOde())
- Objects.hash(Object… values)
• 매개값으로 주어진 값들 이용해 해시 코드 생성하는 역할
• Arrays.hashCode(Object[]) 호출해 해시코드 얻어 리턴
• 클래스의 hashCode()의 리턴값 생성할 때 유용하게 사용
- Objects.hashCode(Object o)
• o.hashCode() 호출하고 받은 값 리턴
• 매개값이 null 이면 0 리턴
public class HashCodeExample {
public static void main(String[] args) {
Student s1 = new Student(1, "홍길동");
Student s2 = new Student(1, "홍길동");
System.out.println(s1.hashCode());
System.out.println(Objects.hashCode(s2));
}
static class Student {
int sno;
String name;
Student(int sno, String name) {
this.sno = sno;
this.name = name;
}
@Override
public int hashCode() {
return Objects.hash(sno, name);
}
}
}
11.4.4 널 여부 조사( isNull(), nonNull(), requireNonNull() )
- Objects.isNull(Object obj)
• obj가 null일 경우 true
- Objects.nonNull(Object obj)
• obj가 not null일 경우 true
- requireNonNull()
첫번째 값이 not null이면 첫번째 매개값을 리턴하고, null이면 모두 NullPointerException을 발생시킨다.
11.4.5 객체 문자 정보( toString() )
Objects.toString은 객체의 문자 정보를 리턴하는데 다음 두 가지로 오버로딩 되어 있다.
리턴 타입 | 메소드(매개 변수) | 설명 |
String | toString(Object o) | not null → o.toString() null → "null" |
String | toString(Object o, String nullDefault) | not null → o.toString() null → nullDefault |
11.5 System 클래스
자바 프로그램은 운영체제상에서 바로 실행되는 것이 아니라 JVM 위에서 실행된다.
따라서 운영체제의 모든 기능을 자바 코드로 직접 접근하기란 어렵다.
하지만 java.lang 패키지에 속하는 System 클래스를 이용하면 운영체제의 일부 기능을 이용할 수 있다.
• 프로그램 종료, 키보드로부터 입력, 모니터 출력, 메모리 정리, 현재 시간 읽기
• 시스템 프로퍼티 읽기, 환경 변수 읽기
System 클래스의 모든 필드와 메소드는 정적(static) 필드와 정적(static) 메소드로 구성되어 있다.
11.5.1 프로그램 종료(exit())
- 기능 - 강제적으로 JVM 종료
• int 매개값을 지정하도록 - 종료 상태 값
º 정상 종료일 경우 0, 비정상 종료일 경우 0 이외 다른 값
º 어떤 값 주더라도 종료
• 만약 특정 상태 값이 입력되었을 경우에만 종료하고 싶다면?
º 자바의 보안 관리자 설정
System.exit(0);
11.5.2 쓰레기 수집기 실행(gc())
자바는 개발자가 메모리를 직접 코드로 관리하지 않고 JVM이 알아서 자동으로 관리한다.
JVM은 메모리가 부족할 때와 CPU가 한가할 떄에 쓰레기 수집기(Garbage Collector)를 실행시켜 사용하지 않는 객체를 자동 제거한다.
다음과 같이 new 연산자로 car 객체를 생성하고 myCar에 객체 번지를 대입했다고 가정하자.
Car myCar = new Car();
만약 myCar에 null을 대입하면, myCar는 객체의 번지를 잃게 된다.
객체의 번지를 모르니, 더 이상 Car 객체는 사용할 수가 없고 이제부터 쓰레기가 된다.
myCar = null;
변수 myCar가 다른 Car 객체를 참조할 경우도 마찬가지이다.
이전 객체의 번지를 잃기 때문에 이전 객체는 쓰레기가 된다.
Car myCar = new Car();
mycar = new Car();
쓰레기 수집기는 개발자가 직접 코드로 실행시킬 수 없고, JVM에게 가능한 빨리 실행해 달라고 요청할 수는 있다.
이것이 System.gc() 메소드이다.
System.gc()메소드가 실행되면 쓰레기 수집기가 바로 실행되는 것은 아니고, JVM은 빠른 시간 내에 실행시키기 위해 노력한다.
System.gc()
11.5.3 현재 시각 읽기( currenctTimeMillis(), nanoTime() )
- 현재 시간을 읽어 밀리 세컨드(currentTimeMillis() -> 1/1000초) 와
나노세컨드(nanoTime()->1/109초) 단위의 long값 리턴
long time = System.currentTimeMillis();
long time = System.nanoTime();
- 주로 프로그램 실행 소요 시간 구할 때 이용
public class SystemTimeExample {
public static void main(String[] args) {
long time1 = System.nanoTime(); //시작시간 읽기
int sum = 0;
for(int i=1; i<=1000000; i++) {
sum += i;
}
long time2 = System.nanoTime(); //종료 시간 읽기
System.out.println("1~1000000까지의 합: " + sum);
System.out.println("계산에 " + (time2-time1) + " 나노초가 소요되었습니다.");
}
}
11.5.4 시스템 프로퍼티 읽기( getProperty() )
- 시스템 프로퍼티란?
• JVM이 시작할 때 자동 설정되는 시스템의 속성값
키(key) | 설명 | 값(value) |
java.version | 자바의 버전 | 1.8.0_20 |
java.home | 사용하는 JRE의 파일 경로 | <jdk 설치 경로>\jre |
os.name | Operating system name | Windows 7 |
file.separator | File separator("/" on Unix) | \ , / |
user.name | 사용자의 이름 | 사용자 계정 |
user.home | 사용자의 홈 디렉토리 | C:\Users\사용자계정 |
user.dir | 사용자가 현재 작업중인 디렉토리 경로 | 다양 |
String value = System.getProperty(String key);
String osName = system.getProperty("os.name");
11.5.5 환경 변수 읽기(getenv())
- 운영체제가 제공하는 환경 변수 값 (문자열) 을 읽음
11.6 Class 클래스
자바는 클래스와 인터페이스 메타 데이터를 java.lang 패키지에 소속된 Class 클래스로 관리한다.
여기서 메타 데이터란 클래스의 이름, 생성자 정보, 필드 정보, 메소드 정보를 말한다.
11.6.1 Class 객체 얻기( getClass(), forName() )
프로그램에서 Class 개겣를 얻기 위해서는 Object 클래스가 가지고 있는 getClass() 메소드를 이용하면 된다.
Object는 모든 클래스의 최상위 클래스이므로 모든 클래스에서 getClass() 메소드를 호출 할 수 있다.
//객체로 부터 얻는 방법
Class clazz = obj.getClass();
//문자열로 부터 얻는 방법
try{
Class class = Class.forName(String className);
}catch(ClassNotFoundException e){
}
11.6.2 리플렉션( getDeclaredConstructors(), getDeclaredFields(), getDeclaredMethods() )
Class객체를 이용하면 클래스의 생성자, 필드, 메소드 정보를 알아 내 수 있다.
이것을 리플렉션(Reflection) 이라고 한다.
Class 객체는 리플렉션을 위해 getDeclaredConstructors(), getDeclaredFields(), getDeclaredMethods()을 제공하고 있다.
각각 Contructor 배열 , Field 배열, Method 배열을 리턴한다.
11.6.3 동적 객체 생성( newInstance() )
Class 객체를 이용하면 new 연산자를 사용하지 않아도 동적으로 객체를 생성 할 수 있다.
이 방법은 코드 작성시 클래스 이름을 결정할 수 없고, 런타임 시에 클래스 이름이 결정되는 경우 유용하게 사용된다.
다음 코드 처름 Class.forName() 메소드로 Class 객체를 얻은 다음 newInstance() 메소드를 호출하면 Object 타입의 객체를 얻을 수 있다.
try{
Class clazz = Class.forName("런타임 시 결정되는 클래스 이름");
Object obj = clazz.newInstance();
}catch(ClassNOtFoundExcepton e){
}catch(InstantiationExcepton e){
}catch(IllegalAccessExcepton e){
newInstance() 메소드의 리턴 타입은 Object 이므로 이것을 원래 클래스 타입으로 변환해야만 메소드를 사용할 수 있다.
그렇게 하기 위해서는 강제 타입 변환을 해야 하는데 클래스 타입을 모르는 상태이므로 변환을 할 수가 없다.
이 문제를 해결 하려면 인터페이스 사용이 필요하다.
예를 들어 Action 인터페이스와 구현 클래스인 SendAction, ReceiveAction이 있다고 가정해보자.
Class.forName() 메소드의 매개값으로 "SendAction" 또는 "ReceiveAction"을 주면 Class 객체가 만들어지고, Class 객체의 newInstance() 메소드로 Object 객체를 얻을 수 있다.
얻어진 객체는 모두 Action 인터페이스를 구현하고 있기 때문에 다음과 같이 Action 인터페이스 타입으로 변환이 가능하다.
그런 다음, Action 인터페이스의 execute() 메소드를 호출하면, 개별 클래스의 실체 메소드인 execute() 메소드가 실행 된다.
Class clazz = Class.forName("SendAction" 또는 "ReceiveAction");
Action action = (Action)clazz.newInstance();
action.execute();
public interface Action {
public void execute();
}
public class SendAction implements Action {
@Override
public void execute() {
System.out.println("데이터를 보냅니다.");
}
}
public class ReceiveAction implements Action {
@Override
public void execute() {
System.out.println("데이터를 받습니다.");
}
}
'JAVA > 이것이 자바다' 카테고리의 다른 글
Chapter .12-1 멀티 스레드 (0) | 2021.07.26 |
---|---|
Chapter .11-2 기본 API 클래스 (0) | 2021.07.25 |
Chapter .10 예외처리 (0) | 2021.07.22 |
Chapter .09 중첩 클래스와 중첩 인터페이스 (0) | 2021.07.20 |
Chapter .08 인터페이스 (0) | 2021.07.19 |