문자열 붙이기 과거에는 "" +"" 혹은 StringBuilder 혹은 StringBuffer 객체를 이용해서 무한 append 했었죠.
Copy String html =
"<html>\n" +
"<body>\n" +
" <h1>Java 15 새로운 문자열 붙이기...완전 좋네요!</h1>\n" +
" <p>didispace.com</p>\n" +
"</body>\n" +
"</html>\n" ;
Copy StringBuilder sb = new StringBuilder() ;
sb . append ( "abc" );
sb . append ( "def" );
sb . append ( "ghi" );
Copy StringBuffer sb = new StringBuffer() ;
sb . append ( "abc" );
sb . append ( "def" );
sb . append ( "ghi" );
하지만 JAVA 15부터 아래와 같이 쓸수 있게 되었네요. "" 아닌 """ (3개)를 쓰면 됩니다. 줄바꿈 자유롭게 할수 있습니다.
Copy String html = """
<html>
<body>
<h1>Java 15 새로운 문자열 붙이기...완전 좋네요!</h1>
<p>didispace.com</p>
</body>
</html>
""";
히든 클래스
다른 class의 bytecode에서 직접 사용할 수 없는 class이다. Hidden class는 runtime에 class를 생성하고 reflection을 통해 간접적으로 사용하는 framework에서 사용하기 위한 것이다. hidden class는 access 제어 중첩의 member로 정의될 수 있으며 다른 class와 독립적으로 unload 될 수 있다.
일반 클래스 하나 생성해 보자
Copy public class JEP371HiddenClasses {
public static String hello () {
return "https://rainsister.tistory.com" ;
}
}
해당 클래스를 컴파일 하고 나서
Copy String filePath = "JEP371HiddenClasses.class" ;
byte [] b = Files . readAllBytes ( Paths . get (filePath));
log . info ( Base64 . getEncoder () . encodeToString (b));
아래와 같은 코드(문자열)을 얻을 수 있다.
Copy yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAZaHR0cHM6Ly93d3cuZGlkaXNwYWNlLmNvbQcACgEALmNvbS9kaWRpc3BhY2UvZGVidWcvamF2YTE1L0pFUDM3MUhpZGRlbkNsYXNzZXMBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAMExjb20vZGlkaXNwYWNlL2RlYnVnL2phdmExNS9KRVAzNzFIaWRkZW5DbGFzc2VzOwEABWhlbGxvAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAYSkVQMzcxSGlkZGVuQ2xhc3Nlcy5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAGAAEAEgAAAAIAEw==
리텐션(Retention)을 이용하여 위에서 작성한 클래스를 로등하여 히든클래스의 hello함수를 호출할수 있다.
Copy @ Test
void testHiddenClasses() throws Throwable {
// 1.히든클래스 로딩
String CLASS_INFO = "yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAZaHR0cHM6Ly93d3cuZGlkaXNwYWNlLmNvbQcACgEALmNvbS9kaWRpc3BhY2UvZGVidWcvamF2YTE1L0pFUDM3MUhpZGRlbkNsYXNzZXMBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAMExjb20vZGlkaXNwYWNlL2RlYnVnL2phdmExNS9KRVAzNzFIaWRkZW5DbGFzc2VzOwEABWhlbGxvAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAYSkVQMzcxSGlkZGVuQ2xhc3Nlcy5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAGAAEAEgAAAAIAEw==";
byte [] classInBytes = getDecoder() . decode (CLASS_INFO);
Class < ? > proxy = MethodHandles . lookup ()
. defineHiddenClass (classInBytes , true , MethodHandles . Lookup . ClassOption . NESTMATE )
. lookupClass ();
// class name
log . info ( proxy . getName ());
// class에 어떤 함수가 있는지 보여주기
for ( Method method : proxy . getDeclaredMethods ()) {
log . info ( method . getName ());
}
// 2. hello함수 호출
MethodHandle mh = MethodHandles . lookup () . findStatic (proxy , "hello" , MethodType . methodType ( String . class ));
String result = (String) mh . invokeExact ();
log . info (result);
}
여기서 CLASS_INFO 는 아까 출력된 문자열들이다.
일단 Base64 로 해당 코드를 복호화하고 리텐션을 이용하여 히든클래스의 프록시를 생성한다.
Copy Class < ? > proxy = MethodHandles . lookup ()
. defineHiddenClass (classInBytes , true , MethodHandles . Lookup . ClassOption . NESTMATE )
. lookupClass ();
이분을 유심히 살펴보면 히든클래스 생성이랑 ,일반 클래스 생성차이를 알수 있다.
initialize: 클래스 코기화 진행 여부.
options:java 클래스 타입 -> 여기를 java.lang.invoke.MethodHandles.Lookup.ClassOption 확인하면 됨.