(객체지향) 캡슐화(encapsulation)란 무엇인가?

자바같은 객체지향언어를 공부하다 보면 중간중간 끊임없이 들려오는 단어가 몇 있다.

바로,

  • 다형성(polymorphism)
  • 추상화(abstraction)
  • 캡슐화(encapsulation)
  • 상속(inheritance)

이렇게 네 개의 단어와 5원칙(SOLID)이 있는데 이번 포스팅에서는 캡슐화에 대해 알아보도록 한다.

캡슐화란?


위키에는 캡슐화는 다음과 같이 서술되어 있다.

캡슐화(encapsulation)는 객체 지향 프로그래밍에서 다음 2가지 측면이 있다.

  • 객체의 속성(data fields)과 행위(메서드, methods)를 하나로 묶고,
  • 실제 구현 내용 일부를 내부에 감추어 은닉한다.

정의만 볼때는 뜻은 대충 알 수도 있겠지만 정확히 어디에 어떻게 사용되는 특성인지 아직 모호하다.

그렇다면 예제를 통해 이해 해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Example {
int num;

public Example(int number) {
num = number;
}
public double getHalf() {
return num / 2;
}
}

class Run {
public static void main(String[] args) {
Example ex = new Example(20);
System.out.println(ex.getHalf());
}
}

여기서 캡슐화는 Example 클래스 자체를 의미한다.

그렇다면 위키에서 말하는 캡슐화의 정의 중

  • field와 method를 하나로 묶는지
  • 구현 내용을 내부에 은닉하는지

이 두 가지를 만족하는지 한번 확인 해 보자.

Example클래스는 num 이라는 int 타입의 필드를 가지고있다.

또, getHalf 라는 메소드도 가지고 있다.

Example 클래스는 첫번째 조건에는 부합한다.

두번째 조건을 보자.

Run 클래스(외부)의 입장에서 Example 객체를 생성하고 메소드를 호출하여 사용하지만 구현이 어떻게 되어있는지 직접적으로 알지 못한다.

이렇게 두번째 조건도 만족이 된다.

이렇게 캡슐화가 무엇인지에 대해 예시를 통해 대략 알아보았다.

그렇다면 의문이 생길 수 있다.

왜 필드와 메소드를 하나로 묶는게 중요한가?

왜 그것이 외부에 노출되면 안되나?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Example {
int num;

public Example(int number) {
num = number;
}
public double getnum() {
return num;
}
}

class Run {

public static void main(String[] args) {
Example ex = new Example(20);
System.out.println(ex.getnum() / 2);
}
}

이렇게 해도 결과는 동일한 10.0이 출력된다.

캡슐화를 하면 달라지는 점은 객체 내부의 데이터를 가져와서 처리하는 대신, 객체에서 처리를 한 후 결과를 가져온다.

왜 이렇게 처리 하는게 좋다는 것일까?

캡슐화의 장점을 보면 코드의 중복을 피할 수 있다는 것, 데이터를 처리하는 동작 방식을 외부에서 알 필요가 없다는 것이다.

첫번째 장점은 그렇다 쳐도 두번째 장점은 했던말을 반복하는것같다.

자 그럼 다시 예제를 통해 두 장점에 대해 알아보자

어떤 물거느이 10% 할인액을 구해야 한다고 하자, 데이터를 객체로부터 받아와서 처리를 한다면 다음과 같은 코드를 추가할 것이다.

1
2
3
4
public double func(ItemData data) {
double discount = data.getPrice() * 0.9;
return discount;
}

객체로부터 가격을 가져오고 10으로 할인된 가격을 변수에 저장한 뒤 변수를 리턴해주고있다.

여기까지는 괜찮다. 그런데 이것을 다른 로직에서도 사용하게 되면 어떨까?

1
2
3
4
5
6
7
8
public double func(ItemData data) {
double discount = data.getPrice() * 0.9;
return discount;
}
public double func2(ItemData data) {
double discount = data.getPrice() * 0.9;
return discount;
}

코드의 중복이 일어나게 되고 이같은 상황이 서비스 내에서 수십, 수백번 반복된다고 생각하면 복붙하는 행위라도 힘들것이다.

그리고 만약 이를 유지보수하는 코더가 바뀌어 10프로 할인 로직을 다음과 같이 작성할 수도 있다.

1
2
3
4
public double func(ItemData data) {
double discount = data.getPrice() - data.getPrice() * 0.1;
return discount;
}

코더가 바뀌는 바람에 중복이라는 문제에 코드의 파편화라는 문제까지 끼얹어졌다.

중복이 좋지 않은건 원래도 알고있던 사실이니…. 그럼 두번째 장점인 코드의 은닉은 어떤 이점이 있을까?

위 예제에서 할인율이 20프로로 상승했다고 하자.

일단 중복된 코드가 수백개가 있다보니 수정하는 것 부터 만만치 않을것이다.

어떻게 IDE의 검색기능을 통해 수정한다고 쳐도 코더가 바뀌어 아예 검색조차 쉽지 않은경우도 많을 것이다.

그렇다고 해서 한두개의 코드라도 변경하지 못하게 될 경우 서비스에 어떤 리스크로 돌아올지 모른다.

그렇다면 이제 캡슐화를 통해 객체에서 처리를 한 후 결과를 가져오는 방식으로 해보자

1
2
3
4
5
6
7
8
9
10
11
public class ItemData {
int price = 10000;

public double getDiscount() {
return price * 0.9;
}
}

public double func(ItemData data) {
return data.getDiscount();
}

이 코드에서 할인율을 20%로 증가시키는 것은 그냥 getDiscount() 메소드만 수정해주면 된다.

이처럼 캡슐화를 하니 공통된 요구사항이 변경되었을때 중복된 여러코드를 수정하는 대신 캡슐화된 클래스 하나만 변경하면 문제가 해결되었다.

Reference.

캡슐화란 무엇인가? 어떤 이점이 있는가? - bperhaps

(JAVA) BufferedWriter.write() 의 출력 타입

버퍼를 요 며칠간 써보고 있지만 아직 스캐너를 쓸 때에 비해서는 익숙치 않다.

그러던 중 백준의 알고리즘 문제를 풀면서 BufferedWriter.write() 함수를 사용할 때 마다 숫자가 아스키코드로 출력되어 문제가 되었다.

문제

예시를 한번 보자면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {

public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

int x = Integer.parseInt(bf.readLine());

for (int i = x; i > 0; i--) {
bw.write(i);
bw.newLine();
}
bw.flush();
bw.close();
}
}

숫자를 입력하면 그 숫자부터 1까지 -1되며 출력되는 매우 간단한 코드이다.

그런데 실행하면 다음과 같이 출력된다.

분명 5를 입력했으니 5부터 1까지 차례로 출력되어야 하지만 글자가 깨져 출력되고있다.

이는 숫자 자체가 아닌 5에서 1까지 해당하는 아스키코드가 대응하여 출력되었기 때문이다.

아스키코드에서 ! 인 33을 입력하면 !와 32인 공백문자가 출력되는걸 볼 수 있다.

원인

원인은 bw.write(i) 부분에서 정수 타입인 i를 그대로 출력하려고 했기 때문이다.

해결

정수형을 그대로 출력하려고 했을때 문제가 발생했기에 형변환을 해주어야 한다.

따라서 정수 타입 변수를 버퍼를 통해 출력하고 싶으면 문자열로의 형변환이 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {

public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

int x = Integer.parseInt(bf.readLine());

for (int i = x; i > 0; i--) {
bw.write(String.valueOf(i));
bw.newLine();
}
bw.flush();
bw.close();
}
}

bw.write(i) 처럼 정수형으로 바로 출력하는 대신 bw.write(String.valueOf(i)) 처럼 문자열로 형변환하면 다음처럼 출력이된다.

- 편안 -

(ORACLE) NULL값을 치환하는 NVL, NVL2 함수

SQL을 사용하다 보면 NULL을 다른 결과로 치환하여 출력하는 문제가 가끔 나온다.

오라클에서는 이를 NVL 함수로 간단히 처리 가능한데 문제가 있다면 이 쿼리를 다른 DBMS에서 사용할 때 이다.

ORACLE : NVL

MYSQL : IFNULL

MSSQL : ISNULL

세 개의 DBMS 모두 사용하는 함수가 다른데 그렇기 때문에 서로 호환이 안된다.

NVL 함수의 사용법

1
2
3
4
5
-- 기본형
NVL(컬럼, NULL일 경우의 반환값)

NVL(컬럼, '') -- 컬럼의 값이 NULL일 경우 ''으로 치환함
NVL(컬럼, 0) -- 컬럼의 값이 NULL일 경우 0으로 치환함

예제

1
2
3
-- NAME 컬럼의 값이 NULL 일 경우 'No name'으로 치환
SELECT NVL(NAME, 'No name')
FROM ANIMAL_INS;

NVL2 함수의 사용법

기본적으로 NVL함수와 비슷하나 NULL값이 아닐때도 치환을 해준다

1
2
3
4
5
-- 기본형
NVL2(컬럼, NULL이 아닐경우의 반환값, NULL일 경우의 반환값)

NVL2(컬럼, '학생', '교사')
NVL2(컬럼, '대중교통', '자동차')

예제

1
2
3
-- DATETIME 컬럼의 값이 NULL 일경우 '정규직' NULL이 아닐경우 '계약직'
SELECT NVL(DATETIME, '계약직', '정규직')
FROM EMPLOYEE;

(JAVA) BufferedReader와 BufferedWriter를 통한 입출력

백준의 자바 알고리즘 문제를 풀어보던 중 버퍼를 활용한 입출력을 사용해야 하나는 문제가 있었다.

나는 자바에서 입출력이라고는 Scanner와 println()밖에 몰랐기에 버퍼를 통한 입출력은 사용해본적이 없었다.

찾아보니 버퍼 입출력이 스캐너보다 효율면에서 훨씬 낫다는것을 알게되었고, 그로인해 작업속도차이가 많이 난다는것도 알게되었다.

그래서! 이번 포스팅의 목표는 버퍼를 활용한 입출력에대해 알아보고 활용할 수 있을 정도로 공부하는게 목적이다.

Buffer


BufferedReader / BufferedWriter는 Buffer에 있는 IO 클래스이다. 스캐너와 다르게 입력된 데이터가 바로 전달되지않고 중간에 버퍼링이 된 후에 전달된다. 출력도 마찬가지로 버퍼를 거쳐 간접적으로 출력장치로 전달되고, 시스템의 데이터 처리 효율성을 높여주며 버퍼스트림을 InputStreamReader / OutputStreamWriter 을 함께 사용하여 버퍼링을 하게 되면 입출력 스트림으로부터 미리 버퍼에 데이터를 갖다 놓기 때문에 보다 효율적인 입출력이 가능하다.

BufferedReader


1
2
3
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); // 객체 생성
Stirng s = bf.readLine(); // 문자열의 경우
int i = Integer.parseInt(bf.readLine()); // 정수의 경우

객체 생성은 1라인에서 bf 라는 이름만 마음대로 바꾸면 된다.

입력은 문자열과 숫자가 받는 방식이 좀 다른데 readLine() 메소드의 결과가 문자열이기때문에 정수형 변수에 저장할 경우 Integer 로 형변환을 해주어야 한다.

주의할 점이 있다면 예외처리를 꼭 해주어야 한다는 점인데 보통은 throws IOException 을 통해 해결한다.

이렇게 하면 버퍼를 통해 읽어들인 문자열 or 숫자를 변수에 저장한 것이다.

StringTokenizer


읽어들인 데이터는 Line 단위로만 나뉘어지기 때문에 공백단위로 데이터를 가공하려면 따로 작업을 해줘야 한다.

1
2
3
4
5
6
String s = "123 456";
StringTokenizer st = new StringTokenizer(s); // 객체생성 및 s를 st의 인자로 전달
String a = st.nextToken(); // 공백으로 나뉘어진 s의 123이 문자열로 a에 저장
int b = Integer.parseInt(st.nextToken()); // s의 456이 숫자로 b에 저장

String array[] = s.split(" "); // 공백마다 데이터를 끊어 배열에 저장함

위 코드에서 보이듯 StringTokenizer에 nextToken()함수를 사용하면 readLine()을 통해 입력받은 값을 공백단위로 구분하여 순서대로 호출할 수 있다.

다음으로는 String.split()함수가 있는데 문자열을 공백단위로 끊어 배열에 저장하는 방식이다.

BufferdedWriter


일반적으로 문자를 출력할때에 System.out.println();을 사용한다.

크지않은 양의 출력일 경우 성능차이는 체감하지 못할 수 있지만 출력이 많아진다면 입력과 마찬가지로 버퍼를 활용하는것이 효율적이다.

1
2
3
4
5
6
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); // 객체생성
String s = "12345"; // 출력할 문자열
bw.write(s); // 버퍼에 "12345"가 담긴 s 문자열을 올린다.
bw.newLine(); // 줄바꿈
bw.flush(); // 버퍼에 있는 데이터를 모두 출력시킴
bw.close(); // 스트림을 닫음

BufferedWriter의 경우 버퍼를 잡아 놓았기 때문에 반드시 flush() / close() 를 반드시 호출하여 버퍼를 비우고 닫아주어야 한다.

또한, bw.write에는 System.out.println();과 같이 자동개행기능이 없기때문에 개행을 해주어야 할 경우에는 \n을 통해 따로 처리해주어야 한다.

사용 예제


백준에 있는 문제를 풀어보자.

백준 문제번호 15552번 (빠른 A+B)

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

int x = Integer.parseInt(bf.readLine());

for (int i = 0; i < x; i++) {
StringTokenizer st = new StringTokenizer(bf.readLine(), " ");
bw.write((Integer.parseInt(st.nextToken()) + Integer.parseInt(st.nextToken())+ "\n");
}
bw.flush();
bw.close();
}

라인 1 : throws IOException을 통해 버퍼 사용에 필요한 예외처리를 해줌

라인 2,3 : 버퍼 입출력 객체 생성

라인 5 : 정수형 변수에 반복횟수 지정을 위해 readLine()함수로 입력을 받고 정수형으로 형변환

라인 8 : st변수명으로 StringTokenizer 객체 생성과 동시에 입력을 공백으로 나누어 받음

라인 9 : 버퍼에 라인 8에서 입력된 나눠진 문자열을 첫번째와 두번째 것만 정수로 변환한 뒤 더하고 그 값을 버퍼에 올린다. 이후 \n으로 버퍼내부에서 개행

라인 11 : 반복문을 통해 버퍼에 작성된 데이터를 모두 출력한다.

라인 12 : 스트림을 닫는다.


스캐너보다는 다소 복잡하다. 그래도 속도면에서 확실한 이점이 있다고 하니 버퍼 사용에 익숙해지는것이 좋을 것 같다.

Reference.

[Java] BufferedReader, BufferedWriter를 활용한 빠른 입출력 - 코딩팩토리

(JAVA) 제네릭(generic)

요즘 쓰는 포스팅의 주제는 항상 듣고있는 강의에서 나온다.

강의에서는 제네릭을 직접 언급하지는 않았지만 List 자료형이나 Map 자료형들을 사용할때 제네릭 <> 을 사용한다.

제네릭을 알고는 있었지만 모양만 알고있었을뿐 용법과, 사용처에대해서는 몰랐기에 포스팅 작성을 통해 알아보려 한다.

제네릭(generic) 이란 ?


자바에서 제네릭이란 데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미한다고 한다.

….

여전히 모호하다. 그래서 다른 글을 읽어본 결과

클래스 / 인터페이스 / 메소드 에서 사용할 매개변수의 타입을 클래스 외부에서 설정하는 것 이라고 한다.

그렇다면 제네릭을 사용하는 이유는 무엇일까

제네릭을 사용하는 이유

제네릭 타입을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있기 때문이다.

자바 컴파일러는 코드에서 잘못 사용된 타입 때문에 발생하는 문제점을 제거하기 위해 제네릭코드에 대해 강한 타입 체크를 한다.

실행시 에러가 나는 것보다 컴파일 시 미리 강하게 체크하여 에러를 사전방지하는게 더 좋기 때문.

또 제네릭 코드를 사용하면 타입을 국한하기 때문에 요소를 찾아올 때 타입 변환을 할 필요가 없어 프로그램 성능이 향상된다.

  • 강한 타입체크를 하여 에러를 사전방지하기 위함
  • 타입 변환을 할 필요가 없게 타입을 국한하여 프로그램 성능 향상을 위함

제네릭의 사용


제네릭 타입은 타입을 매개변수로 갖는 클래스와 인터페이스를 말한다.

제네릭 타입은 클래스 또는 인터페이스 이름 뒤에 <> 부호가 붙으며 사이에 타입 파라미터가 위치한다.

1
2
public class 클래스명<T> {}
public interface 인터페이스명<T> {}

타입 파라미터는 정해진 규칙은 없으며, 일반적으로 대문자 알파벳 한글자로 표현한다.

자주 사용하는 타입 파라미터

타입 파라미터
<T> Type
<E> Element
<K> Key
<N> Number
<V> Value
<R> Result

제네릭의 사용 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class exam<T> {

List<T> examList = new ArrayList<>();

public void method1(T input) {
examList.add(input);
}
}

class GenericClass {
public static void main(String[] args) {
exam<String> stringExam = new exam<>();

exam<Integer> integerExam = new exam<>();
}
}

exam 클래스는 로 제네릭이 설정되어 있고 내부의 List타입의 examList 객체도 로 제네릭 설정이 되어있다.

GenericClass 클래스는 두 개의 객체 stringExam과 intergerExam 이 있으며 두 객체의 타입은 exam 타입이다.

exam 타입은 선언부에 제네릭이 설정되어있으니 사용하는 쪽에서도 제네릭 타입을 명시 해줘야 한다.

이 코드에서는 각각 로 설정해주었다.

이제 stringExam객체는 타입이 String인 데이터만, integerExam객체는 타입이 int 인 데이터만 사용할 수 있다.

정말 그런지 확인을 해 보면

stringExam.method1의 매개변수로 문자열을 주었을때는 컴파일에러가 발생하지 않다가

문자열만 들어가야하는 List에 정수를 매개변수로 주니 컴파일 에러가 발생한다.

반대의 경우도 마찬가지.

이와같이 제네릭을 사용할 경우 클래스 내부에서 사용하는 데이터의 타입을 지정할 수 있으며,

타입을 잘못 사용하여 발생하는 에러를 최소화 할 수 있다.

이는 회원가입폼의 ID란에서 특수문자를 사용하지 못하게 예외처리를 하는것과 비슷하다.

제네릭에 사용 가능한 타입


데이터 타입의 종류

여기서 간단히 데이터 타입의 종류와 특징을 알아보면

기본형 타입 참조형 타입
종류 - 정수형 : byte, short, int, long
- 실수형 : float, double
- 문자형 : char
- 논리 타입 : boolean
기본형 이외의 모든 형태
- 배열(array[])
- 열거(enum)
- 클래스(class)
- 인터페이스(interface)
특징 - 직접 값을 넣을 수 있음
- 스택 영역에 저장
- 값들을 저장하고 있는 객체를 가르키는 ‘주소’를 넣을 수 있음
- 힙 영역에 저장

그렇다면 제네릭에는 어떤 타입을 설정할수 있을까?

제네릭의 타입으로는 참조형 데이터 타입만 설정 가능하다.

int, byte, double, char 같은 기본형 데이터 타입은 설정할 수 없다.

?????

분명 우리는 위 코드에서 정수형데이터로 Integer가 입력이 되는것을 보았다.

Integer와 같은 형태를 래퍼클래스(wrapper class) 라고 한다.

래퍼클래스는 기본형 데이터 타입을 참조형 데이터 타입으로 바꿔주는 클래스다.

기본형 데이터 타입의 래퍼클래스

위 코드에서는 기본형인 int 가 아닌 래퍼클래스인 Integer를 사용함으로써 설정이 가능했던 것이다.

Reference.

제네릭(Generic) 사용법 & 예제 총정리 - 코딩팩토리

제네릭의 개념 - TCP SCHOOL.com

Java 제네릭(Generic) 이란?? - 비실이의 개발 성장기

(JAVA) 자바 플랫폼 3대 구성요소 (JDK, JRE, JVM)

자바, 스프링 강의를 듣거나 책을 보다보면 JDK, JRE, JVM에 대한 내용이 가끔 나온다.
강의에서 나올때마다 자바의 필수요소인가보다 하며 지나쳤었지만 이번 포스팅을 통해 짚어보고 넘어가려 한다.

JDK (자바 개발 키트, Java Development Kit)


JDK란

자바 플랫폼의 등장 이래 지금까지 가장 널리 사용되고 있는 소프트웨어 개발 키트(SDK) - 위키백과

즉, Java로 소프트웨어를 개발할 수 있도록 여러 기능들을 제공하는 패키지이다.

JDK의 구성

Java로의 개발을 담당하는 패키지답게 매우 많다. 자주 들어본것만 간추려보자면

  • javac : 자바 컴파일러, 자바 소스파일(.java)을 바이트코드(.class)로 변환한다.
  • java : javac가 만든 클래스 파일을 해석 및 실행한다.
  • jar : 서로 관련있는 클래스 라이브러리들과 리소스를 하나의 파일로 묶어주는 도구이다.
  • JDB : Java Debugger, 자바 디버깅 툴
  • JRE (Java Runtime Environment) : Java가 동작하는데 필요한 JVM, 라이브러리 등 다양한 파일들을 포함한다. Java를 실행시키기 위해필요하다.
  • JVM(Java Virtual Machine) : Java가 실제로 동작하는 가상환경. JVM 덕분에 하나의 소스파일을 만들더라도 여러 환경(OS) 에서 원활히 동작이 가능하다.

JDK의 종류

  1. Java SE : Standard Edition
    표준 자바 플랫폼으로 표준적인 컴퓨팅 환경을 지원하기 위한 자바 가성머신 규격 및 API 집합을 포함한다.
    이후의 Java EE, Java ME는 구체적인 목적에 따라 Java SE를 기반으로 API를 추가하거나 자바 가상머신 규격 및 API의 일부를 택하여 정의 된다.
  2. Java EE : Enterprise Edition
    Java SE에 웹 어플리케이션 서버에서 동작하는 기능을 추가한 플랫폼
    이 스펙에 따라 제품을 구현한 것을 웹 어플리케이션 서버( WAS )라 한다.(톰캣 등)
  3. Java ME : Micro Edition
    제한된 자원을 가진 휴대전화, PDA 등에서 Java 프로그래밍 언어를 지원하기 위해 만든 플랫폼 중 하나이다.

JRE (자바 런타임 환경, Java Runtime Environment)


컴파일된 자바 프로그램을 실행시킬 수 있는 자바 환경을 말한다.
자바 프로그램을 실행시키기 위한 라이브러리, JVM, 기타 파일들을 포함하고있다.
자바 프로그램을 실행시키기 위해서는 JRE를 반드시 설치해야하고 JRE에는 개발과 관련된 도구는 포함되어 있지 않다.

JVM (자바 가상 머신, Java Virtual Machine)


JVM은 Java Virtual Machine(자바 가상머신)의 약자로 자바 소스코드로 만들어지는 자바 바이트코드 파일을 실행할 수 있다.
JVM은 자바와 다르게 플랫폼에 의존적므로 맥, 윈도우, 리눅스의 JVM은 각각 다르다.
하지만 컴파일된 바이트코드 파일은 어떤 JVM에서도 동작시킬 수 있다. 즉, 코드를 작성하면 JVM이 설치된 어떤 플랫폼에서도 동작시킬수 있다.

JVM의 역할은 다음과 같다.

  • 바이트코드를 읽는다.
  • 바이트코드를 검증한다.
  • 바이트코드를 실행한다.
  • 실행환경(Runtime Environment)의 규격을 제공한다. (필요한 라이브러리 및 기타파일)

마지막으로 각각의 관계를 살펴보면 다음 그림과 같다.
JDK, JRE, JVM의 관계

Reference.

자바 개발키트 위키백과
[Java] Java SE, JDK, JRE

깃허브 프로필 꾸미기

아직까지 꾸준히 깃허브와 블로그를 통해 글을 쓰고있지만 사실 처음부터 마음에 걸렸던 것이 두 가지가 있다.

그건 블로그의 테마(현재 ICARUS)와 다른사람들에 비해 아무 손질도 안한듯한 내 깃허브 프로필인데 이 두 가지 중 오늘은 깃허브 프로필을 조금이라도 꾸며보려고 구글링을 하고 포스팅도 한다.

일단 지금 포스팅과 꾸미기를 동시에 할 예정인데 아래는 꾸미기 전의 내 프로필이다.

Untitled

뭔가 있긴 있어도 재미없고 딱딱한느낌이다

리포지토리 생성


Untitled

깃허브에는 숨겨진 기능이 있는데, 바로 자신의 닉네임으로 리포지토리를 만드는 것이다.

이 리포지토리는 깃허브 프로필에 README.md를 추가해 주는데 이를 통해서 프로필을 꾸밀수 있다.

이후 리포지토리 연결은 여기를 참고하면 된다.

Untitled

만약 README.md가 추가되지않았다면 간단히 추가할 수 있다. (이후 git pull을 통해 로컬로 당겨와 동기화 필수)

Untitled

기본적으로 만들어진 README.md의 프로필 적용모습

Untitled

파일에는 주석으로 간단한 설명들이 적혀져 있고, 이젠 이 README.md를 꾸며주면 된다.

Shields.io 를 통해 README에 뱃지넣기


Untitled

다른사람들의 깃허브 프로필을 보면 위와같은 뱃지들이 있는것을 자주 볼수 있다.

이뻐보이는데? 나도 안 할 수 없다 프로필에 뱃지를 넣는 방법을 알아보자

  1. README.md에 다음과 같이 코드를 작성한다.
1
2
<a href="클릭시 이동할 링크" target="_blank"><img src="https://img.shields.io/badge/뱃지내용-배경색?style=뱃지모양&logo=로고이름&logoColor=로고색상"/></a>
<!--단락 상단 뱃지 코드--><a href="링크" target="_blank"><img src="https://img.shields.io/badge/GithubBlog-skyblue?style=flat&logo=Blogger&logoColor=FFFFFF"/></a>

각각의 설명은 다음과 같다.

  • 뱃지내용 : 뱃지에 작성될 텍스트
  • 배경색 : 뱃지의 배경색

Untitled

등등 헥스코드사용도 가능

  • 뱃지모양

Untitled

위 사진 스타일 다섯가지 중 선택할수 있고 위에 단락 상단 예시의 경우엔 flat 을 사용했다.

사진을 보고 헷갈려서 logo=appveyor 까지 넣으면 안된다.

  • 로고이름

Simple Icons

위 사이트에서 찾고싶은 아이콘을 검색하고

Untitled

적당한 아이콘을 찾아 아이콘의 이름을 입력해주면 된다.(사진에선 Blogger)

  • 로고색상

로고색상은 아이콘마다 색이 있긴하지만 다른색으로 바꾸어 적용할수도 있다.

색상의 적용에 대해서는 배경색 부분 참고.

더 상세한 기능은 Shields.io 사이트에서 확인 후 적용 가능하다.

README에 깃허브 스탯 표시하기


Untitled

위 뱃지들이 익숙하다면 아마 이것도 접해보지 않았을까 싶다.

바로 깃허브 스탯이다.

깃허브의 여러 정보를 알려주는 위젯? 같은건데 이걸 한번 설정해보자.

  1. README.md에 다음 코드를 작성한다.
1
![WooJeong's GitHub stats](https://github-readme-stats.vercel.app/api?username=유저네임&show_icons=true&theme=radical)
  1. 유저네임 부분을 지우고 본인 깃허브 ID를 작성한다.

이렇게 감사하게도 단 두단계로 이루어져있다…

테마나 아이콘을 바꿔가며 꾸밀수도 있는데 아래 링크에서 확인 할 수 있다.

github-readme-stats/README.md at master · anuraghazra/github-readme-stats

REFERENCE.

깃허브 프로필 꾸미기!

(JAVA) 집합 (Set) 자료형

Map자료형에 대해 포스팅하던중 keySet() 메소드의 리턴값이 Set 자료형인걸 알게되었지만 난 그게 무슨 자료형인지 몰랐다.

자바에 자료형이 많은건지 기초공부를 제대로 안한건지 내가 모르는 자료형에는 Map과 Set외에도 Enum 등의 자료형이 있었다.

그래서 일단은 강의를 보면서 중간에 이해를 못하는일을 줄이기 위해 기본적인 자료형들의 사용법을 간단히 포스팅 하려 한다.

집합 자료형의 생성


집합 자료형은 아래 코드처럼 HashSet을 사용하여 만들 수 있다.

1
2
3
4
5
6
7
8
9
import java.util.Arrays;
import java.util.HashSet;

public class SetExample {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>(Arrays.asList("H", "e", "l", "l", "o"));
System.out.println(set); // [e, H, l, o] 출력
}
}

Set 자료형 역시 Map 자료형 처럼 인터페이스이며, Set 인터페이스를 구현한 자료형에는 HashSet, TreeSet, LinkedHashSet 등이 있다.

집합 자료형의 특징


분명 H,e,l,l,o 가 들어있는 배열로 HashSet 자료형을 만들었으나 출력을 보면 l 한개가 빠져있고 순서도 뒤바뀌어 있다.

그 이유는 집합자료형에는 다음과 같은 특징이 있기 때문이다.

  • 중복을 허용하지 않음
  • 순서가 없음

리스트나 배열은 순서가 있으므로 인덱싱을 통해 자료형의 값을 얻을수 있지만 Map과 Set 자료형은 순서가 없기때문에 인덱싱으로 값을 얻을 수 없다.

교집합, 합집합, 차집합 구하기


교집합, 합집합, 차집합을 구하기 위해 일단 중복되는 숫자가 있는 각각의 배열로 Set 자료형을 2개 만든다.

1
2
3
4
5
6
7
8
9
import java.util.Arrays;
import java.util.HashSet;

public class SetExample {
public static void main(String[] args) {
HashSet<Integer> set1 = new HashSet<>(Arrays.asList(1,2,3,4));
HashSet<Integer> set2 = new HashSet<>(Arrays.asList(3,4,5,6));
}
}

교집합

1
2
3
HashSet<Integer> inter = new HashSet<>(set1); // set1 으로 inter 생성
inter.retainAll(set2); // 교집합 수행
System.out.println(inter); // [3, 4] 출력

위에서 만든 set1과 set2의 교집합을 구하는 방법이다.

set1이 있던 inter에 retainAll 을 통해 set2를 교집합해주니 원본이 사라지고 교집합의 결과가 inter가 되었다.

합집합

1
2
3
HashSet<Integer> union = new HashSet<>(set1);
union.addAll(set2); // 합집합 수행
System.out.println(union); // [1, 2, 3, 4, 5, 6] 출력

합집합을 구하는 방법.

set1으로 union을 만들고 addAll 의 인자로 set2를 주면 union의 원본이 사라지고 합집합의 결과가 union에 저장된다.

차집합

1
2
3
HashSet<Integer> sub = new HashSet<>(set1);
sub.removeAll(set2); // 차집합 수행
System.out.println(sub); // [1, 2] 출력

차집합을 구하는 방법.

set1 으로만든 sub에 removeAll 의 인자로 set2를 주면 sub의 원본이 사라지고 sub와 set2의 차집합의 결과가 sub에 저장된다.

집합 자료형 관련 메소드


값 추가하기(add)

집합 자료형에 값을 추가할 때 add 메소드를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
import java.util.HashSet;

public class SetExample {
public static void main(String[] args) {
HashSet<String> addSet = new HashSet<>();
addSet.add("마우스");
addSet.add("키보드");
addSet.add("스피커");
System.out.println(addSet); // [스피커, 키보드, 마우스] 출력
}
}

값 여러개 추가하기(addAll)

여러개의 값을 한꺼번에 추가 할 때 addAll 메소드를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
import java.util.Arrays;
import java.util.HashSet;

public class SetExample {
public static void main(String[] args) {
HashSet<String> addAllSet = new HashSet<>();
addAllSet.add("마우스");
addAllSet.addAll(Arrays.asList("마이크", "모니터"));
System.out.println(addAllSet); // [마이크, 모니터, 마우스] 출력
}
}

합집합을 구할때의 addAll 사용법과 같다.

특정 값 제거하기(remove)

특정 값을 제거하고 싶을 때는 다음과 같이 remove 메소드를 사용한다.

1
2
3
4
5
6
7
8
9
10
import java.util.Arrays;
import java.util.HashSet;

public class SetExample {
public static void main(String[] args) {
HashSet<String> removeSet = new HashSet<>(Arrays.asList("마이크", "모니터", "마우스"));
removeSet.remove("모니터");
System.out.println(removeSet); // [마이크, 마우스] 출력
}
}

이 포스팅은 아래의 출처를 필사하여 작성한 포스팅입니다.

Reference.

점프 투 자바 3-9 집합(Set) - 박응용

(JAVA) 맵 (Map) 자료형

스프링 강의를 듣던중 Map이라는 자료형에 대해 처음으로 접했었는데 어떻게 사용하는지, 문법이 무엇을 의미하는지 전혀 몰랐기 때문에 구글링으로 좀 알아볼 필요성이 생겼다.

key value
music 음악
water

기본적으로 위의 표처럼 파이썬의 딕셔너리, 자바스크립트의 오브젝트같이 key값과 value의 대응관계로 구분된 자료형이다.

Map은 리스트나 배열처럼 인덱스를 통해 해당 요소의 값을 구하는 것이 아닌 key를 통해 value의 값을 얻는다.

HashMap


Map은 인터페이스이며(필자는 포스팅하며 처음 알게되었다.) Map인터페이스를 구현한 Map자료형에는 HashMap, LinkedHashMap, TreeMap 등이 있다.

이번 챕터에서는 이들중 가장 간단한HashMap에 대해 알아본다.

put

제네릭 <> 에서 선언한 타입으로 항목값을 입력할때 사용한다.

1
2
3
4
5
6
7
8
9
import java.util.HashMap;

public class MapExample {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("music", "음악");
map.put("water", "물");
}
}

Map이 그렇듯 HashMap 역시 제네릭을 사용한다.

get

key 값을 통해 value값을 얻을때 사용한다.

1
2
System.out.println(map.get("music")); // 음악 출력
System.out.println(map.get("water")); // 물 출력

위처럼 get 메소드를 통해 key 값을 입력하면 대응하는 value값을 얻을 수 있다.

getOrDefault

맵의 key에 해당하는 value가 없을때 get메서드를 사용하면 null 값이 리턴된다.

이 경우 null 대신 다른 디폴트 값을 얻고 싶다면 getOrDefault 메서드를 사용하면 된다.

1
System.out.println(map.getOrDefault("nation", "Korea")); // nation이 없기 때문에 'Korea' 리턴

containsKey

containsKey 메소드는 Map에 해당 key가 있는지 확인후 그 결과값을 리턴한다.

1
2
System.out.println(map.containsKey("music")); // key값이 있으므로 true
System.out.println(map.containsKey("nation")); // key값이 없으므로 false

remove

remove 메소드는 Map의 항목을 삭제하는 메소드로 key값에 해당되는 항목(key, value)를 삭제한 후 그 value 값을 리턴한다.

1
System.out.println(map.remove("music")); // 음악 출력

map 객체에 있던 “music”와 “음악”항목이 사라지고 “음악”이 리턴 될 것이다.

size

size 메소드는 Map 항목의 갯수를 리턴한다.

1
System.out.println(map.size()); // 1 출력

“music”, “water” 두개의 항목이 있었으나, remove 로 “music”을 삭제했으니 1이 출력 될 것이다.

keySet

keySet은 Map의 모든 key를 모아서 리턴한다.

1
2
3
4
5
6
7
8
9
10
11
import java.util.HashMap;

public class MapExample {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("music", "음악");
map.put("water", "물");

System.out.println(map.keySet()); // [music, water] 출력
}
}

이때 리턴되는 자료형은 Set 자료형이며, list 자료형과는 다르지만 변환하여 사용이 가능하다.

Reference.

점프 투 자바 3-8 맵(Map) - 박응용

(JAVA) 오버로딩, 오버라이딩 차이점

자바를 공부하다가 오버로딩(Overloading)과 오버라이딩(Overriding)이란 용어가 나와 헷갈릴수 있겠다 싶어 차이점을 찾아보고 포스팅 하려 한다.

오버로딩(Overloading)


오버로딩이란

같은 이름을 갖고있지만, 매개변수가 서로 다른 형식인 생성자, 메소드를 새로 정의하는 것.

오버로딩의 조건

  • 메소드의 이름이 같아야 한다.
  • 각 메소드 매개변수의 개수, 타입이 달라야 한다.

오버로딩의 예제

<코드>

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
public class example {

//메서드 오버로딩
public void method1() {
System.out.println("매개변수가 없는 매서드");
}

void method1(String var) {
System.out.println("매개변수 문자열 " + var);
}

void method1(int value) {
System.out.println("매개변수 정수타입 " + value);
}

}

class exampleRun {

public static void main(String[] args) {

example ex = new example();
ex.method1();
ex.method1("안녕하세요");
ex.method1(12345);

}
}

<출력>

1
2
3
매개변수가 없는 매서드
매개변수 문자열 안녕하세요
매개변수 정수타입 12345

위 코드에서는 메소드만 오버로딩하였지만 생성자도 오버로딩하여 사용이 가능하다.

코드에서 확인할수 있는 것이 접근제한자가 달라도 오버로딩이 된다는 것이다.

오버로딩의 장점을 살펴보면 다음과 같다.

  • 다형성 구현
  • 소스코드 가독성 증가
  • 메소드 이름 절약

오버라이딩(Overriding)


오버라이딩이란

상속 관계에 있는 부모 클래스에서 이미 정의된 메소드를 자식클래스에서 같은 이름을 가진 메소드로 다시 정의하는 것.

오버라이딩의 조건

  • 메소드의 선언부가 기존메소드와 같을것
  • 접근제한자를 부모클래스의 메소드보다 더 좁은범위로 변경 불가
  • 부모클래스의 메소드보다 더 큰 범위의 예외 선언 불가

오버로딩의 예제

<코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class parent {
void method1() {
System.out.println("이것은 부모클래스의 메소드입니다.");
}
}

class child extends parent {
@Override
void method1() {
System.out.println("이것은 자식클래스의 메소드입니다.");
}
}

class childExample {
public static void main(String[] args) {
parent pa = new parent();
pa.method1();
child ch = new child();
ch.method1();
parent fa = new child(); // 자동 타입변환
fa.method1();
}
}

<결과>

1
2
3
이것은 부모클래스의 메소드입니다.
이것은 자식클래스의 메소드입니다.
이것은 자식클래스의 메소드입니다.

@Override 는 무엇인가?

@Override 는 어노테이션(Annotation)이라는 기능으로 오버라이딩된 메소드의 위에 추가해주면 정상적으로 오버라이딩 되었는지 검증해주는 역할을 한다.

오버로딩, 오버라이딩의 차이점 정리


오버로딩은 새로운 메소드를 추가하는 것이고, 오버라이딩은 상속받은 메소드를 다시 정의하는 것이다.

이를 표로 나타내면 다음과 같다.

오버로딩(Overloading) 오버라이딩(Overriding)
매개변수 달라야 한다. 같아야 한다.
메소드명 같아야 한다. 달라야 한다.
리턴형 달라도 된다. 같아야 한다.
접근제한자 모든 접근제한자 사용 가능 부모클래스의 접근제한자보다 더 넓거나 같은범위의 접근제한자만 가능
적용 범위 동일 클래스 내에서 적용된다. 상속관계에서 적용된다.