。゚(*´□`)゚。

코딩의 즐거움과 도전, 그리고 일상의 소소한 순간들이 어우러진 블로그

[네이버클라우드] 클라우드 기반의 개발자 과정 7기/웹프로그래밍

[NC7기-42일차(6월23일)] - 웹프로그래밍 23일차 -2

quarrrter 2023. 6. 23. 17:20

Object 클래스 - getClass() : 해당 클래스의 정보를 리턴한다. 

basic.ex01 160-162

// 레퍼런스를 통해서 인스턴스의 클래스 정보를 알아낼 수 있다.

Class classInfo = obj1.getClass();

클래스 정보가 담겨있는 인스턴스 주소를 classinfo가 받는다. 

System.out.println(classInfo.getName()); // 패키지명 + 바깥 클래스명 + 클래스명  //fully qualified name
System.out.println(classInfo.getSimpleName()); // 클래스명


170-174

Object 의 clone()

new Score()   => 인스턴스를 만듬 

복제방법 1. 직접 복제한다. 즉 새 객체를 만들어 기존 객체의 값을 저장한다.

복제방법 2. clone

protected 서브 클래스가 object의 clone 에 접근 가능 

clone은 object의 protect 메서드임. 

그래서 object의 서브 클래스가 clone 사용 가능 

s1은 score꺼여서 score은 자기 object의 clone을 상속 받은거임. 그래서 exam0170은 s1.clone을 할수없음. score 클래스가 받은 clone이어서 exam0170이 object에서 받는 clone이랑 다른거임 !! 안 됨 !!
unhandled : 상황에 따라 예외를 던지니까 예외 코드를 작성해줘야함!

예외 던지는 건 try 안에 넣고 예외를 ㄹ받은 파라미터를 넣음 

인스턴스를 복제할 수 있게 하려면,
// => Object에서 제공하는 clone()을 호출할 수 있어야 한다.
// => 그런데 clone()의 접근 범위가 protected 이라서
// 같은 패키지의 멤버이거나 서브 클래스가 아니면 호출할 수 없다.
// 해결책?
// => Object에서 상속 받은 clone()을 오버라이딩 한다.
// => 다른 패키지의 멤버가 호출하려면 public 으로 접근 제어의 범위를 넓혀야 한다.
// => 어떻게? 다음과 같이 오버라이딩 하라! 
@Override
public String toString() {
return "Score [name=" + name + ", kor=" + kor + ", eng=" + eng + ", math=" + math + ", sum="
+ sum + ", aver=" + aver + "]";
}


// => Object에서 상속 받은 clone()을 오버라이딩하여 다른 패키지의 멤버도 사용할 수 있게
// public 으로 접근 범위를 넓혀라!
// => 오버라이딩은 접근 범위를 좁힐 수는 없지만, 넓힐 수는 있다.
// => 오버라이딩 할 때 리턴 타입을 해당 클래스의 타입으로 변경해도 된다.
@Override
public Score clone() throws CloneNotSupportedException {
// 복제를 위한 코드를 따로 작성할 필요가 없다.
// JVM이 알아서 해준다.
// 그냥 상속 받은 메서드를 오버라이딩하고, 접근 권한을 public 으로 확대한다.
// 원래의 clone() 메서드를 실행한 다음에
// 리턴 타입을 해당 클래스로 형변환 한다.
return (Score) super.clone();
}
}

☞clone 하면 =>JVM이 인스턴스 메모리 그대로 heap 복제 가능 

s1에 200번지 들어가 있으면 s2에도 200번지 들어감,, !! 

 

// 인스턴스 복제 기능을 활성화하려면 Cloneable 인터페이스를 구현해야 한다.
일반 상태에서는 비활성화에 protected으로 막혀있음 
사용법을 정의한게 아니고 단순 활성화 비활성화를 표시하는 용도여서 그 안에 메서드 정의가 따로 없음. 
// => 이 인터페이스에는 메서드가 선언되어 있지 않다.
// => 따라서 클래스는 따로 메서드를 구현할 필요가 없다.
// => Cloneable을 구현하는 이유는
// JVM에게 이 클래스의 인스턴스를 복제할 수 있음을 표시하기 위함이다.
// 이 표시가 안된 클래스는 JVM이 인스턴스를 복제해 주지 않는다. 즉 clone()을 호출할 수 없다.
static class Score implements Cloneable {

 

 

clone() : shallow copy(얕은 복제)  vs deep copy(깊은 복제)

상황에 따라 맞는 copy 방식 따르기 

Engine은 같은 걸 쓰고 있음
engine도 clone 코드를 추가해야함. 개발자가 직접 포함하는 객체를 복제하는 코드를 작성해야 한다.


String 

// String 레퍼런스
// - String은 자바 기본 타입이 아니다.
// - 클래스이다.
String s1; // s1은 String 인스턴스 주소를 담는 레퍼런스이다.


// String 인스턴스
// - 힙에 Hello 문자 코드를 저장할 메모리를 만들고 그 주소를 리턴한다.
// - 내용물의 동일 여부를 검사하지 않고 무조건 인스턴스를 생성한다.
// - 가비지가 되면 가비지 컬렉터에 의해 제거된다.
s1 = new String("Hello");
String s2 = new String("Hello");


// 인스턴스가 같은지를 비교해보면,
System.out.println(s1 == s2); // false => 서로 다른 인스턴스이다.
그래서 .equals 를 써야함 !!! 

문자열 리터럴

문자열 리터럴 : 상수풀에 이미 같은 문자열의 인스턴스가 있다면, 그 주소를 리턴한다. // - 왜? 메모리 절약을 위해 중복 데이터를 갖는 인스턴스를 생성하지 않는다. // - JVM이 끝날 때까지 메모리에 유지된다.

String s1 = new String("Hello"); // Heap 영역에 String 인스턴스를 만든다.
String s2 = "Hello"; // String Pool 영역에 String 인스턴스를 만든다.

 intern()

String s1 = new String("Hello"); // Heap 영역에 String 인스턴스를 생성한다.


intern()
- String 객체에 들어 있는 문자열과 동일한 문자열을 갖고 있는 String 객체를 상수풀에서 찾는다.
- 있으면 그 String 객체의 주소를 리턴한다.
- 없으면 상수풀에 String 객체를 생성한 주소를 리턴한다.
String s2 = s1.intern();  : s1의 문자열을 가지고 string 상수풀에 새 인스턴스를 생성한다. 단, 기존에 이미 같은 문자열의 인스턴                                          스가 있다면 기존객체 주소를 리턴한다. 


String s3 = "Hello"; // 해당 문자열을 가진 String 객체를 String Pool에서 찾는다.
// 있으면 그 객체를 리턴한다. 없으면 새 객체를 만들고 리턴한다.

System.out.println(s1 == s2);
System.out.println(s2 == s3);


 

.equals

// equals()?

// - Object에 정의되어 있는 메서드이다.

// - 인스턴스가 같은지 비교한다.

 

// String의 equals()?

// - Object에서 상속 받은 것을 오버라이딩하였다.

// - 문자열이 같은지 비교한다.

 

// equals()는 대소문자를 구분한다.

System.out.println(s1.equals(s2));

 

// 대소문자 구분없이 문자열을 비교하고 싶다면,

System.out.println(s1.equalsIgnoreCase(s2));

 

 

StringBuffer는 Object에서 상속 받은 equals()를 오버라이딩 하지 않았다.

// StringBuffer 에 들어 있는 문자열을 비교하려면?

// - StringBuffer에서 String을 꺼내 비교하라!

//

// String s1 = b1.toString();

// String s2 = b2.toString();

// System.out.println(s1.equals(s2)); 

System.out.println(b1.toString().equals(b2.toString()));

 

string의 equals 

인스턴스가 달라도 ,,, 문자열이랑 해시값이랑 같으면  같다고 나옴

String의 hashCode()은

// 문자열이 같으면 같은 hashCode()를 리턴하도록 오버라이딩 하였다.

// 이유?

// - 문자열이 같은 경우 같은 객체로 다루기 위함이다.

// - HashSet 에서 객체를 저장할 때 이 메서드의 리턴 값으로 저장 위치를 계산한다.

// - HashMap이나 Hashtable에서는 Key를 다룰 때 이 메서드의 리턴 값을 사용한다.

// - 보통 hashCode()와 equals()를 함께 오버라이딩 한다.

 

// String 클래스는 toString()도 오버라이딩 했다.

Object obj = new String("Hello");

// obj가 String 객체를 가리키더라도
// obj의 타입이 Object이기 때문에 Object에 선언한 멤버만 사용할 수 있다.
// obj가 가리키는 원래 클래스의 메서드를 호출하고 싶다면
// 다음과 같이 원래 타입으로 형변환하라.
String str = ((String) obj).toLowerCase();   // 기존의 것을 바꾸라는게 아니라 새로 만들어서 새 주소를 str에 담기 ! 

immutable 객체(불변)

어떤 객체를 호출하더라도 안 바뀜,,! 

string 객체는 한번 값이 설정되면 절대 안 바뀜 ,, ! 그래서 toLowerCase()는 기존 것이 바뀌는게 아니구 새로 만드는것

 

// String 클래스의 메서드는 원본 인스턴스의 데이터를 변경하지 않는다.
// 다만 새로 String 객체를 만들 뿐이다.
String s2 = s1.replace('l', 'x');
System.out.println(s1 == s2);
System.out.printf("%s : %s\n", s1, s2); // 원본은 바뀌지 않는다.


String s3 = s1.concat(", world!");
System.out.printf("%s : %s\n", s1, s3); // 원본은 바뀌지 않는다.

-----------출력값 
false
Hello : Hexxo
Hello : Hello, world!
원본을 바꾸고 싶으면 ,,!!  -mutable 객체 StringBuffer 
StringBuffer buf = new StringBuffer("Hello");
System.out.println(buf);

buf.replace(2, 4, "xxxx");// 원본을 바꾼다.
System.out.println(buf);
---------출력값
Hello
Hexxxxo


// Wrapper 클래스 - 종류

// 이렇게 primitive data type에 대응하여 만든 클래스를
primitive data를 포장하는 객체라고 해서
"랩퍼(wrapper) 클래스"라 부른다.

// 래퍼 클래스의 주요 용도:
=> primitive data type의 값을 객체로 주고 받을 때 사용한다.
 => primitive data type의 값을 객체에 담아 전달하고 싶다면
언제든 wrapper 클래스의 인스턴스를 만들면 된다.

Java 9 부터 wrapper 클래스의 생성자가 deprecated 상태이다.
가능한 생성자를 사용하여 인스턴스를 생성하지 말라!
deprecated(비난받는, 유지보수가 중단되어, 사용이 권장되지 않는)?
 - 사용하지 않는 것이 좋다고 결정되었고, 가까운 장래에 제거될 것이라는 의미.

// Wrapper 클래스의 인스턴스를 생성할 때는 생성자 대신 클래스 메서드를 사용하라.
Byte b2 = Byte.valueOf((byte)100);
Short s2 = Short.valueOf((short)20000);
Integer i2 = Integer.valueOf(3000000);
Long l2 = Long.valueOf(60000000000L);
Float f2 = Float.valueOf(3.14f);
Double d2 = Double.valueOf(3.14159);
Boolean bool2 = Boolean.valueOf(true);
Character c2 = Character.valueOf((char)0x41);
}
// wapper 클래스는 primitive type의 값을 객체로 다룰 수 있게 해준다.
// primitive type에 상관없이 Object 타입의 파라미터로 값을 받을 수 있다.
static void m(Object value) { // 모든 객체를 받을 수 있다.
System.out.printf("wrapper value=%s\n", value);
}
 

Wrapper 클래스 - 오토박싱(auto-boxing)/오토언박싱(auto-unboxing)

// 오토박싱
// - Java 1.5부터 지원하는 문법이다.
// - 코드의 문맥에 따라 박싱(boxing)과 언박싱(unboxing)을 자동으로 수행한다.
//
// 즉 primitive data type 값을 Wrapper 클래스의 인스턴스에 바로 할당할 수 있다.
//
Integer obj = 100; // ==> Integer.valueOf(100) 컴파일러가 이렇게 바꿈 integer 객체의 주소가 obj에 할당됨


// obj는 레퍼런스인데 어떻게 가능한가?
// => 내부적으로 Integer.valueOf(100) 호출 코드로 바뀐다.
// => 즉 int 값이 obj에 바로 저장되는 것이 아니라,
// 내부적으로 Integer 객체가 생성되어 그 주소가 저장된다.
// => 이렇게 int 값을 자동으로 Integer 객체로 만드는 것을
// "오토박싱(auto-boxing)"이라 한다.
// 오토 언박싱
// - Java 1.5부터 지원하는 문법이다.
// - 코드의 문맥에 따라 박싱(boxing)과 언박싱(unboxing)을 자동으로 수행한다.
//
// 즉 Wrapper 객체의 값을 primitive data type 변수에 직접 할당할 수 있다.
//
Integer obj = Integer.valueOf(300);
int i = obj; // ==> obj.intValue() => 값을 꺼내 i에 저


// obj에 저장된 것은 int 값이 아니라 Integer 객체의 주소인데 어떻게 가능한가?
// => 내부적으로 obj.intValue() 호출 코드로 바뀐다.
// => 즉 obj에 들어있는 인스턴스 주소가 i에 저장되는 것이 아니라,
// obj 인스턴스에 들어 있는 값을 꺼내 i에 저장하는 것이다.
// => 이렇게 Wrapper 객체 안에 들어 있는 값을 자동으로 꺼낸다고 해서
// "오토언박싱"이라 부른다.
public class Exam0223 {
public static void main(String[] args) {


int i1 = 100;
Integer obj = Integer.valueOf(200);


printInt(obj);
// 컴파일러가 printInt(obj.intValue())로 바꾼다.
// 즉 "오토 언박싱"을 수행한다.


printObject(i1);
// 컴파일러가 printObject(Integer.valueOf(100)) 으로 바꾼다.
// 즉 "오토 박싱"을 수행한다.
}

static void printInt(int value) {
System.out.println(value);
}


static void printObject(Integer obj) {
System.out.println(obj.toString());
}
}
 
==========출력값
200
100
// 설명:
// => 정수 값이 -128 ~ 127 범위일 경우
// 자주 사용되는 수이기 때문에
// String 리터럴처럼 상수 풀에 Integer 객체를 생성한다. 범위 넘어가면 heap에 생성됨 
상수풀에 만들어지면 주소 같다고 나오는데 heap 만들어지는건 매번 새로 만들기 때문에 다르다고 함. 

 
// 결론!
// - wrapper 객체의 값을 비교할 때 == 연산자를 사용하지 말라!
// - -128 ~ 127 범위 내의 값이라면 == 연산자를 사용하여 비교할 수도 있지만,
// 매번 비교할 때 마다 범위의 유효성을 생각하는 것이 번거롭다.
// - 그냥 equals() 메서드를 사용하여 값을 비교하라!
// - 더 좋은 방법은 auto-unboxing 하여 primitive type 의 값으로 바꾼후에 == 연산자로 비교하라.