728x90

Whiteship Java live study season 01

현재 네이버 블로그에 정리되어 있어 링크로 대체 합니다.
pc 환경에 최적화 되어 있습니다.


WEEK 01 :: JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

  • JVM 이란 무엇인가
  • 컴파일 하는 방법
  • 실행하는 방법
  • 바이트코드란 무엇인가
  • JIT 컴파일러란 무엇이며 어떻게 동작하는지
  • JVM 구성요소
  • JDK 와 JRE의 차이

WEEK 02 :: 자바 데이터 타입, 변수 그리고 배열

  • 프리미티브 타입 종류와 값의 범위 그리고 기본 값
  • 프리미티브 타입과 레퍼런스 타입
  • 리터럴
  • 변수 선언 및 초기화하는 방법
  • 변수의 스코프와 라이프타임
  • 타입 변환, 캐스팅 그리고 타입 프로모션
  • 1차 및 2차 배열 선언하기
  • 타입 추론, var

WEEK 03 :: 연산자

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 논리 연산자
  • instanceof
  • assignment(=) operator
  • 화살표(->) 연산자
  • 3항 연산자
  • 연산자 우선 순위
  • (optional) Java 13. switch 연산자

WEEK 04 :: 제어문

  • 선택문
  • 반복문
  • 과제
    • live-study 대시 보드를 만드는 코드를 작성하세요.
      • 깃헙 이슈 1번부터 18번까지 댓글을 순회하며 댓글을 남긴 사용자를 체크 할 것.
      • 참여율을 계산하세요. 총 18회에 중에 몇 %를 참여했는지 소숫점 두자리가지 보여줄 것.
      • Github 자바 라이브러리를 사용하면 편리합니다.
      • 깃헙 API를 익명으로 호출하는데 제한이 있기 때문에 본인의 깃헙 프로젝트에 이슈를 만들고 테스트를 하시면 더 자주 테스트할 수 있습니다.
    • LinkedList를 구현하세요
      • LinkedList에 대해 공부하세요.
      • 정수를 저장하는 ListNode 클래스를 구현하세요.
      • ListNode add(ListNode head, ListNode nodeToAdd, int position)를 구현하세요.
      • ListNode remove(ListNode head, int positionToRemove)를 구현하세요.
      • boolean contains(ListNode head, ListNode nodeTocheck)를 구현하세요.
    • Stack을 구현하세요.
      • int 배열을 사용해서 정수를 저장하는 Stack을 구현하세요.
      • void push(int data)를 구현하세요.
      • int pop()을 구현하세요.
    • 앞서 만든 ListNode를 사용해서 Stack을 구현하세요.
      • ListNode head를 가지고 있는 ListNodeStack 클래스를 구현하세요.
      • void push(int data)를 구현하세요.
      • int pop()을 구현하세요.
    • (optional) Queue를 구현하세요.
      • 배열을 사용해서 한번
      • ListNode를 사용해서 한번.

WEEK 05 :: 클래스

  • 클래스 정의하는 방법
  • 객체 만드는 방법 (new 키워드 이해하기)
  • 메소드 정의하는 방법
  • 생성자 정의하는 방법
  • this 키워드 이해하기
  • 과제 (Optional)
    • int 값을 가지고 있는 이진 트리를 나타내는 Node 라는 클래스를 정의하세요.
    • int value, Node left, right를 가지고 있어야 합니다.
    • BinrayTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs(Node node)와 dfs(Node node) 메소드를 구현하세요.
    • DFS는 왼쪽, 루트, 오른쪽 순으로 순회하세요.

WEEK 06 :: 상속

  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

WEEK 07 :: 패키지

  • package 키워드
  • import 키워드
  • 클래스패스
  • CLASSPATH 환경변수
  • -classpath 옵션
  • 접근지시자

 

728x90

 

WEEK 08 :: 인터페이스

  • 인터페이스 정의하는 방법
  • 인터페이스 구현하는 방법
  • 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
  • 인터페이스 상속
  • 인터페이스의 기본 메소드 (Default Method), 자바 8
  • 인터페이스의 static 메소드, 자바 8
  • 인터페이스의 private 메소드, 자바 9

WEEK 09 :: 예외 처리

  • 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • Exception과 Error의 차이는?
  • RuntimeException과 RE가 아닌 것의 차이는?
  • 커스텀한 예외 만드는 방법

WEEK 10 :: 멀티쓰레드 프로그래밍

  • Thread 클래스와 Runnable 인터페이스
  • 쓰레드의 상태
  • 쓰레드의 우선순위
  • Main 쓰레드
  • 동기화
  • 데드락

WEEK 11 :: Enum

  • enum 정의하는 방법
  • enum이 제공하는 메소드 (values()와 valueOf())
  • java.lang.Enum
  • EnumSet

WEEK 12 :: 애노테이션

  • 애노테이션 정의하는 방법
  • @retention
  • @target
  • @documented
  • 애노테이션 프로세서

WEEK 13 :: I/O

  • 스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
  • InputStream과 OutputStream
  • Byte와 Character 스트림
  • 표준 스트림 (System.in, System.out, System.err)
  • 파일 읽고 쓰기

WEEK 14 :: 제네릭

  • 제네릭 사용법
  • 제네릭 메소드 만들기
  • 제네릭 주요 개념(바운디드 타입, 와일드 카드)
  • Erasure

WEEK 15 :: 람다식

  • 람다식 사용법
  • 함수형 인터페이스
  • Variable Capture
  • 메소드, 생성자 레퍼런스

 

 

 

 

 

728x90
728x90

 

 

java를 공부하다보면 중요한 개념으로 this 라는 키워드를 만나게 된다.

java에서는 보통 다음과 같은 경우에 만나볼 수 있다.

 

public class Test {
    private String name;
    private int age;
    public Test() {}
    public Test(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public static void main(String[] ar) {
    
    }
}

 

this가 사용된 부분은 6, 7 라인이다.

여기서 this는 this 키워드가 작성된 클래스 자기 자신을 뜻한다.

그래서 5라인에서의 name, age와 구분하기 위해 자기 자신임을 뜻하는 this를 사용하고 있는 부분이다.

 

그 증거로 다음과 같은 소스로 확인해볼 수 있다.

 

public class Test {
    private String name;
    private int age;
    public Test() {}
    public Test(String name) {
        this.name = name;
    }
    public Test(int age) {
        age = age;
    }
    public void disp() {
        System.out.println("name : " + name + ", age : " + age);
    }
    
    public static void main(String[] ar) {
        // this 키워드를 사용한 경우
        Test t1 = new Test("xxxelppa");
        t1.disp();
        
        // this 키워드를 사용하지 않은 경우
        Test t2 = new Test(21);
        t2.disp();
    }
}

 

[실행결과]

name : xxxelppa, age : 0
name : null, age : 0

 

 

17라인의 t1 객체에서 사용한 5라인의 생성자를 보면, this 키워드를 사용하여

자기 자신의 변수와 매개변수로 넘어온 변수를 구분해서 사용하고 있기 때문에 실행결과의 첫번째 라인과 같이 정상적으로 name이 할당됨을 볼 수 있다.

 

반면, 21라인에서 t2를 생성할 때 사용한 생성자는 8라인에 정의되어 있는데

내용부를 보면 age를 age에 담고 있다.

물론, 매개변수와 클래스의 변수명이 다르면 지금과 같은 상황은 생기지 않지만, 변수명이 동일할 경우 구분할 수 없기 때문에

9라인에서 사용된 this가 없는 age는 모두 자신과 가장 가까운 곳에 있는 매개변수 age를 바라보게 된다.

그래서 9라인의 뜻은, 매개변수로 받은 age에 매개변수 age를 담아라.

즉, 21 = 21 의 연산을 하고 있는 것이다.

 

그렇기 때문에 22라인의 t2.disp()의 결과의 age값이 0으로 할당되지 않음을 볼 수 있다.

 

 

 

 

 

이번 게시글의 목적은 java에서의 this가 아니므로 여기서 정리를 하고

본격적으로 javascript에서의 this에 대해 알게된 내용을 기계적으로 정리하려 한다.

 

결론부터 얘기하면, javascript의 this는 호출 방법에 따라 달라진다.

 

 

 

 

함수를 호출하는 방법에는 네가지가 있다.

1. function을 선언하고 이름으로 호출하는 경우로 일반 함수 호출 이라고 한다.

2. object에 function을 담아 오브젝트의 멤버로써 호출하는 경우로 멤버 함수로의 호출 이라고 한다.

3. javascript의 내장함수인 call을 이용한 호출 방법.

4. javascript의 내장함수인 apply를 이용한 호출 방법.

 

각기 네가지 호출 방식에 따라 this는 경우에 따라 같을 수는 있으나, 서로 다른 것을 나타낸다.

 

 

728x90

 

 

개발자 도구에서 다음의 코드를 작성해보자.

function thisTest() {
    return this.toString();
}
thisTest();

 

thisTest라는 function을 선언하고 호출하는 모습이다.

이 코드의 실행 결과는 다음과 같다.

 

[실행결과]

"[object Window]"

 

즉, 일반함수의 경우 this는 글로벌 객체인 Window를 나타낸다.

 

 

 

두번째 함수 호출 방식인 오브젝트를 선언해서 this를 출력해보기 위해 다음의 코드를 작성해보자.

var thisTest2 = {
    exec : thisTest,
    toString : function() {
        return "[object thisTest2]";
    }
}
thisTest2.exec();

 

[실행결과]

"[object thisTest2]"

 

2라인의 thisTest는 앞서 정의한 thisTest를 뜻하며,

3라인의 toString은 thisTest2의 toString을 재정의한 것으로

만약 exec의 호출 function의 thisTest안에서 this가 thisTest2를 바라보고 있다면 바로 3라인에서 재정의한 toString을 호출한 것이다.

그리고 그 실행 결과로 exec를 호출하여 thisTest를 실행한 결과 this가 바라보는 것이 thisTest2라는 것을 알아낼 수 있다.

 

즉, 오브젝트의 멤버로써 호출할 경우 이 오브젝트를 소유한 객체를 this에 넘겨준다.

 

 

 

마지막으로 call과 apply를 사용할 경우에는

매개변수를 넘겨주느냐 아니냐에 따라 this가 달라지는 것을 볼 수 있다.

.call() 이나 .apply() 를 사용할 경우 Window 객체를

그 이외에 매개변수를 넘겨주면, 해당 매개변수의 객체를 바라보게 된다.

(여기서 예외는 매개변수로 undefined를 넘겨주면 Window를 바라보도록 되어있다.)

 

ECMAScript의 표준 문서를 보면 이러한 결과가 나오는 이유를 알 수 있다고 하는데,

부끄럽게도 보고도 이해하지 못했다.

 

나중에 이해하는 날이 오리라 믿으며 우선 끝까지 한번 학습을 할 계획이다.

언어 공부의 시작은 암기라고 ..

자바를 처음 배울 때 무슨 뜻인지도 모르고 public static void main(String[] ar)을 달달 외우던 때로 돌아온 기분이다.

선암기 후이해.

 

정확한 원인은 이해하지 못했지만, 코드를 보면 어떤 결과가 나오는지부터 알아가려한다.

 

 

 

 

가장 중요한 결론은, javascript에서의 this는 다른 언어의 this와 상당히 다른 모습을 가지고 있다는 것이다.

 

 

 

 

728x90
728x90

 

자바에서 인터페이스를 사용하는 이유가 무엇일까?

아마 인터페이스를 처음 접해보는 분들이 많이 궁금해 하는 부분일거라고 생각한다.

그냥 그 기능을 클래스로 구현해도 될 것 같은데 번거롭게(?) 인터페이스까지 사용해서 또 인터페이스에 대한 학습도 해야하기 때문이다.

 

이세상 모든건 그게 왜 필요한지를 알면 이해하는데 도움이 많이 된다.

 

interface라고 쓰는 이 인터페이스는 결론부터 얘기하면 '공동 작업시 충돌을 방지하기 위해서'라고 한다.

다른 많은 이점이 있지만, 처음 내가 배울 당시 인터페이스 사용 이유는 공동 작업을 할때 유연함을 위해서다.

 

다음과 같은 상황을 생각해보자.

A씨는 필기도구를 사용해서 글씨를 쓰는 프로그램을 개발하는 프로젝트를 진행하고 있다.

그러던 중 같은 팀의 연필을 개발하는 김개발씨와 볼펜을 개발하는 박개발씨에게 '쓴다'라는 기능을 개발하라고 지시했다.

 

김개발씨의 코드는 다음과 같다.

 

class Pencil {
    public void Write() {
        System.out.println("연필로 쓴다.");
    }
}

 

박개발씨의 코드는 다음과 같다.

 

class Ballpoint {
    public void Writing() {
        System.out.println("볼펜으로 쓴다.");
    }
}

 

두 사람에게 개발을 완료했다는 말을 들은 A씨는 당황했다.

각자의 단위에서는 잘 실행이 되지만, 메인 프로그램에 붙여서 사용하자니 서로 이름이 너무 달라서 사용하기에 너무 불편한 것이다.

왜냐면 다음주에 형광펜, 네임펜 등의 필기구에 대해서도 개발을 해야하기 때문에 이대로는 안되겠다 싶었다.

 

class MainClass {
    public static void main(String[] ar) {
        Pencil pencil = new Pencil();
        BallPoint ballpoint = new Ballpoint();

        pencil.write();
        ballpoint.writing();
    }
}

 

 

728x90

 

 

그래서 미안하지만 김개발, 박개발씨에게 다음과 같은 인터페이스를 주며 다시 한 번 개발해달라고 부탁했다.

 

interface Frindle {
    public void Write();
}

 

그리고나서 개선된 김개발씨의 코드이다.

 

class Pencil implements Frindle {
    @Override
    public void Write() {
        System.out.println("연필로 쓴다.");
    }
}

 

박개발씨의 코드는 다음과 같다.

 

class Ballpoint implements Frindle {
    @Override
    public void Write() {
        System.out.println("볼로 쓴다.");
    }
}

 

이로써 A씨는 다음과 같은 메인 클래스를 작성할 수 있게 되었다.

 

package com.tistory.xxxelppa;

public class MainClass {
    public static void main(String[] ar) {
        Frindle frindle = new Pencil();
        frindle.write();
        
        frindle = new Ballpoint();
        frindle.write();
        
    }
}

 

이제는 형광펜으로 써도, 네임펜으로 써도 A씨는 클래스 이름만 알면 된다.

그러면 write라는 메서드를 호출했을 때 어떤 결과를 받아볼 것이라는 기대를 할 수 있게 된다는 장점이 있다.

 

(더 좋고 유연하게 작성할 수 있겠지만, 지금 당장 생각나는 예제가 이것 뿐이다.)

 

 

만약 형광펜으로 쓴다는 기능을 추가한다고 할 때, 이 interface를 상속받아 사용하게 된다면

A씨는 형광펜 클래스 내부에서 어떤 작업을 하는지 모르겠지만, write()를 호출했을 때 형광펜으로 쓴다는 기능을 한다는 것을 기대할 수 있다는 것이다.

 

다른 여러 장점을 가지고 있지만(인터페이스가), 내가 처음 배울 당시 인터페이스를 사용해야 하는 이유로 공동작업의 예를 들었기 때문에

위와같은 예시를 들어 보았다.

 

 

 

 

728x90
728x90

 

Q > 예를 들어 int[] a = new int[5]가 있으면 이 배열도 객체라는데 무슨말인지 잘 모르겠습니다.

    int라는 이름의 클래스가 있다는 말과 동일하나요? Integer Wrapper 클래스와 연관성은 없는 것 인가요? 알기 쉽게 설명해주세요.

 

 

A >

배열을 객체라고 부른다. 그 이유가 무엇일까? 보통 객체를 생성한다라고하면 클래스의 객체를 많이 떠올린다. 그렇다고 클래스부터 설명을 할 수는 없는 노릇이다. 클래스에 대해서는 안다고 가정하고 클래스와 객체와의 관계에 대해 생각해보자.

 

클래스는 자료형이다. 이 사실을 잊어선 안된다. 왜냐면 자료형은 그 자료가 가진 형태를 나타내는 것이기 때문에 스스로는 사용될 수 없다. 누군가가 그 형태를 가지는 실체를 만들어 내야만 사용할 수 있다. 이런 관점에서 배열과 배열 객체를 바라보자. 배열도 동일한 자료형의 값을 담을 수 있는 공간이다. 그렇기 때문에 int[] a; 라고한다면, a라는 변수는 이제부터 int라는 자료형의 값을 담을 수 있어. 딱 여기까지다. 그 이상도 이하도 아니다. '담을 수 있다'는 것과 '사용할 수 있다'는 다르다. (전달이 잘 되고 있는지 모르겠다.)

 

728x90

 

int[] a; 는 어떤 의미에서 추상적인 표현이다. int라는 자료형의 값을 여러개 담을 수 있는 것. 더 나아가 이것을 실체화 시켜야 사용 가능하다는 것이다. 바로 그 실체화 하는 코드가 new int[5] 인 것이다. (다형성에 대해서 알고 있으면 더 좋을 것 같다.) 이 new int[5]라는 코드 덕분에 a는 배열 객체가 되는 것이다. 그 이전에는 그저 int[] 타입의 변수 a를 정의했는데 아직 객체가 할당되지 않은 상태인 것이다.

 

 

그리고 다른 관점에서 한번 더 생각해보자. 배열은 다른 리터럴 값들과 달리, '배열은 어떠한 값이다.'라고 정의할 수 없다. 즉, new라는 연산자를 통해 heap 메모리 영역에 생성된 공간을 참조하는 주소값을 가지고 있기 때문이다. a는 배열에 할당된 값의 주소를 가리키고 있지 배열의 값을 가리키고 있지 않다는 얘기이다. 그렇기 때문에 이 배열도 객체라고 한다.

 

 

 

int라는 이름의 클래스가 있다는 말과 동일하다는 얘기는 클래스도 결국 자료형임을 알고 있는 것 같다. 자료형의 관점에서 int[] 타입의 클래스 라고도 볼 수 있다고 생각한다. 하지만 그렇게는 잘 얘기하지 않는다. 흔히 primitive 타입이라고 부르는 원시타입에 대해서 자료형이라고 하지 클래스라고는 잘 하지 않기 때문이다.

 

배열 객체의 얘기를 함에 있어서 Integer Wrapper 클래스와는 관련성이 부족한것 같다. Integer는 primitive 타입인 int의 reference타입으로, 흔히 8대 자료형이라 부르는 primitive 타입들의 reference 타입의 클래스를 Wrapper 클래스라고 하기 때문이다. 이런 연관성을 생각한 이유는, 내 생각에, 배열 객체 -> 객체 -> 클래스 -> int -> primitive type -> Wrapper class 이런 흐름이었지 않나 생각한다. Wrapper class는 primitive type을 클래스화 한것이다.

 

 

 

 

728x90
728x90

 

나는 개인적으로 클래스와 객체를 설명할 때 붕어빵 틀을 비유하는걸 싫어한다.

마음에 들지 않는다. 내가 배울 당시 이렇게 배우지 않았을 뿐만 아니라 개인적으로 잘 와닿지도 않았다.

그래서 내가 배운 방법으로 클래스란 무엇이며 이 클래스와 객체와의 관계를 어떻게 이해하면 좋은지 정리해보려 한다.

 

클래스는 우리가 일상 생활속에서 볼 수 있는 모든 것들을 표현할 수 있는 도구라고 생각하는 것도 좋은 발상이다.

클래스를 구성하고 있는 큰 두가지 개념이 있다. 하나는 멤버필드라는 개념이며 다른 하나는 멤버 메서드라는 개념이다.

 

멤버 필드란, 그 클래스가 표현하고자 하는 대상이 가지는 속성들을 의미한다. 높이, 색상, 속도, 속력, 방향, 무게 등 일반적인 사물들이 가질 수 있는 모든 속성들을 말한다.

멤버 메서드란, 그 클래스가 표현하고자 하는 대상이 할 수 있는 어떤 동작, 행위들을 의미한다. 클래스가 사람을 표현하고 싶다면, 앞으로 걷기, 뒤로 걷기, 밥먹기 등이 될 수 있다. 강아지라면 밥먹기, 짖기, 뛰기, 재롱부리기 등이 해당한다.

 

실제로 사람은 훨씬 복잡하지만 클래스로 사람을 표현한 예시이다.

 

class Person {
    private String gender;
    private int tall;
    private double weight;
    private double footSize;

    public void walk() { }
    public void eat() { }
}

 

이 클래스에서 정의하고 있는 사람이라는 것은, 성별, 키, 몸무게, 발 사이즈를 알 수 있으며, 걷거나 먹는 행위를 할 수 있다.

앞서 말했듯 이정도로 사람을 표현하기엔 턱없이 부족하지만 간단한 예시를 들자면 이렇다는 것이다.

하지만 이렇게 정의했다고 해서 바로 사용하고 있는것은 아니다. 이건 마치 설계도면과도 같은 것이다.

스스로는 할 수 있는게 아무것도 없기 때문이다.

김춘수 시인의 꽃이라는 시에서 처럼

내가 클래스의 객체를 만들어 주기 전에는 클래스는 다만 하나의 설계 도면에 지나지 않는다.

 

클래스의 개념에 대해 찾아본다면 기본적인 자바 프로그램을 만들어 보면서 알게 되었을 것이다.

모든 자바 프로그램은 main 메서드에서 시작한다고.

이 main 메서드 안에서 클래스의 객체를 만들어주어야 비로소 나에게 사용할 수 있는 객체로서의 의미를 가지게 된다.

 

 

728x90

 

 

자꾸 객체 객체 하는데, 그래서 객체가 뭔지 답답할지 모르겠다.

다음 예시를 보기 전에 클래스와 객체의 관계에 대해서 알아보자.

 

우리가 많이 사용하는 스마트폰. 이건 클래스다. 하지만 내가 손에 들고 있는 스마트폰은 클래스가 아니다. 객체다.

컴퓨터를 하기 위해서 지금 내가 손으로 쥐고있는 마우스는 객체다. 하지만 마우스는 객체가 아니가. 클래스다.

지금 책상위에 놓여있는 달력은 객체다. 하지만 달력은 객체가 아니다. 클래스다.

'머릿속에 손목 시계를 떠올려보세요' 해서 떠오르는 일반적으로 알고있는 손목시계는 클래스다. 하지만 지금 손목에 차고 있는 이 시계는 객체다.

 

무슨 말장난이냐 싶을지 모르겠지만. 위에서 말한 그대로다.

클래스란 이렇게 우리가 특정 사물에 대해서 일반적으로 떠올릴수 있는 개념적인 것이라면, 객체는 이것이 실제 눈앞에 형태를 갖추고 있는 것 그 자체이다. 즉, 클래스는 '이 사물은 이렇게 생겼고 어떤어떤 기능을 가지고 있어요'라면 객체는 클래스가 설명하고 있는 것을 토대로 만든 것이라고 할 수 있겠다. 그래서 마우스는 클래스지만 지금 내 손 안에 들어와 있는 눈에 보이는 이 마우스는 클래스가 아니고 객체라는 것이다.

 

 

 

위에 작성한 Person 클래스를 사용하는 main 메서드를 작성해 보자.

 

public class ClassTest {
    public static void main(String[] ar) {
        Person p1 = new Person();
        Person p2;
        p2 = new Person();
    }
}

 

ClassTest라는 이름의 클래스는 main 메서드를 담고 있다.

3라인과 4-5라인이 의미하는 바는 똑같다.

하지만 굳이 4라인과 5라인으로 나눈데는 이유가 있다.

3라인의 의미는, p1변수는 Person클래스에서 정의한 형태를 가지는 객체를 담을 변수야. 그리고 난 이 변수에 Person 객체를 담았어. 라는 말이다.

4라인의 의미는, p2변수는 Person클래스에서 정의한 형태를 가지는 객체를 담을 공간이야. 라는 말이다.

5라인의 의미는, Person객체를 하나 만들었고, 나는 이 형태를 가지는 변수 p2에 담았어. 라는 말이다.

 

건축물에 비유하자면 단순히 클래스를 만들기만 해서는, 객체화를 하지 않는다면, 그저 설계도만 만들고 건물은 올리지 않은것이 된다.

하지만 설계도만 가지고 있다면 수십 수백 수천개라도 객체를 만들어낼 수 있다. 너무나 당연하게도. 그렇기 때문에 자바는 재사용성이 뛰어난 언어라는 얘기와도 일맥상통한다.

 

이번에는 클래스와 객체의 관계에 대해서 다루었기 때문에 이정도만 정리해두려 한다.

다음에는 자바의 특징중에 하나인 다형성에 대해 정리하면서 재사용성에 대해서 자세히 정리 해둬야 겠다.

 

 

 

 

728x90
728x90

java 개발을 하려면 환경변수를 설정해야한다.
아니 더 정확히는 명령 프롬프트에서 컴파일하고 실행하려면 환경변수를 설정해야 한다.
명령 프롬프트는 cmd창으로 알고 있는 보통 검정색 있어보이는 화면을 말한다.

우선 환경변수라는것이 무엇인지부터 알아보자.

 

IT(정보기술) 용어로, OS의 셸(shell) 등에 설정되어 있다. 변수의 이름과 의미는 미리 정해져 있기 때문에 환경변수를 읽으면 시스템의 설정을 어느 정도 알 수 있다.

 OS의 환경변수는 시스템의 실행파일이 놓여 있는 디렉토리의 지정 등 OS 상에서 동작하는 응용소프트웨어가 참조하기 위한 설정이 기록된다.
응용소프트웨어로부터는 시스템 콜(system call:프로그래밍 언어에서 지원하지 않는 기능에 대하여 운영체계의 루틴을 호출하여 이용하는 것)이나  OS의 표준 API 등을 통하여 간단히 값을 얻을 수 있도록 되어 있다.

또한 웹 브라우저의 내부 데이터의 일부를 환경변수라고 하는 경우도 있는데, 이것은 HTTP를 요청할 때 송신되는 것으로, 브라우저의 종류나 링크되어 있는 웹 페이지 등 웹 서버가 웹 브라우저에 대하여 최적의 처리를 하기 위해 송신되는 것이다. HTTP를 요청하는 응용소프트웨어는 모두 환경변수를 송신하고 있다고 할 수 있다.

 사용자가 의도적으로 변환할 수 있는 환경변수도 많으며, 특히 웹 브라우저에서는 자신의 정체를 숨길 목적으로 브라우저 등의 변수를 변환하는 경우도 있다.

 그러나 사실과 다른 값을 환경변수에 설정하면 그 환경변수를 사용하고 있는 서버나 응용소프트웨어가 올바르게 작동하지 않을 수 있다. 특히, 셸의 환경변수를 변환했을 경우에는 심각한 오작동을 일으킬 수 있기 때문에 이를 취급하는 경우에는 주의가 필요하다.

 

위 본문은 네이버 검색을 통해 가져온 내용이다.

위 본문을 읽어도 잘 모르겠다면,
"여기여기에(사용할 것들이있는 위치) 있는 것들을 내가 사용할건데, 미리 쓰기 편하게 등록해 놓는거야"
정도로 생각해도 될 것 같다.

환경변수를 설정하기 위해서는 설정하는 곳으로 가야한다.

 

 

[내 컴퓨터]를 마우스 우클릭을 해서 속성을 클릭한다.
혹은 키보드의 윈도우키와 오른쪽 위에 pause/break 키를 동시에 눌러주면 바로 아래 이미지와 같은 화면이 뜬다.

 

 

728x90

 

 

새로 뜬 화면의 좌측에 있는 '고금 시스템 설정' 을 클릭한다.

 

 

그리고 뜬 화면의 아래쪽에 '환경 변수' 를 클릭하면 바로 위 이미지의 앞쪽에 뜬 팝업이 하나 더 뜬다.
바로 이곳에서 환경변수를 설정할 수 있다.


바로 위의 그림에 보이는 것처럼 등록할 수 있는 영역이 두군데가 있다.
1. 특정 사용자에 대한 사용자 변수
2. 시스템 변수

 

사실 말이 특정 사용자이지, 로그온 되어있는 현재 사용자 계정에서만 사용하려면 1번의 위치에 등록을 시키고
이 컴퓨터를 사용하는 모든 사용자 계정에 대해 동일한 환경변수를 설정하려면 2번의 위치에 등록을 하면 된다.

 

그리고 java에서 설정하는 환경변수는 두가지 종류가 있다.
PATH와 CLASSPATH이다.
PATH는 실행 프로그램의 위치만을 나타내며
CLASSPATH는 실행 프로그램에서 사용하는 라이브러리의 위치를 나타낸다.

 

그래서 PATH는 java에서 사용할 실행 프로그램들이 위치하고있는
[자바 설치 경로] 하위의 bin 이라는 디렉토리로 설정한다.
그리고 CLASSPATH는 [자바 JRE 설치 경로]하위의 lib 디렉토리의 위치로 설정한다.

 

/**
 * 책을 보던 중 새로 추가할 만한 내용이 있어서 수정하게 되었다.
 * 이 부분이 java version이 달라지면서 바뀌었을 수도 있지만,
 * 5.0 버전때를 기준으로 CLASSPATH에 대한 설명을 더하자면
 *
 * 자바 라이브러리 클래스들이 저장된 디렉토리를 설정하는 것으로
 * J2DJK(Java 2 Development Kit)는 필요한 클래스 라이브러리를  ..\jre\lib\ext 디렉토리에 복사하면 사용 가능하므로
 * ​특별한 경우가 아니면 CLASSPATH는 설정하지 않는 것이 좋다.​
 */


환경변수를 등록하거나 수정하는 작업은 간단하다.
[새로 만들기] 버튼을 눌러 이름과 값을 정해주면 된다.
이미 존재한다면 (아마 path는 존재하고 있을 것이다.) 해당 값을 클릭해둔 상태로 [편집] 버튼을 누르면 된다.


보통 java에 대한 환경변수를 설정할때는
1. JAVA_HOME 이라는 변수를 생성하고, [java 설치 경로]의 jdk 버전이 명시된 디렉토리까지 설정하고
2, PATH에서 JAVA_HOME이라는 1에서 만든 변수를 가져다 사용하여 등록하는데
   PATH에는 이렇게 작성하면 된다.
   %JAVA_HOME%\bin;

환경변수의 경로는 항상 세미콜론(;)으로 구분되어야 하며
환경변수 설정이 바뀌었을 경운에는 명령 프롬프트를 반드시 재시작 해야 수정한 내용이 반영된다.​


만약 환경변수가 제대로 설정되었는지 확인해보고 싶다면
(java의 경우) 명령프롬프트를 띄우고 "javac" 라고 입력했을 때
해당 명령어 사용법이 뜨면 제대로 설치 및 설정이 완료된 것이고
해당 명령어를 찾을 수 없다라는 문구가 나오면
어딘가가 잘못된 것아다.

 

(제대로 설정되지 않은 예)


그리고 또 하나, Java는 설치할 때 환경변수 세팅이 자동으로 등록되지 않는다.
그렇기 때문에 Hello World를 하기 전에 까먹지 말고 세팅해 주어야 한다.
일반적으로 jdk를 설치하면 jre가 자동으로 설치 되지만
jre를 설치하면 jdk는 자동으로 설치되지 않는다.

:jre -> Java Runtime Environment 로 자바 실행 환경이다.
:jdk -> Java Development Kit 으로 개발을 하기 위한 도구이다.

 

이렇게 환경변수 세팅을 마쳤다면 드디어 Hello World 를 출력할 준비가 ? 되었다.
이왕 세팅한거 티키신이 분노하지 않게 '안녕 세상아' 한 번 찍어주자.

 

import java.lang.*;

public class Example {
    public static void main(String[] ar) {
        System.out.println("Hello, World!");
    }
}

 

메모장에서 작성해도 좋다.
대신, ​파일이름과 public class 뒤의 이름이 대소문자까지 완벽하게 동일해야 한다.​
위의 경우 파일을 Example.java 로 저장해야만 한다.

그리고 해당 파일이 있는 위치로 이동한후
javac Example.java
라고 컴파일 명령을 내린다.
정상적으로 컴파일이 되었다면, .java파일이 있던 위치에 동일한 이름의 .class 파일이 생겼을 것이고
명령프롬프트에는 아무런 문구 없이 사용자의 입력을 기다리며 커서가 껌뻑이고 있을 것이다.

컴파일이 완료되었으면 다음 명령어로 실행시켜보자.
java Example
만약 위의 과정을 순서대로 잘 수행했다면 "Hello, World!" 라는 문구가 떡하니 출력되어 있을 것이다.

 

환경변수에 대해 알아보다가 Hello, World! 까지 출력해 보았다.
이제 환경변수가 무엇이며, 그렇기 때문에 왜 설정해줘야 하는지
그리고 어떻게 확인할 수 있는지 까지 알아보았다.

다음엔 무엇을 정리해두지

** 현재 상황이 직접 java를 설치할 수 있는 환경이 아니라 이미지 자료가 부족하다.

 

 

 

 

728x90
728x90

 

자바에는 문자열을 처리하기 위한 클래스로 String, StringBuilder, StringBuffer 클래스가 있다.

단순히 문자열을 처리하기 위함이라면 (예를 들면 System.out.println("문자열");  이런 경우) 어떤 것을 사용해도 별다른 차이는 없다고 알고 있다. 하지만 문자열을 더하는 (+) 연산을 할 경우에 퍼포먼스상의 차이가 발생한다.

 

백번 듣느니 한 번 보는게 낫다.

실제로 아래와 같은 예제는 많은 곳에서 흔히 볼 수 있는 예제일 것이다.

 

package xxxelppa.tistory.com;

public class StringTest {
    public static void main(String[] ar) {
        long StartTime = 0;
        long EndTime = 0;
        
        String str;
        StringBuilder strBdr;
        StringBuffer strBfr;
        
        for(int k = 0; k < 9; ++k) {
            str = new String("문자열");
            strBdr = new StringBuilder("문자열");
            strBfr = new StringBuffer("문자열");
            
            System.out.println("============== " + (k + 1) + "번째 비교 결과" + " ==============");
            
            StartTime = System.nanoTime();
            for(int i = 0; i < 10000; ++i) {
                str += i;
            }
            EndTime = System.nanoTime();
            System.out.println("String의 실행 시간\t\t: " + (EndTime - StartTime));
            
            StartTime = System.nanoTime();
            for(int i = 0; i < 10000; ++i) {
                strBdr.append(i);
            }
            EndTime = System.nanoTime();
            System.out.println("StringBuilder의 실행 시간\t: " + (EndTime - StartTime));
            
            StartTime = System.nanoTime();
            for(int i = 0; i < 10000; ++i) {
                strBfr.append(i);
            }
            EndTime = System.nanoTime();
            System.out.println("StringBuffer의 실행 시간\t: " + (EndTime - StartTime));
            System.out.println("=============================================");
            System.out.println();
            
        }
        
        
    }
}

 

위 예제는 똑같이 문자열을 더하는 연산을 했을 때 실행 속도를 비교하는 소스이다.

실행 결과는 다음과 같다.

 

============== 1번째 비교 결과 ==============
String의 실행 시간             : 192313252
StringBuilder의 실행 시간    : 417904
StringBuffer의 실행 시간     : 613102
=======================================

============== 2번째 비교 결과 ==============
String의 실행 시간             : 165631465
StringBuilder의 실행 시간    : 427854
StringBuffer의 실행 시간     : 460046
=======================================

============== 3번째 비교 결과 ==============
String의 실행 시간             : 178263707
StringBuilder의 실행 시간    : 276554
StringBuffer의 실행 시간     : 340644
=======================================

============== 4번째 비교 결과 ==============
String의 실행 시간             : 184807363
StringBuilder의 실행 시간    : 371959
StringBuffer의 실행 시간     : 376934
=======================================

============== 5번째 비교 결과 ==============
String의 실행 시간             : 178070851
StringBuilder의 실행 시간    : 436048
StringBuffer의 실행 시간     : 479068
=======================================

============== 6번째 비교 결과 ==============
String의 실행 시간             : 133046927
StringBuilder의 실행 시간    : 268945
StringBuffer의 실행 시간     : 361423
=======================================

============== 7번째 비교 결과 ==============
String의 실행 시간             : 153004492
StringBuilder의 실행 시간    : 210708
StringBuffer의 실행 시간     : 355277
=======================================

============== 8번째 비교 결과 ==============
String의 실행 시간             : 137168318
StringBuilder의 실행 시간    : 289139
StringBuffer의 실행 시간     : 418782
=======================================

============== 9번째 비교 결과 ==============
String의 실행 시간             : 136230374
StringBuilder의 실행 시간    : 309039
StringBuffer의 실행 시간     : 373129
=======================================

 

728x90

 

 

실행 시간 결과는 String > StringBuffer > StringBuilder 순서이다.

그렇다면 왜 이런 결과가 나왔을까?

 

String은 문자열을 더하기 위해 String 공간을 하나 더 만드는 작업을 한다.

이게 무슨 소리일까? String이라는 클래스가 가지는 특성이라고 받아들일 수 밖에 없다.

String 클래스형 자료형으로 선언된 문자열이 메모리상에 할당이 되면, 이 값을 바꿀 수 없는 값이 된다.

만약 이 문자열이 더해지거나 빼는 연산을 하게 될 경우 기존에 할당한 메모리에 저장된 값이 바뀌지 않는다는 소리다.

 

String addr100 = new String("문자열A");
String addr101 = new String("문자열B");

addr100 = addr100 + addr101;

 

이 소스가 실행되면 메모리상에서 어떤 일이 생길까?

 

 

위에 첨부한 이미지를 함께 보자.

만약 메모리상에 위처럼 문자열이 저장 되어있다고 했을 때, 100번지의 "문자열A"와 101번지의 "문자열B"를 더해 새로운 문자열 "문자열A문자열B"를 만든 경우이다.

addr100 변수는 100번지를 바라보고 있다가 새로운 102번지를 바라보게 된다. (바라본다 보다 참조한다는 말이 더 옳다)

그럼 100번지는?

가비지 컬렉터가 나중에 알아서 처리해주니 신경쓰지 말자.

 

String 클래스의 경우 기존 값에 새로운 값을 더하는 것이 아닌, 위와 같이 추가 연산이 필요하기 때문에 StringBuilder나 StringBuffer보다 퍼포먼스가 떨어진다.

그렇기 때문에 문자열 연산에서는 StringBuilder나 StringBuffer 클래스의 사용을 권장한다.

 

(내용이 너무 길어질 것 같아서 추가하지 않으려 했지만, 급하면 이 부분은 넘어가도 좋다.)

지금까지 문자열을 더하는 연산이 빈번하게 발생한 부분은 배치 프로그램에서 쿼리를 작성할 때였다.

다음과 같은 쿼리가 있다고 하자. (다소 과장된 부분이 있다.)

 

SELECT
    AAA
    , BBB
    , CCC
    , DDD
    , EEE
FROM TEST
WHERE
    1=1
    AND AAA > 100
    AND BBB > 200
    AND CCC > 300
    AND DDD > 400

 

이 때 이 쿼리문을 다음과 같이 변수에 담을 수 있다.

 

String query = "";

query += "SELECT           ";
query += "    AAA          ";
query += "    , BBB        ";
query += "    , CCC        ";
query += "    , DDD        ";
query += "    , EEE        ";
query += "FROM TEST        ";
query += "WHERE            ";
query += "    1=1          ";
query += "    AND AAA > 100";
query += "    AND BBB > 200";
query += "    AND CCC > 300";
query += "    AND DDD > 400";

 

단순히 더하는게 아니라고 생각해보면 이게 얼마나 재앙인지 이제는 알게 되었으리라 생각한다.

(위와 같은 경우 반드시 StrinfBuilder 클래스나 StringBuffer 클래스를 사용하기를..)

 

 

 

그럼 마지막으로, String과 StringBuilder, StringBuffer의 차이는 알겠는데 StringBuilder와 StringBuffer의 차이는 무엇일까?

StringBuilder와 StringBuffer 클래스는 사실 하는 일은 똑같다. 다만 하나의 차이가 있는데 동기화 처리의 문제이다.

 

StringBuffer클래스는 동기화 (Synchronized)를 지원하며 멀티 스레드 환경에서도 동기화를 지원한다. 그렇기 때문에 단일 스레드일 경우 StringBuilder 클래스를, 멀티 스레드일 경우 StringBuffer 클래스의 사용이 권장된다. (StringBuffer 클래스의 경우 혼용해도 상관없지만, 동기화 처리 문제로 퍼포먼스상의 이슈가 있다고 한다.)

실제로 맨 위에 예제로 만든 소스의 실행 결과를 보면 단일 스레드 환경에서 StringBuilder 클래스가 StringBuffer클래스보다 퍼포먼스가 좋은 것을 확인할 수 있다.

 

동기화라는 개념은 어려운게 아니다.

마치 한칸짜리 공중 화장실이라고 생각하면 될 것 같다.

누구나 사용할 수 있지만, 동시에 사용할 수 없도록 하는 것이 동기화이다.

 

 

 

흔히 찾아볼 수 있는 예제이지만, 보다 정확하게 확인하고 싶어서 주소값을 직접 출력 해보고 싶었는데 그렇게 하지 못해 아쉬움이 남는다.

 

 

 

 

728x90
728x90

 

정규표현식 자체만으로도 정말 많은 이야기를 할 수 있지만.

기본적인 정규표현식의 개념과 언제 사용하면 좋은지에 대해서는 어느정도 알고 있다고 가정한다.

(나중에 기회가 된다면 정규표현식에 대해서 정리를 할 계획인다.)

 

자바에서 정규표현식을 사용하기 위해서는 두 개의 라이브러리를 import해야 한다.

 

import java.util.regexp.Matcher;
import java.util.regexp.Pattern;

 

바로 util package에 있는 클래스 이다.

 

 

두 개의 클래스를 import 했다면, 다음처럼 작성해보자.

 

// 비밀번호 유효성 검사식1 : 숫자, 특수문자가 포함되어야 한다.
String regExp_symbol = "([0-9].*[!,@,#,^,&,*,(,)])|([!,@,#,^,&,*,(,)].*[0-9])";
// 비밀번호 유효성 검사식2 : 영문자 대소문자가 적어도 하나씩은 포함되어야 한다.
String regExp_alpha = "([a-z].*[A-Z])|([A-Z].*[a-z])";

// 정규표현식 컴파일
Pattern pattern_symbol = Pattern.compile(regExp_symbol);
Pattern pattern_alpha = Pattern.compile(regExp_alpha);

// 문자 매칭
Matcher matcher_symbol = pattern_symbol.matcher("검사할문자열");
Matcher matcher_alpha = pattern_alpha.matcher("검사할문자열");

// 매칭 결과 출력
System.out.println("matcher_symbol : " + matcher_symbol.find());
System.out.println("matcher_alpha : " + matcher_alpha.find());

 

비밀번호 작성 규칙은 다음과 같다.

1. 숫자가 포함되어야 한다.

2. 특수문자가 포함되어야 한다.

3. 영문 소문자가 포함되어야 한다.

4. 영문 대문자가 포함되어야 한다.

 

728x90

 

 

실제로 사용할 때 수정해야 할 부분이 있다면 다음과 같다.

 

1. 2라인과 4라인에서 정의한 정규표현식을 수정하여 유효성 검사 조건을 달리 한다.

2. 11라인과 12라인에서 "검사할문자열"에 들어갈 부분에 진짜 검사할 신규 비밀번호 매개변수를 담아 넣어준다.

3. 15라인과 16라인에서 단순히 console에 출력한 부분을 .find()의 리턴타입이 boolean이므로 if절 안에 넣어 수정 가능 여부를 판별한다.

 

 

자바에서 정규표현식을 사용하여 유효성을 검사하는 방법이 위와 같으므로 응용하는건 개발자의 몫이다.

 

 

마지막으로 그냥 이렇게 끝내면 작동하지 않는 것을 첨부한 것 같아서,

사용자로부터 입력 받는것은 아니지만, 배열에 임의의 암호값을 넣어보고 실행해본 소스와 결과를 첨부한다.

 

package xxxelppa.tistory.com;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegExpTest {
    public static void main(String[] ar) {
        
        String arrPw[] = new String[] {"qwer1234", "Qwer1234", "qwer!234", "Qwer!234"};
        
        // 비밀번호 유효성 검사식1 : 숫자, 특수문자가 포함되어야 한다.
        String regExp_symbol = "([0-9].*[!,@,#,^,&,*,(,)])|([!,@,#,^,&,*,(,)].*[0-9])";
        // 비밀번호 유효성 검사식2 : 영문자 대소문자가 적어도 하나씩은 포함되어야 한다.
        String regExp_alpha = "([a-z].*[A-Z])|([A-Z].*[a-z])";
        
        // 정규표현식 컴파일
        Pattern pattern_symbol = Pattern.compile(regExp_symbol);
        Pattern pattern_alpha = Pattern.compile(regExp_alpha);
        
        
        for(int i = 0; i < arrPw.length; ++i) {
            // 문자 매칭
            Matcher matcher_symbol = pattern_symbol.matcher(arrPw[i]);
            Matcher matcher_alpha = pattern_alpha.matcher(arrPw[i]);
            
            // 매칭 결과 출력
            System.out.println("========== " + (i+1) + "번째 비밀번호 : [" + arrPw[i] + "] ==========");
            
            if(matcher_symbol.find() && matcher_alpha.find()) {
                System.out.println("비밀번호로 적절합니다.");
            } else {
                System.out.println("비밀번호로 부적절합니다.");
            }
            System.out.println();
        }
    }
}

 

========== 1번째 비밀번호 : [qwer1234] ==========
비밀번호로 부적절합니다.

========== 2번째 비밀번호 : [Qwer1234] ==========
비밀번호로 부적절합니다.

========== 3번째 비밀번호 : [qwer!234] ==========
비밀번호로 부적절합니다.

========== 4번째 비밀번호 : [Qwer!234] ==========
비밀번호로 적절합니다.

 

 

RegExpTest.java
다운로드

 

 

 

 

728x90

+ Recent posts