이것이 자바다 6장 - 9 ~ 12

오늘·2021년 3월 7일
0

Java

목록 보기
13/42

인스턴스 멤버와 this

  • 인스턴스(Instance) 멤버
    객체(=인스턴스) 생성한 후 사용할 수 있는 필드와 메소드를 말하는데 이들을 각각 인스턴스 필드, 인스턴스 메소드라 부른다.
  • 인스턴스 필드와 메소드는 객체에 소속된 멤버이기 때문에 객체 없이는 사용할 수 없다.
public class Person {
	// 필드
    	String name;
        
        // 메소드
        void setAge(int num){...}
}

Person 클래스에 name필드와 setAge메소드가 위처럼 선언되어있다. 이 필드와 메소드는 인스턴스 멤버이기 때문에 외부 클래스에서 사용하기 위해서는 Person 객체(인스턴스)를 생성하고 참조변수로 접근해야한다.

(1)
Person person = new Person();
person.name = "홍길동";
person.setAge(20);

(2)
Person ps = new Person();
ps.name = "김대감";
ps.setAge(30);

위 두 코드가 실행되면
-인스턴스 필드인 name은 객체마다 따로 존재한다. 불러온 변수만큼 새롭게 존재하는 것이다.
-인스턴스 메소드인 setAge()는 객체마다 존재하지 않는다. 메소드 영역에서 저장되고 공유된다.

  • 객체 외부에서 인스턴스 멤버에 접근하기 위해 참조변수를 사용하는 것과 마찬가지로 객체 내부에서도 인스턴스 멤버에 접근하기 위해 this를 사용할 수 있다.
    -this는 주로 생성자와 메소드의 매개변수 이름이 필드와 동일한 경우, 원하는 것이 필드임을 명시하고자 할 때 사용된다.
Person(String name) {
	this.name = name;
}
void setName(String name){
	this.name = name;
// 와 같은 형태로 사용
}

정적 멤버와 static

  • 정적(static) : '고정된'이란 의미를 가지고 있다.
  • 정적 멤버 : 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 말한다. 이들을 각각 정적 필드, 정적 메소드라 부른다.
  • 정적 멤버(=클래스 멤버) : 객체(인스턴스)에 소속된 멤버가 아니라 클래스에 소속된 멤버이기 때문에

정적 멤버 선언

선언 시 static 키워드를 추가적으로 붙이면 된다.

-정적 필드와 정적 메소드는 클래스에 고정된 멤버이므로 클래스 로더가 클래스 (바이트 코드)를 로딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리된다. 따라서 클래스의 로딩이 끝나면 바로 사용할 수 있다

public class 클래스 {
	// 적적 필드
    	static 타입 필드 [=초기값];
        
        // 정적 메소드
        static 리턴 타입 메소드(매개 변수 선언, ...) {... }
}

-'객체마다 가지고 있어야 할 데이터'라면 인스턴스 필드로 선언하고
-객체마다 가지고 있을 필요성이 없는 '공용적인 데이터' 라면 정적 필드로 선언하는 것이 좋다.

public class calcu {
	String color;
    	// 계산기별로 색이 다를 수 있으니까
        // 인스턴스 필드로 설정
    	static double pi = 3.141592;
        // 파이값은 어디나 동일하니까
        // 정적 필드로 설정
}

정적 멤버 사용

클래스가 메모리로 로딩되면 정적 멤버를 바로 사용할 수 있는데, 클래스 이름과 함께 도트 (.) 연산자로 접근한다.

클래스.필드;
클래스.메소드( 매개값, ... );

// calcu 클래스선언
public class Calcu {
static double pi = 3.14159;
static int plus(int x, int y) {...}
static int minus(int x, int y) {...}
}

// 정적 필드 pi와 정적 메소드 plus(), minus() 사용법
double result1 = 10 * 10 * calcu.pi;
int result2 = calcu.plus(10, 5);
int result3 = calcu.minus(10, 5);

정적 필드와 정적 메소드는 '원칙적으로는' 클래스 이름으로 접근해야 하지만 다음과 같이 객체 참조 변수로도 접근이 가능하다

Calcu myCalcu = new Calcu();
double result1 = 10 * 10 * myCalcu.pi;
int result2 = myCalcu.plus(10, 5);

정적 초기화 블록

정적 필드는 다음과 같이 필드 선언과 동시에 초기값을 주는 것이 보통이다

static double pi = 3.14159;

그러나 계산이 필요한 초기화 작업이 있을 수 있다. 인스턴스 필드는 생성자에서 초기화하지만, 정적 필드는 객체 생성 없이도 사용해야 하므로 생성자에서 초기화 작업을 할 수 없다. 생성자는 객체 생성 시에만 실행되기 때문이다.

// java 에서는 정적 필드의 초기화 작업을 위해
// 정적 블록(static block)을 제공
static {
	...
}

정적 블록은 클래스가 메모리로 로딩될 때 자동적으로 실행된다. 여러개 선언되어도 상관 없다. 같은 타입의 블록은 (인스턴스 끼리, 정적끼리, 기본생성자끼리는) 순서대로 실행된다.

정적 메소드와 블록 선언 시 주의할 점

정적을 선언할 때 주의할 점은 객체가 없어도 실행된다는 특징 때문에, 이들 내부에 인스턴스를 사용할 수 없다. 객체 자신의 참조인 this 키워드도 사용이 불가능하다.

public class ClassName {
	// 인스턴스 필드와 메소드
    	int field1;
        void method1(){...}
        
        // 정적 필드와 메소드
        static int field2;
        static void method2(){...}
        
        // 정적 블록
        static{
        	field1 = 10;		// x 에러
            	method1();		// x 에러
                field2 = 10;		// o 가능
                method2();		// o 가능
        }
        
        // 정적 메소드
        static void Method3 {
        	this.field1 = 10;	// x 에러
            	this.method1();		// x 에러
                field2 = 10;		// o 가능
                method2();		// o 가능
        }
}

정적 메소드와 정적 블록에서 인스턴스 멤버를 사용하고 싶다면 객체를 먼저 생성하고 참조 변수로 접근해야 한다.

static void Method3() {
	ClassName obj = new ClassName();
    obj.field1 = 10;
    obj.method1();
}

싱글톤(Singleton)

가끔 전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우가 있다. 단 하나만 생성된다 해서 이 객체를 싱글톤(singleton)이라 한다. 싱글톤을 만드려면 클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 막아야한다.

public class 클래스 {
        // private 접근 제한자를 붙여
        // 생성자를 외부에서 호출할 수 없도록한다
	// 정적 필드
    	private static 클래스 singleton = new 클래스();
        
        // 생성자
        private 클래스() {}
        
        // 정적 메소드
        // 외부에서 호출할 수 있는 정적 메소드 선언
        // 정적 필드에서 참조하고 있는 자신의 객체를 리턴해준다.
        static 클래스 getInstance() {
        	return singleton;
       }
}

이 상태로 외부에서 객체를 얻는 유일한 방법은 getInstance() 메소드를 호출하는 방법이다. 이 getInstance()메소드는 단 하나의 객체만 리턴하기 때문에 호출 변수를 다르게 해도 동일한 객체를 참조한다.

클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();

final 필드와 상수

final 필드

최종적 이라는 뜻의 final 처럼, final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어 프로그램 실행 도중에 수정할 수 없다는 것이다.

// 선언방법
final 타입 필드 [=초기값];

final 필드의 초기값을 줄 수 있는 방법은 딱 두가지이다.

-첫번째, 필드 선언 시주는 방법
-두번째, 생성자에서 준다

단순값이라면 필드 선언 시 주는 것이 가장 간단하지만, 복잡한 초기화 코드가 필요하거나 객체 생성 시에 외부 데이터로 초기화해야한다면 생성자에서 초기값을 지정해야 한다.

// final 필드 선언과 초기화------------------------------------
// final 필드
// 최종적인 값을 갖고 있는 필드 = 값을 변경할 수 없는 필드

public class Final245 {
	final String nation = "Korea";
	final String ssn;
	String name;
	
	public Final245(String ssn, String name) {
		this.ssn = ssn;
		this.name=name;
	}
	
	// 같은 메소드 내에서도 변경은 불가
	void mehtod() {
		// nation = "usa";
		// ssn = "143566-4315462"
		name = "철수";;
	}
}



// final 필드 테스트-----------------------------------------
public class Final246 {
	public static void main(String[] args) {
		Final245 fi = new Final245("123456-1234567", "홍길동");

		System.out.println(fi.nation);
		System.out.println(fi.ssn);
		System.out.println(fi.name);

		// final 설정한 필드는 값 수정이 불가하다
		// fi.nation = "usa";
		// fi.ssn = "143566-4315462"
		fi.name = "철수";
		System.out.println(fi.name);
	}
}

상수(static final)

  • 일반적으로 불변의 값을 상수라 부른다. final 필드는 객체마다 저장되고, 생성자의 매개값을 통해 다른 값들을 가질 수도 있기 때문에 상수가 될 수 없다. 상수는 static 이면서 final 이여야 한다.
// 단순 값일때
static final 타입 상수 [= 초기값];

// 복잡한 초기화의 경우 정적 블록에서 할 수는 있음
static final 타입 상수;
static {
	상수 = 초기값;
}
  • static final 필드는 객체마다 저장되지 않고, 클래스에만 포함되며, 한 번 초기값이 저장되면 변경할 수 없다.
// 상수 선언
// 일반적으로 불변의 값을 상수라 부른다.
// 지구의 둘레, 은행의 정해진 금리 등
// 불변 값을 캡슐화

// final필드는 객체마다 저장되고,
// 생성자의 매개값을 통해 여러가지 값을 가질 수 있다
// static fianl 필드는 객체마다 저장되지 않고, 클래스에만 포함된다.
// 한 번 초기값이 저장되면 변경할 수 없다

// static 메소드로만 값 설정이 가능하다
	
public class StaticFinalEx {
	static final double EARTh_RADIUS = 6400;
	static final double EARTH_SURFACE_AREA;
	
	static {
		EARTH_SURFACE_AREA = 4 * Math.PI * EARTh_RADIUS * EARTh_RADIUS;
	}

// 아래는 불가능한 모습
// static이 아닌 다른 메소드에서 값을 지정해주는 것은 안됨
//	void StaticFinalEx{
//		EARTH_SURFACE_AREA = 4 * Math.PI * EARTh_RADIUS * EARTh_RADIUS;
//	}
// }







// 상수 사용----------------------------------------------------------------
public class StaticFinalEx1 {

	public static void main(String[] args) {
		System.out.println("지구의 반지름 : " + StaticFinalEx.EARTh_RADIUS + " km");
		System.out.println("지구의 표면적 : " + StaticFinalEx.EARTH_SURFACE_AREA + " km");
	}
}

패키지

프로젝트를 개발하다 보면 적게는 수십개, 많개는 수백 개의 클래스를 작성해야한다. 이때 클래스를 체계적으로 관리하지 않으면 클래스 간 관거ㅖ가 뒤엉켜 복잡하고 난해한 프로그램이 되고, 결국 유지 보수를 어렵게 한다.

때문에 자바에서는 파일을 저장 관리 하듯이 패키지를 만들어 클래스를 저장 관리한다. 패키지의 물리적인 형태는 파일 시스템의 폴더이다.

사용 모습 : 상위패키지명.하위패키지명.클래스

패키지 선언

패키지는 클래스를 컴파일하는 과정에서 자동적으로 생성되는 폴더. 다음은 패키지를 선언하는 방법이다.

package 상위패키지.하위패키지;

public class ClassName { ... }

패키지 이름은 몇가지 규칙만 지켜 임의대로 지어주면 된다.
-숫자로 시작하면 안된다
-특수문자는 사용해서는 안된다(_ $ 제외)
-java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용해서는 안된다
-모두 소문자로 작성하는 것이 관례이다.

imort 문

다른 패키지에 속하는 클래스를 사용하려면 두 가지 방법 중 하나를 선택해야 한다.
1. 패키지와 클래스를 모두 기술하는 것이다
ex: com.hankook.Tire tire = new com.hankook.Tire();
2. import 선언
import com.hankook.Tire or import com.hakook.*; 의 형태
사용하고자 하는 패키지를 import문으로 선언하고, 클래스 사용시 패키지를 생략하는 것이다.

package com;

// (2) import로 불러오기
import com.myCom.myCom1.*;

public class BusinessClass {
	public static void main(String[] args) {

		// (1) 방법. 하나하나 입력해주기------------------------------------------
//		ComClass om = new ComClass();
//		com.myCom.myCom1.MyCom1Class ComClass = new com.myCom.myCom1.MyCom1Class;
		com.myCom.myCom1.MyComClass11 C11 = new com.myCom.myCom1.MyComClass11();
		com.myCom.myCom1.MyComClass22 C22 = new com.myCom.myCom1.MyComClass22();
        }
}

강의

  1. 코드 실행 순서
    : 1.정적블록 2.인스턴스블록영역 3.기본생성자영역 순서
// 코드 실행
// 메인을 먼저 실행한다
// 객체 생성 된 부분이 있다면 해당 클래스로 ㄱㄱ
// (객체 생성이 안되었어도 다른 클래스에 static 된 부분이 있으면 메모리에 먼저 올라가 있기는 함)
// static 블록이 있는지 확인 -> 있다면 코드블록 실행
public class StaticBlockEx {
	public static void main(String[] args) {
		
	new Ex();
	}
}

// 작성 순서와 상관 없이
//1.정적블록 2.인스턴스블록영역 3.기본생성자영역 순서로 실행된다
// static 중에 순서대로 실행
// 인스턴스블록 중에 순서대로 실행
// 기본 생성자영역 중에 순서대로 실행
class Ex{
	static {
		System.out.println("aaa : static 블록 중 첫번째 작성");
	}
	{
		System.out.println("bbb : 인스턴스 블록 중 첫번째 작성");
	}
	{
		System.out.println("ccc : 인스턴스 블록 중 두번째 작성");
	}
	public Ex(){
		// 같은 클래스 내에서 다른 메소드를 불러서 사용하려면 this()를 사용한다
		this(10);
		System.out.println("ccc : 생성자 블록 중 첫번째 작성");
	}
	public Ex(int i) {
		System.out.println("fff : 생성자 블록 중 두번째 작성");
	}
	static {
		System.out.println("eee : static 블록 중 두번째 작성");
	}
}

실행 결과

실행 결과를 보면 블록들이 순서대로 출력되는 것을 볼 수 있다. 생성자 블록의 순서가 바뀐것은 this()를 사용해 중간에서 불러 사용했기 때문이다.


  1. 코드 실행순서
public class StaticEx {
	/*
	 정적(static) 멤버란
	 -클래스에 고정된 필드와 메소드(ex. 정적필드, 정적 메소드)
	 
	 정적 멤보는 클래스에 소속된 멤버
	 -객체 내부에 존재하지 않고, 메소드 영역에 존재
	 -정적 멤보는 객체를 생성하지 않고 클래스로 바로 접근해 사용
	 
	 정적 멤버 선언
	 -필드 또는 메소드 선언할 때 static 카워드를 붙인다
	 */
	public static void main(String[] args) {
		
		new Television();
	}
}

class Television{
	// 코드 작성의 순서와 상관없이
	// 실행 순서는 아래와 같다
	// 1.정적블록 2.인스턴스블록영역 3.기본생성자영역
	
	static int a;
	
	public Television() {
		a = 10000;
		System.out.println("기본 생성자 영역");
	}

	{
		a = 100;
		System.out.println("인스턴스 블록 영역");
	}
	
	static {
		a = 0;
		System.out.println("정적 블록 영역");
	}
}

  1. 정적 필드와 메소드 불러서 사용하는 법
public class StaticEx {
	// Static이 안붙은 멤버
	// = 인스턴스 멤버
	int inVar1;
	void inMethod() {
		int Var1 = inVar1 + 100 + stVar1;
		// 인스턴스 멤버에서 정적 멤버를 호출해도 에러 안남
		System.out.println(inVar1);
		stMethod();
		// 이것도 마찬가지. 사용가능. 에러안남
	}
	
	
	// 정적 멤버
	// Static 멤버
	static int stVar1;
	static void stMethod() {
		stVar1 = 100 + stVar1; //+inVar
		System.out.println(stVar1);
		// inMethod();
		// 순서상 Static이 인스턴스 멤버보다 먼저 올라가기 때문에
		// Static에서 인스턴스를 불러오거나 사용할 수는 없다
	}
}



// 메인 부분----------------------------------------------------
public class StaticEX01 {
	public static void main(String[] args) {
		// 정적멤버에 200을 넣고 메소드 호출해 보세요
        // 정적은 객체 참조 변수 안해도 됨
		// 정적멤버사용법은 클래스명.멤버명
		StaticEx.stVar1 = 200;
		StaticEx.stMethod();

		// 인스턴스 멤버에 300 넣고 메소드 호출하기
		// 1.인스턴스 멤버를 (객체 참조 변수를 사용) 힙메모리에 올린다
		// 2.인스턴스 멤버들 사용방법 : 참조변수.멤버명
		StaticEx st = new StaticEx();
		st.inVar1 = 300;
		st.inMethod();

		// st.stVar1=300;
		// 가능은 한데, 올바르지는 않은 방법
	}
}
  1. 인스턴스와 정적의 사용방법
// InstanceClass1------------------------------------------
public class InstanceClass1 {
	int iC1;
}

// InstanceClass2-------------------------------------------
public class InstanceClass2 {
	int iC2;
}

// StaticEx-------------------------------------------------
public class StaticEx {
	static int sum;
    	// 각 클래스의 자료를 누적할 합에 대한 정적 변수
	int sum1;
    	// 각 클래스의 자료를 누적할 때 사용할 인스턴스 변수
	// 클래스에 인스턴스 변수 값을 넣는다.
}

// StaticEX01-----------------------------------------------
public class StaticEX01 {
	public static void main(String[] args) {
		InstanceClass1 i1 = new InstanceClass1();
		i1.iC1 = 100;

		InstanceClass2 i2 = new InstanceClass2();
		i2.iC2 = 200;

		// 공용으로 사용되는 sum에 각 자료를 누적해서 넣는다
		StaticEx.sum += i1.iC1;
		StaticEx.sum += i2.iC2;

		System.out.println(StaticEx.sum);

		// 인스턴스 변수 sum1사용해 각 자료를 누적해 넣기
		StaticEx st = new StaticEx();
		st.sum1 += i1.iC1;
		st.sum1 += i2.iC2;

		System.out.println(st.sum1);

		// 이렇게 호출을 두 번하면 또다른 변수로 또 다른 공간이 만들어짐
		// st와 st1은 따로따로 누적된다
		StaticEx st1 = new StaticEx();
		st1.sum1 += i1.iC1;
		st.sum1 += i2.iC2;
	}
}

  1. 싱글톤
    싱글톤 : 하나의 애플리케이션 내에서 단 하나만 생성되는 객체
  • 만드는 방법
    외부에서 new 연산자로 생성자를 호출할 수 없도록 막기 : private 접근 제한자를 생성자 앞에 붙임
    클래스 자신의 타입으로 정적 필드 선언
    외부에서 호출할 수 있는 정적 메소드인 getInstance() 생성
// 싱글톤 객체의 구조
// 객체 생성을 단 한번만 하도록 보장해줌
// 싱글톤의 객체에 인스턴스 자료를 캡슐화해서 이용함
// 즉, 객체 생성이 1번만 되므로,
// 주소 변동이 없어 싱글톤 안에 인스턴스 자료를 공용화해 이용할 수 있음
public class Singleton2 {
	
	// 정적 필드
	// (링크 로더 단계에서 컴파일해 올라갈 때) 바로 객체 생성해주고 변수에 담dma
	// private로 설정해줬기에 s2를 다른 곳에서 사용할 수는 없음
	private static Singleton2  s2 = new Singleton2();
	
	
	// 생성자
	private Singleton2() {
	}
	
	
	// 다른 객체와 연결되는 통로(메소드
	static Singleton2 getInstance() {
		return s2;
	}
}



// 메인부분------------------------------------------------
public class SingTon {

	public static void main(String[] args) {
		Singleton st = Singleton.getInstance();
		st.name = "홍길동";
		st.age = 20;
		
		// 참조 변수를 다르게 해서 호출해도
		// 사용하는 주소가 같다
        	// st1과 st이 사용하는 곳이 같다
		Singleton st1 = Singleton.getInstance();
		System.out.println(st1.age);
		System.out.println(st1.name);
	}

}

0개의 댓글