🙋 Reflection API란?

구체적인 클래스 타입을 알지 못해도 메서드, 타입, 변수 등 해당 클래스의 정보에 접근할 수 있게 해주는 Java API이다.

public class Member {
	private String name;
	private int age;

	public Member(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public void memberTest() {
		System.out.println("test입니다.");
	}
}

 

public static void main(String[] args) {
    Object obj = new Member("kim", 10);
    obj.memberTest();    // 에러 발생
}

 

obj는 Object로 타입이 결정(java는 컴파일 시점에 타입이 결정되므로 여기서 obj는 Object로 타입이 결정된다.)됐으므로

Object 클래스 메서드, 변수들만 사용이 가능해서 Member클래스의 memberTest 사용 시 에러가 발생한다.

(Member 클래스의 구체적 타입을 모르기 때문이다.)

 

이때 Member클래스의 정보에 접근 가능하게 해주는 것이 Reflection API이다.

(* 사용법 참고 https://www.baeldung.com/java-reflection)

이런 동작이 가능한 이유는 JVM가 실행되면서 코드가 컴파일러에 의해 바이트 코드로 변환되고, static영역에 저장된다.

Reflection API는 이 static 영역에 저장된 정보를 활용하는 것이다.

 

그러나, 우리가 실제로 코드를 작성할 때는 구체적인 클래스를 모르는 일은 거의 없다.

따라서 애플리케이션 개발보다 프레임워크나 라이브러리에서 많이 사용된다.

 

[ Iterator, Enumeration ]

둘 다 컬렉션 객체들의 요소들을 조회할 때 사용한다.

(for문으로 데이터를 가져오는 방법도 있지만 순환 인터페이스인 iterator와 Enumeration을 사용하는 방법도 있다.)

 

[ Iterator vs Enumeration ]

Iterator는 remove()를 제공한다.

Enumeration보다 Iterator인터페이스를 사용하는 것이 더 낫다.(Enumeration은 legacy)

 

[ Iterator 함수 ]

- hasNext() : 순회할 다음 원소가 남아 있으면 True를 반환한다.

- next() : 다음 원소로 이동하여 반환한다.

- remove() : 가장 최신에 반환 된 원소를 삭제한다.

- forEachRemaining(Consumer<? super E> action) : 모든 원소가 주어진 작업을 처리하거나, 예외가 발생할때까지 작업을 수행한다.

 

[ Enumeration 함수 ]

- asIterator() : Iterator 인터페이스형으로 바꿔서 반환한다.

- hasMoreElements() : enumeration에 순회할 다음 원소가 남아있는지 확인한다. (= Iterator함수 hasNext())

- nextElement() : 다음 원소를 반환한다. (= Iterator함수 next())

 

[ 예제 ]

request.getParameterNames().asIterator().forEachRemaining(...);

request.getParameterNames()의 반환값은 Enumeration이다.

asIterator()을 통해 Enumeration 인터페이스를 Iterator 인터페이스형으로 바꿔서 반환하고,

forEachRemaining()로 예외가 발생하기 전까지 모든 원소에 대해서 주어진 작업을 수행한다.

[ Optional 개념에 앞서, NPE를 먼저 알아보자. ]

* NPE(Null Pointer Exception)란?

개발할 때 가장 많이 발생하는 예외 중 하나로, null을 호출하게 될 때 발생하는 예외사항이다.

이를 방지하기 위해 null 검사를 해야 하는데 변수가 많을수록 코드가 복잡해질 수 있다.

 

[ Optional ]

java.util.Optional<T> 클래스 :
Optional<T> 클래스는 Integer나 Double 클래스처럼 'T'타입의 객체를 포장해 주는 래퍼 클래스(Wrapper class)입니다.
따라서 Optional 인스턴스는 모든 타입의 참조 변수를 저장할 수 있습니다.

이러한 Optional 객체를 사용하면 예상치 못한 NullPointerException 예외를 제공되는 메소드로 간단히 회피할 수 있습니다.
즉, 복잡한 조건문 없이도 널(null) 값으로 인해 발생하는 예외를 처리할 수 있게 됩니다.

(출처 : 코딩의 시작, TCP School)

 

[ Optional 사용 예시 ]

public Optional<Member> findById(Long id) {
	return Optional.ofnullable(store.get(id)); //ofnullable : 값이 null이 들어올 수도 있는 경우에 사용
}

 

[ Optional 객체 접근 ]

Optional<String> opt = Optional.ofNullable("자바 opt 객체")
System.out.println(opt.get());

get() 메소드를 사용하여 Optional 객체 저장된 값에 접근할 수 있다.

이때 저장된 값이 null이면 NoSuchElementException 예외가 발생한다.

 

[ Optional 호출 시 null 체크 ]

get() 사용 시 null이면 예외를 발생시키므로 bool 타입을 반환하는 isPresent()메소드 통해 null 체크가 필요하다.

String text = getText();
Optional<String> optText = Optional.ofNullable(text);
int length;
if(optText.isPresent()) {
	length = optText.get().length();
} else {
	length = 0;
]

이를 통해 의문점이 들 수 있다.
NPE 방지를 위해 복잡한 null 체크를 버리고 Optional를 쓰는 건데 또 null 체크를 하다니?
이 부분은 밑에 블로그에서 잘 설명되어 있어 링크로 대신 첨부해 논다.

 

자바8 Optional 2부: null을 대하는 새로운 방법

Engineering Blog by Dale Seo

www.daleseo.com

 

 

자바8 Optional 3부: Optional을 Optional답게

Engineering Blog by Dale Seo

www.daleseo.com

 

[ Optional 정리 ]

Optional은 NPE null 체크의 복잡한 부담을 줄이기 위해 나온 Wrapper 클래스이다.

Optional은 Wrapping하고 다시 풀고 null일 경우 대체하는 함수를 호출하는 등의 오버헤드가 있으므로 잘못 사용하면 시스템 성능이 저하될 수 있다. 그렇기 때문에 반환 값이 null이 절대 아니라면 사용하지 않는 것이 좋다.

결과가 null이 될 수 있으며 null에 의해 오류가 발생할 가능성이 매우 높은 경우에만 사용되어야 한다.

'Java' 카테고리의 다른 글

[Java] Reflection API  (0) 2024.02.18
[Java] Iterator, Enumeration  (0) 2023.10.17
[Java] import static 사용하는 이유  (0) 2023.08.09
[Java] Static - 정적 변수와 메서드  (0) 2023.06.13
Intellij tomcat 연동 및 설정 (gradle)  (0) 2023.05.19

✏️ import static 사용 전

import org.assertj.core.api.Assertions;
Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());

 

✏️ import static 사용 후

import static org.assertj.core.api.Assertions.*;
assertThat(member.getName()).isEqualTo(findMember.getName());

 

💡 import static을 사용하면 Assertions 즉, 클래스 이름을 붙이지 않고 바로 assertThat 메서드 사용이 가능하다.  ➜  코드가 간결해짐. 가독성 올라감.

'Java' 카테고리의 다른 글

[Java] Reflection API  (0) 2024.02.18
[Java] Iterator, Enumeration  (0) 2023.10.17
[Java] Optional이란? Optional 개념 및 사용  (0) 2023.10.05
[Java] Static - 정적 변수와 메서드  (0) 2023.06.13
Intellij tomcat 연동 및 설정 (gradle)  (0) 2023.05.19

✏️ Static (정적인,고정의) :

- java 코드 상에서 모든 객체가 공유할 수 있다는 의미를 가지고 있다.

- JVM에서 메모리에 딱 한번 올라가 고정적으로 할당되며, 프로그램이 종료될 때 해제된다.

- 객체를 선언하지 않아도 접근할 수 있다.

예시)

public class Test {
 
    public static void test1() {
        System.out.println("test1");
    }
 
    public void test2() {
        System.out.println("test2");
    }
}
 
public class Main {
    public static void main(String[] args) {
        Test.test1(); // 사용 가능
        Test.test2(); // 사용 불가능
 
        Test test = new Test();
        test.test1(); // 사용 불가능
        test.test2(); // 사용 가능
    }
}

 

✏️ 장점 : 

- 생성 할때마다 메모리가 올라가는 것이 아니라, 고정 메모리이므로 효율적이다.

- 객체를 생성하지 않고 접근하기때문에 참조속도가 빠르다. (사용을 안하고 있어도 메모리에 잡히기 때문이다. 반대로 객체생성으로 만들어진 인스턴스는 GC(Garbage Collecion)에 의해 소멸된다.)

 

✏️ 단점 :

- 무분별한 사용은 메모리 낭비를 발생할 수 있다.

- 값이 자주 바뀌는 객체를 Static으로 사용할 경우 공유자원이므로 예상치 못한 오류가 발생하여 디버깅이 힘들어질 수 있다.

 

✏️ Static을 효율적으로 사용하기 :

- 공유하는 값에 사용

- 절대적으로 변하지 않는 값인 경우 final과 함께 사용한다. (ex. 하루인 24시간, 파이값 등)

 

✏️ Static은 객체 생성 없이 공유하며 사용해 메모리를 효율적으로 사용할 수 있지만 무분별한 사용은 비효율적인 상황을 발생시킬 수 있다.

 

'Java' 카테고리의 다른 글

[Java] Reflection API  (0) 2024.02.18
[Java] Iterator, Enumeration  (0) 2023.10.17
[Java] Optional이란? Optional 개념 및 사용  (0) 2023.10.05
[Java] import static 사용하는 이유  (0) 2023.08.09
Intellij tomcat 연동 및 설정 (gradle)  (0) 2023.05.19

1. 의존성 설정 : buil.gradle > dependencies에 embedded tomcat 의존성 추가

추가하는 방법 : 메이븐저장소(https://mvnrepository.com/) - embedded tomcat 검색 - Tomcat Embed Core 클릭 - 최신 버전 클릭 - 탭 중에서 Gradle클릭 - implementation부분부터 끝까지 복사 - buil.gradle파일의 dependencies{} 내부에 추가

 

2. WebApplicationServer.java 코드 추가

public static void main(String[] args) throws Exception {

	// 톰캣의 루트 디렉토리가 webapps이며 톰캣을 8080포트로 실행할것이다.
        String webappDirLocation = "webapps/";
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

	// 경로로 접근했을때 해당 디렉토리(webapps) 안에서 톰캣이 파일을 찾는다
        tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath());
        log.info("configuring app with basedir: {}", new File("./" + webappDirLocation).getAbsolutePath());

        tomcat.start();
        tomcat.getServer().await();
    }

 

✏️참고 1

wepapps 안에는 아무것도 없는데 어떻게 찾아서 실행하나? (현재 webapps 비워져있는상태라면) :

ctrl+alt+shift+s(Project Structure 창 오픈) > Modules > 프로젝트 - main 클릭 > Output path에서 out 부분을 wepapps\WEB-INF로 변경 (produc~부분은 지움)

*main밑에 test도 클릭해서 Test output path 해당 부분 똑같이 변경

 

✏️참고 2

위 설정을 마치면, 톰캣이 해당 클래스를 실행할 수 있다. 

* 구글에 application developer's guide - deployment - apache tomcat 8.5.42 검색(build.gradle 추가한거보면 8.~ 버전 사용했으므로) > Application Developer's Guide 페이지의 Standard Directory Layout 부분에서 (루트디렉토리 생략)/WEB-INF/classes/ 부분 확인하면 'This directory contains any Java class files' 라고 써있음을 확인 가능

 

✏️참고 3

위 설정을 마치면, 

프로젝트-src-main-java-org.example-WebApplicationServer에서 org.example-WebApplicationServer 이 부분을 webapps/WEB-INF/classes밑에 들어가게 해줌. 따라서 톰캣이 실행될때 classes밑에서 자바 클래스 찾아 실행하게 되는것.

'Java' 카테고리의 다른 글

[Java] Reflection API  (0) 2024.02.18
[Java] Iterator, Enumeration  (0) 2023.10.17
[Java] Optional이란? Optional 개념 및 사용  (0) 2023.10.05
[Java] import static 사용하는 이유  (0) 2023.08.09
[Java] Static - 정적 변수와 메서드  (0) 2023.06.13

+ Recent posts