Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JAVA A TO Z #4

Open
skarltjr opened this issue Dec 12, 2020 · 30 comments
Open

JAVA A TO Z #4

skarltjr opened this issue Dec 12, 2020 · 30 comments
Labels

Comments

@skarltjr
Copy link
Owner

skarltjr commented Dec 12, 2020

todo

@skarltjr
Copy link
Owner Author

skarltjr commented Dec 12, 2020

자바의 실행과정

1. 컴파일 과정

  • compile이란 사람이 이해하는 언어를 기계가 이해하는 언어로 바꾸는 것
  • 즉 자바에서 compile이란 .java파일을 읽어 바이트코드(.class)로 변환하는 것
  • 이는 javac(자바 컴파일러)를 통해서

2. 빌드 과정

  • build란 소스코드 파일을 실행가능하게 만들어 주는 것
  • 자바 컴파일러를 통해 자바 클래스 파일(.java)를 자바바이트코드(.class)로 변환한다.
  • 클래스 로더를 통해 자바 바이트코드를 JVM 런타임 데이터 영역에 로드한다.
  • 실행 엔진을 통해 실행한다.

3. 바이트 코드란 :

  • 중간코드는 JVM 이 읽을 수 있는 언어. JVM에서는 이 중간 코드를 바이트코드
    JVM은 이 바이트코드를 읽어 들여서 컴퓨터 이해할 수 있는 언어로 변환한다.

4. jit컴파일러란 :

  • Just In Time. 프로그램을 실제 실행하는 시점에 번역하는 컴파일 기법이다.
    javac의 컴파일과 JIT의 컴파일은 다르다.
    자바컴파일러(javac)는 자바 소스코드를 바이트코드로 변환하고 JIT는 변환된 바이트코드를 해석하고 실행한다.
  • 같은 코드를 매번 해석하지 않고 실행할 때 컴파일 하면서 코드를 캐싱 한다.
    사전에 정의된 임계치에서 시작하여 호출될 때마다 감소시키는 방식으로 자주 사용되는 메서드를 찾는다.
    자주 사용되는 메서드는 저장해서 해석하지 않고 바로 실행시키는 방식으로 성능을 향상시킨다.
  • 그림을 보면 jit컴파일러는 jvm의 일부.
  • java는 jvm으로 인해 os에 독립적인데 그 중 바이트코드를 해석하는 jit 컴파일러가 jvm에 속한다.
  • 즉 바이트코드를 os에 따라 달리 해석 - jit compiler

Screen Shot 2021-07-28 at 9 17 30 AM

5. 전체적인 실행 과정

  • (1) 내가 소스코드를 작성 ~.java
  • (2) 자바 컴파일러가 소스파일을 컴파일
  • (3) 컴파일된 바이트 코드를 jvm의 클래스로더에게 전달
    • 참고: JVM 의 구성요소는 크게 3가지로 구성 되어있다.
      클래스 로더 시스템 (Class Loader)
      메모리 (Jvm Memory)
      엔진 (Execution Engine)
  • (4) 클래스 로더는 동적로딩을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역. == jvm. Memory에 올린다.
  • (5) 실행엔진은 jvm 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행. 이 때 두가지 방식으로...
      1. 인터프리터 : 바이트 코드 명령어를 하나씩 읽고 해석하여 실행 // 하나하나씩은 빠르나 전체적으로 느리다
      1. jit 컴파일러 : 인터프리터를 보완한다. 코드 전체를 컴파일하여 바이너리 코드로 변경하고 앞에서 봤듯이 한 번 변경한건 캐싱하여 다음에 다시 반복하지 않는다. 즉 다음에는 이미 변경된 바이너리 코드로 직접 실행한다.

세부사항

image

  • 자바의 컴파일
    image

-JVM JRE JDK
java는 os에 독립적 그러나 jvm에게 종속적
image

jit 컴파일러
image

-JVM 의 구성요소는 크게 3가지로 구성 되어있다.
클래스 로더 시스템 (Class Loader)
메모리 (Jvm Memory)
엔진 (Execution Engine)
image

클래스 로더 시스템 (Class Loader)
image

메모리 (Jvm Memory)
image

엔진 (Execution Engine)
실행엔진은 말 그대로 클래스 로딩 과정을 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행.
이제 이 과정을 수행하고 기계가 읽을수 있는 코드로 변경을 해주는데, 이때 사용되는게 인터프리터와 JIT컴파일러 이다.

@skarltjr skarltjr changed the title 준비 JAVA A TO Z Dec 12, 2020
@skarltjr
Copy link
Owner Author

skarltjr commented Dec 12, 2020

-JDK / JRE

-JDK
JDK는 자바 프로그램을 작성하고, JRE를 실행하는데 필요한 툴들을 가지고 있다.
그리고 컴파일러 (javaC) 와 자바 어플리케이션 런처, Appletiviewer 등을 포함하고 있다.
컴파일러는 자바 코드르를 바이트 코드로 변경을 해주는데 이는 JVM이 읽을 수 있는 언어로 변경해주는 것이다.
자바 어플리케이션 런처는 JRE를 실행시키는데, 필요한 클래스나, 메인 메서드를 로딩한다.
JDK > JRE > JVM 이런식으로 가지고 있는 것이다. 그래서JRE와 개발에 필요한 툴을 JDK가 제공해 주는 것이다.

-JRE
JRE는 클래스 라이브러리, JVM, 여러 Supporting 파일들을 가지고 있다. Debuger와 Compiler과 같은 개발 도구는 포함되어있지 않는다. 즉, JRE는 소스코드를 읽기 위해, JDK는 소스코드를 작성하기 위한

  • jdk를 통해 코드를 작성하고 개발할 수 있도록 (컴파일 등)
  • jre를 통해 클래스 로더, 클래스 라이브러리를 통해 작성한 자바 코드를 라이브러리와 결합한 후 JVM에 넘겨 실행.
  • JRE는 그 자체로 특별한 기능을 한다기보다는 JVM이 원활하게 잘 작동할 수 있도록 환경을 맞춰주는 역할.
  • jvm을 통해 바이트코드를 컴퓨터가 이해할 수 있는 언어로 변경하여 실행

@skarltjr
Copy link
Owner Author

skarltjr commented Dec 12, 2020

자바의 PRIMITIVE TYPE(기본 타입/ int long ....) vs REFERENCE TYPE(Integer...)

image

-자바의 타입추론 var
타입추론은 말그대로 개발자가 변수의 타입을 명시적으로 적어주지 않고도, 컴파일러가 알아서 이 변수의 타입을 대입된 리터럴로 추론하는 것이다.
Var는 초기화값이 있는 지역변수 (Local Vairable)로만 선언이 가능하다.

잘못된 var 사용
image
image
image
image
image

@skarltjr
Copy link
Owner Author

skarltjr commented Dec 12, 2020

클래스

  • 객체를 만드는 틀 , 객체를 만들기위한 설계도
  • 클래스를 만들때는 class 클래스명{ } 으로 만들 수 있습니다.

객체 (객체란 속성(상태)과 기능(동작)을 가진 덩어리)

  • 객체는 속성과 기능의 집합이며, 속성과 기능을 객체의 멤버라고 한다.
  • 속성(Property) : 멤버변수(member variable), 특성(attribute), 필드(field), 상태(state)
  • 기능(function) : 메서드(method), 행위(behavior), 함수(function)

인스턴스 (생성된 객체, 어떤 클래스에 속하는 각각의 객체)

  • 객체를 생성하여 JVM(자바 가상 머신)이 관리하는 메모리에 적재된 것을 의미
  • 어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스라고 합니다.
  • 클래스로부터 객체를 만드는 과정을 인스턴스화 라고 한다.
  • 특징 : 클래스가 가지고 있는 메소드(method)를 모두 상속 받는다.
  • 클래스 : 자동차 설계도 / 객체 : 설계도를 통해 만들어진 깡통 차량 / 인스턴스 : 번호판이 달린 나의 차량
  • 객체(Object)는 소프트웨어 세계에 구현할 대상이고, 이를 구현하기 위한 설계도가 클래스(Class)이며, 이 설계도에 따라 소프트웨어 세계에 구현된 실체가 인스턴스(Instance)이다.
  • 소프트웨어 관점에서 인스턴스는 객체가 메모리에 할당되어 실제 사용될 때 인스턴스!

Screen Shot 2021-08-05 at 1 45 16 PM


-자바의 배열
배열 타입은 클래스가 아니다. 하지만, 배열의 인스턴스들은 Obejcts(객체)이다. 즉 배열은 ‘java.lang.Objects’ 에 상속 받는다. 그래서 배열은 ‘Cloneable interface’ (객체 자신의 메모리를 복사하는 Object의 인터페이스)를 implement 받는다.
그리고 CloneNotSupportedException 을 따로 throw로 예외처리를 해주지않고도 완벽하게 보장하는 clone() 메소드를 오버라이딩 한다.

-배열의 직렬화
배열은 또한 ‘Serializable (자바의 직렬화, 자바 시스템 내부에서 사용되는 Object , Data를 외부에서 사용할 수 있게 바이트 형태로 변환하는 기술, 즉 JVM에서 데이터를 저장하는 스택, 힙에 쌓여있는 데이터를 바이트 형태로 변환한다고 생각하면 된다.)’ 인터페이스를 implements 한다. 그러므로 배열을 직렬화 시킨다면, 어떤 배열이든 직렬화가 가능하다.
Array type widening conversions
Array (배열)은 기본적으로 ‘Object’를 상속 받고, ‘Cloneable’ 과 ’ Serializable’ 인터페이스를 implements 하기 때문에, 모든 배열 타입은 다음 세가지 유형중 하나로 변경이 확장 할 수 있다. 하지만 특정 배열 타입은 다른 배열 타입으로 확장할 수 있다. 만약, 어떠한 배열이 T 라는 레퍼런스 타입을 타입으로 지정하고 있고, T타입에서 S타입으로 할당 할 수 있다면,T[ ] 배열은 S[ ] 배열로 할당 할 수 있다.

@skarltjr
Copy link
Owner Author

skarltjr commented Dec 12, 2020

-자바 연산자 중 equals - 동등성 / 동일성 - (== 연산자 (EqualsOperators))

‘==’ 연산자는 기본적으로 프리미티브 타입에 한해서 두 피연산자의 값이 같으면 true, 아니면 false를 리턴한다고 생각하면 된다. 이는 프리미티브 타입에 한해서 ‘Value(값)’이 서로 같은지 비교를 한다. 만약 프리미티브 타입이 아닌 ‘레퍼런스 타입’은 각 객체의 참조 주소를 비교하게 된. 결국 두개의 값이 같은지 판단하는게 아니라, 두개의 주소가 같은지 판단하는 것이다.또한 String 문자열은 서로를 비교할 수 없다.

  • 프리미티브값 끼리의 == 비교는 값의 비교 - 동일성
  • 오브젝트간 equals는 내용(값)의 비교 - 동등성
  • 오브젝트간 == 비교는 주소값의 비교
String st1 = new String("aaa");
String st2 = new String("aaa");
 
System.out.println(st1==st2); // false  객체간 == 은 주소값을 비교
System.out.println(st1.equals(st2));  // true equals는 객체의 값을 비교

‘==’연산자가 같은 타입이지 않을 때, 예를들어보자 만약 short 타입과 long 타입이 있다고 가정해보자 어찌 됐든 이 둘을 비교하기 위해서는 서로 타입이 같은 상황에서 비교가 가능하다. 그러므로 8바이트 짜리를 4바이트로 변경하는 건 위험

-Instanceof 연산자(instanceof operator)
프리미티브타입 x / 레퍼런스타입만 가능

        System.out.println("str" instanceof String);//true
        System.out.println("" instanceof Object);//true
        System.out.println(null instanceof Integer); // null은 instanceof 어디에도 false
        Object o = new int[]{1, 2, 3};
        System.out.println(o instanceof Object);//true
        System.out.println(o instanceof int[]);//true

  • 정리 자바에서 정의하고있는 hashCode규약
    equals() 메소드가 true 이면 , 두 객체의 hashCode값은 같아야한다.
    equals() 메소드가 false 이면 , 두 객체의 hashCode값이 꼭 다를 필요는 없다.

equals와 hashcode

  • 위에서 equals 매서드가 값에 따라 두 객체의 동등성을 비교한다고했다. 그러나 기본 equals매서드는
    public boolean equalse(Object obj) { return this == obj; }
    이 경우 당연히 객체 간 ==비교 -> 주소값비교 -> 여기선 두 객체를 생성했으니 서로다른 주소 ->false
public class main {
    public static void main(String[] args) {
        A a = new A(1);
        A b = new A(1);
     
        System.out.println(a.equals(b)); // false

    }
}
  • 즉 객체간 == 비교 -> 주소값비교다.
  • 올바른 equals를 위해 오버라이딩이 필요하다.
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        A a = (A) o;
        return value == a.value;
    }

    @Override
    public int hashCode() {
        return Objects.hash(value);
    }

hashcode란?

  • 객체 해시코드란 객체를 식별하는 하나의 정수값을 말한다. Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 때문에 객체 마다 다른 값을 가지고있다. 객체의 값을 동등성 비교시 hashCode()를 오버라이딩 할 필요성이 있는데, 컬렉션 프레임워크에서 HashSet, HashMap, HashTable은 다음과 같은 방법으로 두 객체가 동등한지 비교한다.
    화면 캡처 2021-01-29 222719

우선 hashCode() 메소드를 실행해서 리턴된 해시코드 값이 같은지를 본다. 해시 코드값이 같으면 equals() 메소드로 다시 비교한다. 이 두개가 모두 맞아야 동등 객체로 판단한다.

롬복 
@EqualsAndHashCode
equals와 hashcode를 만들어 주는 것

equals: 두 객체의 내용이 같은 지 확인
hashcode: 두 객체가 같은 객체인지 확인

Tip.
@EqualsAndHashCode(of="id"): 연관 관계가 복잡해 질 때, @EqualsAndHashCode에서 서로 다른 연관 관계를 순환 참조하느라 무한 루프가 발생하고, 결국 stack overflow가 발생할 수 있기 때문에 id 값만 주로 사용

@skarltjr
Copy link
Owner Author

skarltjr commented Dec 19, 2020

-자바의 클래스

클래스란?

  • 객체들의 협력 관계를 코드로 옮기는 도구이다.
  • 협력에 참여하여 메시지를 주고 받는 객체를 만드는데 필요한 구현 메커니즘이다.

image

-익명클래스

public class Pet{
    String name = "돼지";
    public String getName(){
            return name;
    }
}
public static void main(String[] args){
    Pet pet = new Pet(){
            String name = "익명 돼지";
            @Override
            public String getName(){
                return name;
            }
    };
    System.out.println(pet.getName()); // 결과 : 익명 돼지
}

-그러나 자바에서 기본 클래스와 익명클래스는 동일하지 않다.
같은 놈이라고 생각하지않는다.

-NEW 키워드

ex) Study라는 클래스가 있을 때
Study study;   --> 는 선언이 된건가? x 
Study study = new Study(); 로  new / 생성자로 초기화 해줘야한다. 

-그런데 String 같은 레퍼런스 타입도 클래스로 정의 되었는데 왜 new를 사용하지 않는가?
new 생성은 Heap 메모리 영역에, ' = " "' 은 상수 풀 (Runtime constant pool)에 저장

image
image

      String asd = "qwe";
        String qwe = new String("qwe");
        System.out.println(asd == qwe); // false
        System.out.println(asd.equals(qwe)); //true

-자바의 생성자
생성자는 이전에 설명한 new 연산자와 사용하고, 객체를 생성하는 역할과 객체 초기화 역할을 한다.
생성자가 제대로 실행도지 않는다면, 객체의 주소값이 리턴도지 않을 뿐더라, 객체가 heap에 올라가지도 않을 것이다

@skarltjr
Copy link
Owner Author

skarltjr commented Dec 27, 2020

-상속 extends
이미 존재하는 클래스를 기반으로 새 클래스를 만드는 방법.

-자식 클래스가 부모 클래스의 한 종류(is-a)
is-a 관계 : 상속관계, 부분집합 관계

-부모와 자식이 있을 경우 부모부터 초기화된다

  1. 메모리에 객체 생성
  2. 부모 생성자 호출
  3. 자식 생성자 호출

-다중 상속
자바는 다중 상속을 지원하지 않는다.

부모의 멤버변수,함수 접근- private protected public

★부모에서 자식으로 타입변환은 안전하지만

자식에서 부모는 컴파일러가 오류를 발생시킨다.
+부모가 동일해도 형제끼리는 캐스팅 할 수 없다.

-상속의 장단점

-장점
재사용성
코드 중복 줄어든다
관련 코드를 한 파일로 관리할 수 있다

-단점

상속 단계가 증가하면 추상화하기 힘들다
잘못된 상속

-super 키워드
super는 현 객체의 부모.
super()는 부모 생성자 호출

-다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
=동적함수

정적 디스패치
컴파일 시점에 어떤 메서드가 실행될지 알 수 있다.

동적 디스패치
method가 ★오버라이드 되어있는 경우 컴파일 시점이 아닌 실행시점에서 어떤 메소드를 실행할 지 결정된다.

-추상클래스
인스턴스를 만들 수 없는 클래스 (인스턴스를 만들 수 있는 클레스는 구체 클래스)
다른 클래스의 부모 클래스가 될 수 있다
반드시 추상 메서드가 있을 필요는 없다

-Final 키워드
클래스 앞에 붙는 final 더 이상 상속하지 못함

@skarltjr
Copy link
Owner Author

skarltjr commented Dec 27, 2020

-Static
1.클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통적으로 사용해야하는 것에 static을 붙인다.

  • 인스턴스를 생성하면, 각 인스턴스들은 서로 독립적기 때문에 서로 다른 값을 유지한다. 경우에 따라서는 각 인스턴스들이 공통적으로 같은 값이 유지되어야 하는 경우 static을 붙인다.
  1. static이 붙은 멤버변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
  • static이 붙은 멤버변수(클래스변수)는 클래스가 메모리에 올라갈때 이미 자동적으로 생성되기 때문이다.
public class main {
   public static void main(String[] args) {
       String name = new pet().name;
       new per.xxxxxxxxxxxxxx; 사용불가 // static이 붙은 메서드(함수)에서는 인스턴스 변수를 사용할 수 없다.
       System.out.println(name);
   }
   public static class pet{
       String name = "qwe";
   }
   public class per{
       String name2 = "qweasd";
   }
}

  1. static이 붙은 메서드(함수)에서는 인스턴스 변수를 사용할 수 없다.
  • static이 메서드는 인스턴스 생성 없이 호출가능한 반면, 인스턴스 변수는 인스턴스를 생성해야만 존재하기 때문에... static이 붙은 메서드(클래스메서드)를 호출할 때 인스턴스가 생성되어있을수도 그렇지 않을 수도 있어서 static이 붙은 메서드에서 인스턴스변수의 사용을 허용하지 않는다. (반대로, 인스턴스변수나 인스턴스메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능하다. 인스턴스변수가 존재한다는 것은 static이 붙은 변수가 이미 메모리에 존재한다는 것을 의미하기 때문이다.)
  1. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.
  • 메서드의 작업내용중에서 인스턴스 변수를 필요로 한다면, static을 붙일 수 없다. 반대로 인스턴스변수를 필요로 하지 않는다면, 가능하면 static을 붙이는 것이 좋다. 메서드 호출시간이 짧아지기 때문에 효율이 높아진다. (static을 안붙인 메서드는 실행시 호출되어야할 메서드를 찾는 과정이 추가적으로 필요하기 때문에 시간이 더 걸린다.)
  1. 클래스 설계시 static의 사용지침
  • 먼저 클래스의 멤버변수중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지

    살펴보고 있으면, static을 붙여준다.

  • 작성한 메서드 중에서 인스턴스 변수를 사용하지 않는 메서드에 대해서 static을

    붙일 것을 고려한다.

예제

class MemberCall {

      int iv = 10;

      static int cv = 20;

 

      int iv2 = cv;

//   static int cv2 = iv;                   에러. 클래스변수는 인스턴스 변수를 사용할 수 없음.

      static int cv2 = new MemberCall().iv;   // 굳이 사용하려면 이처럼 객체를 생성해야함.

 

      static void classMethod1() {

            System.out.println(cv);

//         System.out.println(iv);       에러. 클래스메서드에서 인스턴스변수를 바로 사용할 수 없음.

            MemberCall c = new MemberCall();      

            System.out.println(c.iv);   // 객체를 생성한 후에야 인스턴스변수의 참조가 가능함.

     }

 

      void instanceMethod1() {

            System.out.println(cv);             

            System.out.println(iv);  // 인스턴스메서드에서는 인스턴스변수를 바로 사용가능.

     }

 

      static void classMethod2() {

            classMethod1();

//         instanceMethod1(); 에러. 클래스메서드에서는 인스턴스메서드를 바로 호출할 수 없음.

            MemberCall c = new MemberCall();

            c.instanceMethod1(); // 인스턴스를 생성한 후에야 인스턴스메서드를 호출할 수 있음.

      }

     

      void instanceMethod2() { // 인스턴스메서드에서는 인스턴스메서드와 클래스메서드

            classMethod1();         // 모두 인스턴스생성없이 바로 호출이 가능하다.

            instanceMethod1();

     }

}

@skarltjr
Copy link
Owner Author

skarltjr commented Jan 2, 2021

자바의 패키지

  • Package란 다수의 클래스와 인터페이스를 캡슐화하기 위한 매커니즘이다.

1. 패키지의 역할

패키지는 연관된 클래스를 담는 컨테이너 역할을 한다. 패키지 내에는 외부로부터 접근 가능한 클래스들과 특정 목적을 위해 접근이 제한된 클래스가 존재한다.

1-1) 동일한 이름을 가진 클래스들을 구분할 수 있다.

livestudy.week7.Test , livestudy.week6.Test

1-2) 접근을 제어할 수 있다

protected : 같은 패키지에 존재하는 클래스와 자식 클래스에서 접근 가능
default : 같은 패키지에 존재하는 클래스에서 접근 가능

1-3) 데이터캡슐화(혹은 데이터 은닉)의 용도로 사용할 수 있다.


2. 패키지 사용방법

  • 패키지의 이름은 디렉토리 구조와 동일한 형태를 띄고 있다
  • 예를 들어 패키지 이름이 korea.seoul.gangnam이라면, korea, seoul, gangnam 3개의 디렉토리가 존재함을 의미한다. gangnam디렉토리는 seoul디렉토리 안에 존재하고, seoul디렉토리는 korea디렉토리 안에 존재한다. 이 때, korea 디렉토리는 CLASSPATH변수에 의해 접근 가능하다. korea패키지의 부모 디렉토리가 CLASSPATH에 존재하기 때문이다.

패키지에 클래스 추가

  • 파일 상단에 패키지이름을 추가하여, 해당 클래스를 패키지에 포함시킬 수 있다.
    ex. package javabasic.week7;

하위 패키지

  • 패키지 내에 존재하는 또 다른 패키지를 하위 패키지라 일컫는다.
    하위 패키지는 자동적으로 import 되지 않기 때문에, 명시적으로 import 키워드를 사용하여 가져와야 한다.
    ex. import java.util.*;
    java 패키지 내에 util 이라는 하위패키지가 존재
    또한 하위패키지의 멤버들은 각각 다른 패키지처럼 여겨지기 때문에, 하나의 하위패키지를 import했다고 해서 다른 하위패키지들도 import된 것이 아니다.

3. 패키지 내의 클래스에 접근하기

// util 패키지의 모든 클래스 가져오기
import java.util.*;

// 일반적으로 클래스명은 2개의 패키지에 동일한 이름의 클래스가 존재할 때 구별하기 위해 쓰인다.
import java.util.Date;
import my.package.Date;

4. 패키지의 종류

  • 사용자 정의 패키지
  • Built in package

4-1) Built-in 패키지

  • Java API에 포함되는 다수의 클래스를 포함하는 패키지이다.
  • java.util : 자료 구조 구현을 위한 유틸리티 클래스를 포함하는 패키지
    Linked List, Dictionary…
  • java.lang : language support 클래스들을 포함하는 패키지
    프리미티브 타입이나 수학 연산이 정의되어 있는 클래스들
    자동적으로 import 됨
    String, System

4-2) 사용자 정의 패키지

  • 사용자가 직접 정의한 패키지이다.
    먼저 패키지명과 동일한 디렉토리를 생성하고, 해당 패키지에 포함할 클래스를 만든 뒤, 맨 첫번째 명령문으로 패키지 이름을 넣는다
// 패키지 이름은 파일이 저장된 디렉토리의 이름과 반드시 같아야 함
package myPackage;

public class MyClass{
	public void getNames(String s){
		System.out.println(s);
	}
}
다른 패키지에서 import 해서 사용할 수 있다.

// myPackage에 존재하는 `MyClass` 클래스를 import해서 사용할 수 있다.
import myPackage.MyClass;

public class PrintName{
	public static void main(String args[]){
		String name = "Pikachu";
		MyClass mc = new MyClass();
		mc.getNames(name);
	}
}

5. static import 사용하기

  • 임의의 패키지의 클래스에서 public static으로 정의된 멤버(필드나 메서드)를 사용할 때, 클래스명을 언급하지 않고도 사용할 수 있다.

6. 디렉토리 구조

패키지명은 클래스들을 저장하기 위해 사용하는 디렉토리의 구조와 연관되어 있다.

  • 실제 디렉토리 구조는 패키지명과 동일하게(패키지\하위 패키지) 저장되어 있다.
    com.zzz.project1.subproject2패키지의 MyClass 클래스가 존재한다면, 실제 클래스 파일은 다음과 같이 저장되어 있다.
    $BASE_DIR/com/zzz/project1/subproject2/MyClass.class
    $BASE_DIR은 패키지의 기본 디렉토리가 된다.
  • 기본 디렉토리($BASE_DIR)은 파일 시스템의 어디에나 위치할 수 있다.
    따라서 자바 컴파일러와 JVM은 기본 디렉토리의 위치를 알고 있어야 한다.
    이를 위해서 필요한 환경 변수(environment variable)을 CLASSPATH라 부른다.
  • CLASSPATH는 커맨드 쉘이 실행할 프로그램을 찾을 수 있게끔 PATH를 알려주는데 사용된다.

CLASSPATH란?

import org.company.Menu;
해당 명령어의 의미는 org.company 패키지에 있는 Menu라는 클래스를 현재 클래스에서 사용할 수 있게 하는 것이다.

JVM은 Menu클래스의 위치를 찾아서 해당 클래스의 인스턴스를 생성한다.

Menu menu = new Menu();
그럼 JVM은 어떻게 해당 클래스를 찾는 것일까?
만약 JVM이 Menu 클래스를 찾기 위해 존재하는 모든 클래스를 검사해야 한다면 매우 비효율적일 것이다. 그러므로 우리는 CLASSPATH 변수를 사용하여 해당 클래스가 위치한 곳을 JVM에게 알려준다.

만약 Menu 클래스가 dir이라는 디렉토리에 존재한다면, Menu 클래스의 전체 경로는 dir/org/company/Menu가 된다.

이 때 dir이라는 디렉토리를 classpath 변수로 등록해 놓고, 나머지 정보(org/company/Meny)는 import 명령어를 통해 제공해주므로서 외부 패키지의 클래스를 가져와 사용할 수 있게 된다.

접근제어자

화면 캡처 2021-01-03 013702

화면 캡처 2021-01-03 014159

  • 모든 클래스는 패키지에 속해있다.
  • 만약 파일 내에 패키지가 명시되어 있지 않다면, 해당 클래스 파일은 특별한 unnamed package(defualt)로 이동한다.

@skarltjr
Copy link
Owner Author

skarltjr commented Jan 10, 2021

인터페이스

인터페이스는 일종의 추상클래스이다. 인터페이스는 추상클래스처럼 추상메소드를 갖지만
추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 일반 메소드 또는 멤버변수를 구성원으로 가질 수 없다.
오직 추상메소드와 상수만을 멤버로 가질 수 있으며, 그 외의 다른 어떠한 요소도 허용하지 않는다.
(자바 8버전부터 default 예약어를 통해 일반 메소드구현이 가능하다.

  • 정의
    인터페이스는 static final 필드만 가질 수 있다. 필드를 선언할 때 public static final이 생략되어 있다고 생각하자.
    인터페이스에 적는 모든 메소드들은 추상 메소드로 간주된다.
    인터페이스 내에 존재하는 메소드는 무조건 "public abstract"로 선언되며, 이를 생략할 수 있다.
    인터페이스 내에 존재하는 변수는 무조건 "public static final"로 선언되며, 이를 생략할 수 있다.
⭐️ 그렇다면 추상클래스와 인터페이스는 왜 분리했을까?
- 사용용도!!! 를 생각해라
- 다중상속은 불가하지만 다중impl은 가능하다


Dog라는 클래스가 있다고 생각해보자
모든 Dog들이 공통적으로 가져야할 특성이 있을것이다. kind, age, height, weight 등..
이렇게 공통적으로 가져가는 부분을 추상클래스로 정의한다면 모든 Dog는 공통부분을 지닐 수 있다.

그러나 모든 개가 동일한 행동을하진 않을것이다 어떤개는 짖을수도, 물수도, 수영을 할 수도 있을것이다.
따라서 인터페이스를 통해 여러 기능을 특정 개에 맞게 구현해낼 수 있다.

⭐️
즉 추상클래스는 ~이다
즉 인터페이스는 ~를 할 수 있다.
interface 인터페이스 { 
         public static final 타입 상수이름 = 값;
         public abstract 메소드이름(매개변수);
}
  • 구현
    인터페이스는 구현한다는 의미의 키워드 'implements'를 사용한다.
public interface Animal {
    void sound();
}

public class Dog implements Animal {

    @Override
    public void sound() {
        System.out.println("멍멍");
    }

    public void sleep() {
        System.out.println("새근새근 잡니다.");
    }
}
public class Lion implements Animal {

    @Override
    public void sound() {
        System.out.println("크아앙");
    }

    public void hunting() {
        System.out.println("사냥을 합니다.");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal lion = new Lion();

        dog.sound();
        lion.sound();

      //  dog.sleep();     X 
      //  lion.hunting();  X
      
      ((Dog)dog).sleep(); 	  // O
      ((Lion)lion).hunting();     // O
    }
}
  • 인터페이스 상속
    자바에서 다중상속은 불가능하지만, 인터페이스는 예외이다.
    인터페이스는 다중상속, 즉 여러 개의 인터페이스로부터 상속 받는 것이 가능하다.
    인터페이스는 인터페이스로부터만 상속받을 수 있다.
public interface EchoMode {

    void EchoMode();
}

public interface NormalMode {

    void NormalMode();
}

public interface SportsMode {

    void SportsMode();
}
public class Car implements EchoMode, SportsMode, NormalMode{
    
    @Override
    public void EchoMode() {
        System.out.println("에코 모드로 주행 합니다.");
    }

    @Override
    public void NormalMode() {
        System.out.println("기본 모드로 주행 합니다.");
    }

    @Override
    public void SportsMode() {
        System.out.println("스포츠 모드로 주행 합니다.");
    }
}

  • 인터페이스 기본 메소드 (default method), 자바 8
    인터페이스는 기능에 대한 선언만 가능하기 때문에, 실제 코드를 구현한 로직은 포함될 수 없다.
    하지만 자바8에서 이러한 룰을 깨트리는 기능이 나오게 되었는데, 그것이 default method이다.
    메소드 선언시에 default를 명시하게 되면 인터페이스 내부에서도 코드가 포함된 메소드를 선언할 수 있다.

접근제어자에서 사용하는 default와 같은 키워드이지만, 접근제어자는 아무것도 명시하지 않은 접근제어자를 default라 하며

인터페이스의 default method는 'default'라는 키워드를 명시해야 한다.

interface MyInterface {
   default void printHello() {
      System.out.println("Hello World!");
      }
}

-★implements한 클래스에서 재정의가 가능하다.

  • 인터페이스의 static 메소드, 자바 8
  1. body가 있어야 한다.
  2. implements 한 곳에서 override가 불가능하다.
public interface ICalculator {
 
    int add(int x, int y);
    int sub(int x, int y);
 
    default int mul(int x, int y) {
 
        return x * y;
    }
 
    static void print(int value) {
 
        System.out.println(value);
    }
}
public class CalcTest {
 
    public static void main(String[] args) {
 
        ICalculator cal = new Calculator();
 
        // cal.print(100); error
        ICalculator.print(100); 
        // interface의 static 메소드는 반드시 interface명.메소드 형식으로 호출
    }
}

-★interface이름.메소드로 호출해야 한다.

  • 인터페이스의 private 메소드, 자바 9
    java8 에서는 default method와 static method가 추가 되었고,

java9 에서는 private method와 private static method가 추가 되었다.

java8의 default method와 static method는 여전히 불편하게 만든다.

단지 특정 기능을 처리하는 내부 method일 뿐인데도, 외부에 공개되는public method로 만들어야하기 때문이다.

interface를 구현하는 다른 interface 혹은 class가 해당 method에 엑세스 하거나 상속할 수 있는 것을 원하지 않아도,

그렇게 될 수 있는 것이다.

java9 에서는 위와 같은 사항으로 인해 private method와 private static method라는 새로운 기능을 제공해준다.

→코드의 중복을 피하고 interface에 대한 캡슐화를 유지 할 수 있게 되었다.

public interface Car {
    void carMethod();

    default void defaultCarMethod() {
        System.out.println("Default Car Method");

        privateCarMethod();
        privateStaticCarMethod();
    }

    private void privateCarMethod() {
        System.out.println("private car method");
    }

    private static void privateStaticCarMethod() {
        System.out.println("private static car method");
    }
}
DefaultCar.java 클래스 - Car 인터페이스 구현체

public class DefaultCar implements Car{

    @Override
    public void carMethod() {
        System.out.println("car method by DefaultCar");
    }
}
public class Main {
    public static void main(String[] args) {
        DefaultCar car = new DefaultCar();

        car.carMethod();
        car.defaultCarMethod();
    }
}

car method by DefaultCar
Default Car Method
private car method
private static car method

Process finished with exit code 0

@skarltjr
Copy link
Owner Author

skarltjr commented Jan 18, 2021

자바의 예외처리

화면 캡처 2021-01-18 181844

  • 예외와 오류는 Throwable 클래스 의 하위 클래스
  • 에러는 보통 시스템 리소스 문제로 발생하게 되는데, 어플리케이션 리소스 문제를 알면 안되기도 한다. Error는 보통 시스템 충돌, 메모리 부족 이 있고,
  • 예외는 컴파일 시간과 런타임시 발생하는게 대부분이다. 주로 개발자가 작성한 코드로 발생한다.

  • 일반예외 (Exception) : 일반 예외와 실행 예외 클래스를 구별하는 방법은 예외 Exception을 상속받지만, RuntimeException은 상속받지 않이함.

  • 실행예외 (Runtime Exception) : 실행 예외는 RuntimeException을 상속 받는다. 물론, 표에서 보다시피 Rumtime Exception 이 java.lang.Exception을 상속받지만, jvm에서는 runtimeException 상속 여부를 보고 판단하게 된다.

RuntimeException 종류

  1. NullPointerException(NPE) - NullPointerException 의 발생 원인은 비어있는 객체에 무슨 행동을 하려할 때, 발생
  2. ArrayIndexOufOfBoundsException
  • Checked Exception 과 Unchecked Exception - 컴파일러단계에서 확인이 가능한가? -> 런타임exception경우 unchecked

  • 프로그램에서 예외든 에러든 발생하는 경우 프로그램이 종료되는데 이렇게 갑작스러운 종료를 막기위해 필요한 것이 예외처리
    화면 캡처 2021-01-18 182406

  • 다중 catch 문을 작성할 때 주의할점이 있다. 상위 예외클래스가 하위 예외 클래스보다 아래에 위치해야 한다는 점이다. 이유는 간단하다. 하위 예외 클래스가 이미 상위 예외 클래스에 속해있기 때문에, 상위 예외 클래스가 앞에있으면 문제가 된다는 점이다.


예외를 던지기 throws

화면 캡처 2021-01-18 182823
화면 캡처 2021-01-18 182839

+++ mvc 예외처리
https://www.notion.so/cce3fc21976f4400aa4ed8d3fb26497b

@skarltjr
Copy link
Owner Author

skarltjr commented Jan 25, 2021

멀티쓰레드 프로그래밍

Process란

  • 실행중인 프로그램으로 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당 받아 실행중인것
  • 프로세스는 데이터 메모리 등의 자원과 쓰레드로 구성된다

Thread란

  • 프로세스 내에서 실제로 작업을 수행하는 주체
  • 모든 프로세스에는 1개 이상의 쓰레드가 존재하여 작업을 수행
  • 두 개 이상의 쓰레드를 가진 프로세스 = 멀티쓰레드프로세스

Thread클래스와 Runnable인터페이스

  • 쓰레드를 생성하는 방법
  1. Runnable 인터페이스사용
  2. Thread클래스사용(Runnable인터페이스를 구현한 클래스)
  • Thread클래스가 다른 클래스를 확장할 필요가있다면 Runnable 그게 아니라면 Thread클래스를 사용하자
    화면 캡처 2021-01-25 180353

  • 쓰레드는 순서대로 작동하지않는다 ! - > start가 종료될 떄 까지 기다린후 다음을 실행하는것이아니다

  • 컴퓨터의 성능에 따라 달라질 수 있으며 결과는 매번 다르다.

  • run메서드가 끝날떄까지 애플리케이션은 종료되지않는다.

  • 쓰레드 대기를 위한 sleep메서드

  • 주의할점은 Thread.sleep() 사용시 항상 try-catch로 묶어줘야한다. / InterruptedException

Thread의 상태

화면 캡처 2021-01-25 180925

Thread메서드

  • join() : 해당 쓰레드가 종료될 떄 까지 기다린다. 이전 쓰레드가아닌 현재 이 매서드를 호출한 쓰레드를 !!!★
  • interrupt() : 현재 수행중인 쓰레를 중단시킨다. + InterruptedException예외를 던지면서

Thread의 상태를 확인하는 메서드

화면 캡처 2021-01-25 181705

쓰레드의 우선순위

  • 쓰레드의 우선순위는 getPriority()와 setPriority()를 통해 리턴 or 변경할 수 있다.
  • 1~10까지로 설정할 수 있으며 숫자가 높을수록 높은 우선순위를 갖는다. 그러나 10이 1보다 10배 더 빠르게 실행되는것이아니라 우선순위가 높은만큼 더 많은 작업시간을 할당받아 빠르게 처리하는것이다.
    화면 캡처 2021-01-25 182528

메인 쓰레드

  • 자바는 JVM을 통해 돌아가는데 이것이 하나의 프로세스고 PSVM-> main()메서드가 곧 메인쓰레드다. / 따로 쓰레드를 실행하지않고 main만 실행하는 것이 싱글쓰레드애플리케이션

  • 싱글쓰레드 애플리케이션
    화면 캡처 2021-01-25 182827

  • 멀티쓰레드 애플리케이션
    화면 캡처 2021-01-25 182903

동기화 Synchronize

  • 여러개의 쓰레드가 하나의 자원을 동시에 사용하려고할 때 하나의 쓰레드를 제외한 나머지의 접근을 막는것 (Thread -safe)
  • 사용방법은 매서드를 synchronized로 선언하거나 block을 통해 감싸기
    화면 캡처 2021-01-25 184227
    화면 캡처 2021-01-25 184240

화면 캡처 2021-01-26 012838
화면 캡처 2021-01-26 012848
화면 캡처 2021-01-26 012821


데드락 DeadLock

  • 둘 이상의 쓰레드가 같은 lock을 동시에 다른 명령에 의해 획득하려할 떄 발생
  • Thread1이 A의 lock을 갖고있고 Thread2가 B의 lock을 갖고 있을 때 Thread1이 B의 lock을 얻으려고 할 때 데드락이 발생.
    화면 캡처 2021-01-25 185248

참고 : https://sujl95.tistory.com/63


자바는 크게 3가지 영역의 메모리 영역이 존재
클래스파일은 크게 필드 , 생성자, 메서드로 구성

  • static - 필드부분의 전역변수와 정적멤버변수(static)
  • stack - 메서드내에서 정의하는 지역변수
  • heap - 레퍼런스타입의 객체, 배열 등을 저장

자바의 멀티쓰레드에서는 쓰레드끼리 stack을 제외한 다른 영역 공유 -> 동기화문제

  • 자바의 모든 객체는 락을 갖고있다. synchronized블록은 이 락을 다룬다.

멀티 쓰레드 환경에서 공유변수 volatile키워드

  • volatile keyword는 Java 변수를 Main Memory에 저장하겠다라는 것을 명시하는 것
  • 왜 ? : 멀티 쓰레드 환경에서 자바는 task를 수행할 때 성능향상을 위해 메인 메모리에서 읽은 변수 값을 cpu cache에 저장한다.
    • Screen Shot 2021-08-11 at 5 00 55 PM
    • 이 때 예를 들어 public int counter = 0; 같은 변수가 존재한다고 했을 때
    • 1번 쓰레드는 counter++을 수행하더라도 해당 값은 메인 메모리가 아닌 1번 쓰레드의 캐쉬에 저장되어있다.
    • 즉 2번 쓰레드에서는 변경된 counter값을 얻을 수 없는 것
  • 이 때 counter변수에 volatile키워드를 붙여주면 메인 메모리에 값을 저장하고 2번 쓰레드 또한 변경된 값을 얻을 수 있다.
  • ★ 가장 좋은 상황은 하나의 쓰레드만 값을 변경하는 read & write를 수행하고 나머지 쓰레드는 read를 수행하는 경우가 최적
  • ★ 위 상황이 가장 좋은 이유는 만약에 1,2 번 쓰레드가 동시에 write를 할 땐 원자성 보장이 안된다. 즉 이럴 땐 synchronized가 필요한 것
    - thread1 에서 counter = 0. ->>> counter ++;
    - thread2 에서 counter = 0 -->>> counter++;
    - 1번 2번이 "동시에" 수행되기 때문에 모두 0 +1 을 수행하고 결국 의도랑 다른 1이 나오는 것
    - 당연히 이런 경우는 volatile이 아니라 동기화 - synchronized를 통해 한 번에 한 명씩만 수행할 수 있도록 해야한다.
  • 추가로 cpu cache보다 메모리가 더 비싸다. 즉 성능 측면에서 비용이 더 발생

참고 : https://nesoy.github.io/articles/2018-06/Java-volatile

@skarltjr
Copy link
Owner Author

skarltjr commented Feb 3, 2021

자바 Enum

  • enum : 열거형 / 상수들의 집합
public enum EventType {
    FCFS,CONFIRMATIVE;
}
  • ordinal : enum의 순서
  • 주의 : 만약 jpa에서 아래와 같이 기존 열거형의 순서가 변하거나 새로운 열거형 요소가 추가된다면 이미 데이터베이스에 저장된 내용과 다르기 때문에 위험하다. 따라서 jpa에서는 @Enumerated(EnumType.STRING)
public enum EventType {
    CONFIRMATIVE,FCFS;
}
  • enum에서 값 가져오기
1.System.out.println(EventType .CONFIRMATIVE);
2.System.out.println(Language.valueOf("CONFIRMATIVE"));
.. 등

혹은 enum을 정의할 때
enum Fruit { APPLE, PEACH, BANANA }
처럼 사용할 수 있다.

  • 또한 생성자와 매서드를 추가할 수 있다.
enum Fruit {
    APPLE, PEACH, BANANA;    

    Fruit() {
        System.out.println("생성자 호출 " + this.name());
    }
}

enum의 메서드

  • ordinal() : enum의 순서
  • name() : 각 요소들의 이름(toString에 기본으로 작성되어있다.)
  • valueOf() : 문자열로 enum요소의 이름을 찾아서 요소의 이름을 리턴한다.
  • values() : 모든 enum의 요소들을 배열로 만들어준다.

java.lang.Enum은 모든 열거형의 조상이고 모든 열거형은 Enum을 상속받는다

public abstract class Enum<E extends Enum<E>>
        implements Constable, Comparable<E>, Serializable {
        
    private final String name;
    
    private final String name() {
        return name;
    }
    
}

EnumSet

enum Color {
    RED, YELLOW, GREEN, BLUE, BLACK, WHITE
    
}

public class EnumDemo {

    public static void main(String[] args) {
        EnumSet<Color> set1, set2, set3, set4, set5;

        set1 = EnumSet.allOf(Color.class);
        set2 = EnumSet.of(Color.RED, Color.GREEN, Color.BLUE);
        set3 = EnumSet.complementOf(set2);
        set4 = EnumSet.range(Color.YELLOW, Color.BLACK);

        set5 = EnumSet.noneOf(Color.class);
        set5.add(Color.BLACK);
        set5.add(Color.BLUE);
        set5.remove(Color.BLUE);

        System.out.println("set1 = " + set1);
        System.out.println("set2 = " + set2);
        System.out.println("set3 = " + set3);
        System.out.println("set4 = " + set4);
        System.out.println("set5 = " + set5);
        System.out.println(set5.contains(Color.BLACK));
    }
}

@skarltjr
Copy link
Owner Author

skarltjr commented Feb 7, 2021

어노테이션

장점

  • 데이터 유효성 검사 등 직접 클래스에 명시해 줄 수 있어 수정이 필요할때 쉽게 파악할 수 있게 되었고 어노테이션 재사용도 가능.
  • AOP(관점 지향 프로그래밍)을 쉽게 구성할 수 있게 해준다.

사용이유

  • 컴파일 시 체크, 코드 분석,자동 생성등의 이유로 사용된다.
  • 유효성검사와 같이 메타데이터로써 사용도 가능하며 reflection을 통해 특정 클래스를 주입할 수 있다.
  • 메타데이터 : 특정한 목적을 위한 데이터. 즉 어떤 데이터를 설명해주는 데이터다.
  • reflection : jpa에서 기본생성자가 필요한 이유 중 하나로 객체를 통해 클래스의 정보를 분석하는 방법

@Override

빌트인 어노테이션과 메타어노테이션

빌트인 어노테이션

  • java에 내장되어 있는 어노테이션으로 컴파일러를 위한 어노테이션
  • @Override : 현재 매서드가 부모 클래스의 매서드를 오버라이드한것을 컴파일러에게 알려준다
  • @Deprecated : 마커어노테이션으로 다음 버전에서 지원되지 않을 수 있기때문에 사용을 자제하라고 경고해준다.
  • @SuppressWarning : 이름 그대로 경고를 띄우지 않기 위해 사용하는 어노테이션
  • @FunctionalInterface : 함수형인터페이스라는 것을 컴파일러에게 알리는 어노테이션 // 함수형 인터페이스 = 1개의 추상 메서드만을 갖고 있는 인터페이스

메타 어노테이션

  • 어노테이션에 사용되는 어노테이션. 어노테이션을 정의하기 위한 어노테이션

  • @Retention : 어노테이션이 유지되는 기간을 설정
    -SOURCE : 소스파일에만 존재하고, 클래스파일에는 존재x, 컴파일러에 의해 버려진다.
    -CLASS : 클래스파일에는 존재하지만 런타임 시에 유지할 필요 없다는 것을 알리고 이 값이 default이다.
    -RUNTIME : 클래스파일에도 존재하고 런타임애 VM에 의해 유지되어 리플랙션을 통해 클래스 파일의 정보를 읽어 처리 가능하다.

  • @Target : 어노테이션을 적용할 대상을 설정

  • 파라미터 / 매서드 / 필드 등..

  • @Inherited : 하위 클래스에도 어노테이션이 상속이 되도록 설정

  • @Repeatable : 어노테이션을 반복적으로 선언할 수 있다.

커스텀 어노테이션 - 메타어노테이션을 조합하여 새로운 어노테이션을 만들어서 사용할 수 있다.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
public @interface CurrentUser {
}

어노테이션 프로세서

  • 런타임시에 리플랙션을 사용하는 어노테이션과는 달리 컴파일 타임에 이루어진다.
  • 컴파일 타임에 어노테이션들을 프로세싱하는 javac에 속한 빌드 툴로 어노테이션의 소스코드를 분석하고 처리하기 위해 사용.
  • 여러곳에서 사용되면 반복적으로 비슷한 형태를 띄는 코드들(보일러 플레이트 코드)을 제거하는데 도움이 된다.
    ( AbstractProcessor를 implements하여 구현체를 만들 수 있으며 Lombok의 @getter, @Setter와 같은 어노테이션을 이용하는 것만으로도 컴파일 타임에 알아서 getter/setter를 만들어주는 방식으로 보일러플레이트 코드 제거 )

@skarltjr
Copy link
Owner Author

skarltjr commented Feb 23, 2021

자바의 I/O

입출력

  • I/O 입출력 : input / output 의 약자
  • 입출력이란 : 컴퓨터 내부 또는 외부 장치와 프로그램간의 데이터를 주고 받는 것
  • 키보드로부터 입력받기 / sout을 통해 출력하기 등.

스트림 stream

  • 람다/스트림의 그 스트림이랑 다르다.
  • 자바에서 어느 한 쪽에서 다른 쪽으로 데이터를 전달하려면, 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한데, 이것이 바로 스트림.
  • ★즉 데이터를 운반하는 연결통로 = 스트림
  • ★ 하나의 스트림(연결통로)은 단방향(입출력 중 하나만)
  • 입출력을 동시에 처리하기 위해선 2개의 스트림이 필요하다.

바이트 기반 스트림 - inputStream , OutputStream

  • 종류
    화면 캡처 2021-02-23 153828

  • 매서드
    화면 캡처 2021-02-23 154217

  • 매서드를 보면 abstract 즉 상황에 맞게 구현하여 사용

보조 스트림

  • 보조스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다.
  • 그렇다면 이 보조 스트림 Buffer를 왜 사용하는가? : 성능상 이점 ->속도가 빠르다
  • ★그렇다면 왜 성능이 향상되는가? : 버퍼에 담아서 모아뒀다가 한 번에 보내는데 이 경우 시스템 콜의 횟수 자체가 줄어들기 때문에 성능이 향상된다.
  • 즉 물을 한 모금씩 여러번 계속 떠오는 것 보다 한 컵 통째로 떠오는 것
// 먼저 기반 스트림을 생성한다.
FileInputStream fileInputStream = new FileInputStream("test.txt");

// 기반 스트림을 이용해 보조 스트림을 생성한다.
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

// Buffered**Stream 생성 시 사이즈도 정의하여 생성할 수 있다. (2번째 파라미터)
// default : 8192
BufferedInputStream bis = 
	new BufferedInputStream(fileInputStream, 8192);

// 보조스트림을 이용해 데이터를 읽는다.
bufferedInputStream.read();
  • ★입출력 자체의 기능은 당연히 보조스트림이 아닌 InputStream/outputstream 이 수행한다.

문자기반 스트림 - Reader,Writer

  • 바이트기반(1byte)이 아닌 문자기반(2byte)
  • InputStream =>Reader , OutpuStream=>writer

채널기반의 스트림 NIO (new input/output)

  • 새로운 입출력 패키지

화면 캡처 2021-02-27 132641

  • I/O는 스트림기반 vs NIO는 채널기반
    화면 캡처 2021-02-27 133051

  • 스트림 VS 채널
    : IO는 스트림기반으로 입 / 출력을 구분하여 단방향 스트림을 생성
    : NIO는 채널기반으로 채널은 양방향 - 입출력을 위한 별도의 채널생성X

  • NON버퍼 VS 버퍼
    : IO는 버퍼를 사용하지않는다. - 읽은 데이터를 즉시처리
    : NIO는 기본적으로 무조건 버퍼에 저장한다 - IO보다 성능면에서 우수

  • 블로킹 : 데이터를 읽어 올 때 데이터를 기다리기 위해 멈춰있는 것을 뜻한다. 예를 들어 사용자가 데이터를 입력하기 전까지 기다리고 있을 때 블락킹 상태에 있다고 한다.

  • 블로킹 VS NON블로킹
    : IO는 블로킹 : 입력 스트림의 read() 메소드를 호출하면 데이터가 입력되기 전까지 Thread는 블로킹(대기상태) - 빠져나오기 위해선 스트림을 종료하는 방법 뿐
    : NIO는 블로킹 , 넌 블로킹 둘 다 - NIO는 interrupt를 통해 블로킹을 빠져나올 수 있다.


바이트기반 스트림 InputStream / OutputStream

  • 매서드
    화면 캡처 2021-02-23 164145
    화면 캡처 2021-02-23 164200

print스트림

  • print, println, printf등으로 데이터를 문자로 출력하는 문자기반 스트림역할을 수행
  • 흔히 사용하는 sout도 print스트림
  • 문자기반 스트림 reader writer : 문자를 다루기 위해 byte대신 char타입을 다루는 것 뿐만 아니라 문자를 다루기 위한 encoding을 자동제공

InputStreamReader와 OutputStreamWriter

  • 바이트 기반 스트림을 문자 기반 스트림으로 연결시켜주는 역할을 수행
  • 추가적으로 바이트기반 스트림의 데이터를 지정된 인코딩의 문자데이터로 변환하는 작업을 수행
InputStream resourceAsStream = getClass().getResourceAsStream("/zones_kr.csv");
            BufferedReader reader = new BufferedReader(new InputStreamReader(resourceAsStream));
            List<Zone> zoneList = reader.lines().map(line -> {
                String[] split = line.split(",");
                return Zone.builder().city(split[0]).localNameOfCity(split[1]).province(split[2]).build();
            }).collect(Collectors.toList());
            zoneRepository.saveAll(zoneList);

표준 입출력-System.in, System.out, System.err

public final static InputStream in = null;

public final static PrintStream out = null;

public final static PrintStream err = null;
  • 실제로는 return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);처럼 BufferedInputStream과 BufferedOutputStream의 인스턴스를 사용

표준입출력의 대상을 변경하는 setOut(), setErr(), setIn()

  try (FileOutputStream fos = new FileOutputStream("test.txt");
            PrintStream ps = new PrintStream(fos)) {

           // System.out 의 출력 대상을 test.txt 파일로 변경
           System.setOut(ps);

           System.out.println("남기석");
       }
       catch (IOException e) {
           e.printStackTrace();
       }
  • sout의 대상이 test파일로 변경 -> 남기석이 파일에 작성

파일과 객체 ㅣ 직렬화

  • serialization : 객체를 데이터스트림으로 (ex) json으로)

  • deserialization : 반대
    화면 캡처 2021-02-27 142244

  • ★ A라는 객체! 객체란 결국 다양한 인스턴스 변수의 집합체
    A객체 아래
    인스턴스변수1 인스턴스변수2 .... 이 존재

  • 이것을 말 그래도 쭉 펴주는것이 직렬화 - 이를통해 객체의 모든 인스턴스 변수값을 스트림에 write 할 수 있도록


ObjectInputStream, ObjectOutputStream

  • 직렬화(스트림에 객체를 출력)에는 ObjectOutputStream을 사용

  • 역직렬화(스트림으로부터 객체를 입력)에는 ObjectInputStream을 사용

  • 파일에 객체저장하기

FileOutputStream fos = new FileOutputStream("objectfile.ser");
ObjectOutputStream out = new ObjectOutputStream(fos);

out.writeObject(new UserInfo());
  • ★그런데 여기서 객체가 직렬화가 불가능하다면? : Serializable
public class UserInfo implements java.io.Serializable{
	String name;
	String password;
	int age;
}
  • ★ 1. 부모가 Serializable이라면 자식은 자동으로 Serializable
  • ★ 2. 자식이 Serializable이라면 부모는 Serializable (x ) - 해당사항 x
  • transient 키워드를 통해 인스턴스 변수를 직렬화에서 제외시킬 수 있다.

파일입출력

  • File클래스
  • exist를 통해 파일이 존재하는지 확인 후 사용
  • ★주의 파일클래스는 입출력을 지원하는것이 아니라 파일 자체에 대한 내용(크기,속성 등)을 위한 클래스로 입출력은 스트림을 통해서
  • FileInputStream fis = new FileInputStream("C:/Temp/image.gif");
    예시
  public static void main(String[] args) {
        System.out.println("hi");

        try(FileInputStream fis = new FileInputStream("경로")){
            int data;
            while((data = fis.read()) != -1){
                System.out.write(data);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
  • FileOutputStream
  • 기본적으로 동일한 경로의 파일이 존재하면 지우고 다시 만든다.
//만약 기존내용에 추가적으로 작성을 하고싶다면 파라미터 true를 추가
FileOutputStream fos = new FileOutputStream("C:/Temp/image.gif", true); 

FileOutputStream fos = new FileOutputStream(file, true);
  • write() 메소드를 호출한 이후 "flush()" 메소드로 출력 버퍼에 잔류하는 데이터를 완전히 출력하도록 하며, 모든 작업이 끝난 후 close() 로 파일을 닫아줘야 한다.

  • 문자기반 (텍스트 파일을 위한) FileReader

  • 문자기반이기 때문에 텍스트가 아닌 그림 오디오등은 x

Resource resource = new ClassPathResource("zones_kr.csv");
            List<Zone> zoneList = Files.readAllLines(resource.getFile().toPath(), StandardCharsets.UTF_8).stream()
                    .map(line -> {
                        String[] split = line.split(",");
                        return Zone.builder().city(split[0]).localNameOfCity(split[1]).province(split[2]).build();
                    }).collect(Collectors.toList());
            zoneRepository.saveAll(zoneList);

@skarltjr
Copy link
Owner Author

skarltjr commented Feb 28, 2021

자바의 제네릭

제네릭이란? : 데이터의 타입을 일반화하는 것

  • 제네릭은 클래스나 메서드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법

왜? 사용하는가 : 컴파일타임에 잘못된 타입사용을 걸러낼 수 있다.

class GenericSample<T>{
    T element;
    void setElement(T element){
    this.element = element;
  }
}

GenericSample<Integer> inte = newGenericSample<>();

  • 처럼 타입을 지정하여 인스턴스를 생성하고 이를 사용하면되는데 이 때 inte인스턴스에 string값을 set한다면 컴파일시점에 걸러내게된다.
  • 추가로 처럼 String 타입으로 지정해주면 string set이 가능해진다.

제네릭의 주요개념( 바운디드타입, 와일드카드)

바운드타입 : 특정한 타입의 하위 타입들로 제한 / ex) Number타입 하위의 int long 등

public class BoundType<T extends Number> {
    T element;

    public void setElement(T element) {
        this.element = element;
    }

    public T getElement() {
        return element;
    }
}
   public static void main(String[] args) {
        BoundType<Integer> inte = new BoundType<>(); //number하위타입integer
        inte.setElement(3);  // set
        System.out.println(inte.getElement());//get이 가능
    }
  • 만약 여기서 string을 넣어본다면?
    화면 캡처 2021-02-28 133233

  • Number의 하위타입이 아니기때문에 string을 컴파일시점에서 걸러냈다.

와일드카드

  • 제네릭★으로 구현된 메서드★의 경우 GenericSample inte처럼 선언된 타입으로만 매개변수를 받는다. 이를 보완하기위한것이 와일드카드
  1. Unbounded WildCard
    :말 그대로 특정범위가 정해지지않은 와일드카드로 List<>의 경우를 생각하면된다. 어떠한 Object타입이든 올 수 있듯이

  2. Upper Bounded WildCard
    :List<? extends AnyClass>처럼 AnyClass를 상속받은 어떠한 하위 클래스가 와도 사용가능

  3. Lower Bounded WildCard
    :반대로 List<? super AnyClass> AnyClass의 부모 클래스만

메서드와 제네릭

public class GenericSample{
    public <T> void sample(T ele) {
        T value = ele;
        System.out.println(value);
    }
}
 public static void main(String[] args) {
        GenericSample genericSample = new GenericSample();
        String data = " hello";
        genericSample.sample(data);
    }

Erasure : 제네릭의 타입 소거

  • 컴파일상태에서 원소타입을 검사 후 런타임시 해당 타입에 대한 정보를 알 수 없다. 즉 런타임때는 타입에 대한 정보가 소거
 public static void main(String[] args) {
        List<String> qwe = new ArrayList<>(); 
        qwe.add(1); // 먼저 타입을 검사한 후 타입에 대해 euqlas가 false이면 컴파일 에러
    }
  • Type Erasure는 Type파라미터가 바인딩 되지않은★ 경우 object로 변환
  • 바인딩된★ 경우 바인딩된 타입으로 진행
  • 이 후 처음으로 바인딩된 타입을 갖고 다음으로 들어오는 것들에 대해 타입을 비교하기위해 Comparable로 대체

ex) 스택 구현예시와 같은 클래스가 존재할 때

public class Stack<T> {
    public T[] elements;

    public Stack(int capacity) {
        elements = (T[])new Object[capacity];
    }

    public void push(T data) {
        // ~~~
    }
}
1. 만약 파라미터가 바인딩되지않은경우 T->object
2. 바인딩된다면 그대로 진행 후 첫 번째로 들어온(바인딩된) 파라미터를 Comparable로 대체

https://sujl95.tistory.com/73 참고

@skarltjr
Copy link
Owner Author

skarltjr commented Mar 6, 2021

자바의 람다식

1. 람다식 사용법

  • 람다식이란? : 메서드를 하나의 ‘식(expression)’으로 표현한 것이다. 람다식은 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해준다.
  • 람다식은 매서드를 식으로 표현하기 때문에 매서드의 이름, 리턴타입이 없다 -> 익명 함수라고도 한다.
  int[] arr = new int[5];
    Arrays.setAll(arr, (i) -> (int)(Math.random() * 5) + 1);

여기서 람다식 (i) -> (int)(Math.random() * 5) + 1) 는 아래 함수를 대체한다.

 int method() {
        return (int)(Math.random() * 5) + 1;
    }

람다식을 작성하는 방법

  • 매서드의 이름과 리턴타입이 없는 람다식은 매개변수 선언부와 body{ } 사이에 ->를 추가
반환타입 메서드이름(매개변수 선언) {
      문장들
}
         ⇩
반환타입 메서드이름 (매개변수 선언) -> {
      문장들
}

ex) 람다식 변환 예시

int max(int first ,int seond)
{ 
  return first > second ? first : second;
}

이 매서드는 람다식으로 표현하면

(int first, int second) -> { 
  return first > second ? first : second;
}
  • 람다식에 사용된 매개변수의 타입이 추론가능하다면 타입은 생략이 가능하다. 그러나 두 매개변수 중 하나만 생략하는것은 불가능
(first, second) -> { 
  return first > second ? first : second;
}
  • 추가로 하나의 매개변수에 대해서는 괄호 생략이 가능하다. but 타입이 있다면 생략불가
   a -> a * a     // o
    int a -> a * a // x

2. 함수형 인터페이스

  • 자바의 모든 매서드는 모두! 어떠한 클래스 내에 포함되어야한다. 그러나 사실 람다식은 매서드가 아닌 익명 클래스의 객체와 동등
  • 익 명 객 체와 동등하다

아래의 인터페이스가 존재한다고 했을 때

 interface MyFunction {
        public abstract int max(int a, int b);
    }

인터페이스를 구현한 익명 객체 생성은

    MyFunction f = new MyFunction() {
                          public int max(int a, int b) {
                              return a > b ? a : b;
                          }
                  };

    int big = f.max(5, 3);

★ 앞서 람다식은 익명객체와 동등하다고 했다. - 람다식으로 변환하면

MyFunction  f = (int a, int b) -> {
  return a>b ? a : b;
}

★ 주의할 점은 이러한 기능 하나의 매서드를 포함한 인터페이스일 때만 해당된다.

  • 즉 하나의 매서드를 가진 인터페이스를 구현한 익명객체는 람다식으로 대체가 가능하고
  • 람다식을 다루기 위한 인터페이스(매서드)를 함수형 인터페이스라고 한다.
  • 따라서 함수형 인터페이스는 결국 하나의 추상 매서드만 정의되어야 하는 제약이 있다는 점을 주의하자.

@FunctionalInterface를 통해 컴파일러가 검증을 해준다.

@FunctionalInterface
    interface MyFunction {
        public abstract int max(int a, int b);
    }
  • 람다식의 형변환 : 람다식은 결국 익명객체이고 익명객체는 타입은 존재하지만 컴파일러가 임의로 이름을 지정하기 때문에 없다고 표현. 따라서 대입 연산자를 사용하기 위해선 형변환이 필요
    MyFunction f = (MyFunction)( () -> { } );
  • 이 떄 주의할 점은 람다식은 Object로 형변환 불가 / 오직 함수형 인터페이스로만 변환가능

3. Variable Capture

  • 멤버 매서드 내부에서 클래스의 객체를 생성하여 사용하는 경우를 생각해보자.!!
  • ★ 객체의 생성은 new 키워드를 통해 이뤄지고 -> 이는 동적 메모리 할당영역인 heap에 객체를 생성하는 것. -> 매서드가 종료되어도 heap영역에 남아있다.
  • ★ 그러나 매서드 내부에서 사용하는 로컬변수는 스택의 영역에 잡히는데 -> 매서드가 종료되면 사라진다.
  • ★ 따라서 멤버 매서드 내부에서 생성된 객체가 매서드 내부 지역변수를 사용하면 문제가 발생한다.!!

자바가 이 문제를 해결하는 방법이 바로 Variable Capture

: 컴파일시점에서 멤버 매서드 내부에서 생성된 개게가 멤버 매서드 내부에서 사용되는 지역변수를 사용할 경우 객체 내부로 값을 복사해온다. 주의해야할 점은 final 키워드 or final 성격을 가진 변수만 가능하다.

++ 매서드 생성자에 대한 내용은 아래 참조
https://yadon079.github.io/2021/java%20study%20halle/week-15 참고

@skarltjr
Copy link
Owner Author

skarltjr commented Jun 4, 2021

객체 지향과 데코레이터 패턴

  • ocp를 위배하지 않고 기능을 추가하기 위한 디자인 패턴.
  • 직접적인 수정 x / 확장 o
  • 또한 HorizontalScrollBarDecorator result = new HorizontalScrollBarDecorator(new VerticalScrollBarDecorator(new SimpleWindow())); 처럼 절차적인 문장을 피하고 선언형★으로 유지보수성 상승
    main
public class Main {
    public static void main(String[] args) {
        HorizantalScrollBarDecorator result =
                new HorizantalScrollBarDecorator(new VerticalScrollBarDecorator(new SimpleWindow()));
        result.draw();
    }
}
public interface Window {
    public void draw();
}
public class SimpleWindow implements Window {
    @Override
    public void draw() {
        System.out.println("draw");
    }
}
abstract class WindowDecorator implements Window {
    protected Window decoratedWindow;

    public WindowDecorator(Window decoratedWindow) {
        this.decoratedWindow = decoratedWindow;
    }
}
public class VerticalScrollBarDecorator extends WindowDecorator {
    public VerticalScrollBarDecorator(Window decoratedWindow) {
        super(decoratedWindow);
    }

    @Override
    public void draw() {
        System.out.println("draw vertical");
    }

}
public class HorizontalScrollBarDecorator extends WindowDecorator {
    public HorizontalScrollBarDecorator(Window decoratedWindow) {
        super(decoratedWindow);
    }

    @Override
    public void draw() {
        System.out.println("horizon");
    }

}

@skarltjr
Copy link
Owner Author

skarltjr commented Jul 27, 2021

주의 ! 어떤 결과가 나올까?

class Main {
  public static void main(String[] args) {
    Integer a = 127;  // == > Integer a = Integer.valueOf(127);
    Integer b = 127;
    Integer c = 128;
    Integer d = 128;
    Integer e = 1;
    Integer f = 1;

    System.out.println(a==b);
    System.out.println(c==d);
    System.out.println(e==f);

  }
}
  • 당연히 동일성을 비교하면 두 개의 객체가 서로다른 주소를 갖기 때문에 둘 다 false라고 생각했다
  • 그러나 자바에는 Reference Types에 대해 캐싱을 한다
  • Integer의 경우 -128 ~ 127까지에 대해 캐싱을 하는데

즉 Integer a = 1 / Integer b = 1 라고 했을 때 매 번 새로운 객체를 만들어서 반환 해주는 것이 아니라 싱글톤처럼 동일한 객체를 둘에게 전달한다.

참고 : https://meetup.toast.com/posts/185

@skarltjr
Copy link
Owner Author

skarltjr commented Jul 27, 2021

  • 쓰레드 세이프한 스트링버퍼 스트링빌더

왜 String대신 StringBuffer / StringBuilder 를 사용해야 하는가? ++ 스레드 세이프한가?

  • 아래 코드를 보고 예상해보기
class Main {
  public static void main(String[] args) {
    String str1 = "hello";
    System.out.println(str1.hashCode());
    str1 = str1+"plus";
     System.out.println(str1.hashCode());

    StringBuffer str3 = new StringBuffer();
    str3.append("test");
    System.out.println(str3.hashCode());
    str3.append("plus");
    System.out.println(str3.hashCode());


  }
}

결과

99162322
1197534572
989110044
989110044

1. 메모리

  • String은 값을 변경하면 해쉬코드가 변하는 것을 확인할 수 있다
  • 반면 StringBuffer는 변하지 않는다.
  • 즉 String의 값이 계속 변하면 주소값이 스택에 쌓이고 클래스들은 Garbage Collector가 호출되기 전까지 heap에 지속적으로 쌓이게 된다
  • 메모리 관리적인 측면에서의 장점

Screen Shot 2021-08-09 at 11 41 19 AM

Screen Shot 2021-08-09 at 11 41 25 AM

2. 스레드 세이프

  • StringBuilder와 StringBuffer의 큰 차이점은 바로 StringBuilder는 변경가능한 문자열이지만 synchronization이 적용되지 않는다는 것

  • 반면 StringBuffer는 synchronization이 적용되어 / multi thread에서 스레드 세이프하다!

  • StringBuffer에서 동기화부분이 빠진것이 빌더

  • 스트링 빌더와 버퍼의 결과 값 차이를 확인해보자

import java.util.*;

class Main {

  public static void main(String[] args) {
    StringBuffer stringBuffer = new StringBuffer();
    StringBuilder stringBuilder = new StringBuilder();

    new Thread(() -> {
        for(int i=0; i<10000; i++) {
            stringBuffer.append(i);
            stringBuilder.append(i);
        }
    }).start();

    new Thread(() -> {
        for(int i=0; i<10000; i++) {
            stringBuffer.append(i);
            stringBuilder.append(i);
        }
    }).start();

    new Thread(() -> {
        try {
            Thread.sleep(5000);

            System.out.println("StringBuffer.length: "+ stringBuffer.length());
            System.out.println("StringBuilder.length: "+ stringBuilder.length());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

  }
}

빌더의 값이 더 작은 것을 확인할 수 있다.

결과 : 스트링버퍼는 멀티스레드 환경에서 동시에 건드리지 못하도록 막는다. // 반면 빌더는 이런 동기화 부분이 제외됐고 결과가 다르다.
따라서 : 멀티스레드 환경에서 스레드 세이프한 스트링버퍼 사용

StringBuffer.length: 77780
StringBuilder.length: 46982

--- 직접 작성 쓰레드 코드

class Main {
  public static void main(String[] args) {
    StringBuilder bl= new StringBuilder();
    StringBuffer bf = new StringBuffer();


    class Mythread extends Thread{
    @Override
    public void run(){
      for(int i=0;i<10000;i++){
        bf.append(i);
        bl.append(i);
      }
    }

    public void check(){
        System.out.println("buffer = "+bf.length());
        System.out.println("builder = "+bl.length());
      }
    }


    Mythread mt1 = new Mythread();
    Mythread mt2 = new Mythread();

    mt1.start();
    mt2.start();


    Mythread mt3 = new Mythread();
    try{
        mt3.sleep(10000);
        mt3.check();
    }catch (InterruptedException e) {
        e.printStackTrace();
    }
  }
}

결과

buffer = 77780
builder = 51572
  • 예외로 당연히 조인을 추가한다면 mythread1이 끝날 때 까지 기다리기 때문에 둘 다 같은 값. 정상적으로 나온다
mythread1.start();
        try {
            mythread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mythread2.start();

@skarltjr
Copy link
Owner Author

skarltjr commented Aug 4, 2021

정적 멤버 클래스

  • 특징
    • class 내부에서 static으로 선언된 클래스
    • 다른 클래스 안에 선언된다.
    • 바깥 클래스의 private 멤버에도 접근할 수 있다
    • 다른 점은 일반 클래스와 동일
  • 주의 ! : 멤버 클래스가 바깥 클래스를 참조하지 않을 때 사용한다

simple ex)

main

class Main {
  public static void main(String[] args) {
      Myclass ss = new Myclass(3);
      ss.getMadeId();
  }
}

Myclass / Idmaker

public class Myclass{
  private int count = 100;
  private int id = 0;

  Myclass(){
  }
  Myclass(int count){
    this.count = count;
    System.out.println("turn = a");
  }

  void getMadeId(){
    System.out.println("turn = b");
    this.id = new Idmaker(0).getId();
    System.out.println("turn = d");
    System.out.println(this.id);
  }

  private static class Idmaker{
    private int id = 0;
    Idmaker(int count){
        this.id = count + 123;
        System.out.println("turn = c");
    }

    private int getId(){
      return this.id;
    }    
  }
}
  • 주의 깊게 살펴봐야 할 점은
    • 실행의 순서를 파악해라
    • 바깥 클래스의 생성자 먼저 동작하고
    • main에서 실행하는 ss.getMadeId()를 하면서 getMadeId 매서드 내부에서
    • 멤버 클래스의 생성자 동작

turn a / turn b / turn c / turn d 순서 봐보기

  • 이 순서를 통해 알 수 있는건 적어도 바깥 클래스가 생성된 후 멤버 클래스가 바로 생성되는 것이 아니라 필요에 의해 생성된다.

@skarltjr
Copy link
Owner Author

skarltjr commented Aug 5, 2021

자바 제네릭 - 배열과 리스트

공변과 불공변

  • 공변이란? : 함께 변한다.
class Main {
  public static void main(String[] args) {
      Object[] ob = new Long[1];
      ob[0] = "hello";

      List<Object> ob = new ArrayList<Long>();
  }
}

  • 위 코드에서 배열과 리스트는 어떤 차이가 있을까?
  • 에러 발생의 시점에 차이가 있다.
    ✔︎ 위 상황에서 배열은 런타임 시점에 ArrayStoreException가 나온다
    ✔︎ 리스트는 런타임 x, 컴파일 시점에 이미 에러가 잡힌다.
  • 즉 제네릭 - 배열 / 리스트에서 배열은 프로그램이 이미 실행된 후 에러가 발생하지만 리스트는 실행하기 전 미리 에러를 파악할 수 있다는 장점이 있다.

@skarltjr
Copy link
Owner Author

skarltjr commented Aug 23, 2021

프록시와 프록시 패턴(디자인 패턴)

프록시

  • 일반적으로 말하는 프록시는 클라이언트와 사용 대상 사이에 대리 역할을 맡은 오브젝트를 두는 방법을 총칭
    • ex) 우리가 유튜브 동영상을 볼 때 유튜브 origin server로 요청하면 엄청 오래걸릴 것.
    • 이 때 나의 위치에 가까운 곳(ex. 서울어딘가)에 프록시 서버가 존재한다면
    • 프록시 서버에 이미 사용자들이 자주 본 동영상들은 캐싱되어 있고 프록시 서버에 접근하여 훨씬 빠르게 영상을 볼 수 있다.
    • 프록시 서버가 origin의 대리 역할을 수행

프록시 패턴

  • 프록시를 사용하는 방법 중에서 타깃에 대한 접근 방법을 제어하려는 목적을 가진 경우
    • lazy로딩을 생각해보자 - 진짜 객체 접근에 대해 생각해보며
    • 진짜 객체를 가져오는것이 아니라 미리 가짜 객체(프록시)를 주고 진짜 접근할 때 진짜 객체를 가져온다.
    • 다만 프록시는 코드에서 자신이 만들거나 접근할 타깃 클래스 정보를 알고 있는 경우가 많다. 생성을 지연하는 프록시라면 구체적인 생성 방법을 알아야 하기 때문에 타깃 클래스에 대한 직접적인 정보를 알아야 한다.

@skarltjr
Copy link
Owner Author

skarltjr commented Aug 23, 2021

자바 리플렉션

리플렉션이란?

  • 자바는 정적 언어다.
  • 리플렉션은 구체적인 클래스 타입을 알지 못해도, 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API
  • 프레임워크, 라이브러리는 사용하는 사람이 어떤 클래스를 만들지 모른다. 이럴 때 동적으로 해결해주기 위해서 리플렉션을 사용 // 스프링의 DI(dpendency injection), Proxy, ModelMapper
@Controller
@RequestMapping("/articles")
public class ArticleController {    

    @Autowired    
    private ArticleService articleService;       
       

    @PostMapping
    public String write(UserSession userSession, ArticleDto.Request articleDto){
       
    }

    @GetMapping("/{id}")
    public String show(@PathVariable int id, Model model) {
       
    }
}
  • @controller를 통해 articleController를 빈으로 등록한다.
    • 이 때 articleController의 존재를 어떻게 알고 빈으로 등록하는가?
    • 개발자는 해당 컨트롤러의 정보를 알지만 스프링은 알지못한다.

리플렉션의 동작 방식을 알아보자

  • 스프링의 DI를 구현해보기
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoWired {
}
  • 인스턴스 생성 및 DI역할의 container service
public class ContainerService {
  public static <T> T getObject(Class<T> classType) {
    // 기본생성자를 통해서 인스턴스를 만든다.
    T instance = createInstance(classType);

    // 클래스의 모든 필드를 불러온다.
    Stream.of(classType.getDeclaredFields())
      .filter(field -> field.isAnnotationPresent(AutoWired.class)) // 어노테이션에 AutoWired를 갖는 필드만 필터
      .forEach(field -> {
        try {
          // 필드의 인스턴스 생성
          Object fieldInstance = createInstance(field.getType());
          // 필드의 접근제어자가 private인 경우 수정가능하게 설정
          field.setAccessible(true);
          // 인스턴스에 생성된 필드 주입
          field.set(instance, fieldInstance);
        } catch (IllegalAccessException e) {
          throw new RuntimeException(e);
        }
      });
    return instance;
  }

  private static <T> T createInstance(final Class<T> classType) {
    try {
      // 해당 클래스 타입의 기본생성자로 인스턴스 생성
      return classType.getConstructor().newInstance();
    } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
  }
}
  • 여기까지 프레임워크의 관점

  • 내가 개발할 때 작성하는 관점으로
public class ArticleController {
  @AutoWired
  private ArticleService articleService;

  public void foo(){
    articleService.foo();
  }
}


public class ArticleService {

  public void foo() {
    System.out.println("call foo");
  }
}

public static void main(String[] args){
      ContainerService containerService = new ContainerService();

      ArticleController articleController = containerService.getObject(ArticleController.class);

      articleController.foo();
}

전체적인 동작을 살펴보자

문제 상황 : articleController의 foo는 articleService의 foo를 실행한다. 그러나 컨트롤러가 articleService가 뭔지알고 가져오는가?

public class ArticleController {
  @AutoWired
  private ArticleService articleService;

  public void foo(){
    articleService.foo();
  }
}
  1. main에서 containerService.getObject(ArticleController.class);
  2. ContainerServicecreateInstance매서드가 실행 -> ArticleController인스턴스 생성
  3. ArticleController의 필드 중 Autowired 어노테이션을 가진 필드만 선별
  4. ArticleController인스턴스의 필드 중 Autowired어노테이션을 가진 ArticleService필드 주입
    이렇게 DI가 리플렉션을 통해 어떻게 주입되는지 살펴볼 수 있다.

@skarltjr
Copy link
Owner Author

skarltjr commented Oct 8, 2021

결과를 예측해봐라

 int[] a = {1, 2, 3};
        int[] b = a;
        a[0] = 3;

        for (int i : b) {
            System.out.println(i);
        }

  • 아주 기본적이지만 그냥 넘어갈 수 없는 부분
  • result = 3 2 3

즉 b는 a와 동일한 주소를 가르킨다

int[] a = {1, 2, 3};
        int[] b = a;
        a[0] = 3;

        for (int i : b) {
            System.out.println(i);
        }

        System.out.println(a.hashCode());
        System.out.println(b.hashCode());
        // 2083562754
        // 2083562754

@skarltjr
Copy link
Owner Author

skarltjr commented Oct 20, 2021

스레드를 활용하여 싱글톤 패턴 동시에 접근테스트

  1. 싱글톤
public class Settings {
    private static Settings instance;

    public Settings() {
    }

    public static synchronized Settings getInstance() {
        if (instance == null) {
            instance = new Settings();
        }
        return instance;
    }

}

public class Main {
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread(1);
        MyThread myThread2 = new MyThread(2);

        myThread1.start();
        myThread2.start();

    }
}

class MyThread extends Thread {
    public Settings settings;
    private int num;
    public MyThread(int num) {
        this.num = num;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            settings = Settings.getInstance();
            System.out.println(settings + " thread " + num);
        }
    }
}

  1. 결과는
algo.Settings@74719816 thread 2
algo.Settings@74719816 thread 1
algo.Settings@74719816 thread 2
algo.Settings@74719816 thread 1
.....

@skarltjr
Copy link
Owner Author

skarltjr commented Nov 4, 2021

  • 소숫점을 제어해보자
상황 : 123.456에서 나는 소숫점 아래 2자리까지만 반영하고 싶었다
  • 구현
double originPercentage = 123.456;
DecimalFormat form = new DecimalFormat("#.##");
String format = form.format(originPercentage);
float percentage = Float.parseFloat(format);

@skarltjr
Copy link
Owner Author

skarltjr commented Apr 1, 2022

  • l value와 r value
L value : 단일 표현식이후에도 사라지지 않는 객체
R value : 단일 표현식이후에 사라지는 임시값
  • final이 붙은 객체의 값을 변경할 수 있냐?
ex)
private final Myclass a = Myclass("hello");

a의 값을 변경할 수 있는가?
기본적으로 final은 l value의 r value값을 고정한다.
그 말은 즉 a의 값은 Myclass("hello")의 주소값으로 고정되어있다.
a의 객체의 값을 바꿀 수 있느냐? Myclass("hello") 주소를 따라가서 나오는 값을 변경한다.

@skarltjr
Copy link
Owner Author

자바의 this keyword

자바의 'this' keyword는 이 클래스를 기반으로 생성된 인스턴스를 가리키는 참조

잘 생각해보자.
this는 인스턴스를 가리키는! 참조
인스턴스 자체가 아니다!
public class Temp {

    public Temp getTempClass() {
        return this;
    }
}

이와같은 클래스가 존재한다고 해보자
this는 해당 클래스로 생성된 인스턴스를 가리키는 참조

그렇다면 아래의 결과는 어떨까?
public class Main {
    public static void main(String[] args) throws IOException {
        Temp temp = new Temp();

        System.out.println(temp.hashCode());
        System.out.println(temp.getTempClass().hashCode());

        System.out.println(temp == temp.getTempClass());
        System.out.println(temp.equals(temp.getTempClass()));
    }
}


747464370
747464370
true
true

결국 this 키워드는 인스턴스를 가리키는 참조로 temp 인스턴스를 가리키는 참조를 따라가보면 temp가 나온다

@skarltjr
Copy link
Owner Author

skarltjr commented Jul 15, 2022

Java 직렬화

직렬화란??

직렬화란

자바 시스템 내부에서 사용되는 object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 바이트 형태로 데이터를 변환하는 기술
- jvm의 메모리에 상주되어 있는 객체 데이터를 바이트 형태로 변환
- JVM의 메모리에만 상주되어 있는 객체 데이터를 그대로 영속화(persist)가 필요할 때 사용

장점 : 
- 시스템이 종료되더라도 없어지지 않는 장점을 가지며 영속화된 데이터이기 때문에 네트워크로 전송도 가능. 
- 필요할 때 직렬화 된 객체 데이터를 가져와서 역직렬화하여 객체를 바로 사용
⭐️단점 :
- 직렬화를 통해 변환된 바이트 형태의 데이터를 역직렬화 할 때 이전 객체와 동일한 객체여야한다
- 즉 자주 변경되는 비즈니스적인 데이터를 Java 직렬화을 사용하면 문제가 발생
- 따라서 serialVersionUID를 통해 관리가 필요


정리하자면 직렬화는 장점만큼 단점이 뚜렷하다
jvm 메모리에 상주하는 객체 데이터를 영속화하여 편리하게 사용할 수 있지만 변경에 굉장히 취약
개발자가 직접 컨트롤이 가능한 클래스의 객체가 아니라면 직렬화를 지양해야한다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant