728x90
반응형
728x90
반응형
반응형

환경: java11

 

java8이후로 java.util.Date, Calendar가 deprecated되고 LocalDateTime, ZonedDateTime, OffsetDateTime이 등장하였다. 각각 어떤 것인지 살펴보자.

 

LocalDateTime

날짜와 시간에 대한 정보만 있지 zone이나 offset에 대한 정보는 없다. zone에 상관없이 그냥 시간 그 자체를 담는 용도이다.

따라서 LocalDateTime을 아래처럼 포매팅하면 에러가 난다.

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX") 
private LocalDateTime endRegDate;
Unsupported field: OffsetSeconds

 

 

지금 시간 가져오기

LocalDateTime now = LocalDateTime.now();

서울에서, 현재 시각이 2024-02-14 11:00:00 이면 위 결과값도 동일하게 11시이다.

 

특정 시간 가져오기

LocalDateTime now = LocalDateTime.of(2024, 1, 1, 0, 0, 0);

ZonedDateTime

LocalDateTime에 zone이 들어간 형태(UTC/Greenwich)

서머타임(Daylight Saving Time): 시간대 변환 시 서머타임을 고려해야!! ZonedDateTime은 서머타임을 자동으로 처리함

 

지금 시간 가져오기

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX") 
ZonedDateTime now = ZonedDateTime.now();
//"2024-02-14T11:57:23.797+09"

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX") 
ZonedDateTime.now(ZoneId.of("UTC"));
//"2024-02-14T02:57:23.797Z"

그냥 now는 서버 시스템 존(Asia/Seoul)을 기반으로 가져오고, zone id를 주면 변환된다.

포매팅할 때 X를 추가하면(Z아님..) 끝에 존 정보 +09 혹은 UTC면 Z가 들어간다.

 

특정 시간 가져오기

ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("America/New_York"));

zone 정보를 줘야 한다.

참고: https://www.baeldung.com/java-format-zoned-datetime-string


OffsetDateTime

ZonedDateTime 처럼 zone을 기반으로 움직이는 데이터이지만 zoneId가 아닌 offset("+02:00", "-08:00")을 기반으로 한다.

특정 zone id를 모를 때 사용하기 좋음

 

현재 시간 가져오기

OffsetDateTime.now();
//"2024-02-14T12:10:05.884+09"
OffsetDateTime.now(ZoneId.of("UTC"));
//"2024-02-14T03:10:05.884Z"

사용법은 ZonedDateTime과 동일하다. 심지어 zone id로도 세팅 가능.

특정 시간 가져오기

OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.ofHours(-5));

zone offset으로 시간을 줘야 한다.

 

Instant:

  • Instant는 시간대와 상관없이 UTC 시간을 기준으로 시간을 관리할 때 유용
  • 타임존 정보를 포함하지 않는 가장 기본적인 시간 표현 방식
  • 즉, 1970-01-01T00:00:00Z(Epoch Time) 이후의 시간을 초 또는 나노초 단위로 계산한 시간
  • 타임존과 무관하게 시간의 순간을 표현할 때 사용
  • 네트워크 요청, 로그 등 글로벌 시스템에서 시간대를 고려하지 않고 순수 시간을 기록할 때 사용
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class TimeZoneConversionExample {
    public static void main(String[] args) {
        // 서버에서 UTC 시간으로 받은 Instant
        Instant serverTime = Instant.now();
        System.out.println("Server Time (UTC): " + serverTime);

        // 사용자 시간대 (예: 아시아/서울 시간대)
        ZonedDateTime userTime = serverTime.atZone(ZoneId.of("Asia/Seoul"));
        System.out.println("User Time (Asia/Seoul): " + userTime);
    }
}
728x90
반응형
반응형

환경: java11

 

요구사항: size 5인 array에 데이터를 넣고 없으면 빈 값으로 입력

String motionAvatar = "1022,1,,,"
int motionAvatarCount = 5;
String[] motionAvatarDetails = Stream
                                .concat(this.motionAvatar.split(","), Stream.generate(() -> ""))
                                .limit(motionAvatarCount)
                                .toArray(String[]::new);
                                
    //["1022", "1", "", "", ""]
  1. Stream.generate(() -> "") creates an infinite stream of empty strings.
  2. Stream.concat(...) concatenates the split array with the stream of empty strings.
  3. limit(motionAvatarCount) limits the concatenated stream to motionAvatarCount elements.
  4. toArray(String[]::new) converts the limited stream into an array of strings.

for loop 안돌고 세련된 방법으로 해보고 싶었는데 stream.generate 는 처음 봤다..

728x90
반응형
반응형

 

@Getter
@Setter
public class Message {

    private Long    receiverSno;

    private String  receiverHid;

    private String  receiverNickname;

    private Long    senderSno;

    private String  senderNickname;

    public Message (User sender, User receiver) {
        this.setSenderSno(sender.getMemberNo());
        this.setSenderNickname(sender.getNickname());
        this.setReceiverSno(receiver.getMemberNo());
        this.setReceiverNickname(receiver.getNickname());    
    }
}

위와 같이 한 클래스 내의 변수에 접근하기 위해 위처럼 getter/setter를 써야 할지 아니면 직접 접근해야 할지 고민되었다.

기능상 하는 일이 같으니 상관없지 않을까 싶은 생각이 들었고, 혹시 다른 의견이 있나 싶었다.

대부분 private 변수인 경우 같은 클래스 내에서는 직접 접근을 선호하였다.

그 이유로는

  • 인스턴스가 내부 상태를 자체적으로 숨길 필요 없음. getter/setter는 내부 상세 구현을 가리는 외부 노출용(캡슐화)
  • getter/setter가 순수하게 그 일만 하는게 아니라 다른 일(validation 등)을 할 수도 있기에(override 할 경우) 예상과는 다르게 작동할 수 있음
  • 함수가 서로 의존하게되고 그 양이 많아지면 나중에 코드 수정이 복잡해짐
  • 가독성

 

혹시 성능상 차이가 있을까 싶어 더 찾아보았는데, 성능은 거의 똑같은 것으로 확인되었다.

 


참고

https://copyprogramming.com/howto/is-getter-method-call-to-access-variable-better-than-direct-variable-access-within-a-class

https://stackoverflow.com/questions/39767559/should-data-members-be-accessed-directly-or-using-getter-method

https://stackoverflow.com/questions/23931546/java-getter-and-setter-faster-than-direct-access

728x90
반응형
반응형

Map.getOrDefault(key, defaultValue)

key에 해당하는 값이 map에 있으면 그 값을 내리고 없으면 설정한 defaultValue를 내린다.

Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);

int valueA = map.getOrDefault("A", 0); // returns 1
int valueC = map.getOrDefault("C", 0); // returns 0

여기서 주의해야할 점은 map 값의 유무와 상관없이 default value를 생성한다는 것이다.

그래서 값이 있어도 의미없는 객체가 생성되어 메모리를 차지할 수 있다.

따라서 혹시 기본 객체를 반환하려고 한다면(new AAOBJECT()), 매번 생성하는 것보다 하나 생성해놓고 쓰는게 효율적이다.

 

Map.computeIfAbsent(key, mappingFunction)

key에 해당하는 값이 map에 있으면 그 값을 내리고

없거나 null이면 mappingFunction을 실행하여 값을 계산한다.

계산 결과를 다시 map에 저장(put)한다.

getOrDefault 함수의 대안으로 사용하는 경우가 있는데, 불필요한 값이 map에 저장될 수 있어 조심해야 한다.

또한 사용 시 UnsupportedOpertionException 이 발생할 수 있는데, 이는 map이 unmodifiable map 이기 때문이다. hashmap으로 생성된 맵은 괜찮은데 혹시 없을 때 기본 값으로 Collections.emptyMap() 을 선언했다면 해당 맵은 unmodifiable이라 해당 에러가 발생할 수 있다.

Map<String, Integer> map = new HashMap<>();
        
// If "A" is not present, compute a new value using the mapping function
int valueA = map.computeIfAbsent("A", k -> 42);

// If "B" is not present, compute a new value using the mapping function
int valueB = map.computeIfAbsent("B", k -> 100);

System.out.println("Value for A: " + valueA); // Output: Value for A: 42
System.out.println("Value for B: " + valueB); // Output: Value for B: 100
Map<String, List<String>> map = new HashMap<>();
map.computeIfAbsent("A", k -> new ArrayList<>()).add("Apple");
map.computeIfAbsent("B", k -> new ArrayList<>()).add("Banana");

// After the operations, the map will contain {"A"=["Apple"], "B"=["Banana"]}
String[] words = {"Hello", "world", "Java", "programming"};

Map<Integer, StringBuilder> sentenceMap = new HashMap<>();

for (String word : words) {
	sentenceMap.computeIfAbsent(word.length(), k -> new StringBuilder()).append(word).append(" ");
}

sentenceMap.forEach((length, sentence) -> System.out.println("Length " + length + ": " + sentence.toString().trim()));

//Length 4: Java
//Length 5: Hello world
//Length 11: programming
public class FibonacciExample {
    private static Map<Integer, Long> fibCache = new HashMap<>();

    public static void main(String[] args) {
        int n = 10;
        long fibonacci = computeFibonacci(n);
        System.out.println("Fibonacci(" + n + ") = " + fibonacci);
    }

    private static long computeFibonacci(int n) {
        return fibCache.computeIfAbsent(n, k -> (k <= 1) ? k : computeFibonacci(k - 1) + computeFibonacci(k - 2));
    }
}

 

putIfAbsent와 차이점 요약

  1. 값 생성 방식:
    • putIfAbsent: 이미 준비된 값을 Map에 넣기만 함. 값이 항상 미리 준비되어 있어야 함.
    • computeIfAbsent: 값이 필요할 때만 mappingFunction을 호출해 값을 계산하고 삽입함. 값 생성 비용이 클 때 유용함.
  2. 값의 조건적 생성:
    • putIfAbsent: 키가 존재하지 않으면 값을 그대로 추가.
    • computeIfAbsent: 키가 존재하지 않으면 주어진 함수로 값을 계산해 추가.
  3. 사용 용도:
    • putIfAbsent: 단순히 키가 없을 때 특정 값을 추가하고 싶을 때 사용.
    • computeIfAbsent: 값 생성이 비용이 크거나, 키에 따라 동적으로 값을 계산해야 할 때 사용.

성능 및 유용성 측면

  • **putIfAbsent**는 값이 미리 존재하는 경우에 적합하고, 이미 계산된 값을 단순히 Map에 추가할 때 사용됩니다.
  • **computeIfAbsent**는 값의 계산이 복잡하거나 키 기반으로 계산해야 할 경우 적합하며, 이 경우 성능 최적화를 위해 값 계산이 필요한 시점에서만 수행할 수 있어 유리합니다.

 

추가!

concurrentHashMap의 경우 computeIfAbsent는 thread safe 하다

값이 없을 때만 값을 추가하는 연산을 원자적으로 처리해야, 이를 위해 ConcurrentHashMap의 computeIfAbsent() 메서드를 사용하여 해당 연산을 하나의 원자적 작업으로 처리

String value = cache.get("gameSetting");
if (value == null) {
    cache.put("gameSetting", "newValue");
}

cache가 concurrentHashMap이어도 위 코드는 원자성이 부족하여 멀티스레드 환경에 race condition에 놓이게 되어 위험하다.

728x90
반응형
반응형

환경: java 8+

java optional을 사용할 경우

null값을 대비해 기본 값을 return 할 수 있는 아래 두 함수가 있다.

orElse vs orElseGet

 

두 함수 모두 기본 값을 반환 할 수 있는데

orElse는 T객체 그 자체를, orElseGet은 람다식을 넘길 수 있다는 차이가 있다.

//orElse
UserStatistics one = statistics.stream().filter(UserStatistics::isHoldem).findFirst().orElse(new UserStatistics());
//orElseGet
UserStatistics one = statistics.stream().filter(UserStatistics::isHoldem).findFirst().orElseGet(() -> new UserStatistics());

 

또한 메모리/성능 상에서 차이도 있다.

orElse 는 값이 존재하여 else에 설정한 값을 반환하지 않게 될 때에도 이미 만들어 놓는다.

orElseGet은 값이 없어 진짜 필요할 때 실행된다.

 

따라서, 

constant, static variable, or caching result 을 반환할 경우라면 orElse를 사용해도 되지만, 대량 작업을 하거나 새로운 객체를 생성해야할 경우 orElseGet를 사용하는게 좋겠다.


참고

https://medium.com/@ductin.tran/use-java-optional-orelse-and-orelseget-wisely-4eebb50aa12e

 

Use Java Optional OrElse and OrElseGet wisely

1. Introduction

medium.com

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[pom] http로 repository 연결  (1) 2023.10.23
[pom] element annotationprocessorpaths is not allowed here  (0) 2023.10.23
디자인 패턴  (0) 2023.07.11
[이슈해결] NPE at HSSFSheet.autoSizeColumn  (1) 2023.06.28
[java] lambda stream and final  (0) 2023.02.06
반응형

요즘.. 디자인 패턴의 중요도 낮아짐

  • 개발환경 성숙: 프래임워크, 라이브러리 사용
    • 직접 구현할 필요 없음
  • 아키텍쳐의 변화: 모노리틱 -> MSA
    • 복잡성이 떨어지게 됨(구조같은데 좀 더 심플해짐)
  •  개발 언어의 변화(자바 1.8~)
    • 디자인패턴을 쓰지 않아도 해결할 수 있음(ex. 람다 등)

 

디자인 패턴?

소프트웨어 디자인 과정에서 자주 혹은 공통적으로 발생하는 문제들에 대해 재사용 가능한 해결책 혹은 형식화 된 가장 좋은 관행

 

알고리즘과의 차이점?

  • 실질적인 문제를 해결하기 위한 절차나 방법을 의미
    • 속도와 효율성(리소스)을 중요시(목표가 여기에 있음)
  • 패턴은 해결책에 대한 더 상위 수준의 설명 / 설계 구조 등 / 유지보수에 초점

 

바이블?ㅋ

왜 패턴?

  • 개발자 간의 원활한 협업이 가능
  • 소프트웨어의 구조를 파악하기 용이함
  • 재사용을 통해 개발 시간 단축
  • 설계 변경이 있을 경우 비교적 원활하게 조치가 가능

 

패턴에 대한 비판

  • 비 효율적인 해결책일 수 있음
    • Strategy패턴은 간단한 익명(람다) 함수로 구현할 수 있음
    • 많은 사람들이 이렇게 통합된 패턴들을 도그마처럼 신봉하여 패턴을 프로젝트의 맥락에 따라 적용하지 않고 문자 그대로 구현
    • 더 간단한 코드로도 문제 해결이 되는 상황에도 모든 곳에 패턴을 적용하려고 하는 패턴병에 걸리지 않도록 조심해야
    • 디자인 패턴은 모든 상황의 해결책이 아닌 코딩 방법론일 뿐이며 디자인 패턴에 얽매이지 말자

유지보수 쉽고 가독성 좋고 짧고 중복없고 객체지향 원칙에 맞는지 가 더 중요

 

객체지향 5대 원칙(SOLID)

> 높은 응집도와 낮은 결합도

  • SRP 단일 책임 원칙
    • proxy, aop
  • OCP 개방 폐쇄 원칙
    • 옵저버 패턴, if문 제거, 변경 발생 시 클래스 추가로 수정에는 닫혀있고 확장에는 열려있게
  • LSP 리스코프 치환
    • 다형성에 관한 원칙 제공; 상위 타입의 코드로 이뤄진 메서드에서 하위 타입으로 대신해도 메서드에는 아무 문제가 없어야 함
    • List list = new ArrayList(); <<- 받을 때 인터페이스로 하는게 맞다
  • ISP 인터페이스 분리 원칙
    • 거대 인터페이스를 쪼개서 기능별 인터페이스 분리
  • DIP 의존 역전 원칙
    • 인터페이스 호출; 구현체 변경으로 기능 변경 가능하도록 추상화에 의존해야함(구체화 의존 노노)

 

디자인 패턴 종류(23개)

https://m.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823 

 

[Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류

디자인 패턴을 활용하면 단지 코드만 ‘재사용’하는 것이 아니라, 더 큰 그림을 그리기 위한 디자인도 재사용할 수 있습니다. 우리가 일상적으로 접하는 문제 중 상당수는 다른 많은 이들이 접

m.hanbit.co.kr

생성 패턴

특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 최소화 할 수 있도록 유연성 제공(ocp와 비슷)

 

추상 팩토리 패턴

  • (interface를 통해) 연관된 객체를 묶는데 초점

1. if else로 분기 겁나 많고 거기 안에서 로직 마니마니

2. switch..로 하고 거기 안에서 클래스호출(빈 등록이 계속되는 문제)

3. 중간 매소드인 팩토리 클래스를 만듦, 타입을 넘기고 거기 안에서 return service

맵으로 서비스를 가질 수 있음(Map<String, VehicleService> <- 빈 이름과 interface를 구현한 해당 빈)

나머지들이 그 interface를 구현

 

팩토리 매서드 패턴

  • 추상 팩토리 패턴과의 차이점
    • 팩토리 메서드 패턴은 어떤 객체를 생성할지에 집중
    • 추상 팩토리 패턴은 연관된 객체들을 모아둔다는 것에 집중(Factory)
  • 객체 생성을 서브클래스로 분리하여 위임(캡슐화)

 

빌더 패턴(@Builder)

  • 문제
    • 자바 빈즈 패턴(facet): data/getter/setter 사용
    • 점층적 생성자 패턴(생성자 파라미터 갯수별로 생성자 만드는거)
      • : 인자가 많아지면 생성자도 많아지지고
        • 3개 이상은 생성자 사용하지 말고 빌더를..
      • : 순서도 헷갈림
  • 빌더 패턴의 장점
    • 필요한 데이터만 설정
    • 유연성을 확보
    • 가독성을 확보
    • 불변성을 확보
      • 그 후에 set하면 안됨?? setter가 있으면 되고 없으면 안되고(setter를 선언하면 안되겠지)

 

프로토타입 패턴

  • JPA -> entity를 내리면 영속성이 유지될 수 있음
  • 별도의 dto 리턴(model mapper, mapstruct)하므로써 영속성을 끊는게 맞고
  • 변환하는 걸 프로토타입 패턴이라고 함
    • OSIV default true 

https://bangpurin.tistory.com/36

 

[jpa] OSIV란; spring.jpa.open-in-view

OSIV에 대한 이해를 하려면 영속성 컨텍스트가 뭔지 알아야한다. 이전 글을 참고하자. 2022.01.27 - [개발/spring] - [jpa] 영속성 컨텍스트 in spring [jpa] 영속성 컨텍스트 in spring 영속성 컨텍스트? 엔티티

bangpurin.tistory.com

Member member = memberService.saveMember(snsType, token);
member.setSnsType("kakao"); /////<<<<<<<-- 여기서 실수하면 반영됨 상하로 트랜잭션이 있어서
memberService.emptyLogic();

=> dto 리턴 시 해결!

 

싱글톤 패턴

  • 한 클래스마다 인스턴스를 하나만 생성하여 어디서든 참조
  • 병렬로 여러 스레드에서 돌릴 경우 조심
  • 직접 구현할 일 적음(스프링 빈)
  • 장점
    • 새로운 인스턴스를 계속해서 생성하지 않으므로 메모리 낭비가 적음
  • 단점
    • 코드량 많고, 테스트 어렵고, 자식 클래스 만들기 어렵, 클라이언트가 구체 클래스에 의존(dip 위반)
    • 스프링이 모두 해결해 줌!
public class SingletonService {
    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance() {
        return instance;
    }

    // 생성자를 private으로 적용시켜 외부에서 인스턴스 생성을 막는다
    private SingletonService() {

    }
}

구조 패턴

클래스나 객체를 조합하여 더 큰 구조를 만들 수 있게 해줌

 

어댑터 패턴

  • 타사의 라이브러리를 호출부의 변경 없이 사용하고 싶을 경우
  • Array.AsList(), Collections.enumeration(), Collections.list() 등도 어댑터 패턴을 활용한 예시
@Service
@Primary //얘랑 동일한 빈이 있어도 이게 우선이다라고 해줘야(override)
public class MailAdapter implements MailSenderA {
    private final MailSolutionB mailSolutionB;

    @Override
    public void send(MailSolutionA.MailParam mailParam) {
        MailSolutionB.MailParam param = MailSolutionB.MailParam.builder().mailTitle(mailParam.getTitle())
                .mailBody(mailParam.getBody()).receiverEmail(mailParam.getEmail()).build();
        mailSolutionB.sendApi(param);
    }
}
  • 위 처럼 A -> B로 바꿀 때, 기존 호출부를 그대로 두고
  • A를 쏘는 것 처럼 보이지만 사실 B로 쏘는 것
  • Interface를 써야 구현체를 교체할 수 있음 그래야 primary로 오버라이딩이 가능
  • 모든 부분을 다 고치지 않고 한 곳만 고칠 때 좋음

 

브릿지 패턴

  • 기능의 구현 클래스를 런타임 때 자유롭게 지정이 가능
    • 주입을 set을 이용하여 동적으로 받음
  • 기능과 구현을 분리하여 구현이 변경되더라도 기능 클래스 부분에 대한 변경은 필요 없음
if (StringUtils.isNotEmpty(member.getAccountNumber())) {
    paymentService.setPaymentMethod(new CardMethodService()); // <<-- 서비스 주입을 런타임에
} else if (StringUtils.isNotEmpty(member.getPhoneNumber())) {
    paymentService.setPaymentMethod(new PhoneMethodService());
}
paymentService.pay1(order.getAmount(), member);

 

컴포지트 패턴

  • 일반적으로 직접 구현하지 않음
  • 클라이언트 전체와 부분을 구별하지 않고 동일한 인터페이스로 사용
  • 트리구조와 상당히 유사한 성격을 가지고 있다
  • 하지만 기능이 너무 다른 클래스들에는 공통 인터페이스를 제공하기 어려움

 

데코레이터 패턴

  • 일반적으로 직접 구현하지 않음
  • 기존 코드를 수정하지 않고도 행동을 확장 가능
  • 객체를 여러 데코레이터로 래핑하여 여러 행동들을 합성 가능
  • 데코레이터를 너무 많이사용하면 코드가 필요 이상으로 복잡해짐
  • file, buffered reader 등등 New로 만들어서 래핑해서 합성하는 것

 

퍼사드 패턴

  • 복잡한 로직들을 숨기고 간단한 인터페이스 함수만을 호출(추상화)
  • 우리는 이미 MVC패턴을 통해서 퍼사드 패턴을 알게 모르게 사용하고 있다.

 

플라이웨이트 패턴

  • 일반적으로 직접 구현하지 않음
  • 인스턴스가 필요할 때마나 매번 생성하는게 아니라 가능한 공유해서 메모리를 절약
    • 캐시, 레디스.. 디비 접근 최소화
  • String 객체를 생성하는 리터럴 방식의 String Constant Pool이 대표적인 플라이웨이트 패턴을 적용한 예시
  • https://www.baeldung.com/java-string-constant-pool-heap-stack
@Test
void 스트링_플라이웨이트패턴_테스트() {
    String str1 = "Hello";
    String str2 = "Hello";
    String str3 = new String("Hello");

    // 서로 같다!
    Assertions.assertSame(str1, str2);

    // 서로 다르다!
    Assertions.assertNotSame(str1, str3);

    // 서로 같다!
    Assertions.assertSame(str1, str3.intern());

    /*
        클래스가 JVM에 로드되면 모든 리터럴은 constant pool에 위치하게 된다.
        그리고 리터럴을 통해 같은 문자를 생성한다면 풀 안의 같은 상수를 참조하게 되는데 이를 String interning이라고 한다.
        String을 리터럴로 생성될 때 intern()이라는 메서드가 호출되고 이 intern() 메서드는
        constant pool에 같은 문자가 존재하는지 확인 후 존재한다면 그 참조 값을 가지게 된다.
     */
}
@Test
void Integer_플라이웨이트패턴_테스트() {
    Integer integer1 = Integer.valueOf("123");
    Integer integer2 = Integer.valueOf("123");

    // 서로 같다!
    Assertions.assertSame(integer1, integer2);


    Integer integer3 = Integer.valueOf("128");
    Integer integer4 = Integer.valueOf("128");

    // 서로 다르다!
    Assertions.assertNotSame(integer3, integer4);

    //직접 Integer.valueOf 함수 까보기!
    //-128 ~ 127 까지는 캐싱이라 같고 그 이외 값은 캐싱안하고 새롭게 인스턴스 생성이라 다름
}

 

프록시 패턴

  • SRP와 연관
  • 공통되는 로직 빼서 모듈화
  • AOP(aspect oriented programming; cglib etc..)
    • OOP로 독립적으로 분리하기 어려운 부가 기능을 모듈화 하는 방식
    • oop를 더 oop스럽게 해주는 방법
    • 객체지향을 더 객체지향스럽게 만들어 주는 프로그래밍
  • transactional, cache, test에 사용하는..
  • spring 큰 특징 : DI, AOP(aspect), PSA(portable service abstraction)
import com.example.pattern.order.service.OrderService;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.objenesis.SpringObjenesis;

@Configuration
public class ProxyConfig {
    private final SpringObjenesis objenesis = new SpringObjenesis();

    @Bean
    @Primary //order service말고 프락시를 빈으로 등록하도록 해야 aop 적용
    public OrderService orderServiceProxy() {
        return (OrderService) createCGLibProxy(OrderService.class, new MethodCallLogInterceptor());
    }

    private Object createCGLibProxy(Class<? extends Object> targetClass, MethodInterceptor interceptor) {
        // Create the proxy
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(interceptor);
        return enhancer.create();
    }
}

행동 패턴

반복적으로 사용되는 객체들의 커뮤니케이션을 패턴화, 결합도를 최소화 하는 것이 목적

 

책임 연쇄 패턴

스프링 필터 사용(필터 체인)

  • 검증 절차는 자유롭게 추가될 수 있어야 하고, 순서도 자유롭게 변경할 수 있어야 할 때 
  • 기존 클라이언트 코드를 수정하지 않고 앱에 새 핸들러를 도입 가능
@Configuration
public class FilterConfig {
    //    @Bean
//    @Order(0)
    public FilterRegistrationBean userAgentCheckFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new UserAgentCheckFilter());
        registrationBean.addUrlPatterns("/orders/*");
        return registrationBean;
    }

    //    @Bean
//    @Order(1)
    public FilterRegistrationBean memberCheckFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new MemberCheckFilter());
        registrationBean.addUrlPatterns("/orders/*");
        return registrationBean;
    }
}

 

커맨드 패턴

  • 잘 사용하지 않음
  • 기존 클라이언트 코드를 수정하지 않고 새로운 커맨드들을 도입 가능
  • 요청부와 동작부를 분리시켜 주기 때문에 결합도를 낮출 수 있음
  • 보통 비동기
@Test
void 혜택_발송_스레드_테스트() throws InterruptedException {
    int numberOfThreads = 5;
    CountDownLatch latch = new CountDownLatch(numberOfThreads);
    //ExecutorService 객체가 Invoker를 의미
    ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
    for (int i = 0; i < numberOfThreads; i++) {
        //Runnable 구현 객체가 Command를 의미;
        // runnable interface 특징? 구현할 함수가 1개, callable과 디르게 반환타입이 없음
        //CouponApiService 객체가 Receiver를 의미
        Runnable doThread = new CouponService(new CouponApiService());
        //위 대신 lambda로 대체가능
        executorService.execute(() -> {
            doThread.run();
            latch.countDown();
        });
    }
    latch.await();
}

Runnable -> void

Callable -> return 있음

 

반복자 패턴

  • 잘 사용하지 않음
  • 혜택이 아이템을 어떻게 구현하였는지 호출하는 쪽에서는 알 필요가 없음
    • 즉 캡슐화가 잘 되어 있음
  • 내부 구현을 외부로 노출시키지 않으면서도 모든 항목에 접근 가능
  • 적용 시 덜 효율적이거나 과도하지는 않은지 확인

 

옵저버 패턴

  • 한 객체의 상태가 변경되어 다른 객체들을 변경해야 할 필요성이 생겼을 때 사용(Pub/Sub 패턴이라고도 함)
  • message push, pull 하는 방식을 통해 결합도를 낮출 수/없앨 수 있음 
    • rabbitMQ, kafka 등
  • 느슨한 결합으로 객체간의 의존성 제거
  • 너무 많이 사용하게 되면 상태 관리가 힘듦
private final ApplicationEventPublisher eventPublisher;

//함수 내부에서 호출 publish
   eventPublisher.publishEvent(new OrderEvent(order.getId(), OrderType.CREATE));
   
//

import com.example.pattern.order.model.OrderEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Async
public class OrderListener {
    @EventListener
    public void onApplicationEvent(OrderEvent event) {
        log.info("주문이벤트 Published [orderId : {}, orderType : {}]", event.getOrderId(), event.getOrderType().toString());
        log.info("이후 리뷰 알림 발송이 시작됩니다.");
        log.info("이후 정산 API 호출이 시작됩니다.");
    }
}

 

중재자 패턴

  • 옵저버 패턴
    • 1개의 publish에 대해 N개의 subcriber가 존재하고, observer가 pulling 이나 push방식을 통해 관리
  • 중재자 패턴
    • M개의 publisher와 N개의 subcriber 사이에서 1개의 mediator를 통해 통신

 

메멘토 패턴

  • 상태정보 저장 관련 메모리에 들고 있을 일 없음; 잘 사용하지 않음
  • 캡슐화를 위반하지 않고 객체의 state 스냅샷을 생성할 수 있음
  • 메멘토를 너무 자주 생성하면 많은 Ram을 소모할 수 있음

 

상태 패턴

  • 잘 사용하지 않음

 

전략 패턴

  • 하나의 메시지와 책임을 정의하고 이를 수행할 수 있는 다양한 전략을 만든 후, 다형성을 통해 전략을 선택해 구현을 실행
  • 유사한 패턴들
    • 팩토리, 추상 팩토리, 브릿지, 커맨드 패턴 등 이미 사용 중
  • 특징
    • OCP 준수, 하지만 과도하게 복잡해질 수 있음

 

템플릿 메서드 패턴

  • 잘 사용하지 않음, 요즘은 implement
    • interface는 내부 instance를 둘 수 없음 -> 필요 시 abstract class 를 사용
  • 상속을 통해 슈퍼 클래스의 기능을 확장할 때 사용하는 대표적인 방법
  • 변하지 않는 기능은 슈퍼 클래스에 만들어 두고, 확장할 기능은 서브 클래스에서 만듦
  • 단점
    • 알고리즘 변경 시 거의 모든 클래스에 수정이 가해질 수 있음
    • 상속의 단점.. 결합이 커서 
public abstract class Review { //abstract 사용예시
    public void review() {
        //부모 클래스에서 알고리즘의 골격을 정의
        login();
        selectBooks();
        putContent();
        selectEvaluation();
    }

    public void login() {
        log.info("로그인 성공!");
    }

    public void selectBooks() {
        log.info("리뷰 대상 상품 선택");
    }

    public void selectEvaluation() {
        log.info("별점 선택");
    }

    public abstract void putContent(); //

}

 

방문자 패턴

  • 잘 사용하지 않음
  • 맴버는 맴버 클래스로, 리워드는 리워드 클래스로.. 둘을 섞지 않는다
  • OCP : 다른 클래스를 변경하지 않으면서 새로운 행동 도입 가능
  • SRP : 같은 행동의 여러 버전을 같은 클래스로 이동할 수 있음
public class PointBenefit implements Benefit {
    @Override
    public void getBenefit(GoldMember member) {
        log.info("골드 멤버를 위한 포인트 제공 혜택");
    }

    @Override
    public void getBenefit(VIPMember member) {
        log.info("VIP 멤버를 위한 포인트 제공 혜택");
    }

    @Override
    public void getBenefit(SilverMember member) {
        log.info("실버 멤버를 위한 포인트 제공 혜택");
    }
}
public class GoldMember implements Member {
//    public void point() {
//        log.info("골드 멤버를 위한 포인트 제공 혜택");
//    }
//    public void discount() {
//        log.info("골드 멤버를 위한 할인 혜택");
//    }

    @Override
    public void getBenefit(Benefit benefit) {
        benefit.getBenefit(this);
    }
}
728x90
반응형
반응형

환경: java 8 ++

 

stream을 사용하여 리스트의 홀수번째(index 기준 0, 2, 4)에 있는 원소를 콘솔에 찍어본다고 가정하자.

아래와 같이 짜야지라고 쉽게 생각할 수 있다.

variable used in lambda expression should be final or effectively final

근데 위와 같은 에러가 난다.

그 이유는 아래와 같다. 자세한 내용을 알려면 람다 캡쳐링과 그 원리에 대해 이해해야한다.

The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems.

람다식 내부에서는 람다식 안에서 정의된 변수가 아닌 외부 변수에 접근할 수 있는데 이를 람다 캡쳐링이라고 한다.

스트림은 여러 thread의 병렬처리를 염두하고 만들어졌다. 즉 별도의 스레드에서 실행할 수 있다. 그렇다면 어떻게 기존 스레드의 값을 참조하여 쓸 수 있을까? 기존 스레드의 작업이 종료되었을 수도 있는데 말이다.

람다 캡쳐링이 일어날 때 데이터의 참조값(call by reference)이 아닌 데이터 값 그 자체(call by value)를 복사하여 자신의 스택에 두고 작업을 한다. 그렇기 때문에 값이 변경될 여지가 있는 변수는 사용할 수 없고 final에 준하는(effectively final) 변수만 람다 안에서 사용할 수 있다.

반면 heap에 저장된 값은 thread끼리 공유하고 있기 때문에 언제든지 구할 수 있으므로 변경하더라도 에러가 나지 않는다.

Java의 스트림 API에서 외부 변수를 사용할 때 해당 변수가 effectively final해야 하는 이유는 다음과 같습니다:

1. 스레드 안전성

  • 스트림의 연산은 종종 병렬로 실행되며, 외부 변수가 여러 스레드에서 동시에 접근될 수 있습니다. 이를 방지하기 위해 외부 변수가 변경되지 않도록 보장해야 합니다.

2. 불변성

  • 외부 변수가 effectively final이면, 그 값이 변경되지 않는 것을 보장합니다. 이로 인해, 람다 표현식이나 메서드 참조가 이 변수를 안전하게 사용할 수 있습니다. 불변성을 유지함으로써 예측 가능한 동작을 보장합니다.

3. 람다 캡처

  • Java의 람다 표현식은 외부 변수를 캡처할 수 있지만, 캡처된 변수는 내부적으로 복사되어 사용됩니다. 만약 이 변수가 변경 가능하다면, 예상치 못한 결과를 초래할 수 있습니다. 이를 방지하기 위해 effectively final 조건이 필요합니다.`

 

위 함수의 에러는 여러 방법으로 수정할 수 있다.

1. AtomicInteger를 사용하여 수정

2. list/array를 이용해 수정

 

2번 방식으로 수정하다 보니 신기했던 것은 단순 값이 변경된다는 것에 초점을 둘게 아니라 final이면 된다는 점에 초점을 뒀어야 한다는 것이다. 일반적으로 final이라고 하면 불변, 즉 string이나 int인 경우 값이 바뀌면 안 된다고 인식하기 때문에 '값의 변경'에 나도 모르게 초점이 갔는데, collection 같은 경우에는 final이어도 값이 변동(추가 혹은 수정)될 수 있다!

전체 변경; 재할당
일부 변경

즉 참조값이 바뀌면 안 되고 같은 참조값 안에서의 변경은 된다(final but mutable)

final --> You cannot change the reference to the collection (Object). You can modify the collection / Object the reference points to. You can still add elements to the collection
immutable --> You cannot modify the contents of the Collection / Object the reference points to. You cannot add elements to the collection.

실제로 list를 final로 선언하고 값을 수정할 때 별문제 없이 작동한다.


위에서 말한 것과 같이 heap에 있는 변수는 수정이 가능하다.

stack
heap

 


 

Arrays.asList는 값 추가 안됨; size가 정해진 list라서: https://www.baeldung.com/java-list-unsupported-operation-exception

collection의 final이란? https://stackoverflow.com/questions/26500423/what-does-it-mean-for-a-collection-to-be-final-in-java

https://www.geeksforgeeks.org/final-arrays-in-java/

람다 캡쳐링: https://perfectacle.github.io/2019/06/30/java-8-lambda-capturing/

728x90
반응형
반응형

jdk가 무료 버전이 있고 유료버전이 있는데, 사용하던 jdk 11, 17이 oracle jdk인 듯하여 openJdk로 교체해본다.

https://auramin.tistory.com/25

 

오라클 Java SE(Standard Edition) 유,무료 버전

오라클 정책 변경으로 유료버전의 오라클 Java SE(JDK:Java Development Kit/  JRE:Java Runtime Environment)를 다운로드 또는 업데이트 할 경우 라이센스 계약 및 비용을 지불해야한다 합니다. 오라클 정책을..

auramin.tistory.com

 

현재 나의 jdk현황은 아래와 같다.

 

1.7.0_80은 무료 버전이라 그냥 두기로 하였고

1.7.80.15 버전은 지워야 할 것 같아서 아래와 같은 명령어를 통해 삭제 후, 1.7.0_80 버전을 심볼릭으로 걸어주었다.

sudo rm -fr /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin
sudo rm -fr /Library/PreferencesPanes/JavaControlPanel.prefPane
sudo rm -fr ~/Library/Application\ Support/Oracle/Java

-----------------
sudo rm -rf “/Library/Internet Plug-Ins/JavaAppletPlugin.plugin”
sudo ln -s /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk /Library/Internet Plug-Ins/JavaAppletPlugin.plugin

 

openJdk11, 17 버전은 mac을 사용하면 brew 명령어를 통해 설치가 가능하나, 시도해본 결과 의도한 대로 잘 되지 않아서 uninstall 하고 수동으로 설치하였다.

설치 파일은 아래 사이트에서 다운로드하였고 zip을 풀어준다.

https://jdk.java.net/archive/

 

Archived OpenJDK GA Releases

Archived OpenJDK General-Availability Releases This page is an archive of previously released builds of the JDK licensed under the GNU General Public License, version 2, with Classpath Exception. WARNING: These older versions of the JDK are provided to he

jdk.java.net

https://adoptium.net/marketplace

 

아래처럼 기존 자바를 지우고

cd /Library/Java/JavaVirtualMachines
sudo rm -rf jdk-17.0.3.1.jdk

zip을 풀어준 폴더를 아래 경로로 옮긴다.

sudo mv jdk-17.0.2.jdk/ /Library/Java/JavaVirtualMachines/

 

잘 되었는지 확인

/usr/libexec/java_home -V

 

참고로 잘못된 파일을 받은 경우(나의 경우는 Mac/AArch64를 받았었다) 위 명령어로 잡히지 않는다.

success?

어쨌건 위 명령어로 잘 깔린 것을 확인하고 intellij의 프로젝트를 빌드해보는데.. 아래와 같은 에러가 난다.

mac 시스템 환경설정 > 보안 및 개인 정보보호 메뉴로 가서 팝업창 하단 아래쪽에 뜬 아래 문구를 허용을 눌렀다.

확인된 개발자가 등록한 응용 프로그램이 아니기 때문에 ~~ 사용을 차단했습니다. 

하지만 그래도 gradle clean 조차 failed 하길래 아래와 같이 우선 owner설정을 바꾸고(이게 무슨 의미가 있는지는 확실하게 모르겠다. 단지 기존 jdk가 저렇게 설정이 되어 있어서 똑같이 맞춰봤다)

sudo chown -R root:wheel ./jdk-17.0.2.jdk

 

intellij에서 project close -> reopen -> invalid cache를 하니 정상적으로 실행되었다..

 


참고

https://engineering.linecorp.com/ko/blog/line-open-jdk/

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[google admob] ssv 콜백 적용  (0) 2023.01.06
[Date] java8 이하에서 날짜 timezone 변환  (0) 2022.11.04
[java] for loops and performance  (0) 2022.08.04
[jmh] benchmark test  (0) 2022.08.04
[powermock] mock static method  (0) 2022.07.21
반응형

java를 사용하여 배열 안의 원소 loop을 도는 많은 방법들에 대해 정리한다.

 

old school for loop

List<Student> studentList = new ArrayList<>();
studentList.add(a);
studentList.add(b);
studentList.add(c);

for (int i = 0; i < studentList.size(); i++) {
  System.out.println("Roll number: " + studentList.get(i).getRollNumber());
}

advanced for loop

for (Student st : studentList) {
   System.out.println("Roll number: " +  st.getRollNumber());
}

while, hasNext

Iterator<Object> it = list.iterator();
while (it.hasNext()) {
  Object o = it.next();
  // do stuff
}
  old for advanced for
since jdk1 jdk5
index increasing custom가능(2씩 증가 등); 역순 가능 무조건 1씩 증가만 가능; 역순 불가
index approach index 접근 가능 index 접근 불가
usage 어떠한 셀 수 있는 container object에 사용 가능 iterable interface를 구현한 구현체만 사용가능

위 세 방법 모두 성능상에 큰 차이는 없고, 굳이 따지자면 old for loop이 index의 객체를 탐사해야 하니(Object.get(i)) 조금 더 느릴 수 있다는 글이 있다. advance for loop으로 짜면 컴파일러가 while hasNext 문으로 변환할 거라 두 방법은 사실 거의 같은 거라고 볼 수 있다.

단순 1씩 증가한 loop이라면 advanced for loop을 사용하는 게 보기에도 더 좋을 듯하다.

라는 의견을 보았으나.. 믿기 힘든 상황이 되었다.(아래 테스트 참고)

 

Collection.foreach

List<Integer> list = Arrays.asList(1,2,3,4);
list.forEach(System.out::println);
// Output
1
2
3
4

Collection.stream.foreach

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().forEach(System.out::println);
// Output
1
2
3
4
  foreach stream.foreach
iterator collection iterator사용 collection을 stream으로 바꾸고 stream의 iterator 사용
order 순서 보장 순서 보장 않음
exception 구조 변화가 있으면 바로 exception 반환 나중에 exception반환
lock synchronized collection에 대한 작업이 중첩되면 우선 lock을 걸고 작업이 끝날 때 까지 기다림  lock없이 바로 접근

이 두 foreach의 성능을 비교하자면 stream.foreach가 스트림으로 변환 수 iterator를 사용하므로 더 느릴 것이라는 의견이 많았다.


benchmark test: jdk18, gradle7.4, jmh1.29, mac intel i7

그냥 글로 보기에는 와닿지 않아서 간단하게나마 벤치마크 테스트를 돌려본다.

아래 소스 외, 나머지는 기본 세팅으로 진행했다.

private static List<Integer> list = new ArrayList<>();
static {
    for(int i=0; i < 1_000_000; i++) {
        list.add(i);
    }
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingStream(Blackhole blackhole) {
    list.stream().forEach(i -> blackhole.consume(i));
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingIterator(Blackhole blackhole) {
    //list.listIterator().forEachRemaining(i -> blackhole.consume(i));
    list.forEach(i -> blackhole.consume(i));
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingAdvancedForLoop(Blackhole blackhole) {
    for(Integer i : list) {
        blackhole.consume(i);
    }
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingSimpleForLoop(Blackhole blackhole) {
    for(int i = 0; i < list.size() ; i++) {
        blackhole.consume(i);
    }
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingWhileHasNext(Blackhole blackhole) {
    Iterator<Integer> itr = list.iterator();
    while(itr.hasNext()){
        blackhole.consume(itr.next());
    }
}

benchmark result

throughput mode(1초당 진행횟수)에서는 점수가 클수록 성능이 좋은 거다.

구관이 명관인가. (의외로) old for loop이 제일 성능이 좋았다.. 다른 것보다 거의 두배 정도?(정확히 두배라고 말할 순 없지만..) 그다음이 while문이라니.. 나머지는 비슷비슷한 것 같은데 어쨌건 꼴찌는 Collection.foreach였다..ㅋㅋㅋ 어느 글을 믿어야 하나,, 신기방기 하다.

 

jmh modes: http://hg.openjdk.java.net/code-tools/jmh/file/6cc1450c6a0f/jmh-core/src/main/java/org/openjdk/jmh/annotations/Mode.java

 

code-tools/jmh: 6cc1450c6a0f jmh-core/src/main/java/org/openjdk/jmh/annotations/Mode.java

view jmh-core/src/main/java/org/openjdk/jmh/annotations/Mode.java @ 929:6cc1450c6a0f profilers: perfasm, warn about low event count. author shade date Thu, 24 Jul 2014 00:21:17 +0400 parents 8d5845e7d89a children 0ca62574e95f line source /* * Copyright (c)

hg.openjdk.java.net

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[Date] java8 이하에서 날짜 timezone 변환  (0) 2022.11.04
[mac] oracle jdk -> open jdk java교체하기  (0) 2022.08.22
[jmh] benchmark test  (0) 2022.08.04
[powermock] mock static method  (0) 2022.07.21
[java] jvm, java, gc  (0) 2022.02.24
반응형

단순한 자바 성능 테스트를 하기 위한 툴로 benchmark가 있다. 간단하게 세팅하고 실행해본다.

환경: java 11 / gradle 7.4

 

1. intellij에서 gradle로 프로젝트 생성

 

2. 파일 구조 아래와 같이 만들기

main/test 폴더 삭제

 

3. build.gradle에 plugin 추가

plugins {
    id 'java'
    id "me.champeau.jmh" version "0.6.6"
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}

test {
    useJUnitPlatform()
}

dependency에 추가하는 게 아니었다..

 

4. benchmark code 작성

private static List<Integer> list = new ArrayList<>();
static {
    for(int i=0; i < 1_000_000; i++) {
        list.add(i);
    }
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingStream(Blackhole blackhole) {
    list.stream().forEach(i -> blackhole.consume(i));
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingIterator(Blackhole blackhole) {
    list.listIterator().forEachRemaining(i -> blackhole.consume(i));
}

 

5. 테스트 실행

터미널에 gradle jmh로 실행(intellij run 버튼이나 다른 걸로 하면 에러남)

이렇게 그냥 실행하면 jmh 기본 설정 값으로 테스트를 진행한다(warmup을 5번 하고 등등..).

기본적인 테스트임에도 시간이 상당히 걸리는데, 아래 readme를 참고하면 옵션들을 바꿀 수 있어 다양한 값으로 테스트가 가능하다.

공통 룰에 대한 옵션은 build.gradle의 jmh{}(블록) 안에다 작성하면 된다. 그 외 각 테스트 별로 어노테이션을 통한 개별 옵션을 줄 수도 있다.


jmh README: https://github.com/melix/jmh-gradle-plugin#configuration-options

 

GitHub - melix/jmh-gradle-plugin: Integrates the JMH benchmarking framework with Gradle

Integrates the JMH benchmarking framework with Gradle - GitHub - melix/jmh-gradle-plugin: Integrates the JMH benchmarking framework with Gradle

github.com

 

https://mkyong.com/java/java-jmh-benchmark-tutorial/

 

Java JMH Benchmark Tutorial - Mkyong.com

- Java JMH Benchmark Tutorial

mkyong.com

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[mac] oracle jdk -> open jdk java교체하기  (0) 2022.08.22
[java] for loops and performance  (0) 2022.08.04
[powermock] mock static method  (0) 2022.07.21
[java] jvm, java, gc  (0) 2022.02.24
[keyword] transient in serialization  (0) 2022.02.16

+ Recent posts