[211117] 교육 17일차

oxllz·2022년 1월 30일
0

교육

목록 보기
13/41

Static

class Temp {
	static int add( int i, int j ) {  return 100; }
}
//
public class Test136 {
	public static void main( String[] args ) {
		int r = Temp.add( 10, 20 );
		System.out.println( r );
	}
}

static 이 붙은 멤버함수는?

  • 인스턴스 없이 클래스명.함수명으로 호출이 가능하다. ( 참조형변수도 불필요 )

'C에서 보던 보통함수와 동일 개념'

int add( int i , int j ) {
	return 100;
}

int main() {
	int r;
	r = add( 10, 20 );
	printf("%d\n", r );
	
	return 0;
}

C 은 클래스 밖에서 함수선언이 가능하지만 java 는 불가능하다.
해서 클래스 안에서 선언하면서 멤버함수와 구별되도록 static 이라는 키워드를 사용한다.

static initializer

class Temp {
	static {
		System.out.println("static");
	}
	//
	Temp() {
		System.out.println("생성");
	}
}
//
public class Test137 {
	public static void main( String[] args ) {
		new Temp();
		new Temp();
		new Temp();		
	}
}

static initializer : 인스턴스 생성 이전에 딱 한번 호출된다. 즉 클래스가 로딩되는 시점에 호출된다는 뜻이다.

Test137 class 실행 시 처음에는 Test137.class 만 사용한다. new Temp() 를 만나면 Temp.class 가 필요해지고 Temp.class 는 메모리에 로딩되어야 한다. 그 다음 로딩된 클래스로 인스턴스가 생성된다.

이 시점( 클래스가 로딩되는 시점 )에 static initializer 가 호출된다. 인스턴스 생성시 멤버를 가리키는 함수 포인터는 메모리를 할당 받기때문에 클래스가 로딩되는 시점에는 멤버 호출이 불가능하다.

class Temp {
	static {	// 얼마든지 OK
		Temp t = new Temp();
		t.print();
	}
	static void print2() {
		// 에러 : print();
	}
	void print() {
		print2();
	}
}
/*
인터페이스는 오버라이딩을 전제로 하는 함수를 가진다. 
오버라이딩은 함수 포인터로 운용되어야하므로 인스턴스에 함수포인터가 있어야 한다.
따라서 인터페이스 안에서는 non-static 한 메소드를 사용해야 한다.
*/
interface ITemp {
	// public static void print();	
}
//
public class Test138 {
	public static void main( String[] args ) {
		//...
	}
}

non-static method print() cannot be referenced from a static context

  • static 이 언급된 멤버변수와 함수가 생성되는 시점은 class 로딩시. 이때는 non-static 한 멤버는 존재하기 전이다.

static 안에서는 non-static 멤버는 사용불가!


Singleton Pattern

class Temp {
	// uniq 변수는 jvm 안에 유일하게 된다.
	private static Temp uniq = null;
	// static initializer 안에서 uniq 로 인스턴스를 가리키게 하라.
	static {
		uniq = new Temp();
	}
	// public static synchronized 하게 uniq 를 제공하는 getInstance 함수를 선언한다.
	public static synchronized Temp getInstance() {
		return uniq;
	}
	// Temp 밖에서는 생성자를 호출할 수 없다. 즉 인스턴스를 못 만든다.
	private Temp() { }
}	// 이렇게 설계하는 설계 패턴을 Singletone Pattern 이라고 한다.
//
public class Test139 {
	public static void main( String[] args ) {
		// 에러 new Temp();
		Temp a = Temp.getInstance();
		Temp b = Temp.getInstance();
		System.out.println( a == b );	// 인스턴스가 재활용되고 있고 공유되고 있음.
	}
}
class Temp {
	private static Temp uniq = null;
	//	처음 호출시에 인스턴스가 생성되고 , 그 이후에는 인스턴스는 재활용된다.
	public static synchronized Temp getInstance() {
		if( uniq == null ) {
			uniq = new Temp();
		}
		return uniq;
	}
	private Temp() { }
}

클래스 로딩

package banana;
class Temp {
	static {
		System.out.println("static");
	}
	public void print() {
		System.out.println("print");
	}
}
//
public class Test141 {
	public static void main( String[] args ) throws Exception {
		Class<?> cls = Class.forName("banana.Temp");
		Object obj = cls.newInstance();
		System.out.println( obj.getClass().getName() );
	}
}

Class.forName : 문자열에 주어진 이름의 클래스를 강제 로딩
Object obj = cls.newInstance() : cls 에 해당하는 클래스의 인스턴스를 만든다. new 없이 인스턴스 생성 가능


getMethod

package banana;
import java.lang.reflect.Method;
//
class Temp {
	public void print() {
		System.out.println("print");
	}
}
public class Test143 {
	public static void main( String[] args ) throws Exception {
		Temp t = new Temp();
		t.print();
		//
		Class<?> cls = Class.forName("banana.Temp");
		Object obj = cls.newInstance();
		System.out.println( obj );
		//
		Method mtd = cls.getMethod("print");
		mtd.invoke( obj );
	}
}

이제는 함수 이름을 문자열로 호출 가능
getMethod : cls 에서 지정된 print 라는 이름의 함수를 호출할 수 있는 포인터를 제공하는 중이라고 생각

invoke : 함수 호출시 해당 인스턴스에 대한 포인터(obj) 를 넣어주어야 한다.

import java.lang.reflect.Method;
//
class Temp {
	public void print( int i ) {
		System.out.println("Hello " + i );
	}
	public void print() {
		System.out.println("Apple" );
	}
	public void print( int i, double j ) {
		System.out.println("Banana" + j );
	}
}
//
public class Test148 {
	public static void main( String[] args ) throws Exception {
		Class<?> cls = Class.forName("banana.Temp");
		Object obj = cls.newInstance();
		// cls 에 선언된 이름이 print 이고 매개변수가 int 로 선언된 함수 포인터를 넘긴다.
		Method mtd = cls.getMethod("print", int.class );
		mtd.invoke( obj, 100 );
		// getMethod 는 가변길이 파라미터를 쓰는 형태를 지원
		Method mtd2 = cls.getMethod("print", int.class, double.class );
		Object rv = mtd2.invoke( obj, 100, 3.14 );
		System.out.println( rv );
	}
}

public Object invoke(Object obj, Object... args)

  • obj 는 매개함수를 호출할 인스턴스 에 대한 포인터
  • args 는 매개변수에 넘길 값
  • 리턴되는 것은 함수의 리턴 값에 해당

Class<?> cls

class Temp {
	public int print() {
		System.out.println("Apple" );
		return 100;
	}
}
//
public class Test149 {
	public static void main( String[] args ) throws Exception {
		Class<?> cls = Class.forName("banana.Temp");
		Object obj = cls.newInstance();
		//
		System.out.println( Temp.class == cls );	
		System.out.println( obj.getClass() == cls );
		// invoke 함수의 리턴타입은 Object 니까 언박싱을 이용하여 자료형변수에 대입하려면 캐스팅 필요
		Method mtd = cls.getMethod("print");
		int r = (Integer)mtd.invoke( obj );
		System.out.println( r );
	}
}

Class<?> cls = ... 를 이용하여 포인터를 넘겨받는 방법
1. Class.forName("banana.Temp");
2. [클래스명].class
3. Object 에서 선언된 getClass() 함수를 호출


향상된 for문

class Temp {
	public void print( int i, int j ) {
		System.out.println("Apple" );
	}
}
//
public class Test150 {
	public static void main( String[] args ) throws Exception {
		Class<?> cls = Class.forName("banana.Temp");
		Object obj = cls.newInstance();
		//
		Method[] mtds = cls.getMethods();
		for( Method mtd : mtds ) 
		{
			System.out.println( mtd.getName() );
		}
	}
}

배열, List, Set 의 경우에 사용 가능하다.

0개의 댓글