참조하지 않는 배열이나 객체는 쓰레기 수집기 (Garbage Collector) 가 자동으로 힙 영역에서 소멸 시킨다. 이 쓰레기 수집기는 객체를 소멸하기 직전에 마지막으로 객체의 소멸자 (finalize())를 실행시킨다.
-> 실제 실행 결과를 보면 순서대로 소멸시키는게 아니라 무작위로 소멸시키는 것을 확인할 수 있다. 또한 전부 소멸시키는 것이 아니라 메모리의 상태를 보고 일부만 소멸시킨다.
-> finalize() 메소드가 호출되는 시점은 명확하지 않고, 될 수 있으면 소멸자는 사용하지 말 것.
Object와 이름은 비슷하지만 다르다. 객체 비교, 해시코드 생성, null여부, 객체 문자열 리턴 등의 연산 수행하는 정적 메소드들로 구성되어있는 Object의 유틸리티 클래스이다
Objects의 정적 메소드를 간단하게 몇 가지 사용해보자
public class Example {
public static void main(String[] args) {
boolean result = Objects.equals(10, 10);
boolean result1 = Objects.equals(10, 30);
System.out.println(result + ", "+ result1);
// 객체가 들어갈 수도 있다.
Car car1 = new Car(1000);
Car car2 = new Car(1000);
boolean result2 = Objects.equals(car1.price, car2.price);
System.out.println(result2);
System.out.println("-------------------------------------------------");
// Deep_equals
/*
public static boolean deepEquals(Object a, Object b) {
if (a == b)
return true;
else if (a == null || b == null)
return false;
else
return Arrays.deepEquals0(a, b);
}
*/
int[] intArr1 = {10, 20, 30};
int[] intArr2 = {40, 50, 60};
boolean result3 = Objects.deepEquals(intArr1, intArr2);
System.out.println(result3);
int[] intArr3 = {10, 20};
int[] intArr4 = {10, 20};
boolean result4 = Objects.deepEquals(intArr3, intArr4);
System.out.println(result4);
System.out.println("-------------------------------------------------");
/*
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
*/
int hasResult = Objects.hash("김자바", "이자바");
System.out.println(hasResult);
int hasCodeResult = Objects.hashCode("김자바");
System.out.println(hasCodeResult);
System.out.println("-------------------------------------------------");
/*
public static boolean isNull(Object obj) {
return obj == null;
}
*/
// null 인지 조사하기
String name = null;
if (Objects.isNull(name)) {
System.out.println("이름이 없습니다");
} else {
System.out.println("이름 : " + name);
}
System.out.println("-------------------------------------------------");
}
}
class Car{
int price;
public Car(int price) {
this.price = price;
}
}
실행 모습
public class Example2 {
public static void main(String[] args) {
/*
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
*/
// 객체가 null인 경우 예외 발생
// 들어가는 타입에 따라 나오는 타입도 동일하게 결정된다
int num1 = 9;
int iRetrun = Objects.requireNonNull(num1);
System.out.println(iRetrun);
String name1 = "홍길동";
String tRetrun = Objects.requireNonNull(name1);
System.out.println(tRetrun);
// null 일때 예외 발생하니 try-catch로 잡아주기
String name2 = null;
try {
String tReturn2 = Objects.requireNonNull(name2);
System.out.println(tReturn2);
} catch (NullPointerException e) {
System.out.println("이름이 없습니다");
System.out.println("에러 메시지 : " + e.getMessage());
}
System.out.println("-------------------------------------------------");
/*
public static String toString(Object o) {
return String.valueOf(o);
}
*/
int num2 = 1234;
String toStringResult = Objects.toString(num2);
System.out.println(toStringResult);
Car2 c2 = new Car2(10000);
String toStringResult2 = Objects.toString(c2.price);
System.out.println(toStringResult2);
// 공백이 들어가면 "빈 자료" 가 대신 들어가는 출력
String str = null;
String toStringResult3 = Objects.toString(str, "빈 자료");
System.out.println(toStringResult3);
}
}
class Car2{
int price;
public Car2(int price) {
this.price = price;
}
}
실행 모습
: Objects.compare(T a, T b, Domparator<T>c)
메소드는 두 객체를 비교해 int 값을 리턴한다.
: a가 b보다 작으면 음수, 같으면 0, 크면 양수를 리턴하도록 구현 클래스를 만들어야 한다.
Comparator
의 원형은 Comparator<타입>
이 모습이다. 아래 예시에서는 타입을 지정 안해줬기 때문에 재정의하면서 다운 캐스팅 해주고 사용public class CompareEx {
public static void main(String[] args) {
Comparator c = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// 값이 들어가는지 확인하기
// System.out.println(o1);
// System.out.println(o2);
// 다운 캐스팅 하기
Integer io1 = (Integer) o1;
Integer io2 = (Integer) o2;
int r = 0;
if (io1 < io2) {
r = -1;
} else if (io1 == io2) {
r = 0;
} else {
r = 1;
}
return r;
}
};
// 호출해 사용
// (a, b, c) 에서 비교할 수를 a와 b에 넣고
// Comparator을 객체 생성한 c를 넣어 사용
int result1 = Objects.compare(120, 127, c);
System.out.println(result1);
if (result1 == 0) {
System.out.println("입력 수 두 개는 같습니다");
} else if (result1 > 0) {
System.out.println("먼저 입력한 수가 더 크네요");
} else {
System.out.println("두번째 입력 수가 더 크네요");
}
}
}
실행 결과
-1 두번째 입력 수가 더 크네요
Comparator <사용할 자료 타입이 쓰이는 곳>
public class compareEx1 {
public static void main(String[] args) {
// 다운캐스팅을 재정의 내부에서 해주지 않아도 된다.
Comparator<Double> c = new Comparator<Double>() {
@Override
public int compare(Double o1, Double o2) {
if(o1 < o2) { return -1; }
if(o1 > o2) { return 1; }
return 0;
}
};
Double a = new Double(10.45);
Double b = new Double(5.24);
int result = Objects.compare(a, b, c);
System.out.println(result);
}
}
실행 결과
1
Character
비교public class compareEx2 {
public static void main(String[] args) {
Character a = new Character('C');
Character b = new Character('B');
Comparator<Character> c = new Comparator<Character>() {
@Override
public int compare(Character o1, Character o2) {
if (o1 < o2) {
return -1;
}
if (o1 > o2) {
return 1;
}
return 0;
}
};
int result = Objects.compare(a, b, c);
if (result < 0) {
System.out.println(a + "가 " + b
+ " 보다 사전상 앞에 있겠네요");
} else if (result > 0) {
System.out.println(a + "가 " + b
+ " 보다 사전상 뒤에 있겠네요");
} else {
System.out.println(a + "와 " + b
+ " 는같은 문자네요");
}
}
}
실행결과
C가 B 보다 사전상 뒤에 있겠네요
public class compareEx3 {
public static void main(String[] args) {
String a = "김자바";
String b = "홍길동";
Comparator<String> c = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// compareTo
// : 문자의 유니코드를 비교해 계산 후 리턴된다
return o1.compareTo(o2);
}
};
int result = Objects.compare(a, b, c);
if (result < 0) {
System.out.println(a + " (이)가 " + b
+ "보다 사전상 먼저 있습니다");
} else if (result > 0) {
System.out.println(a + " (이)가 " + b
+ "보다 사전상 뒤에 있습니다");
} else {
System.out.println("같은 단어 입니다");
}
}
}
실행결과
김자바(이)가 홍길동보다 사전상 먼저 있습니다
public class CompareEx4_1 {
public static void main(String[] args) throws IOException {
// Student객체를 2번 생성해 2명 각각 넣기
Student s1 = new Student();
Student s2 = new Student();
// 학번 입력하기
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
System.out.println("A의 학번을 입력해주세요");
s1.hakbun = br.readLine();
System.out.println("B의 학번을 입력해주세요");
s2.hakbun = br.readLine();
// 학번 비교하기
Comparator<Student> c = new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.hakbun.compareTo(o2.hakbun);
}
};
int result = Objects.compare(s1, s2, c);
if (result > 0) {
System.out.println("A가 후배입니다");
} else if (result < 0) {
System.out.println("A가 선배입니다");
} else {
System.out.println("A와 B는 동기입니다");
}
}
}
class Student {
String hakbun;
}
실행 결과
public class CompareEx4 {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
System.out.println("A의 학번을 입력해주세요");
String a = br.readLine();
System.out.println("B의 학번을 입력해주세요");
String b = br.readLine();
Comparator<String> c = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
int result = Objects.compare(a, b, c);
if (result > 0) {
System.out.println("A가 후배입니다");
} else if (result < 0) {
System.out.println("A가 선배입니다");
} else {
System.out.println("A와 B는 동기입니다");
}
}
}
Objects.equals(Object a, Object b)
는 두 객체의 동등을 비교하는데 다음과 같은 결과를 리턴한다. 특이한 점은 모두 null일 경우 true를 리턴한다는 점이다. a와 b가 null 이 아닌 경우에는 a.equals(b)의 결과를 리턴한다.
Objects.deepEquals(Object a, Object b)
역시 두 객체의 동등을 비교하는 데, a와 b가 서로 다른 배열일 경우 그리고 항목 값이 모두 같다면 true를 리턴한다.
Deep_equals는 위 Object 클래스의 사용 예시 중에 이미 사용해본 경험이 있다. 아래와 같다.
// Deep_equals 의 원 형태
/*
public static boolean deepEquals(Object a, Object b) {
if (a == b)
return true;
else if (a == null || b == null)
return false;
else
return Arrays.deepEquals0(a, b);
}
*/
int[] intArr1 = {10, 20, 30};
int[] intArr2 = {40, 50, 60};
boolean result3 = Objects.deepEquals(intArr1, intArr2);
System.out.println(result3);
int[] intArr3 = {10, 20};
int[] intArr4 = {10, 20};
boolean result4 = Objects.deepEquals(intArr3, intArr4);
System.out.println(result4);
System.out.println("-------------------------------------------------");
: 자바 프로그램은 운영체제상에서 바로 실행되는 것이 아니라 JVM 위에서 실행된다.
-> java.lang 패키지에 속하는 System 클래스를 이용하면 운영체제의 일부 기능을 이용할 수 있다. 즉 프로그램 종료, 입력, 출력, 메모리 정리, 현재 시간 읽기 등이 가능하다는 것이다.
경우에 따라 강제적으로 JVM을 종료시키고 싶을 때, System 클래스의 exit() 메소드를 호출하면 된다. exit() 메소드는 현재 실행하고 있는 프로세스를 강제 종료시키는 역할을 한다.
사용자가 원하는 종료 설정. 종료 이전에는 for 문이 정상적으로 계속 실행 상태를 유지한다.
public class ExitEx {
public static void main(String[] args) {
// 보안 관리자 설정
// 익명 자손 객체 만들기
System.setSecurityManager(new SecurityManager() {
@Override
public void checkExit(int status) {
// 사용자가 원하는 종료 설정
// 종료 상태값이 5일 경우 예외를 던지고
// 예외 처리에서 프로그램을 종료하도록 처리
if(status != 5) {
throw new SecurityException();
}
}
});
for (int i=0; i<10; i++) {
//i값 출력
// 5까지는 정상적으로 출력되다가
System.out.println(i);
try {
// i에 5가 들어가면 exit() 실행 되는 모습
System.exit(i);
} catch (SecurityException e) { }
}
}
}
JVM은 메모리가 부족할 때와 CPU가 한가할 때에 쓰레기 수집기(Garbage Collector)를 실행시켜 사용하지 않는 객체를 자동 제거한다.
-> gc() 메소드는 메모리가 열악하지 않은 환경이라면 거의 사용할 일이 없다.
System 클래스의 currentTimeMillis(0 메소드와 nanoTime() 메소드는 컴퓨터의 시계로부터 현재 시간을 읽어서 밀리세컨드(1/1000초) 단위와 나노세컨드(1/1999999999초) 단위의 long값을 리턴한다.
// 컴퓨터의 시계로부터 현재 시간을 읽어서
// 밀리 세컨드 단위와 나노세컨드 단위의 long값을 리턴
long time = System.currentTimeMillis();
System.out.println(time);
time = System.nanoTime();
System.out.println(time);
for문으로 돌리며 프로그램 실행 소요 시간을 구해보자
// 시스템의 현재 시간
long time1 = System.nanoTime();
int sum = 0;
for (int i = 1; i <= 1000000; i++) {
sum += i;
}
long time2 = System.nanoTime();
System.out.println("1~100000 까지의 누적 합 : " + sum);
System.out.println("계산에 걸린 시간 : " + (time2 - time1)
+ " (단위: 나노초)");
시스템 프로퍼티는 JVM이 시작할 때 자동 설정되는 시스템의 속성값을 말한다. 예를 들어 운영체제의 종류 및 자바 프로그램을 실행시킨 사용자 아이디, JVM의 버전, 운영체제에서 사용되는 파일 경로 구분자 등이 여기에 속한다.
String value = System.getProperty(String key);
시스템 프로퍼티를 읽어오기 위해서는 System.getProperty()
메소드를 이용하면 된다. 이 메소드는 시스템 프로퍼티의 키 이름을 매개값으로 받고, 해당 키에 대한 문자열로 리턴한다.
프로퍼티의 키, 값을 읽어온다면 아래와 같다.
// (키, 값)으로 구성된 속성(Properties)들을 리턴받는다
Properties props = System.getProperties();
// keySet() 은 '키'로만 구성된 set 객체를 받는다
// Set은 자료구조의 하나로 컬렉션의 종류 중 하나
Set keys = props.keySet();
// for문은 Set 객체로부터 키를 하나씩 얻어내어 문자열로 변환한 다음
// System.getProperty() 메소드로 값을 얻어와 키와 값을 모두 출력
for (Object objkey : keys) {
String key = (String) objkey;
String value = System.getProperty(key);
System.out.println("[" + key + "]" + value);
}
사용하는 모습을 더 보자
String osName = System.getProperty("os.name");
String userName = System.getProperty("user.name");
String userHome = System.getProperty("user.home");
String javaVer = System.getProperty("java.version");
String dir = System.getProperty("user.dir");
System.out.println("운영체제 이름 : " + osName);
System.out.println("사용자 이름 : " + userName);
System.out.println("사용자의 홈 디렉토리 : " + userHome);
System.out.println("자바 버젼 : " + javaVer);
System.out.println("현재 작업중인 디렉토리 경로 : " + dir);
System.out.println("----------------------------------");
System.out.println(" [key] value");
System.out.println("----------------------------------");
// (키, 값)으로 구성된 속성(Properties)들을 리턴받는다
Properties props = System.getProperties();
// keySet() 은 '키'로만 구성된 set 객체를 받는다
// Set은 자료구조의 하나로 컬렉션의 종류 중 하나
Set keys = props.keySet();
// for문은 Set 객체로부터 키를 하나씩 얻어내어 문자열로 변환한 다음
// System.getProperty() 메소드로 값을 얻어와 키와 값을 모두 출력
for (Object objkey : keys) {
String key = (String) objkey;
String value = System.getProperty(key);
System.out.println("[" + key + "]" + value);
}
System.out.println("----------------------------------");
// 여기 있는 것이 null 값을 가지고 있다면, "여기 있는 것이 기본설정으로 출력된다"
// 키가 null을 가지고 있다면(키가 유호하지 않다면), 여기 설정한 것이 기본 값이 된다
String userDir = System.getProperty("user_dir", "기본 설정값");
System.out.println(userDir);
String userjava = System.getProperty("java.version", "기본 설정값");
System.out.println(userjava);
윈도우 환경변수에 있는 것 출력하기
// name에 해당하는 value 값만 구해준다
String getenvValue = System.getenv("JAVA_HOME");
System.out.println(getenvValue);
// 모든 환경변수 출력하기
Map allGetEnv = System.getenv();
Set keys = allGetEnv.keySet();
int cnt = 1;
for(Object objkey : keys ) {
String key = (String) objkey;
String value = System.getenv(key);
System.out.println("NO " + cnt++
+ " | key : " + key + "\t\t\t value : " + value);
프로그램에서 Class 객체를 얻기 위해서는 Object 클래스가 가지고 있는 getClass() 메소드를 이용하면 된다.
public class ClassEx {
public static void main(String[] args) {
Car car = new Car();
// Class는 java.lang 안에 있으니
// 따로 import 같은거 안해줘도 된다
Class cc = car.getClass();
// 참조변수를 출력하면 자동으로 toString()이 붙는다
System.out.println(cc.toString());
System.out.println(cc);
System.out.println(cc.getName());
System.out.println(cc.getSimpleName());
System.out.println(cc.getPackage());
System.out.println(cc.getPackage().getName());
}
}
class Car{ }
실행 결과
class a6_Class.Car class a6_Class.Car a6_Class.Car Car package a6_Class a6_Class
Class 객체를 이용하면 클래스의 생성자, 필드, 메소드 정보를 알아낼 수 있다. 이를 리플렉션(Reflection)이라 한다.
동적으로 클래스 멤버 정보 얻기
public class ReflectionEx {
public static void main(String[] args) throws Exception {
// 얻어올 java파일의 경로를 forName에 넣어준 것
Class clazz = Class.forName("a5_System.SystemTimeEx");
System.out.println("[클래스 이름]");
System.out.println(clazz.getName());
System.out.println("[생성자 정보]");
Constructor[] constructors = clazz.getDeclaredConstructors();
for(Constructor constructor : constructors) {
System.out.print(constructor.getName() + "(");
Class[] parameters = constructor.getParameterTypes();
printParameters(parameters);
System.out.println(")");
}
System.out.println("[필드 정보]");
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields) {
System.out.println(field.getType().getSimpleName() + " " + field.getName());
}
System.out.println("[메소드 정보]");
Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods) {
System.out.print(method.getName() + "(");
Class[] parameters = method.getParameterTypes();
printParameters(parameters);
System.out.println(")");
}
}
private static void printParameters(Class[] parameters) {
for(int i=0; i<parameters.length; i++) {
System.out.print(parameters[i].getName());
if(i<(parameters.length-1)) {
System.out.print(",");
}
}
}
}
실행 결과
[클래스 이름] a5_System.SystemTimeEx [생성자 정보] a5_System.SystemTimeEx() [필드 정보] [메소드 정보] main([Ljava.lang.String;)