[JAVA] Call-By-Reference vs Call-By-Value / 인스턴스멤버 vs 정적멤버 / 자바 접근제한자
※ 메소드 오버로딩(Overloading)
- 클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것을 말한다.
- 하나의 메소드 이름으로 다양한 매개값을 받기 위해 메소드를 오버로딩한다.
- 오버로딩의 조건 : 매개변수 타입, 개수, 순서가 달라야 한다.
※ Call By Value
: 값을 복사해서 사용하기 때문에 한 쪽에서 값을 변경하더라도 영향을 받지 않는다.
Changer changer = new Changer();
// Call by value (값을 복사해서 호출)
int v1 = 1;
changer.change(v1);
// void change(int value) { value = 10; }
// int value = 1 -> v1이 가지고 있는 "값"을 value 변수에 저장 한다.
System.out.println(v1);
※ Call By Reference
: 객체의 번지를 복사하기 때문에 값을 변경하면 영향을 받는다.
(예외). String 객체는 불변이다. 한번 String 객체가 만들어지면 그 안에 값은 바꿀 수 없다.
// Call by Reference (번지를 복사해서 호출)
String v3 = "A";
changer.change(v3);
System.out.println(v3); // "A" 출력
/*
void change(String value) { // 참조타입
value = "B";
}
*/
// Call by Reference (번지를 복사해서 호출)
int[] arr1 = {0, 0, 0};
changer.change(arr1);
System.out.println(arr1[0]); // 2 출력
String[] arr3 = {"A", "B", "C"};
changer.change(arr3);
System.out.println(arr3[0]); // "B" 출력
/*
void change(String[] arr) { // 참조타입
arr[0] = "B";
}
void change(String[] arr) { // 참조타입
arr[0] = "B";
}
*/
※ 인스턴스 멤버와 this
▶ 인스턴스 멤버란 객체(인스턴스)마다 가지고 있는 필드와 메소드를 말한다.
- 인스턴스 멤버(인스턴스 필드, 인스턴스 메소드)는 객체에 소속된 멤버이기 때문에 객체가 없이는 사용할 수 없다.
▶ 주의: 생성자는 인스턴스 멤버가 될 수 없다. 왜? 생성자는 객체가 생성될 때 쓰이고, 그 이후에는 사용되지 않는다.
public class Car {
// Field
int gas;
// Method
void setSpeed(int speed) {
}
}
Car myCar = new Car();
myCar.gas = 10;
myCar.setSpeed(60);
Car yourCar = new Car();
yourCar.gas = 20;
yourCar.setSpeed(80);
- 인스턴스 필드는 객체마다 각각 그 내부에 존재한다.
하지만 메소드는 코드 덩어리로 객체마다 존재하면 비효율적이기 때문에
메소드 영역에 따로 만들어 놓고 공유해서 사용한다.
- 메소드는 엄연히 객체가 가진 멤버이기는 하다.
▶this란 객체 자신의 참조를 가지고 있는 키워드이다.
※ 메모리 사용 영역
: JVM은 OS에서 할당받은 메모리 영역(Runtime Data Area)를 세 영역으로 구분한다.
1. 메소드 영역
- JVM을 시작할 때 생성
- 로딩된 클래스 바이트 코드 내용을 분석 후 저장
- 모든 스레드가 공유
2. 힙 영역
- JVM을 시작할 때 생성
- 객체/배열 저장
- 사용되지 않는 객체는 GC가 자동 제거
3. JVM 스택
- 스레드별로 생성
- 메소드를 호출할 때마다 프레임을 스택에 추가(push)
- 메소드가 종료하면 프레임을 제거(pop)
※ 정적 필드 (static field) : 값이 바뀌지 않고 정적인 것.
정적 필드는 메소드 영역(여러 객체가 공유하는 영역)에 만들어지고, 인스턴스 필드는 힙 영역의 객체안에 만들어진다.
※ 인스턴스 멤버와 정적 멤버 선언의 기준
객체안의 데이터를 이용해야하는 메소드가 있고, 반면에 객체없이도 실행가능한 메소드가 있다.
=> 이 둘을 구분하는 것이 중요하다.
주의) static 메소드에다가 객체의 데이터 (즉, 인스턴스필드)를 사용할 수 없다.
또한, static 메소드에 this 키워드를 사용할 수 없다.
- 정적 멤버를 클래스에 고정된 필드와 메소드이다.
- 정적 멤버는 객체를 생성하지 않고, 클래스로 바로 접근해서 사용한다.
- 정적 필드를 선언할 때 보통 초기 값을 준다. (고정값을 가지기 때문에)
- 인스턴스 필드는 객체마다 값이 다르기 때문에 초기값을 주지 않는다.
- 정적 멤버는 메소드 영역에 저장된다.
[실습]
package pr06.exam11;
public class User { // 클래스는 객체를 만들기 위한 설계도이다.
// Field -> 인스턴스 필드를 선언한다.
String id;
String name;
String password;
static String nation = "한국";
// static 키워드를 붙였다는 것은 객체를 만들지 않아도 사용할 수 있다는 의미이다.
// 즉, nation은 힙 영역의 객체안이 아니라 메소드 영역에 저장한다.
// Constructor -> 생성자는 인스턴스의 멤버가 될 수 없다.
User(String name) {
this.name = name;
}
// Method -> 인스턴스 메소드안에서는 인스턴스 필드를 얼마든지 쓸 수 있다.
void login() {
// 인스턴스 필드를 사용하고 있다.
System.out.println(name + "님이 로그인합니다. ");
}
void logout() {
System.out.println(name + "님이 로그아웃 합니다. ");
}
static void info() {
// 이 메소드는 객체가 꼭 있어야 하나? NO
// 객체가 없어도 사용가능한 메소드도 있다.
System.out.println("모든 user는 한국인입니다.");
}
}
package pr06.exam11;
public class Example {
// 메인 메소드는 객체와 무관하게 실행되기 때문에 static이 붙은 것이다.
public static void main(String[] args) {
// 인스턴스 멤버(필드, 메소)는 반드시 객체 참조 변수를 통해서 접근
User user1 = new User("이채정");
System.out.println(user1.name);
user1.login();
user1.logout();
// 정적 멤버 (필드, 메소드)는 객체가 없이도 사용 가능하다.
// how? 클래스로 접근하면 된다.
System.out.println(User.nation); // User는 클래스이다.
User.info(); //참조변수가 아니라 클래스로 접근 한다.
}
}
※ 싱글톤 (Singleton) 패턴
- Factory() 메소드중에서 똑같은 객체가 리턴되도록 하는 것이 싱글톤 디자인 패턴이다.
package pr06.exam13;
public class Example {
public static void main(String[] args) {
// 생성자를 이용해서 객체 얻기
// Singleton result1 = new Singleton();
// 생성자가 private으로 제한되면 밖에서 new로 객체 생성할 수 없다.
// Factory Method
Singleton result1 = Singleton.getInstance();
Singleton result2 = Singleton.getInstance();
if(result1 == result2) {
System.out.println("같은 객체를 참조");
}else {
System.out.println("다른 객체를 참조");
}
}
}
/*
public class Singleton { // 클래스를 디자인할 때 디자인 패턴
// Field
private static Singleton sinleton = new Singleton();
// static -> 객체와 무관하게 생성
// Constructor
private Singleton() {} // 외부에서 new 로 객체 생성 못한다.
// Method
static Singleton getInstance() {
return sinleton;
}
}
*/
※ final 필드와 상수(static final)
- final 필드
: 값을 변경할 수 없는 필드
public class Korean {
// Field
static final String NATION = "한국";
final String ssn;
/*
* static은 객체마다 부여되는 것이 아니라는 의미이고,
* final은 값이 변하지 않는다는 의미이다.
*
* static final은 대문자로 선언하는게 관례이다.
*
* final만 붙어있다면 객체마다 갖는 값이고, 한 번만 값을 줘야하는데
* 처음 생성자를 호출할 때 값을 준다.
*
*/
// Constructor
Korean(String ssn) {
this.ssn = ssn;
}
// Method
}
/*
public class Example {
public static void main(String[] args) {
System.out.println(Korean.NATION);
Korean k1 = new Korean("123456-1234567");
// k1.ssn = "111231-1341414"; 불가능한 코드
}
}
*/
※ 패키지
패키지는 클래스의 일부이다.
전체 클래스 이름 = 상위패키지.하위패키지.클래스
- 주의) 패키지에 속한 클래스를 컴파일할 때는 javac의 옵션으로 -d를 주어야 한다.
- 패키지이름은 소문자로 하는 것이 관례이다.
- 자바 도큐먼트에 있는 패키지이름과 똑같이 만들면 안된다.
프로젝트의 산출물은 .jar 이고, 이 안에는 .class 파일들이 들어있다.
최종 산출물을 만들겠다. == Export
※ import 문
- 패키지가 다른 클래스를 사용해야 할 경우에 사용.
- 패키지명을 전체 다 써주어도 되고 or 패키지를 import 한 뒤에 클래스명만 써도 된다.
※ 접근 제한자
접근 제한 | 적용 대상 | 접근할 수 없는 클래스 |
public | 클래스, 필드, 생성자, 메서드 | 없음 |
protected | 필드, 생성자, 메서드 | 자식 클래스가 아닌 다른 패키지에 소속된 클래스 (상속과 관련 있음 ) |
default | 클래스, 필드, 생성자, 메서드 | 다른 패키지에 소속된 클래스 (즉, 같은 패키지에서만 쓸 수 있다.) |
private | 필드, 생성자, 메서드 | 모든 외부 클래스는 접근할 수 없다. (즉, 같은 클래스에서만 쓸 수 있다.) |
주의) 클래스 앞에는 private을 붙일 수 없다.
무조건 클래스 앞에는 public 아니면 디폴트만 가능하다.
※ getter & setter
package pr06.exam17;
public class User {
// Field
private String id;
private String name;
private String password;
private int age;
private boolean adult;
// Method
public void setAdult(boolean adult) {
this.adult = adult;
}
public Boolean isAdult() { // boolean getter는 is로 시작한다.
return adult;
}
// Setter
public void setAge(int age) {
if(age < 0) {
this.age = 0;
}else {
this.age = age;
}
}
// getter
public int getAge() {
return age;
}
public void setPassword(String password) { // 쓰기전용필드
this.password = password;
}
}