Study/System Engineer

KDT - java 기본 타입과 참조타입

jxx_yxjx 2024. 1. 5. 11:46

기본 타입(primitive type) vs 참조 타입(reference type)

  • 기본 타입이란 정수, 실수, 문자, 논리 값을 저장하는 데이터 타입입니다.
  • 참조 타입이란 객체의 주소를 참조하는 타입으로 배열, 클래스, 인터페이스 타입을 말합니다.
  • 기본 타입으로 선언된 변수는 실제 값(value)을 변수 안에 저장하지만, 참조 타입으로 선언된 변수는 메모리의 주소값을 변수 안에 저장합니다.
  • 참조 타입으로 선언된 변수는 스택(stack)영역에 주소값을 저장하고 내부의 실제 값은 힙(heap)영역에 저장합니다.

- 깊은 복사 vs 얕은 복사

  • 참조 타입 String과 객체 동등 비교 메서드 equals()
  • 자바는 문자열이 동일하다면 String 객체를 공유하도록 되어있습니다. 그래서 단순히 문자열을 String 변수에 할당한다면 같은 주소값을 갖게 됩니다.
  • => 주소값으로 비교

ex) String str1 = "Hello"; String str2 = "Hello"; --> str1 == str2 -> true

  • 그러나 new키워드를 사용해서 String객체를 직접 heap영역에 생성한다면 문자열의 내용이 같더라도 다른 주소값을 가지게 되므로 동등, 비동등 연산자(==, !=)의 결과가 false로 나오게 됩니다.

ex) String str3 = new String("Hello"); String str4 = new String("Hello"); --> str3 == str4 -> false

  • 그래서 동일 String객체이든 다른 String 객체이든 상관없이 문자열의 내용 값 그자체를 비교할 때는 equals() 메서드를 사용해야 합니다.

실습

ArrayEqualsTest.java

package equals.array;

import java.util.Arrays;

public class ArrayEqualsTest {

	public static void main(String[] args) {
		//배열은 참조형 변수이기 때문에 단순 대입 시 문제 발생
		int[] intArray1 = {1,2,3,4,5};
		System.out.println("배열 1의 주소 : " + intArray1);
		//배열도 참조형 변수이므로 주소가 아닌 실제 자료 조회하려면
		//Arrays.toString(배열자료) 형식으로 호출해야 합니다.
		System.out.println(Arrays.toString(intArray1));
		System.out.println("------------------------");
	}
}

배열의 주소 뿐만 아니라 값까지 알 수 있음
메모리 구조

이어서, .clone 사용 전 복사 실행 // 스택에서 일어나는 복사

		//새로 생성하지 않고, intArray1을 대입받는 intArray2
		//힙에 저장된 자료를 새로 똑같이 할당하는 복사 = 깊은 복사
		// 변수 뒤에 .clone()을 써서 수행함
		int[] intArray2 = intArray1;
		
		intArray1[0] = 100;
		System.out.println(Arrays.toString(intArray1));
		System.out.println(Arrays.toString(intArray2));
		System.out.println("배열 2의 주소 : " + intArray2);

결과 화면
메모리 구조

.clone(깊은 복사) 사용 후  // 힙에서 일어나는 복사

이전 복사와 다른 결과화면

배열의 주소와 값이 상이함

.clone 메모리 구조

---------------------------------------------------------------------------------------------------------------------------

user.java // 스캐너용

package equals.str;

public class User {

	String id;
	String pw;
	
	public User(String upw) {
		pw = upw;
	}
}

usermain.java

package equals.str;

public class UserMain {

	public static void main(String[] args) {
		// 같은 클래스 안에서 동일한 문자열을 직접 대입하는 형식으로
		// String 객체를 생서할 시 같은 주소값을 공유함
		
		String s1 = "룰루";
		String s2 = "룰루";
		System.out.println(s1 == s2);

	}

}

주소가 같기 때문에 true

- update

package equals.str;

public class UserMain {

	public static void main(String[] args) {
		// 같은 클래스 안에서 동일한 문자열을 직접 대입하는 형식으로
		// String 객체를 생서할 시 같은 주소값을 공유함
		
		String s1 = "룰루";
		String s2 = "룰루";
		System.out.println(s1 == s2);
		String s3 = new String("룰루");
		//s1,s2,s3 모두가 룰루라는 문자열을 갖고 있음
		System.out.println(s1 + s2 + s3);
		System.out.println(s1 == s3);

	}

}

결과 화면

 s1 룰루와 s3 룰루의 같은 문자임에도 다른 주소임으로  false 처리됨 -> 주소비교

이어서,

		//문자열도 참조형 변수이므로 단순비교는 주소값 비교만 함
		// 따라서 주소가 아닌 자료의 동등성을 따질 때는 .equals()를 사용함
		System.out.println(s1.equals(s3));

해당 상황에서는 주소값이 아닌 자료의 동등성을 따졌으므로 true처리 되는 것을 알 수 있음

-----------------------------------------------------------------------------------------------------------------------------

접근 제한자와 상속

객체 지향 프로그래밍 기술

  • OOP 기술에는 은닉(캡슐화:Encapsulation), 상속(Inheritance), 다형성(Polymorphism)이 있습니다.

상속(Inheritance)

  • OOP에서 상속은 기존의 클래스를 확장하여 새로운 클래스를 이끌어내는 것을 의미합니다.
  • 상속 관계는 is a 관계를 만족하는 관계입니다. ex) 돌고래 is a 포유류 --> 돌고래는 포유류의 속성을 가지고 있다.
  • 상속은 기존의 코드를 재사용함으로써 불필요한 코드를 재작성하는 번거로움을 없앨 수 있고, 새로운 클래스를 만드는 시간과 노력을 줄일 수 있습니다.
  • 자바에서는 C++에서 사용했던 다중상속의 문제점때문에 단일상속만을 지원합니다.
  • 어떤 클래스가 다른 클래스로부터 상속을 받아 만들어지면 새롭게 만들어진 클래스를 자식(child or sub)클래스라고 부르며, 멤버변수와 메서드를 물려준 클래스는 부모(parent or super)클래스라고 부릅니다.
  • 상속을 하면 부모클래스의 멤버변수와 메서드가 자식클래스에 상속이 됩니다. 그러나 부모클래스의 생성자는 상속이 되지 않습니다.
  • 상속을 사용하는 키워드는 extends 입니다.
public class 자식클래스 extends 부모클래스 {
	//정의사항 기술
} 
  • 상속을 하더라도 부모 클래스에서 private 접근제한을 갖는 멤버변수와 메서드는 상속대상에서 제외됩니다.
  • 자바의 모든 클래스는 Object 클래스를 상속받고 있습니다. Object클래스는 자바의 최상위 클래스입니다.

상속 실습

구조

package inheritance;

public class Human {

	public String name;
	public int age;
}
package inheritance;

public class Student extends Human {
	//human의 특성인 name, age는 적지 않아도 자동으로 설정됩니다.
	
	public String major;
}
package inheritance;

public class Salaryman extends Human{
	
	public int salary;

}
package inheritance;

public class InheritanceExample {

	public static void main(String[] args) {
		
		Student st1 = new Student();
		
		st1.name = "김학생";
		st1.age = 20;
		st1.major = "공학";
		
		Salaryman sm1 = new Salaryman();
		sm1.name = "나직장";
		sm1.age = 40;
		sm1.salary = 8000;

	}

}

메모리 구조

 

메서드 재정의(Overriding)

  • 메서드 재정의란 부모클래스로부터 상속받은 메서드를 자식클래스에서 행위를 바꾸거나 보완하기 위해 다시 정의해서 사용하는 것을 말합니다.
  • 이는 부모클래스에서 특별한 용도로 사용하던 메서드를 자식클래스에서 다른 용도로 사용할 때 필요합니다.
  • 부모클래스의 모든 메서드가 자식클래스에 맞게 설계되어 있다면 가장 이상적인 상속이지만, 특정 메서드는 자식 클래스가 사용하기에 적합하지 않을 수도 있습니다.
  • 이 경우 상속된 일부 메서드는 자식클래스에서 재정의하여 사용해야 합니다.
  • 메서드가 자식클래스에서 재정의되었다면 자식객체를 통해 메서드를 호출했을 때 새롭게 재정의된 메서드가 호출됩니다.

 

  • 메서드 재정의 규칙
  1. 반드시 상속을 전제로 해야합니다.
  2. 반드시 반환 유형이 같아야 합니다.
  3. 메서드 이름이 같아야 합니다.
  4. 매개 변수 선언이 정확히 일치해야 합니다.
  5. 접근제한자는 같거나 더 제한이 없어야 합니다.(more public)

 

+ 오버로딩 vs 오버라이딩

오버로딩 :

과적재 라는 영단어 뜻에 걸맞게 같은 이름의 함수를 여럿 정의하는 것

다만, 요구 파라미터의 자료형이나, 개수는 달라야 함.

오버라이딩 :

상속시 부모가 물려준 함수가 마음에 들지 않는다면

같은 이름과 같은 파라미터, 그리고 같은 리턴자료형을 재정의하는 것