달력

11

« 2024/11 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

'JAVA'에 해당되는 글 3

  1. 2019.02.13 Singleton Pattern(싱글톤 패턴)
  2. 2019.02.10 람다식 기본
  3. 2018.07.08 MAVEN 기초
2019. 2. 13. 22:10

Singleton Pattern(싱글톤 패턴) JAVA2019. 2. 13. 22:10

자바에서는 new 키워드를 이용해서 객체를 생성한다. 고로 한번만 생성하면 될 객체를 여러번 생성하게 되면 그만큼 메모리에 낭비가 생길 수가 밖에 없다. 때문에 싱글톤 패턴을 적용하여 객체를 한 번만 생성하도록 하는 경우가 있다. 


public class Test {
public static void main(String[] args) {
NonSingleton n1 = new NonSingleton();
NonSingleton n2 = new NonSingleton();
System.out.println(n1 == n2); // false
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // true
}
}


class NonSingleton {
public NonSingleton() {} 
}


class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {} 
public static Singleton getInstance() {
return INSTANCE;
}
}

위 예제에서 n1과 n2는 각기 다른 객체다. 반면, s1과 s2는 같은 객체라는 점을 알 수 있다. 


문제(?)는 내가 지금까지 알던 이 패턴에 허점이 있다는 것. 첫째는 해당 인스턴스를 사용할 필요가 없을 때도 생성한다는 점(Lazy implementation으로 해결 가능), 둘째는 직렬화 시에는 또 다시 새로운 객체가 생성된다는 점이고(readResolve() 메서드를 구현해서 해결 가능), 셋째는 아래 코드와 같이 reflection을 이용하면 기껏 만들어 놓은 싱글톤 패턴을 무시할 수 있다는 것이다.


import java.lang.reflect.Constructor;


public class Test {

public static void main(String[] args) {

Singleton s3 = Singleton.getInstance();

Singleton s4 = null;

try {

Constructor<?>[] constructors = 

                                    Singleton.class.getDeclaredConstructors();

for ( Constructor<?> constructor : constructors ) {

constructor.setAccessible(true);

s4 = (Singleton)constructor.newInstance();

}

} catch (Exception ex) {

System.out.println(ex);

}

System.out.println(s3 == s4); // false

}

}



class Singleton {

private static final Singleton INSTANCE= new Singleton();

private Singleton() {} 

public static Singleton getInstance() {

return INSTANCE;

}

}



그래서 어떻게 하면 되느냐, enum을 사용하면 된다. 아래 처럼


public class Test {

public static void main(String[] args) {

Singleton s3 = Singleton.INSTANCE;

Singleton s4 = Singleton.INSTANCE;

System.out.println(s3==s4);  // true

}


public enum Singleton {

INSTANCE;

}     

        * 예시라서 이렇게 썼지만 실제로 이 부분은 다른 어딘가에 있을 것이다

        * 그 외에 변수나 메서드 등등 필요한게 있을 것이다

}


매우 심플해지지 않았는가? 직렬화 구현도 필요 없고, reflection으로부터도 안전하며, 객체가 여러개 생기는 문제도 없는 해결책이다. 

(단, 직렬화시 멤버변수는 잃게 된다고 한다....https://docs.oracle.com/javase/6/docs/platform/serialization/spec/serial-arch.html#6469)


참고: https://dzone.com/articles/java-singletons-using-enum

'JAVA' 카테고리의 다른 글

람다식 기본  (0) 2019.02.10
MAVEN 기초  (0) 2018.07.08
:
Posted by SK
2019. 2. 10. 10:37

람다식 기본 JAVA2019. 2. 10. 10:37

Java8부터 도입된 람다표현식. 회사에서는 Java6를 쓰고 있다보니 쓸 일이 없어서 무관심 하다가, 신규 서버로 전환 후 일괄 버전업이 되어 이제 쓸 수 있게 되었다. 내친 김에 간단히 정리해본다.


람다표현식이 중요한게 아니라 함수형 프로그래밍의 사상을 아는게 더 중요하다고 생각되는데, 귀찮으니까 링크로 대체...

위키피디아: 함수형프로그래밍 설명

https://ko.wikipedia.org/wiki/%ED%95%A8%EC%88%98%ED%98%95_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D


람다식을 이용하지 않고 인터페이스를 구현하는 경우, 원래는 아래처럼 사용했었다.

클래스로 implements한 뒤 사용하거나, 일회성이라면 익명함수로 바로 구현하거나.


public class Test {

public static void main(String[] args) {

Food food0 = new Banana();

food0.eat("banana");

Food food1 = new Food() {

@Override

public void eat(String target) {

System.out.println("eat:"+target);

}

};

food1.eat("banana");

}

}


interface Food {

void eat(String target);

}


class Banana implements Food{

@Override

public void eat(String target) {

System.out.println("eat:"+target);

}

}



그런데 자바8 이후 람다식이 가능해지면서 더 간단하게 표현이 가능해졌다. 아래와 같이... (익명함수 구현시 7줄이던게 2줄로)


public class Test {

public static void main(String[] args) {

Food food3 = (target) -> System.out.println("eat:"+target);

food3.eat("banana");

}

}

@FunctionalInterface

interface Food {

void eat(String target);

}



이것이 어떻게 가능한고 하니... 자바가 함수형 프로그래밍 언어가 아니다보니 람다를 도입하기 위해 함수형 인터페이스, 즉 '단 하나의 추상 메서드만 가진' 인터페이스라는 개념을 만들었다. 

이 때문에 다음과 같은 추론이 가능하게 된다. (위 예시 기준)

1. new Food() 가 필요 없다. Food 타입을 구현하려는 것이 명확하기 때문에 생략 가능하다.

2. eat()가 필요 없다. Food 인터페이스에는 단 하나의 메서드만이 있기 때문에 구현하고자 하는 메서드가 해당 메서드임이 분명하다.

3. String이 필요 없다. 메서드가 하나이므로, 해당 메서드가 받는 파라미터 수와 타입이 정해져있어 파라미터타입 명시가 불필요하다.


확실히 편해졌다. 그런데 인터페이스에 항상 한 메서드만 있어야 한다는데... 처음 구현할 때야 그렇다 치지만, 나중에 누군가 메서드를 추가하면 어떻게 될까? 물론 컴파일이 되지 않고 에러가 난다. 하지만 좀 더 명확히 하기 위해 해당 인터페이스에 @FunctionalInterface 어노테이션을 붙여주자.


람다식과 함께 새롭게 등장한 녀석중 Stream이 있는데, 람다로 인해 이 API가 빛을 발한다. 이건 나중에 포스팅.



'JAVA' 카테고리의 다른 글

Singleton Pattern(싱글톤 패턴)  (0) 2019.02.13
MAVEN 기초  (0) 2018.07.08
:
Posted by SK
2018. 7. 8. 11:27

MAVEN 기초 JAVA2018. 7. 8. 11:27

사내에서는 인터넷 차단이 되어있기 때문에 메이븐이나 그래들 같은 건 사용할 수 없다. 

다만, 책 등을 보면서 혼자 뭘 해보려하면 다들 기본으로 깔고 들어가기 때문에 기초정도는 알아둘 필요가 있을 것 같다.



메이븐 프로젝트 시작해보기


메이븐 프로젝트를 생성하는 과정은 다음과 같다

1. 이클립스에서 신규 프로젝트를 만들때 Maven Project를 선택한다

2. 처음 설정 창에서 Create a simple project에 체크해주고 Next 클릭

3. 다음 설정 창에서 Group Id에 com.test , Artifact Id에 testPjt로 적고 Finish 클릭

                    

기본적인 구조는 아래와 같다.

PROJECT/

src/main/java                    <- 일반 프로젝트의 src에 해당하는 곳이다(프로덕션 코드)

src/main/resources            <- 프로덕션 코드에서 사용할 자원들이 위치한다

src/test/java                     <- 테스트 코드

src/test/resources              <- 테스트 코드의 자원

src/ 

main/

webapp/               <- 웹 프로젝트에서 JSP, JS 등의 웹 애플리케이션 컨텐츠가 위치하는 곳에 해당한다 (WEB-INF 등)

target/                             <- 메이븐 빌드 시, 빌드 된 jar 파일 등이 생성된다

pom.xml                           <- 메이븐의 설정파일


위 구조에서 제일 중요한 것은 pom.xml이다. 여기서 필요한 의존성을 설정할 수 있다. 

예를 들어 JUnit을 추가로 사용한다고 하면, 

<dependencies>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

</dependency>

</dependencies>

위와 같은 코드만 pom.xml에 추가하면된다.(이클립스에서는 pom.xml을 더블클릭하여 오픈하고, 하단 탭에서 pom.xml 선택)  
그러면 사용자 폴더(나의 경우는 C:\Users\user\.m2\repository)를 로컬 레파지토리로 하여 해당 라이브러리를 다운받고 프로젝트에 추가한다.
C:\Users\user\.m2\repository에 가보면 <groupId>\<artifactId>\<version>으로 된 폴더가 생성되어 있고 그 안에 라이브러 관련 파일이 있는 것을 확인할 수 있다. 
필요한 라이브러리는 <dependencies></dependencies> 태그 안에 계속 추가하면 되는데, 추가할 내용은 

https://search.maven.org에 접속하여 원하는 라이브러리 검색후 클릭해보면 dependency information이라는 부분에 무슨 내용을 넣어야하는지 친절하게 나와있다.


그런데 JUnit을 추가시켜보면 이클립스 기준으로 Maven Dependencies에 JUnit의 jar뿐 아니라 hamcrest-core라는 녀석도 추가되어 있는 것을 볼 수 있다.

이 jar는 JUnit을 사용하기 위해서 필요한 라이브러리로 Maven에서 JUnit을 추가할 때 자동으로 추가한 녀석이다. 

이클립스에서 pom.xml을 열어보면, Dependency Hierarchy탭에 각 라이브러리간의 상관관계를 한눈에 확인 할 수 있다.

또 로컬 레파지토리로 이동하여 해당하는 라이브러리 폴더를 뒤져보면 <library-name>.pom 파일이 있다.

이 파일을 메모장으로 열어서 <dependencies></dependencies> 태그 내용을 살펴보면 해당 라이브러리가 종속되어 있는 것이 무엇인지 보인다.

(이를 transitive dependencies 의존성 전이 라고 한다)


만약 프로젝트에서 JUnit의 다른 특정한 버전이 필요하다고 한다면 어떻게 될까?

그 때는 <version></version> 태그 안에 필요한 버전만 적어 넣으면 maven에서 알아서 다운로드하여 프로젝트에 주입시킨다.



'JAVA' 카테고리의 다른 글

Singleton Pattern(싱글톤 패턴)  (0) 2019.02.13
람다식 기본  (0) 2019.02.10
:
Posted by SK