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 이라는 키워드를 사용한다.
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 안에서는 non-static 멤버는 사용불가!
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 없이 인스턴스 생성 가능
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)
인스턴스
에 대한 포인터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() 함수를 호출
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 의 경우에 사용 가능하다.