Study

[STUDY] JAVA 웹 개발 코스 - 개발 언어

jeonniu 2022. 6. 30. 18:24

 

함수
package 함수;

public class JavaTest{
    static void myFunction(){ // static 으로 선언된 함수는 static으로 선언된 함수에서 선언 가능
        System.out.println("return 값을 가져갈 수 없음");
    }

    static void myAdd(int num){ // 매개변수 int num
        System.out.println("매개변수 하나가 있습니다." + num);
    }

    static void myAdd(String a, String b){
        System.out.println(a + b);
    }
    // 오버로딩: 함수 이름이 같지만 매개변수의 개수나 자료형이 다른 경우 호출 가능

    int myAdd(int a, int b){
        return a+b;
    }

    public static void main(String[] args) {
        JavaTest.myFunction();
        // 함수에 인자값 5를 전달
        JavaTest.myAdd(5);
        JavaTest.myAdd("지", "연");
        // 인스턴스 생성
        JavaTest jt = new JavaTest();
        int result = jt.myAdd(10, 13);
        System.out.println("int myAdd: " + result);
    }
}

오버로딩: 함수 이름이 같지만 그 안의 매개변수의 개수나 자료형이 다를 경우

static: 정적 메소드는 클래스가 메모리에 올라갈 때 정적 메소드가 자동적으로 생성된다. 따라서 호출 할 때 인스턴스를 생성해주지 않아도 된다.

그러나 int myAdd() 함수는 static 메소드가 붙지 않았기 때문에 인스턴스를 생성해서 호출해줘야 한다.

 

 

 

 

 

 

예외 처리
import java.util.Scanner;

public class JavaTest{
    public static void main(String[] args) {
        try{
            // 사용자로부터 입력 받기
            System.out.println("값 입력");
            Scanner sc = new Scanner(System.in);
            int n = sc.nextInt();

            int[] members = {1, 2, 3, 4, 5};
            System.out.println("배열의 값: " + members[n]);

            // 무조건 오류 출력
            // throw new Exception("사용자오류");

        }
        catch (ArrayIndexOutOfBoundsException e){   // 배열의 요소를 초과하였을 때
            System.out.println(e.getMessage());
        }
        catch (Exception e){    // 예외가 발생하였을 때
            System.out.println(e.getMessage());
        }
        finally{    // 오류가 발생하더라도, 발생하지 않더라도 문구 출력
            System.out.println("오류가 발생하였습니다.");
        }
    }
}

 

 

 

 

 

접근 제어자 

 

접근 권한 범위: public > protected > default > private

 

private: private로 선언된 함수, 멤버변수, 생성자는 선언된 클래스 내부에서만 접근 가능

default: 접근 제어자를 사용하지 않을 경우 클래스, 함수, 멤버변수, 생성자는 동일한 패키지에서만 접근 가능 

protected: 함수, 멤버변수, 생성자는 동일 패키지 혹은 다른 패키지의 하위 클래스에서만 접근 가능

public: public으로 선언된 함수, 멤버변수, 생성자는 모든 다른 클래스에서 접근 가능 

 

- final: 클래스 사용 시 다른 클래스에서 상속 불가능, 매소드, 멤버 변수에 사용 시 오버라이딩 수정 불가

- static: 매소드, 멤버변수에 사용, 클래스에 속하게 되며 객체의 생성 없이 바로 접근 가능

- abstract: 클래스 사용시 객체 생성 불가능, 오직 다른 클래스가 상속 받아 사용해야 함 

 

- 메소드 상속시 오직 abstract 클래스에서만 abstract 메소드 정의 가능, 메소드는 바디(중괄호) 부분이 없음

- 메소드 예: abstract void run(): 바디 부분은 상속받은 곳에서 기능에 맞게 작성

 

package 접근제어자;

public class JavaTest{
    public static void main(String[] args) {
        // 접근 권한 범위: public > protected > default > private
        JavaTests javaTest = new JavaTests();
        System.out.println("값 열기 " + javaTest.var);
        System.out.println("문자열 열기 " + javaTest.str);

        // 값과 문자열 초기화
        int var = 0;
        String str = " ";

        JavaTests javaTest2 = new JavaTests(5, "100");
        System.out.println("값 열기 " + javaTest2.var);
        System.out.println("문자열 열기 " + javaTest2.str);

        // javaTest2.a = 5 final로 선언한 변수는 값을 변경하는 것이 불가능
        System.out.println(javaTest2.a);
        // System.out.println(javaTest2.abc); private로 선언한 변수는 외부에서 가져와 사용하는 것이 불가능
        javaTest2.setAbc(100);;
        System.out.println("private 요소 얻기: " + javaTest2.getAbc());
    }
}
package 접근제어자;

public class JavaTests {
    private int abc; // 외부에서 접근 불가능
    final int a = 5; // 값 변경 불가능

    public int var;
    public String str;

    public JavaTests(){
        var = 01;
        str = "null1";
    }

    public JavaTests(int a, String s){
        var = 02;
        str = "null2";
    }

    public int getAbc() { // 값을 가져오고
        return abc;
    }

    public void setAbc(int abc) { // 값을 할당
        this.abc = abc;
    }
}

 

 

 

 

캡슐화

'데이터의 은닉'을 목적으로 함

 

- 민감한 데이터를 private로 감추어서 public 의 setter 또는 getter 메소드로만

private 으로 감춘 데이터에 접근하거나 변경할 수 있음

- 따라서 이러한 private 변수에 read-only(읽기 전용-getter만 제공) 또는 write-only(변경만 가능-setter만 제공)한 선택적 접근을 제어할 수 있음

- public 보다 보안성이 증가하여 멤버변수와 함수를 더 좋게 제어할 수 있음 

 

데이터의 은닉 이외에도 아래와 같은 목적이 있음 

- 유연성: 같은 타입을 이루는 자료형에서 Map, Stack, Queue 와 같은 Collection 변경을 클래스 내부의 일부분만 변경하여 자료형을 변경할 수 있음

- 초기화 작업을 클래스에 모아서 처리함으로써 초기화 중에 데이터의 가공이 일어날 경우 일부분만 변경하면 됨

 

 

 

 

 

상속

부모 클래스가 자식 클래스를 상속

- 상속받을 때는 extends 키워드 사용

package 상속;

// 부모, 상위 클래스
public class Vehicle {
    protected String model = "홍길동";

    public void sound(){
        System.out.println("정적이 울렸습니다.");
    }
}
package 상속;

// 자식, 하위 클래스 
public class Car extends Vehicle{
    public String handle =" ";

    public void run()
    {
        System.out.println("경적이 울립니다.");
    }

    public void sound()
    {
        System.out.println("경적이 울렸습니다.");
    }

}
package 상속;

public class JavaTest {
    public static void main(String[] args) {
        Car car = new Car(); // 자식 클래스의 인스턴스
        System.out.println("부모 클래스의 요소: " + car.model);
        car.sound();
        System.out.println("자식 클래스의 요소: " + car.handle);
    }
}

- 같은 패키지 내 클래스에서 사용 가능함

- 상속은 코드를 재사용하는데 유용

- 이미 존재하고 검증된 클래스의 멤버변수와 메소드를 재사용 하여 새로운 클래스를 만들 때 사용

 

 

 

 

 

다형성

- 여러 가지 자료형을 가질 수 있는 것을 의미

- 앞의 상속에서 멤버변수, 메소드를 다른 클래스로부터 물려 받음

- 다형성은 이러한 상속받은 메소드들을 동일하게 호출하여 다른 작업을 수행하는 것을 가능하게 함 

 

package 다형성;

// 부모, 상위 클래스
public class Animal {
    public void animalBark(){
        System.out.println("동물 소리");
    }
}
package 다형성;

// 자식, 하위 클래스
public class Bird extends Animal{
    public void animalBark(){
        System.out.println("짹짹");
    }
}
package 다형성;

// 자식, 하위 클래스
public class Dog extends Animal{
    public void animalBark() {
        System.out.println("멍멍");
    }
}
package 다형성;

// 자식, 하위 클래스
public class Owl extends Animal{
    public void animalBark(){
        System.out.println("부엉부엉");
    }
}
package 다형성;

public class JavaTest {
    public static void main(String[] args) {
        /*
        Animal animal = new Animal();
        Animal bird = new Bird();
        Animal dog = new Dog();
        Animal owl = new Owl();

        animal.animalBark();
        bird.animalBark();
        dog.animalBark();
        owl.animalBark();
        */


        // 쉬운 교체로 기존 코드의 재사용
        // 동일한 데이터 타입으로 다양한 작업 수행
        Animal animal = new Owl();
        animal.animalBark();

        // 코드의 확장
        if (animal instanceof Owl) {
            System.out.println("부엉이 입니다.");
        }
    }
}

 

 

오버라이딩

 - 부모 클래스의 메소드를 자식 클래스에서 재정의 하여 사용하는 것

 

 

오버로딩

- 같은 이름의 매소드를 여러개 가지면서 매개변수의 개수, 유형이 다른 것

- 실무에서는 기능이 추가되거나 변경될 때 사용 

 

 

 

추상화 

-  메소드의 세부 사항을 숨기고, 메소드의 필수적인 정보만 보여주는 과정

- 이러한 추상화를 추상 클래스 또는 인터페이스라 함

 

- Abstract class: 객체 생성이 불가능하며 상속 받아서 해당 기능을 사용해야 함

- Abstract method: Abstract class 에서만 Abstract method 생성이 가능하며, 중괄호가 없는 것이 특징 ({,} 없음)

 

- 추상 클래스 Car

package 다형성;

abstract class Car {
    public abstract void start();
    // abstract 클래스는 무조건 abstract 메소드를 재정의 해줘야 함

    public void accelerate(){
        System.out.println("가속합니다.");
    }
}

 

- Car의 자식 클래스 

package 다형성;

public class X500Car extends Car{

	// 오버라이딩: 부모 클래스의 메소드를 자식 클래스에서 재정의 하여 사용하느 것
    @Override
    public void start() {
        System.out.println("X500 모델의 시작");
    }
}

 

- main 클래스

package 다형성;

public class JavaTest {
    public static void main(String[] args) {
        X500Car x500Car = new X500Car();
        x500Car.accelerate();
        x500Car.start();
    }
}

 

 

 

 

인터페이스

- 인터페이스는 완전한 추상 클래스임

- 인터페이스는 멤버 변수를 가지고 있지 않음

- 메소드의 바디{} 부분이 비어 있으며, 관련된 메소드들을 그룹화하는데 사용함

- 인터페이스는 new를 통한 객체 생성 불가

- 인터페이스는 생성자를 포함할 수 없음

- 인터페이스를 구현한 클래스는 모든 메소드를 오버라이드로 재정의를 해야 함

 

 

package 인터페이스;

public interface Menu {
    public void coffee();

    public void water();

    public void drink();
}
package 인터페이스;

public interface State {
    public void hot();

    public void ice();
}

- 인터페이스의 핵심은 implements 라는 키워드를 사용

- interface 키워드를 통해 인터페이스 생성

- 인터페이스에는 메소드의 이름, 타입만 적어줌

 

package 인터페이스;

public class Client implements Menu, State {

    @Override
    public void coffee() {
        System.out.println("아메리카노 나왔습니다.");
    }

    @Override
    public void water() {
        System.out.println("물 나왔습니다.");

    }

    @Override
    public void drink() {
        System.out.println("음료 나왔습니다.");
    }

    @Override
    public void hot() {
        System.out.print("따뜻한 ");
    }

    @Override
    public void ice() {
        System.out.print("차가운 ");
    }
}

- Menu, State 인터페이스를 implements

 

package 인터페이스;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        Client cnt = new Client();

        System.out.println("어서오세요 손님, 어떤 걸로 드릴까요? (아메리카노, 물, 음료)");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String order = br.readLine();
        System.out.println("음료는 hot/ice 중, 어떤 상태로 드릴까요? ");
        BufferedReader brState = new BufferedReader(new InputStreamReader(System.in));
        String state = brState.readLine();

        switch (order){
            case "아메리카노":
                if(state.equals("hot")){
                    cnt.hot();
                    cnt.coffee();
                }
                else if(state.equals("ice")){
                    cnt.ice();
                    cnt.coffee();
                }
                break;
            case "물":
                if(state.equals("hot")){
                    cnt.hot();
                    cnt.water();
                }
                else if(state.equals("ice")){
                    cnt.ice();
                    cnt.water();
                }
                break;
            case "음료":
                if(state.equals("hot")){
                    cnt.hot();
                    cnt.drink();
                }
                else if(state.equals("ice")){
                    cnt.ice();
                    cnt.drink();
                }
                break;
        }
    }
}

 

출력 결과

 

장점

- 다중 인터페이스를 구현함으로써 대체 가능 

ex) implements Menu, State

 

 

 

 

내부 클래스

 

- 클래스 안에 클래스 선언이 가능함

- 이러한 내부 클래스를 내포하는 클래스와 내부 클래스는 더욱 밀접한 관계를 가지게 되며, 코드를 읽기 쉬워질 뿐만 아니라 서로 연관되어 있기 때문에 관리와 파악이 쉬움

 

package 내부클래스;

public class StudyJava {
    public int a = 5;

    // 내부 클래스
    // 외부 클래스의 멤버 변수를 사용하고 싶기 때문에 static 메서드 붙이지 않는다.
    class InnerTest{
        int b = 7;
        public int add() {
            return a+b;
        }
    }
}
package 내부클래스;

public class JavaTest {
    public static void main(String[] args) {
        StudyJava studyJava = new StudyJava();
        // StudyJava 객체 안에 있는 내부 클래스인 InnerTest 선언
        StudyJava.InnerTest innerTest = studyJava.new InnerTest();
        System.out.println("더한 값은? " + innerTest.add());
    }
}

 

 

 

 

열거형

- 서로 관련 있는 상수들을 모아둔 클래스와 비슷한 형식

- 제약 사항들

  • 다른 클래스를 상속받을 수 없음
  • 객체의 생성 불가
  • 값의 재할당 불가

- 보통 메소드는 생성하지 않음

package 열거형;

import javax.swing.*;

public class JavaTest {

    // 열거형 선언
    public enum Season{
        // 멤버 변수에 public static final 키워드가 붙은 것과 동일
        // 사용할 상수는 대문자로 적는 것이 원칙
        // 예) 주로 변하지 않는 것들: 모델 타입, 요일, 계절 등..
        Spring, Summer, Autumn, Winter;
    }

    public static void main(String[] args) {
        // 열거형에 선언된 상수들 중 하나로 일단 넣어줌
        Season season = Season.Spring;
        switch (season){
            case Spring:
                System.out.println("봄이다!");
                break;
            case Summer:
                System.out.println("여름이다!");
                break;
            case Autumn:
                System.out.println("가을이다!");
                break;
            case Winter:
                System.out.println("겨울이다!");
                break;
        }
        System.out.print("열거형에서 해당 계절이 적힌 순서는 ");
        System.out.println(season.ordinal() + " 입니다.");

        // season = 0;
    }

}

 

 

 

 

ArrayList

- 배열 대신 자주 사용되며, 배열과 달리 크기를 조절할 수 있음

- 자바의 List 인터페이스를 상속받고 있음 

- 배열의 크기는 한 번 정해지면 크기를 변경할 수 없고, 속도가 약간 더 뛰어남

- 그러나 ArrayList와 배열의 사용 방법은 전혀 다름

package ArrayList;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class JavaTest {
    public static void main(String[] args) {
        // String data
        ArrayList<String> arrayList = new ArrayList<String>();

        // int data
        // ArrayList<Integer> arrayList1 = new ArrayList<Integer>();

        arrayList.add("봄");      // 0
        arrayList.add("여름");    // 1
        arrayList.add("가을");    // 2
        arrayList.add("겨울");    // 3

        System.out.println("계절 뽑기: " + arrayList.get(0));
        System.out.println("ArrayList 크기: " + arrayList.size());

        // ArrayList에 저장된 값 변경 .set(인덱스 값, 변경하고자 하는 값)
        arrayList.set(0, "벚꽃");
        // 변경한 값 출력
        System.out.println("봄 -> " + arrayList.get(0));


        // arrayList의 상위 클래스 : List
        List list;
        list = arrayList;
        arrayList.set(0, "봄");

        System.out.println(list.get(0));        // List 인터페이스에 포함된 모든 list들은 이렇게 사용 가능
        System.out.println(arrayList.get(0));


        // ArrayList 정렬할 때엔 Collections.sort()
        // 지금 ArrayList에는 한글 문자가 들어가 있으므로 ㄱ~ㅎ 순서대로 정렬
        // 영어는 A~ 부터 순서대로 정렬
        // 숫자는 마이너스 포함해서 가장 작은 값부터 ~ 순서대로 정렬
        Collections.sort(arrayList);
        // arrayList.size(): arrayList 크기만큼 반복하기
        for(int i =0; i<arrayList.size(); i++){
            System.out.println("List 정렬 (ㄱ~ ): " + arrayList.get(i));
        }
    }
}

 

 

 

 

 

HashMap

- HashMap은 Map 인터페이스를 구현한 대표적인 Map 컬렉션

- Map 인터페이스의 기능들을 모두 구현할 수 있음

- HashMap을 사용하면 시간 복잡도가 O(1) 로 효율적으로 사용 가능 

- 키와 값의 1:1 저장  ex) "101" 혹은 "apple" 이라는 키로 "사과" 값을 저장하거나 얻을 수 있음

- 키와 값의 자료형은 서로 달라도 괜찮음 

 

package HashMap;

import java.util.HashMap;

public class JavaTest {
    public static void main(String[] args){
        HashMap<String, Object> hashMap = new HashMap<String, Object>();

        // HashMap<key, value>
        hashMap.put("fruit", "복숭아");
        hashMap.put("userVALUE", 23);
        hashMap.put("userID", "아이디");
        hashMap.put("userNAME", "이름");

//        hashMap.put(key) 하면 해당 key에 해당하는 value 출력 됨
        System.out.println("hashMap.get(key value)을 통한 값 얻기: " + hashMap.get("fruit"));

//        get 함수의 data 자료형이 object이기 때문에 오류가 발생
//        int var = hashMap.get("userNAME");

        // String 타입의 key: "userVALUE" 의 value: 300 출력 hashMap<String, Object>
        // 여기서 Object는 int 타입
        int var = Integer.parseInt(hashMap.get("userVALUE").toString());

        // String 타입인 key: "101"의 value: "복숭아" 출력 hashMap<String, Object>
        // 여기서 Object는 String 타입
        String str = hashMap.get("fruit").toString();


        // hashMap의 size 찾기
        System.out.println("hashMap의 size: " + hashMap.size());

        // for( key 값을 가져올 변수 i : key에 해당 되는 값을 가져옴)
        for (String i : hashMap.keySet()) {
            System.out.println("저장된 key: " + i);
            System.out.println(hashMap.get(i));
        }


    }

}

 

package HashMap;

import java.util.HashMap;

public class JavaTest {
    public static void main(String[] args){

        HashMap<Integer, String> hashMap = new HashMap<Integer, String>();

        hashMap.put(01, "key(01)- value");
        hashMap.put(02, "key(02)- value");
        hashMap.put(03, "key(03)- value");
        hashMap.put(04, "key(04)- value");
        hashMap.put(05, "key(05)- value");


        // <Integer, String>
//        int value = Integer.parseInt(hashMap.get(01).toString());
//        System.out.println(value);


        // key: 01
        // hashMap.get() 을 통해 key에 저장된 값 얻어오기
        String str = hashMap.get(01).toString();
        System.out.println(str);

        System.out.println("hashMap 크기: " + hashMap.size());

        for(Integer i : hashMap.keySet()){
            System.out.println("key: " + i);
            // key에 저장된 값 얻어오기
            System.out.println(hashMap.get(i));
        }
    }

}

 

 

 

 

 

Stack

- LIFO(Last In First Out)

- 맨 나중에 넣은 값이 가장 먼저 나오게 됨

ex) 1,2,3,4,5  순서대로 넣고 -> 5,4,3,2,1 순으로 나옴 

- push (값 넣기) pop (값 뽑기)

 

 

package Stack;

import java.util.Stack;

public class JavaTest {
    public static void main(String[] args){

        Stack<Integer> stack = new Stack<Integer>();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        stack.push(4);
        stack.push(5);


        System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
        System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
        System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
        System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
        System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
    }

}

 

 

 

 

Queue

- FIFO(First In First Out)

- 가장 먼저 넣은 값이 가장 먼저 나옴

- java에서는 Queue를 인터페이스로 지원함

- add (값 넣기) poll (값 뽑기)

package Queue;

import java.util.LinkedList;
import java.util.Queue;

public class JavaTest {
    public static void main(String[] args){
        Queue<Integer> queue = new LinkedList<Integer>();

        queue.add(1);
        queue.add(2);
        queue.add(3);
        queue.add(4);
        queue.add(5);

        System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
        System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
        System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
        System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
        System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
    }

}

출력 결과

 

 

 

 

Stream

- 스트림은 단일 방향의 흐름을 의미

- 프로그램이 데이터를 입력 받을 때 사용하는 입력 스트림

- 프로그램이 데이터를 내보낼 때 사용하는 출력 스트림 

 

 

package Stream;

import java.io.*;

public class JavaTest {
    public static void main(String[] args) throws FileNotFoundException {
        try {

            // 복사 할 파일의 경로와
            // 복사 될 파일의 경로를 적음
            String testFile = "C:\\Users\\study\\test.txt";
            String copyFile = "C:\\Users\\study\\copy.txt";

            // 복사할 파일의 내용을 읽을 변수
            FileInputStream input = new FileInputStream(testFile);
            // 복사 될 파일에 내용을 쓰기 위한 변수
            FileOutputStream output = new FileOutputStream(copyFile);

            // 문자가 얼마나 읽어졌나에 대한 숫자를 확인하기 위한 변수
            int readByteNum;
            // 읽어드릴 문자열을 저장하기 위한 byte 배열
            byte[] bytes = new byte[100];
            
            // 파일 복사가 이루어지는 동작 
            while ((readByteNum = input.read(bytes)) != -1){ // testFile에 적힌 내용응 bytes 배열에 저장 
                // 0부터 시작해서 읽은 byte 단위까지 읽을 것임 
                output.write(bytes, 0, readByteNum);
            }
            
            // 사용한 자원을 닫음 
            output.flush();
            output.close();
            input.close();

            System.out.println("Copy finished! - - - ");
        } catch (IOException e) {
            System.out.println("error: " + e.getMessage());
        }

    }
}

- 복사가 완료된 모습을 확인

 

 

 

 

 

스레드

- 비디오 재생 프로그램은 화면 출력과 동시에 오디오 출력도 가능

- 이처럼 한 개의 프로그램에서 여러 작업을 가능하게 해주는 것이  '스레드'

 

package thread;

public class Sound implements Runnable{
    @Override
    public void run() {
        // thread: start() 호출 시에 동작하는 함수
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println("== == == sound == == == ");
                Thread.sleep(300); // 0.3초마다 출력
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
package thread;

public class Main {
    public static void main(String[] args) {
        Sound sound = new Sound();
        Thread thread = new Thread(sound);

        thread.start(); // Sound 클래스의 thread 가 동작

        for (int i = 0; i < 5; i++) {
            try {
                System.out.println("-- -- -- Main -- -- --");
                Thread.sleep(500); // 0.5초마다 출력
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

+ 비동기: 스레드에서 각자의 작업을 서로 병행하면서 실행

- 다수의 작업에 대해 병렬작업을 진행하는 것을 의미

 

+ 동기: 하나하나 작업을 처리해가는 방식

- 멀티스레드 환경에서 같은 자원(변수)을 사용하면 그 자원이 다른 스레드가 작업을 끝날 때까지 기다리는 교착상태가 발생할 수 있음

 

출력 결과

 

 

 

 

싱글톤 패턴

- 시스템 설계에서 다루는 내용

- 싱글톤 패턴은 클래스의 인스턴스가 하나만 생성되도록 하는 개발 방법임

 

적용되는 사례)

  • 응용프로그램에서 단일 창만 띄우거나 클래스의 객체를 생성할 때 사용
  • Spring은 사용자의 요청이 매우 많을 수 있기 때문에 그 때마다 많은 객체를 생성할 수 없음 -> 하나의 객체를 생성하여 스레드로 공유하는 싱글톤 패턴과 비슷한 방식을 사용

 

package SingleTon;


// 멀티 환경에 취약
// 가장 기본적인 싱글톤 형식으로 개발 상황에 맞게 변경이 필요
public class SingleTon {

    // 자기 자신의 객체를 생성
    private static SingleTon singleTonObject;

    // 다른 객체의 생성을 막음
    private SingleTon() {
    }

    public static SingleTon getSingleTonObject() {
        if (singleTonObject == null) {
            singleTonObject = new SingleTon();
        }
        return singleTonObject;
    }
}