본문 바로가기
자바

자바

by kcj3054 2021. 12. 3.

자바에서 공유객체는 어떻게 처리하는 가?

  • 싱글톤을 보자! 싱글톤에서 멀티 스레드가 접근 시 synchronized 키워드를 붙여주는 방법도 있다.

  • staic 변수를 통해서도 가능하다.

package simgleton;

public class Database {

    private String name;
    private static Database singleton;

    //Database 생성자를 private으로 하면 외부에서 마음대로 Dabase 객체를 만들 수 없다
    private Database(String name) {
        this.name = name;
    }
    public static Database getInstance(String name) {
        if(singleton == null) {
            singleton = new Database(name);
        }
        return  singleton;
    }

    public String getName() {
        return name;
    }


}

String

  • String은 기본적으로 불변 객체이다.

  • StringBuffer랑 StringBuilder가 있는데 이것들은 가변이고, 멀티스레딩 데이터가 변경될때 StringBuffer이고, 데이터가 변경되지않고 특수한 상황 StringBuilder이다.

함수형 인터페이스

  • @FuntionalInterface가 있다 이것은 컴파일 돌 때 이게 함수형인터페이스라고 가리키는 용도이다.

  • 함수형인터페이스는 단일 추상 메서드만 허용한다 이유는 람다식에서 타입추론을 할 수 있는데 이것이 단일 추상 메서드가 아니면 타입 추론이 안되기 때문이다.

Exception (예외처리) checkedException과 UncheckedException의 차이를 아시나요?

  • checkedException는 예외처리가 필수이고, 개발자가 컴파일에서 난 에러를 try catch로 잡는 것이고 대표적으로 IOException이 있다.

  • uncheck는 개발자가 잡지 않아도 되는 것이고 런타이 exception이다.

  • thorw는 예외를 발생시킨 것이고, throws는 예외를 미루는 것이다.

인터페이스

  • jdbc는 인터페이스이다, 구현한 구현체들이 oracle, mysql 등이있다.
  • 프로그래머들은 oracle이 구현한 jar파일을 까서 보지않고 interface의 이용 가능한 기능들만 보고 사용한다.
  • 모 회사가 oracle을 사용하다가 mysql로 바꿔야할 때는 구현체만 바꾸면된다. 그렇기 인터페이스를 쓰면 확장성이 좋다
  • 인터페이스의 요소
    • 상수 : 모든 변수는 상수로 치환
    • 메서드 : 모든 메서드는 추상 메서드로 된다.
    • 디폴트 메서드 : 자바 8부터 기본 구현을 가지고 있는 메서드가 있을 수 있고, 구현 클래스에서 재정의 가능
    • 정적 메서드 : 인스턴스 생성과 상관없이 인터페이스 타입으로 사용할 수 있는 메서드
  • 코드 : https://github.com/kcj3054/TIL/blob/main/%EC%9E%90%EB%B0%94/%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4.md

thread

  • java의 thread 인스턴스의 start()가 호출되면 커널 스레드를 할당 받아 사용한다, 커널스레드를 사용하고 반납하는 연산은 비싸다. 무수히 많은 스레드를 사용하고 반납하면 메모리 부족 현상이 발생하거나, 메모리를 해제하는 일도 많아져서 cpu가 할 일이 많아진다, 이러한 것들을 방지하기 위해서 미리 thread를 생성해 놓고 필요할 때만 가져가고, 커널 스레드를 반납하지 않고 재사용할 수 있도록 하는 것이 thread pool이다.
  • thread pool의 thread가 모두 바쁘다면 공간이 남을때까지 작업은 미뤄진다, 각 스케쥴마다 thread pool을 생성하여 운영하는 것이 안정적이다.

satic

  • 자바의 정석을 보면 변수에 인스턴스 변수, 클래스변수가 있다 여기에서 클래스변수를 보자
  • 클래스 변수 : 클래스 선언시 static 키워와 함께 선언된 필드, 이 필드는 모든 인스턴스들이 공유하는 값이다 즉 클래스 하나에 하나의 값이라는 것이다.
  • 클래스 변수라고 붙여진 이유는 객체를 통해서 접근하는 것이 아니라 객체를 생성하지 않더라도 클래스를 통해서 바로 접근 가능하기 때문이다.
  • 밑의 Student 캘르스를 보면 static으로 선언된 getSerialNum()을 보게되면 해당 메서드 안에서는 인스턴스변수인 studentName을 사용할 수 없다 이유는 메모리 할당시기를 보면 알 수 있다, 인스턴스 변수는 해당 객체가 new 된 후에 메모리의 heap영역에 할당되지만, static 이 붙여진 것은 프로그램에 메모리에 로딩될 때 바로 같이 code영역으로 메모리가 할당되게 되어서, 객체가 만들어지기 전부터 static이 붙여진 것들은 메모리가 할당된다.
  • 지역변수는 스태틱메모리에 할당
  • public class Student { private static int serialNum = 10000; int studentID; String studentName; public Student() { serialNum++; studentID = serialNum; } // static 변수는 메모리가 데이터 영역에 생긴다. public static int getSerialNum() { int i = 10; // //static 메서드 안에서는 인스턴스 변수는 사용할 수 없다 //studentName = "홍길동"; return serialNum; } }
  • 밑의 예시를 보면 생성된 객체 studentK를 통해서 static함수를 접근할 수도 있지만, 클래스인 Student를 통해서 바로 static 메서드로 접근할 수 있다.
Student studentK = new Student();
System.out.println(studentK.getSerialNum());
System.out.println(Student.getSerialNum());

ToString

  • ToStringd을 재정의하여 사용한다면 해당 객체의 필드 값들이 호된다.
  • 그러나 String은 재정의하지않고 사용해되 바로 필드값이 나오게된다, 왜냐하면 내부에 이미 toString이 재정의 되어있기 때문이다.

//String은 내부에 이미 toString이 재정의 되어있다  
String str = new String("test");  
System.out.println(str); // test가 출련된다.

Equals

  • 일반적으로 자바는 '==' 연산은 객체가 가리키는 레퍼런스가 같은지를 묻는 것이다. 그래서 new를 두번해서 같은 타입의 객체를 2개만들면 객체마다 주소가 다르기때문에 'false'가 나오게된다, (다른 힙메모리에 있어서 주소값이 다르다.)
  • Student student1 = new Student(1001, "aaa1"); Student student2 = new Student(1001, "aaa2"); System.out.println(student1 == student2); // false가 나오게된다.
  • ..
  • equals 메소드를 보자, 일반적으로 equals의 원래 구현도 == 와 동일해서 재정의를 통해서 구현해야한다

equals 재정의 부분  
@Override  
public boolean equals(Object obj) {  
if(obj instanceof Student) {  
Student student = (Student) obj;
        if(student.studentID == studentID) return true;
        else return false;
    }
    return false;
}

System.out.println(student1.equals(student2)); // true 발생
  • 일반저그올 논리적으로 동일함을 위해 equals() 메서드를 재정의 하였다면 hashCode() 메서드도 재정의하여 동일한 값이 반환 되도록 해야한다.

왜 Long, Integet reference type을 사용하는가?

  • reference type에는 null을 사용할 수 있지만 primitive type에는 null을 할당 할 수 없다 그러면 primitive type의 null에 .를 찍으면 말 그대로 널포인트 exception이 터지는 것이다.

다형성

  • 다형성에서 알아야할 것은 가상함수이다. 만약 부모에 move()라는 메서드가 있고 자식이 move()함수를 재정의 했다고하면 다형성을 통해서 만들어진 객체는 부모의 것만 호출할 수 있는데 부모의 move가 호출되는 것이 아니라 자식을 통해 재정의된 move가 호출된다. 이것의 이유는 가상함수가 가리키는 레퍼런스가 다르기때문이다.
  • public void moveAnimal(Animal animal)는 매개변수쪽에서 호랑이 사람을 다 받을 수 있는 부모형태인 Animal 타입으로 받아준다.
  • animal.move()를 통해서 재정의된 것의 move를 호출할 수 있지만, 자식 고유한 메서드를 호출하고 싶을때도있다 이럴때는 다운캐스팅을 한 후에 해당 객체의 메서드를 호출하면된다. 예를 들면 밑의 코드에서 read()가 그 예이다.
class Animal {  
public void move() {  
System.out.println("동물이 움직입니다.");  
}  
}

class Human extends Animal {  
@Override  
public void move() {  
System.out.println("사람이 두발로 걷습니다");  
}


public void readBook() {
    System.out.println("사람이 책을 읽습니다");
}

}

class Tiger extends Animal {  
@Override  
public void move() {  
System.out.println("호랑이가 네발로 뜁니다.");  
}


public void hunting() {
    System.out.println("호랑이가 사냥을합니다");
}


}

public class AnimalTest1 {


public static void main(String[] args) {
    AnimalTest1 test = new AnimalTest1();

    //동일한 메소드를 호출하지만 다양한 형태를 가질 수 있다  다형성
    test.moveAnimal(new Human());
    test.moveAnimal(new Tiger());


    List<Animal> animals = new ArrayList<>();
    animals.add(new Tiger());

}

public void moveAnimal(Animal animal) {
    //상속 가상함수 .. .
    animal.move();

    //다운 캐스팅
    // 하위클래스가 상위클래스로 변환하는 것은 명시적으로 이루어진다,
    // 다시 원래 자료형인 하위 클래스로 형 변환 하려면 명시적으로 다운캐스팅을 해야함 ,
    //instanceof -> 원래 인스턴스 타입을 체크하는 예약어가 instanceof이다.
    if(animal instanceof Human) {
        Human human = (Human) animal;
        human.readBook();
    }
    else if(animal instanceof  Tiger) {
        Tiger tiger = new Tiger();
        tiger.hunting();
    } else{
        System.out.println("지원되지 않는 기능입니다 ");
    }
}


}

hashMap

  • 일반적인 hash에 대해서 조금 짚어서보자
    • 해쉬 코드는 검색을 할 때 O(1)만에 검색이 가능해서 매우 유용한 알고리즘이다
    • 대표적인 형태는 index(저장위치) = hash(kye) ->hash함수에 특정 키 값을 넣으면 저장위치를 빠르게 알 수 있다
    • jvm에서 heap관리르 할 때 hash로 관리한다.
  • 자바의 Map인터페이스를 상속해서 구현한 것들 중에서 hashMap, hashTable, ConcurrentHashMap이 있다. 강의를 듣다보면 hashMap은 멀티스레드 환경에서 쓰면 안된다고 한다 이때는 ConcurrentHashMap을 써야한다고한다. 왜냐? ConcurrentHashMap은 thread - safe해서 연산이 이루어질 때도 값의 atomic이나 정합성을 유지해 주기때문이다.
  • hashTable도 스레드 세이프하기는 하다 그러나 차이점이 있다. hashTable은 메소드 전체 synchronized 키워드가 붙어 있어서 비용측면에서 조금더 비싸다고한다. 또한 hashTable은 key, value에 null을 허용하지않는다 (hashMap은 null을 허용)
  • ConcurrentHashMap은 스레드 세이프한데 부분적으로 lock을 걸어서 성능적으로 hashTable보다 더 이득이다.

스트림

  • 스트림이란? -> 컬렉션 요소들을 하나씩 참조해 람다식으로 처리할 수 있는 반복자, 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 값 요소

  • 스트림은 한번 생성하고 사용한 스트림은 재사용할 수 없다,

  • 기존 자료를 변경하지 않는다 (스트림을 생성하면 별도의 메모리 공간을 사용하므로 기존 자료를 변경하지 않음)

  • 스트림 연산은 중간연산과 최종연산으로 구분된다. 최종연산이 호출되어야 중간 연산의 결과가 모두 적용된다, 이를 지연연산이라고한다

  • 예제 코드

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


        List<String> sList = new ArrayList<>();
        sList.add("Tomas");
        sList.add("James");
        sList.add("Edward");

        Stream<String> stream = sList.stream();
        stream.forEach(s -> System.out.println(s));

        sList.stream().sorted().forEach(s -> System.out.println(s));
    }
}
  • 장점
    • 명시적이다
    • double average = emps.stream() .filter(emp -> emp.getSalary() > 100000000) .mapToInt() .average() .orElse(0);
    • 각각의 메소드명으로 명시적으로 기능을 알 수 있다.
  • 사용법
      1. stream(스트림생성) 2. filter, map -> 데이터 처리 연산 3. collect 최종연산
  • flatMap
    • List manyFish = fishes.stream()
      • 리스트를들 묶는다고 생각하면 좋다 묶고 -> collect로 연결시킨다.
    • .map() .flatMap(List::stream) .collect(Collectors.toList())
  • reduce
  • - reduce(10(초기값), (acc, x) -> acc + x) => acc + x에 해서 값들이 더해진다.

불변객체, final 키워드

  • 불변객체, final
    • 자바에서 final를 붙이면 변경될 필요가 없는 부분에는 final을 붙여서 실수를 방지할 수 있다. -> 안정성, 버그 발생을 줄임,
    • 불변객체는 객체에 final을 붙인 것이다. -> 한 번 생성되면 상태를 수정할 수 없는 객체이다.
  • 불변객체 예제
package com.example.socket;

import java.lang.management.MonitorInfo;

public final class App {
    private final int money;


    private App(final  int money){
        this.money = money;
    }

//    public 정적 팩토리 메서드
    public static App of(final  int money) {
        return new App(money);
    }
}
  • 불변객체를 사용하면 스레드 동기화 문제를 방지할 수 있다. 왜냐 ?
package com.example.socket;

import java.lang.management.MonitorInfo;

public final class Money {
    private final int money;


    private Money(final  int money){
        this.money = money;
    }

//    public 정적 팩토리 메서드 ?
    public static Money of(final  int money) {
        return new Money(money);
    }


    public Money plus(final  int money) {
        return new Money(this.money + money);
    }
}
  • 위의 예제 plus에서 plus를 호출할 때마다 새롭게 불변객체를 만들어서 서로에게 영향을 끼치지 않는다.
  • 불변객체는 사용해야한다 -> 신뢰성 있는 코드를 만들 수 있고, 오류가 생길 여지(멀티스레딩에서 동시성 문제)도 적다
  • 출처 : 우테톡, 모던 자바 인 액션,

'자바' 카테고리의 다른 글

jvm구조, 리플렉션  (0) 2021.12.29
lambda, stream, innerclass  (0) 2021.12.22
split(), StringTokenizer 차이  (0) 2021.12.21
cpp vs java (비교)  (0) 2021.12.10
toString  (0) 2021.12.02