2023.03.09 30일차 Java
30일차
12. java.base 모듈
12.1 Object
모든클래스의 상위클래스 배열, 기본타입 조차도 그렇다.
12.1.7 hashCode()
object의 해쉬코드를 리턴한다.
HashFuncthion 특정데이터를 특정값으로 매칭시키는 것 라벨을 붙이는 것이다.
특정 정수로 결과가 나온다.
그 값이 항상 유일할필요는 없다.
Object의 재정의하지 않은 hachCode()는 서로다른 객체는 서로다른 값을 가지게 된다.
즉 참조값을 주게 된다. 재정의하게 되면 hashCode가 꼭 참조값은 아니다.
public class C15HashCode {
public static void main(String[] args) {
Object o1 = new MyClass15();
Object o2 = new MyClass15();
int h1 = o1.hashCode();
int h2 = o2.hashCode();
System.out.println(h1); //617901222
System.out.println(h2); //1159190947
}
}class MyClass16 {
@Override
public int hashCode() {
return 99;
}
}public class C16HashCode {
public static void main(String[] args) {
Object o1 = new MyClass16();
Object o2 = new MyClass16();
int h1 = o1.hashCode();
int h2 = o2.hashCode();
System.out.println(h1); //99
System.out.println(h2); //99
}
}재정의를 하지 않으면 참조값을 반환해서 다른 값이 나오지만
재정의하게 되면 재정의한 코드가 나오게 된다.
String을 보면 같은 문자열은 같은 해쉬코드를 가지게 된다.
이러면 재정의 되었다는 것을 유추할 수 있다.
API를 보면 문자열에 각 값을 곱한 것을 알 수있는데 즉 문자열이 같으면 같은 해쉬코드를 반환하게 된다.
public class C17HashCode {
public static void main(String[] args) {
Object o1 = new String("java");
Object o2 = new String("spring");
Object o3 = new String("java");
int h1 = o1.hashCode();
int h2 = o2.hashCode();
int h3 = o3.hashCode();
System.out.println(h1); //3254818
System.out.println(h2); //-895679987
System.out.println(h3); //3254818
}
}12.1.8 equals()
서로다른객체여도 같냐라고 물어보면 같다라고 해야한다.
분류도 같은 곳으로 분류해야한다.
분류 : hashcode
equals메소드로 같다고 나오기전에 hashcode로 같다고 나와야한다.
둘이 짝을 이룬다고 생각하면 된다.
그래서 재정의할때 보통 같이 재정의한다.
class MyClass19 {
@Override
public int hashCode() {
return 33;
}
@Override
public boolean equals(Object obj) {
return true;
}
}public class C19Equals {
public static void main(String[] args) {
Object o1 = new MyClass19();
Object o2 = new MyClass19();
Object o3 = o1;
System.out.println(o1.hashCode()); //33
System.out.println(o2.hashCode()); //33
System.out.println(o3.hashCode()); //33
System.out.println(o1.equals(o3)); //true
System.out.println(o1.equals(o2)); //true
}
}무조건 해쉬코드가 같고 equals를 트루가 나오게 짯으니 같은 객체라고 나온다.
해쉬코드가 같다고 eqauls에서 false가 나올 수 있다.
그런데 eqauls에서 true가 나오려면 해쉬코드는 무조건 같아야한다.
객체의 데이터의 필드값이 같으면 같다고 하겠다. 하면 해쉬코드와 eqauls를 재정의하면된다.
파라미터로 넘어온것이 MyClass21이고 id와 넘어온 객체의 id가 같다면 같은 것으로 하겠다고 한다.
class MyClass21 {
private int id;
MyClass21(int id) {
this.id = id;
}
@Override
public int hashCode() {
return this.id;
}
@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof MyClass21 o) {
return this.id == o.id;
}
return false;
}
}public class C21Equals {
public static void main(String[] args) {
Object o1 = new MyClass21(5);
Object o2 = new MyClass21(5);
Object o3 = new MyClass21(6);
System.out.println(o1.hashCode()); // 5
System.out.println(o2.hashCode()); // 5
System.out.println(o1.equals(o2)); // true
System.out.println(o1.equals(o3)); // false
}
}12.1.9 hashcode() & equals()
source - generate하면 필드에맞춰 hash코드와 equals를 재정의하게 할 수 있다.
실제 참조 값은 다르다. 사용자가 재정의해서 같은 객체 취급을 할뿐이다.
실제로 같은 객체는 아니다. 사용자가 같은 객체라고 정의하는 것임!!
class MyClass22 {
private int id;
private String name;
private String address;
private boolean married;
public MyClass22(int id, String name, String address, boolean married) {
this.id = id;
this.name = name;
this.address = address;
this.married = married;
}
@Override
public int hashCode() {
return Objects.hash(address, id, married, name);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyClass22 other = (MyClass22) obj;
return Objects.equals(address, other.address) && id == other.id && married == other.married
&& Objects.equals(name, other.name);
}
}public class C22HashCodeEquals {
public static void main(String[] args) {
Object o1 = new MyClass22(1, "kim", "seoul", false);
Object o2 = new MyClass22(1, "kim", "seoul", false);
System.out.println(o1.hashCode()); //1293458344
System.out.println(o2.hashCode()); //1293458344
System.out.println(o1.equals(o2)); //true
}
}12.1.10 HashSet
집합 별개 원소들의 모임 각각 다른 원소들만 있다.
이 집합을 구현한 자바의 타입이 SET이다
중복없는 원소들의 집합
SET은 인터페이스 이기때문에 이것을 구현한 클래스를 사용해야하는데 그중 많이 사용하는게 HashSet이다.
hashcode와 equals를 기준으로 한다.
class MyClass24 {
private int id;
private String name;
public MyClass24(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyClass24 other = (MyClass24) obj;
return id == other.id && Objects.equals(name, other.name);
}
}public class C24HashCodeEquals {
public static void main(String[] args) {
Set set = new HashSet();
set.add(new MyClass24(1, "kim"));
set.add(new MyClass24(2, "edman"));
set.add(new MyClass24(1, "kim"));
System.out.println(set.size()); // 2
}
}12.1.11 확인문제 5번
public class Student {
private String studentNum;
public Student(String studentNum) {
this.studentNum = studentNum;
}
public String getStudentNum() {
return studentNum;
}
//여기부터 작성
@Override
public int hashCode() {
return Objects.hash(studentNum);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
return Objects.equals(studentNum, other.studentNum);
}
}public class StudentExample {
public static void main(String[] args) {
HashSet<Student> hashSet = new HashSet<>();
hashSet.add(new Student("1"));
hashSet.add(new Student("1"));
hashSet.add(new Student("2"));
// 저장된 수 출력
System.out.println("저장된 Student 수: " + hashSet.size());
}
}12.2 포장클래스
Wrapper 클래스
기본타입을 참조타입처럼 사용할 수 있게 해주는 클래스이다.
기본타입이 8개이니 포장클래스도 각각 매치해서 8개가있다.
byte : Byte
short : Short
int : Integer
long : Long
char : Character
float : Float
double : Double
boolean : Boolean
기본타입을 가지고 있는 참조타입을 만들어내었다. -> 이것을 박싱이라고 한다.
박싱은 객체를 리턴하는 것을 말한다.
Integer o2 = new Integer(i1); // 예전 버전 (포장, boxing)
Integer o3 = Integer.valueOf(i1); //지금버전 (포장, boxing) (권장)
그런데 코드가 너무 길어서
기본타입이 참조타입으로 자동 박싱 되도록 바뀌었다.
Integer o1 = i1; // 현재버전 (auto boxing)
그래서 Integer의 상위 클래스인 Number에 건너건너 가능하게 되었다.
Number o4 = i1; // auto boxing, 자동형변환
Object는 모든 클래스의 상위클래스이다.
Object o5 = i1; // auto boxing, 자동형변환
12.2.1 AutoBoxing
자동으로 박싱되고 건너건너 자동형변환도 된다.
public class C03AutoBoxing {
public static void main(String[] args) {
byte b = 3;
Short s = 20000;
Integer i = 200000;
Long l = 99L;
Float f = 3.14f;
Double d = 3.14;
Character c = '가';
Boolean bool = true;
Object o1 = b;
Object o2 = bool;
Object o3 = true;
}
}12.2.2 Boxing
자동 박싱할때 주의할점이잇다.
박싱을 하면 참조타입이 되서 값을 비교하려면 ==이 아니라 equals메소드를 사용해야한다.
public class C04Boxing {
public static void main(String[] args) {
int i1 = 30000;
int i2 = 30000;
System.out.println(i1 == i2);
Integer o1 = i1;
Integer o2 = i2;
System.out.println(o1 == o2); // false
//참조값 비교 사용하지말기
System.out.println(o1.equals(o2)); // true
//필드비교 (이 메소드 사용)
}
}12.2.3 UnBoxing
기본타입과 참조타입의 산술연산이 하고싶다.
예전에는 박싱을 수동으로 햇듯이 언박싱도 수동으로 햇엇다.
int i4 = i1.intValue() * i2; //예전버전
int i3 = i1 * i2; //지금버전
int i5 = i1.intValue(); //unboixng
int i6 = i1; // auto unboxing
12.2.4 확인문제 12번
==로 비교햇는데 false가 나왔다. 왜일까/
==는 참조값 비교이기때문이다.
public class IntegerCompareExample {
public static void main(String[] args) {
Integer obj1 = 100;
Integer obj2 = 100;
Integer obj3 = 300;
Integer obj4 = 300;
System.out.println(obj1.equals(obj2));
System.out.println(obj3.equals(obj4));
}
}12.3 Math클래스
수학 연산에 필요한 메소드를 가진 수학 클래스이다.
모두 static 메소드 이다.
12.3.1 Math.round() : 반올림
반올림하는 메소드로 'long'타입으로 변환한다.
long l1 = Math.round(3.5);
System.out.println(l1); //3
long l2 = Math.round(3.14);
System.out.println(l2); //312.3.2 Math.ceil : 올림
올림하는 메소드로 'double'타입으로 변환한다.
double d1 = Math.ceil(3.5);
System.out.println(d1); //4.0
double d2 = Math.ceil(3.1);
System.out.println(d2); //3.012.3.3 Math.floor : 내림
내림하는 메소드로 'double'타입으로 변환한다.
double d3 = Math.floor(3.14);
double d4 = Math.floor(3.9);
System.out.println(d3); //3.0
System.out.println(d3); //3.012.3.4 Math.max : 둘 중에 큰 값
둘중에 큰값을 반환하는데 비교한 타입으로 반환한다.
double d5 = Math.max(3.14, 3.5);
long d6 = Math.max(300L, 500L);
int d7 = Math.max(5, 3);
System.out.println(d5); //3.5
System.out.println(d6); //500
System.out.println(d7); //512.3.5 Math.min : 둘 중에 작은 값
둘중에 작은값을 반환하는데 비교한 타입으로 반환한다.
double d8 = Math.min(3.14, 3.5);
long d9 = Math.min(300L, 500L);
int d10 = Math.min(5, 3);
System.out.println(d8); //3.14
System.out.println(d9); //300
System.out.println(d10); //312.3.6 확인문제 13번
리턴값이 잘못된 것은?
1.Math.ceil(5.3) -> 6.0
2.Math.floor(5.3) -> 5.0
3.Math.max(5.3,2.5)-> 5.3
4.Math.round(5.7) -> 6.0
정답: 4번 round메소드는 long값을 리턴한다.
12.3.7 확인문제 5장 7번
배열 int[] array = {1,5,3,8,2}; 에서 최대값을 출력해보자.
public class exam07 {
public static void main(String[] args) {
int[] array = {1,5,3,8,2};
int max = Integer.MIN_VALUE;
for (int n : array) {
max = Math.max(max, n)
}
System.out.println(max);
}
}기존 for문이랑 비교해봣는데 이 루프마다 메소드를 실행시켜서 그런지 더 비효율적이다.
12.3.8 coding bat bigDiff
배열에서 가장 큰값과 작은값의 차를 리턴하여라
public int bigDiff(int[] nums) {
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (int n : nums) {
max = Math.max(max, n);
min = Math.min(min, n);
}
return max - min;
}12.4 System 클래스
currentTimeMillis(), nanoTime() 나노초
현재시간(1970년 1월1일 0시부터 현재가지의 milli second경과)
long current = System.currentTimeMillis();
System.out.println(current);
언제 사용하나? 작성한 프로그램이 얼마나 걸리는지 구하고싶을때 사용한다.
public class C02System {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
int r = 3 * 5;
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}12.4.2 확인문제 8번
전체 코드를 실행하는데 걸린시간을 구하는 코드
단위 : 나노초
public class exam08 {
public static void main(String[] args) {
long start = System.nanoTime();
int[] scores = new int[1000];
for (int i = 0; i < scores.length; i++) {
scores[i] = i;
}
int sum = 0 ;
for (int score : scores) {
sum += score;
}
double avg = sum / scores.length;
System.out.println(avg);
long end = System.nanoTime();
System.out.println(end - start);
}
}13.제네릭
12장의리플렉션과 어노테이션 나중에 돌아와서 할 예정
13.1 제네릭
제네릭이 없던 시절과 있는 시절의 차이
Object에 다 넣고 강제 형변환하여 사용했다.
그런데 뭐가 들어올지 몰라서 타입을 체크하고 null값인지 체크하고 출력햇엇다.
public class C01Generic {
public static void main(String[] args) {
MyClass01 o1 = new MyClass01();
o1.setItem("java");
//리턴 타입이 Object라 String으로 강제 형변환
String s1 = null;
Object o2 = o1.getItem();
if (o2 instanceof String) {
s1 = (String) o2;
}
if (s1 != null) {
System.out.println(s1.length());
}
}
}아무 글자로 쓰고 나중에 파라미터로 받겠다는 것을 클래스 명 옆에 붙이게 되었다.
타입파라미터를 작성에 아무거나 적어도된다.
작성 관습은 대문자 한글자이다.
그런데 여기서 의미있게 작성하고 싶다.
E - Element(요소)
K - Key(키)
N - Number(넘버)
T - Type(아무것도모르겟고 타입)
V - Value(밸류)
S U V etc - 의미없게 사용 T S U V ...
class MyClass02<T> {
private T item;
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}public class C02Generic {
public static void main(String[] args) {
//T가 String이 되는 것임
MyClass02<String> o1 = new MyClass02<String>();
o1.setItem("java");
String s1 = o1.getItem();
System.out.println(s1.length());
}
}강제 형변환 없이 안전하게 사용할 수 있게 되었다.
파라미터에 기본타입은 넣을 수 없다.
무조건 클래스 타입이 들어가야한다.
즉 기본타입의 경우 박싱클래스를 넣으면 된다.
MyClass02<Integer> o3 = new MyClass02<Integer>();
o3.setItem(3); //autoboxing
int i = o3.getItem(); //unboixng
System.out.println(i);13.1.2 diamond
MyClass03<String> o1 = new MyClass03<String>();
이렇게 작성하면 앞의 타입으로 인해서 뒤의 인스턴스를 예측할 수 있다.
그래서 인스턴스 만드는 타입의 아규먼트 생략가능하다.
다이아몬드처럼 생겨서 다이아몬드 표기법이라고 한다.
MyClass03<String> o2 = new MyClass03<>();
13.1.3 확인문제 2번
Container제네릭 타입을 사용하고 있다. main에서 사용하는 방법 참고하여 제네릭 타입 선언해보기
public class ContainerExample {
public static void main(String[] args) {
Container<String> container1 = new Container<String>();
container1.set("홍길동");
String str = container1.get();
Container<Integer> container2 = new Container<>();
container2.set(6);
int value = container2.get();
}
}public class Container<T> {
private T a;
public T get() {
return a;
}
public void set(T a) {
this.a = a;
}
}13.2 제네릭타입
,로 연결하면된다. 여러개 있어도 된다.
class MyClass04<T, U> {
public T item;
public U item2;
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
public U getItem2() {
return item2;
}
public void setItem2(U item2) {
this.item2 = item2;
}
}public class C04GenericType {
public static void main(String[] args) {
MyClass04<String, Integer> o1 = new MyClass04<>();
o1.setItem("hello");
o1.setItem2(99);
String s1 = o1.getItem();
int i1 = o1.getItem2();
MyClass04<String, String> o2 = new MyClass04<>();
o2.setItem("hello");
o2.setItem2("world");
String s2 = o2.getItem();
String s3 = o2.getItem2();
}
}13.2.1 확인문제 3번
Container제네릭 타입을 사용하고 있다. main에서 사용하는 방법 참고하여 제네릭 타입 선언해보기
public class ContainerExample {
public static void main(String[] args) {
Container<String, String> container1 = new Container<>();
container1.set("홍길동", "도적");
String name1 = container1.getKey();
String job = container1.getValue();
Container<String, Integer> container2 = new Container<>();
container2.set("홍길동", 35);
String name2 = container2.getKey();
int age = container2.getValue();
}
}public class Container<T, U> {
private T key;
private U value;
public void set (T key, U value) {
this.key = key;
this.value = value;
}
public T getKey() {
return key;
}
public U getValue() {
return value;
}
}13.3 제네릭 메소드
클래스 전체에서 제네릭을 하는게 아니라 딱 메소드 레벨에서만 제네릭을 할수도 있다.
언제 사용하지 하는데 내가 직접 만들일은 거의 없다.
API에서 어떤 메소드나 클래스를 보앗을때 제네릭으로 작성한 것들이 있다.
그런것들을 구분하고 사용할때 결정할때 정하면된다.
메소드 호출할때 앞에 붙여주면 된다.
그런데 파라미터의 타입때문에 메소드의타입을 유추할 수 있어서 그냥 생략해도 된다.
class MyClass05 {
// generic method
public <T> void method(T t) {
}
}public class C05GenericMethod {
public static void main(String[] args) {
MyClass05 o1 = new MyClass05();
o1.<String>method("java");
o1.<Integer>method(9);
o1.method("spring");
o1.method(99);
}
}13.3.2 리턴타입 제네릭
메소드 호출할때 앞에 붙여주면 된다.
그런데 파라미터가 없으면 리턴타입을 어떻게 아는가?
받을때 변수에 저장하는데 이 변수의 타입을 보면 유추할 수있어서 이것으로 생략할 수 있다.
class MyClass06 {
public <T> T method() {
return null;
}
}public class C06GenericMethod {
public static void main(String[] args) {
MyClass06 o1 = new MyClass06();
String s1 = o1.<String>method();
Integer i1 = o1.<Integer>method();
String s2 = o1.method();
Integer i2 = o1.method();
}
}2023.03.09 후기
진도가 확확나가며 어느새 제네릭에 오게 되었다.
제네릭같은 부분은 배웠지만 내가 직접 사용하는데는 무리가 있고 API같은 것을 보는데 도움이 많이 되고 있다.
약간 헷갈리는 부분을 같이 보면서 배우면 좋을 것 같다.