
모든 클래스의 최상위 부모 클래스
⇒ 모든 클래스는 Object클래스를 자동으로 상속
class Test /*extends Object 생략 */ {}
Object클래스에는 모든 객체가 사용할 수 있는 기본 메서드 포함
equals()== 연산자와 같이 메모리 주소를 비교 하지만 override를 통해 논리적비교 구현 가능(값 비교)hashCode()hashCode값을 반환해야함 ⇒ equals() 오버라이딩할때 함꼐 오버라이딩해야 하는 이유toString()클래스명@해시코드 형태로 출력equals() 오버라이딩equals()는 기본적으로 == 와 동일하게 객체의 실제 메모리 주소값을 비교
⇒ 같은 데이터값을 가진 객체라도 다르다고 판단
객체간의 동등성을 비교하기 위해선 equals()를 오버라이딩하여 사용해야한다.
public class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true; // 같은 객체인지 확인(두 객체의 메모리 주소가 완전히 같은 객체)
if (o == null || getClass() != o.getClass()) return false;//null이면 false, getClass를 통해 클래스 타입확인
Person person = (Person) o; //o가 Person타입임이 확인 -> Person타입으로 다운캐스팅
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("John Doe", 27);
Person person2 = new Person("John Doe", 27);
boolean equals = person1.equals(person2);
System.out.println(equals);
}
}
출력
Override 전 : false
Override 후 : true
equals()를 오버라이딩하는 과정에서 마지막에 실질적인 비교를 할때
a.equals(b) 를 사용하면 a가 null 인 경우 NullPointerException 발생 가능
⇒ Objects.equals(a, b) 는 null를 자동으로 처리해 안전하게 비교가능
hashCode()는 객체를 해시기반 컬렉션(HashMap, HashSet …)에 저장하거나 비교할 때 사용equals()만 오버라이딩하면 논리적으로 같은 객체가 다른 해시코드를 가질 수 있음.⇒ 결과적으로 논리적으로 같은 객체(모든 필드의 값이 같은 객체)를 컬렉션에 중복 저장될 수 있음
사실 같은 값을 가지고 있는String타입들을 비교하면 == 연산자를 통해서도 true 가 반환된다.
그렇다면 equals() 를 오버라이딩해야하는 이유가 없지 않은가!
자바에서는 문자열(String)을 효율적으로 관리하기 위해서 문자열 상수 풀이라는 메모리 영역을 사용한다.
문자열 상수풀은 Heap영역에 일부로, 동일한 문자열 리터럴을 재사용하여 메모리 낭비를 방지한다.
⇒ 즉 같은 값을 가진 String 객체들은 그 하나의 객체만을 재사용하여 참조한다.
public class Main {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
boolean areTheySame = str1==str2;
System.out.println(areTheySame); //ture
}
}
하지만 new 키워드로 String 객체를 생성하면 Heap 영역에 새로운 객체가 생성된다.
⇒ 다른 객체를 참조하게 되어 == 연산자로 비교하면 false 반환
public class Main {
public static void main(String[] args) {
String str1 = new String("Hello");
String str2 = new String("Hello");
boolean areTheySame = str1==str2;
boolean theValuesAreSame = str1.equals(str2);
System.out.println(areTheySame); //false
System.out.println(theValuesAreSame); //true
}
}
따라서 이럴 땐 equals()를 사용하여 객체의 값을 비교해야한다.
compareTo() 메소드를 오버라이딩하여 정의Collections.sort() , Arrays.sort()를 사용하면 자동으로 정용public class BodyBuilders implements Comparable<BodyBuilders> {
protected String name;
protected int age;
protected int volume;
public BodyBuilders(String name, int age, int volume) {
this.name = name;
this.age = age;
this.volume = volume;
}
@Override
public int compareTo(BodyBuilders o) {
return Integer.compare(this.volume, o.volume);
}
@Override
public String toString() {
return
"name='" + name + '\'' +
", age=" + age +
", volume=" + volume +
'}';
}
}
public class Main {
public static void main(String[] args) {
List<BodyBuilders> builders = new ArrayList<>();
builders.add(new BodyBuilders("John", 20, 20));
builders.add(new BodyBuilders("Tom", 23, 28));
builders.add(new BodyBuilders("Jack", 28, 30));
builders.add(new BodyBuilders("Jane", 31, 18));
System.out.println(builders);
builders.sort(null); //Collections.sort(builders);
System.out.println(builders);
}
}
public int compareTo(BodyBuilders o) {
return Integer.compare(this.volume, o.volume);
}
이 부분을 내림차순으로 하고 싶다면
public int compareTo(BodyBuilders o) {
return Integer.compare(o.volume, this.volume);
}
이렇게 둘의 순서를 바꿔주면 된다.
왜냐하면
Integer.compare() 은
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
이렇게 생겨먹었다.
그러므로 비교 순서를 바꾸면 내림차순으로 정렬 된다.
이는 Comparator의 compare()에도 동일하게 적용된다.
compare() 메소드를 오버라이딩하여 정의public class BuilderComparator implements Comparator<BodyBuilders> {
@Override
public int compare(BodyBuilders o1, BodyBuilders o2) {
return Integer.compare(o1.age,o2.age);
}
}
public class Main {
public static void main(String[] args) {
List<BodyBuilders> builders = new ArrayList<>();
builders.add(new BodyBuilders("John", 45, 20));
builders.add(new BodyBuilders("Tom", 23, 28));
builders.add(new BodyBuilders("Jack", 28, 30));
builders.add(new BodyBuilders("Jane", 31, 18));
System.out.println(builders);
builders.sort(new BuilderComparator()); //Collections.sort(builders, new BuilderComparator);
System.out.println(builders);
}
}
builders.sort((o1, o2) -> {
if(o1.age == o2.age) {
return Integer.compare(o2.volume, o1.volume);
}
return Integer.compare(o1.age, o2.age);
});
이 방법이 지금의 나에겐 가장 현실적인 방법 같다.
밑의 두 방법은 아직 나에겐 무리다.. 미래를 기약하겠다.
userGuild.sort(
Comparator.comparing(
(Character c) -> c.getGold(), Comparator.reverseOrder()
).thenComparing(
(Character c) -> c.getLevel()
)
);
userGuild.sort(
Comparator.comparing(
Character::getGold, Comparator.reverseOrder()
).thenComparing(
Character::getLevel
)
);
자바가 기본적으로 제공하는 어노테이션이다.
@Override
오버라이딩을 올바르게 했는지 컴파일러가 체크하게 한다.
@Deprected
앞으로 사용하지 않는 것을 권장함.
@Deprecated
public class TrashClass {
@Deprecated
public void trash() {
System.out.println("Trash");
}
}
@Deprecated를 붙은 클래스나 메소드를 사용하려고 하면 밑줄따위가 그어지며 사용하지 않을 것을 권장한다.
@FunctionalInterface
함수형 인터페이스가 하나의 추상메서드만 가지고 있는지를 확인한다.
이는 람다을 사용하기 위해서 필요한 개념이다.
@SuppressWarnings(무시할 경고)
컴파일러의 경고메세지를 무시한다.
"unchecked", “deprected” , "all" 등이 올 수 있다.
어노테이션을 위한 어노테이션
@Target
어노테이션을 정의할 때, 적용대상을 지정
@Target({ElementType.TYPE, ElementType.METHOD}) 이런식으로 사용
@Retention
어노테이션의 유지기간을 지정
Runtime 클래스 파일에도 존재, 실행시 사용가능(값 호출 가능)Source 컴파일되면 사라짐 (걍 주석)@Rentention(RetentionPolicy.SOURCE) 사용 예
그 외. @Documented, @Inherited, @Repeatable 등..
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetTest {
String name() default "Test";
int testCnt();
}
런타임(실행 시간)에 클래스, 메서드, 필드 등의 정보를 조회하고 조작할 수 있는 기능을 의미합니다.
⇒ 실행 중에 객체의 구조를 분석하고 동적으로 변경
JVM의 클래스 로더에서 클래스 파일에 대한 로딩을 완료 후 해당 클래스의 정보를 담은 Class 타입의 객체를 생성 후 힙 영역에 저장 → new키워드로 만드는 객체 와는 다르다.
클래스 이름으로 Class 객체 가져오기
Class<?> cl1 = Example.class;
객체를 통한 Class 객체 가져오기
Example example = new Example();
Class<?> cls2 = example.getClass();
클래스 이름을 문자열로 가져오기
Class<?> cls3 = Class.forName("Example");
Example example = new Example();
Class<?> cls2 = example.getClass();
여기서 나는 저 example 객체와 cls2 객체가 다른건가라는 생각을 했다.
결과적으로 다르다.
여기까지 난 특정 클래스의 정보를 가지고 있는Class 객체를 생성?불러와서 이 객체를 통해서 특정 클래스의 정보를 가져올 수 있구나로 이해했다.
근데 왜 이게 되는거지가 궁금해서 좀 찾아보니 다음과 같은 과정으로 설명할 수 있을 거 같다.
이제야 좀 이해가 되는 거 같다. 근데 의문점이 있다. 어째서 Class객체는 메타데이터의 접근제어자를 무시하는가..
⇒ 좀 찾아보니 JVM내부적으로 setAccessible(true)를 사용해서 접근 제한을 해제한단다.. 너무 딥해지니 이제 그만알아보자..
클래스 관련
getName() : 클래스 전체 이름 (패키지 포함)getSimpleName() : 클래스 이름 (패키지 제외)getPackage() : 패키지정보getSuperclass() : 부모클래스 정보getInterfaces() : 클래스가 구현한 인터페이스 정보생성자 관련
Constructor<?>[] constructor = cls.getConstructors(); : public 생성자들을 배열의 형태로 가져옴Constructor<?>[] constructor = cls.getConstructor(String.class); : 특정 매개변수 생성자 가져오기Constructor<?>[] constructor = cls.getDeclaredConstructors(); : private 생성자도 가져Object obj = constructor.newInstance("어쩌고"); : 생성자를 이용해 객체 생성필드 관련
Field[] fields = cls.getFields() : 필드 배열 가져오기
Field field = cls.getField("필드명") : 특정 public 필드 가져오기getDeclaredFields() : 모든 필드 가져오기
getDeclaredField("필드명") : 특정 private필드 가져오기
get(obj) : 객체의 필드 값을 가져옴(인스턴스 → 객체 필요, Static필드 → 객체 불필요)
set(obj, "필드이름") : 필드값 변경
메소드 관련
invoke(obj) : 매개변수 없는 메서드 실행invoke(obj, "매개변수 명", 인자값) : 매개변수 있는 메서드 실행어노테이션 관련
isAnnotationPresent(MyAnnotation.class) : 클래스의 모든 어노테이션 가져오기getAnnotation(MyAnnotation.class) : 특정 어노테이션 가져오기