자바에서 공유객체는 어떻게 처리하는 가?
싱글톤을 보자! 싱글톤에서 멀티 스레드가 접근 시 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);
- 각각의 메소드명으로 명시적으로 기능을 알 수 있다.
- 사용법
- stream(스트림생성) 2. filter, map -> 데이터 처리 연산 3. collect 최종연산
- flatMap
- List manyFish = fishes.stream()
- 리스트를들 묶는다고 생각하면 좋다 묶고 -> collect로 연결시킨다.
.map() .flatMap(List::stream) .collect(Collectors.toList())
- List manyFish = fishes.stream()
- 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 |