-
[java] this, static, 싱글톤, final, 상수java 2023. 1. 19. 18:35
인스턴스 멤버 vs 정적 멤버
인스턴스멤버는 객체마다 가지고 있는 멤버를 말하고, 즉 객체를 생성한 후 사용할 수 있는 필드나 메소드(대부분의 필드나 메소드)
정적 멤버는 클래스메모리에 위치시키고 객체들이 공유하는 멤버를 말한다, 즉 객체를 생성하지 않고 클래스로 바로 호출이 가능한 필드와 메소드 (static 필드나 static메소드)
<판단 기준>
만약 객체마다 가지고 있어야할 데이터라면 인스턴스 필드를 선언하고
객체마다 가지고 있을 필요가 없는 공용데이터라면 정적필드로 선언하는 것이 좋다.
인스턴스 필드를 포함하고 있다면 인스턴스 메소드로 선언하고
인스턴스 필드를 포함하고 있지 않다면 정적메소드로 선언한다.
class HouseLee{ String lastname = "lee"; // <-인스턴스 정적-> static String lastname = "lee"; } public class Sample{ public static void main(String[] args) { HouseLee lee1 = new HouseLee(); HouseLee lee2 = new HouseLee(); } }
인스턴스 멤버와 this
인스턴스 멤버란 객체를 생성한 후(new 키워드이용) 사용할 수 있는 필드와 메소드를 말하는데.
위 예제에서 String last name = "lee"는 인스턴스필드이다. 인스턴스멤버는 객체없이는 사용할 수가 없다.
위 예제처럼.. 객체를 생성하면, 객체마다 객체변수 lastname을 저장하기 위한 메모리가 별도로 할당된다.
this
객체 외부에서 인스턴스 멤버에 접근하기 위해 참조변수를 만들어 사용하는 것과 마찬가지로
참조변수? Car mycar = new Car()에서 참조변수는 mycar이다.
객체 내부에서 인스턴스 멤버에 접근하기 위해서는 this를 사용해야한다.
객체는 자신을 this라고 한다. this.count는 해당 객체가 가지고있는 count 객체변수(필드)이다. (아래 예제참고)
c1으로 호출할때 this는 c1.cout , c2로 호출할때 this는 c2.count
class Counter { int count = 0; //field(객체변수) : 독립적인 값을 갖는다. Counter() { this.count++; System.out.println(this.count); } } public class Sample{ public static void main(String[] args) { Counter c1 = new Counter(); //this.count = c1.count++ = 1 Counter c2 = new Counter(); //this.count = c2.count++ = 1 } }
c1과 c2는 서로 다른 메모리를 가르키고 있기때문에 1, 1이 출력된다. 객체변수(인스턴스 필드)는 항상 독립적인 값을 갖기에 당연한결과이다.
정적 멤버와 static
만약 가장 위 예제처럼 HouseLee클래스의 lastname으로 어떤 객체든지 동일한 값인 "lee"를 넘기고자 할 경우,, static을 사용한다.
정적멤버는 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 말한다.
static이점 : 메모리 효율
static키워드를 붙히면 메모리 할당을 한번만 하게되어 메모리 사용에 이점이 있다. 이렇게 항상 값이 변하지 않는 경우라면 static.
static이점 : 공유 ( 많이 사용)
static으로 설정하면 같은 곳의 메모리 주소만을 바라보기 때문에 static변수의 값을 공유한다.
class Counter { static int count = 0; Counter() { count++; System.out.println(count); } } public class Sample{ public static void main(String[] args) { Counter c1 = new Counter(); //1출력 static으로 공유하므로 c1.count = 0 호출후-> 1 Counter c2 = new Counter(); //2출력 c2.count = 1 ->호출 후 2 } }
static을 붙히면 count를 공유하게 되어 다음과 같이 count가 증가되어 출력된다. 이제 객체변수가아니므로 this는 뺐다.
static 메소드
메소드 앞에 static키워드를 붙이면 Counter.getCount()와 같이 객체 생성없이 클래스를통해 메소드를 직접 호출할 수있다.(정적멤버는 클래스에 위치하기 떄문에)
class Counter { static int count = 0; Counter() { count++; System.out.println(count); } public static int getCount(){ return count; } } public class Sample{ public static void main(String[] args) { Counter c1 = new Counter(); Counter c2 = new Counter(); System.out.println(Counter.getCount()); //정적 멤버, 정적메소드 이기때문에 객체생성없이 바로 사용하는 모습 } }
스태틱 메소드안에서는, 스태틱 변수 즉 정적멤버끼리만 접근이 가능하고, 인스턴스 필드나 인스턴스메소드같은 인스턴스멤버에는 접근이 불가능하다. 또한 객체 자신에 대한 참조인 this도 사용이 불가능하다.
보통 static메소드는 유틸리티성 메소드를 작성할때 많이 사용된다
ex) 오늘 날짜 구하기, 숫자에 콤마 추가하기 등
import java.text.SimpleDateFormat; import java.util.Date; class Util{ public static String getCurrentDate(String fmt){ SimpleDateFormat sdf = new SimpleDateFormat(fmt); return sdf.format(new Date()); } } public class Sample{ public static void main(String[] args) { System.out.println(Util.getCurrentDate("yyyyMMdd")); } }
싱글톤
싱글톤은 단 하나의 객체만을 생성하게 강제하는 패턴이다. 즉 클래스를 통해 생성할수 있는 객체를 단 한개만 되도록 만드는것.
1. 생성자에 private선언
class Singletone{ private Singletone() { //생성자에 private을 설정하면 다른 클래스에서 new를 이용하여 객체를 생성할 수가없다. } } public class Sample{ public static void main(String[] args) { Singletone singletone = new Singletone(); //error발생!! } }
new를 이용해 무수히 많은 객체를 생성한다면 싱글톤에 의미가 없다, 그래서 new로 객체생성을 못하도록 막은것이다.
2. 생성자를 호출할 수있는 메소드를 싱글톤 내부에서 선언
class Singletone{ private Singletone() { //해당클래스내부에서는 호출이 가능하다! } public static Singletone getInstance(){//객체생성없이 사용하기위해 static으로 선언 return new Singletone(); //생성자 호출!! } } public class Sample{ public static void main(String[] args) { Singletone singletone = Singletone.getInstance(); } }
이렇게 하면 getInstance라는 static메소드를 사용하여 객체를 생성할수있다, 하지만 getInstance를 호출할때마다 새로운 객체가 생성될 것이다 그렇다면 싱글톤이아니다.
3. 정적필드 이용해서 싱글톤 구현
class Singletone{ private static Singletone one; //정적필드선언! - 공유하는 필드 private Singletone() { //해당클래스내부에서는 호출이 가능하다! } public static Singletone getInstance(){//객체생성없이 사용하기위해 static으로 선언 if( one == null){ //최초 getInstance()호출이되면 정적필드가 null이므로 one = new Singletone();//객체생성 } return one; //그 이후부터는 null이 아닌값이므로 바로 정적 필드 리턴 } } public class Sample{ public static void main(String[] args) { Singletone singletone1 = Singletone.getInstance(); //one = new Singleton() Singletone singletone2 = Singletone.getInstance(); //one = new Singleton() System.out.println(singletone2 == singletone1); //true출력 } }
최초 getInstance가 호출되면 one이 null이므로 new에 의해 객체가 생성된다, 이렇게 한번 생성이 되면 one은 static변수이기 때문에 그 이후로는 null이 아니게 되어 새로운 객체를 생성하는 경우가 없어진다. getInstance를 다시한번 호출하면 이미 만들어진 싱글톤 객체인 one을 항상 리턴하게 된다.
final
final필드는 초기값이 저장되면, 이것이 최종적이 값이 되어 프로그램 실행 도중에 수정할 수 없다.
static과 고정값을 다룬다는 점은 비슷하지만, final필드는 static하지 않아 공유 메모리가 아닌 객체마다 각각 저장되고, 생성자의 매개값을 통해 여러값을 가질 수 있다.
final필드에 초기값을 주는 경우는 딱 두가지이다. 첫째는 필드선언시에 주는방법 둘째는 생성자에서 주는 방법이다.
class Person{ final String nation = " Korea"; final String ssn; String name; public Person(String ssn, String name) { this.ssn = ssn; this.name = name; } } public class Sample{ public static void main(String[] args){ Person p1 = new Person("12345,878954", "홍길동"); System.out.println(p1.nation); System.out.println(p1.ssn); p1.nation = "홍삼원"; //error System.out.println(p1.name); } }
상수
상수는 static( 객체마다 저장할 필요가 없는 공용성을 띤다) + final(여러 가지 값으로 초기화 될 수 없다.)한 값이다.
- static final 필드는 객체마다 존재하지 않고 클래스에만 존재한다, 그리고 한번 초기값이 저장되면 변경할수 없다.
- 상수 이름은 모두 대문자로 작성하는것이 관례이다.
static final 타입 상수 = 초기화;
정리
인스턴스 멤버(인스턴스 필드,메소드) - 객체를 생성한 후 사용할 수 있고, 객체마다 갖고있는 멤버이다. 다수의 객체를 갖는다. 사용할때마다 객체를 생성한다.
static 정적멤버(정적 필드,메소드) - 객체생성없이 바로 사용가능하다, 객체를 공유한다, 클래스에만존재하고 메모리할당을 하나만 한다.
싱글톤 - 객체를 하나만 가지게 설계하는 클래스
final - 초기화하면 바꿀수 없는 값이다.
static final - 초기화하면 바꿀수 없고, 객체를 공유한다.
'java' 카테고리의 다른 글
[java] Thread / Runnable (0) 2023.01.20 [java] 예외처리 (Exception), 트랜잭션 (0) 2023.01.20 [java] 패키지와 접근제한자(public,protected/default/private) (0) 2023.01.19 [java] 입출력 ( 콘솔입출력, 파일입출력) (0) 2023.01.18 [java] 추상클래스 (0) 2023.01.17