본문 바로가기

독서

자바의 신(1, 2권)

📚

자바의 신

  • 기본 타입은 8 개다 (정수 5개, 실수 2개, 불리언 1개)
  • 변수는 네가지 (지역, 매개, 인스턴스, 클래스)
  • VO 는 데이터를 담아 두기 위한 목적
  • DTO 는 데이터를 다른 서버로 전달하기 위한 목적 (DTO 가 VO 를 포함)
  • 클래스에 static 변수를 사용하면 클래스 변수로 선언되기 때문에 모든 인스턴스가 같은 값을 바라보게 된다.

 

  • static 블럭은 객체가 생성되기 전에 한 번만 호출되고, 그 이후에는 호출하려고 해도 호출 할 수가 없다. 그리고, 클래스 내에 선언되어 있어야 하며, 메소드 내에서는 선언할 수 없다. 즉, 인스턴스 변수나 클래스 변수와 같이 어떤 메서드나 생성자에 속해 있으면 안 된다.
  • static 블럭은 여러 개의 객체가 생성되도 처음 객체를 생성할 때 한번만 선언되고, 생성자보다 먼저 실행된다.
  • String 은 pass by reference 지만 값을 넣을 때 = new String(" ") 와 같으므로 새로 생성되기 때문에 기존 값이 바뀌지 않고 call by value 처럼 동작한다.

 

  • Public 으로 선언된 클래스가 소스 내에 있다먄, 그 소스 파일은 public 클래스 이름과 동일해야된다 즉 한 클래스 파일에는 한 개의 public 만 된다

 

  • 확장을 한 클래스가 생성자를 호출하면 자동으로 부모 클래스의 기본 생성자(매개 변수가 아무것도 없는 생성자) 가 호출된다.
  • 부모 클래스에서는 기본 생성자를 만들어 놓는 것 이외에는 상속을 위해서 아무런 작업을 할 필요는 없다.
  • 부모 클래스에 매개변수를 받는 생성자 밖에 없는 경우 기본 생성자를 자동으로 생성하지 않는다. 따라서, 아에 없는 경우에는 오류가 나지 않지만, 매개변수가 있는 생성자만 있는 경우는 오류가 난다.
  • 이런 경우 자식 클래스에서 부모 클래스의 생성자를 명시적으로 지정하는 super() 를 사용한다.
  • 자식 클래스를 컴파일할 때 자동으로 super() 라는 문장이 들어간다.
  • 따라서, 오류가 나고 싶지 않으면 자식 생성자에 super(매개변수)를 선언해줘야한다. 아니면(super() 가 자동으로 선언되면서 오류가 발생함)
  • 만약 다른 래퍼런스의 객체를 파라미터로 받는 부모가 있을 때 null 을 파라미터로 넘기면 Ambiguous 오류를 뱉는다.
  • 자바는 부모의 매개 변수가 없는 기본 생성자를 찾는 것이 기본이다. 그래서, 부모 클래스에 매개 변수가 있는 생성자만 있을 경우에는 super() 를 이용해서 부모 생성자를 꼭 호출해야만 한다.
  • 그리고, 자식 클래스의 생성자에서 super() 를 명시적로 지정하지 않으면, 컴파일시 자동으로 super() 가 추가된다. 그리고, 부모 클래스의 생성자를 호출하는 super() 는 반드시 자식 클래스의 생성자에서 가장 첫줄에 선언되어야만 한다.
  • 동일한 시그니처를 가진 경우 오버라이딩이 된다. (오버라이드 애노테이션이 별도로 선언하지 않아도)
  • 오버라이드는 부모보다 자식의 메서드가 접근제어자가 더 강한건 되지만 약한건 안된다.(약하다는 건 private 해진다는 뜻)

 

  • Overloading : 확장(메소드의 매개 변수들을 확장하기 때문에 확장)ㅇ
  • Overriding : 덮어 씀(부모 클래스의 메소드 시그니처를 복제해서 자식 클래스에서 새로운 것을 만들어 내어 부모 ㅋ르래스의 기능은 무시하고, 자식 클래스에서 덮어 씀)
  • instanceof 로 타입 점검할 때는 하위클래스부터 해야된다. 왜냐면 상위부터하면 전부 상위로 찍힐 것이기 때문이다. 부모 타입도 true 로 주기 때문이다.

 

  • API 는 Application Programming Interface 로 애플리케이션에 선언되어 있는 클래스와 메소드에 대한 상세한 설명이 포함된 문서 를 말한다.
  • 클래스를 사용할 때 참조하는 문서

 

 

 

  • 참조 자료형은 == 값을 비교하는 것이 아니라 주소 값을 비교하는 것
  • 만약 equals 를 ovveride 하지 않으면 equals 에서 hashcode 를 비교한다.
  • hashcode() 는 객체의 주소 값을 리턴한다.
  • eqauls 를 override 할 때 5가지를 만족해야한다.
    1. 재귀(reflexive)
      • null 이 아닌 x 라는 객체의 x.equals(x) 결과는 항상 true 여야만 한다.
    1. 대칭(symmetric)
      • null 이 아닌 x와 y 객체가 있을 때, y.equlas(x) 가 true 를 리턴했다면, x.equals(y) 가 true 를 리턴해야한다.
    1. 타동적(transitive)
      • null 이 아닌 x와 y, z객체가 있을 때, y.equlas(x) 가 true 를 리턴했다면, x.equals(y) 가 true 를 하면 z.equals(z) 는 true 를 리턴해야 한다.
    1. 일관(consistent)
      • null 이 아닌 x와 y가 있을 때 깩체가 변경되지 않은 상황에서는 몇 번을 호출하더라도, x.equals(y) 의 결과는 항상 true 이거나 항상 false 이여만 한다.
    1. null과 비교
      • null 이 아닌 x 라는 객체의 x.equals(null) 결과는 항상 false 이여만 한다.

       

  • equals 를 구현할 때 hashcode() 도 같이 override 해야 한다. 그 이유는, equals 가 true 인데도 불구하고, hashcode() 메소드의 값은 다르게 되기 때문이다. 따라서, 같은 hashcode() 메소드 결과를 갖도록 하면 구현해야 한다.
  • hashcode() 는 객체의 메모리 주소를 16진수로 리턴한다. 만약 어떤 두 개의 객체가 동일하다면 hascode() 는 무조건 동일해야한다. 따라서, equals() 메소드를 override 하면, hashcode() 메소드도ovveride 해서 도일한 결과가 나오도록 만들어야 한다.
    • 자바 애플리케이션이 수행되는 동안에 어떤 객체에 대해서 이 메소드가 호출될 때에는 항상 동일한 int 값을 리턴해 주어야 한다. 하지만, 자바를 실행할 때마다 같은 값이어야 할 필요는 전혀 없다.
    • 어떤 두개의 객체에 대하여 eqauls() 메소드를 사용하여 비교한 결과가 true 일 때에, 두 객체의 hashcode() 메소드를 호출하면 동일한 int 값을 리턴해야만 한다.
    • 두 객체를 equals() 메소드를 사용하여 비교한 결과 false 를 리턴했다고 해서, hashCode() 메소들르 호출한 int 값이 무조건 달라야 할 필요는 없다. 하지만, 이 경우에 서로 다른 int 값을 제공하면 hashtable 의 성능을 향상시키는데 도움이 된다.
    • Object 의 clone() 은 객체의 복사본을 리턴한다.

 

13장 인터페이스와 추상클래스, enum

  • 인터페이스와 abstract 클래스를 사용하는 이유
    • 설계시 선언해 두면 개발할 때 기능을 구현하는 데에만 집중할 수 있다.
    • 개발자의 역량에 따른 메소드의 이름과 매개 변수 선언의 격차를 줄일 수 있다.
    • 공통적인 인터페이스와 abstract 클래스를 선언해 놓으면, 선언과 구현을 구분할 수 있다.
    • 인터페이스는 외부에 노출되는 것을 정의해 놓고자 할 때 사용한다.

 

  • abstract 클래스
    • abstract 클래스 안에는 abstract 으로 선언된 메소드가 0개 이상 있으면 된다.
    • abstract 로 선언된 메소드가 하나라도 있으면, 그 클래스는 abstract 으로 선언되어야만 한다.
    • 인터페이스를 선언하다 보니, 어떤 메소드는 미리 만들어 놓아도 전혀 문제가 없는 경우가 발생한다. 그렇다고, 해당 클래스를 만들기느 좀 애매하다. 특히 아주 공통적인 기능을 미리 구현해 놓음녀 많은 도움이 된다. 이럴 때 사용하는 것이 바로 abstract 클래스다.

 

  • 멤버 변수는 final 로 선언 시 초기화하지 않으면 컴파일 오류가 난다.
  • 지역 변수는 final 로 선언 시 컴파일 오류가 나지 않는다. 지역 변수는 메소드를 선언하는 중괄호 내에서만 참조되므로 다른 곳에서 변경할 일이 없다.

 

enum

  • enum 클래스도 생성자를 사용할 수 있지만, 생성자의 선번부를 package-private 와 private 만 접근제어자로 사용할 수 있다. 각 상수를 enum 클래스 내에서만 선언할 때 사용할 수 있다. 선언하지 않으면 자동으로 기본 생성자를 생성해준다.
  • java.lang.Enum 이 부모이어야 한다. 즉, extends java.lang.Enum 이 되어 있기 때문에 다른 것을 상속받을 수 없다. 또한 만들어 놓은 enum 을 extends 를 이용하여 선언할 수 없다.
  • Enum 은 protected Enum(String name, int ordinal) 로 컴파일러에서 자동으로 호출되도록 해놓은 생성자다. 하지만, 개발자가 이 생성자를 호출할 수 없다.
  • Object 기본 4가지의 메소드를 사용할 수 없지만 override 를 하지 못하도록 final 로 막아 놓았다.
  • equals 와 hashcode 는 사용해도 되지만, clone 과 finalize 는 사용하면 안된다.
  • toString() 은 막아놓지 않았다.
  • enum 에 선언되어있는 메소드
    1. compareTo(E e) - 매게 변수로 enum 타입과의 순서 차이를 리턴한다.
    1. getDeclaringClass() - 클래스 타입의 enum 을 리턴한다.
    1. name() - 상수의 이름을 리턴한다.
    1. ordinal() - 상수의 순서를 리턴한다.
    1. valueOf(Class<T> enumType, String name) - static 메소드로, 첫 번째 매개 변수로는 클래스 타입의 enum 을, 두 번째 매게 변수로는 상수의 이름을 넘겨주면 된다.
  • JDK 1.5 부터 추가되었다.
  • compareTo 는 compareTo 를 선언한 객체가 매개변수의 객체보다 뒤에 있으면 양수, 앞에 있으면 음수를 리턴한다.

 

예외

  • 예외의 최상위 객체 Excetion
  • 예외의 종류 (3가지)
    • checked exception
    • error
    • runtime exception or unchecked exception
    • 2 , 3 을 제외한 모든 것은 1
  • error
    • 자바 프로그램 밖에서 발생한 예외
    • 디스크가 고장났다던지, 메인보드가 맛이 갔다던지,
    • 프로그램이 멈추어 버리냐 : error, 계속 실행할 수 있냐 : Exception
  • RuntimeException
    • compile 시점에 발생하지 않는다.

     

  • Exception 을 확핮앟 ㄴ클래스들이 Checked 예외이며, RuntimeException 밑에 확장되어 있는 클래스들이 런타임 예외들이다.
  • 모든 예외의 할아버지는 Throwable 클래스다.
  • Exception 과 Error 는 Throwalbe 클래스를 상속받아 처리하도록 되어 있다. 그래서 Exception 이나 Error 를 처리할 때 Throwable 로 처리해도 무관하다.
  • 생성자 종류
    • Throwable()
    • Throwable(String message)
    • Throwable(String message, Throwable cause)
    • Throwable(Throwable cause)
  • Exception 이 오버로드한 메소드는 10가지가 넘지만 그 중 핵심
    • getMessage()
    • toString()
    • printStackTrace()

자바 예외 처리 전략

  • Exception 으로 할건지, RuntimeException 으로 할건지 구분이 필요하다.
  • Exception 을 바로 확장한 것이 Chekced 예외이고, Exception 을 상속한 RuntimeException 을 다시 확장한 예외들이 런타임 예외(Unchekced) 이다.
  • RuntimeException 은 따로 throws 나 try catch 를 잡지 않아도 compile 타임에 오류가 나지 않는다. 하지만, RuntimeException 에 대해서 어딘가에는 처리해주는 로직이 필요하다.
  • 즉, 꼭 try 나 throws 를 처리해줘야하는 예외는 Excpetion 을 상속받아 compile 시점에 checked 로 받아 custom 예외를 만들어야 하고, 그게 아니라면 RuntimeExcpetion 을 받아 unchecked 로 잡아준다.
  • 즉, checked 는 컴파일에 예외를 강제할 때 사용한다.
  • 임의의 예외 클래스를 만들 때에는 반드시 try - catch 로 묶어줄 필요가 있을 경우에만 Exception 클래스를 확장한다. 일반적으로 실행 시 예외를 처리할 수 있는 경우에는 RuntimeExcpetion 클래스를 확장하는 것을 권장한다.
  • Java Exception Stractegy 라는 키워드로 검색을 해본다.

 

String

public final class String extends Object 
	implements Serializalble, Comparable<String>, CharSequence

 

  • Serializable 은 구현해야 하는 메소드가 없는 특이한 인터페이스다. 이 Serializable 인터페이스를 구현한다고 선언해 놓으면, 해당 객체를 파일로 저장하거나 다른 서버에 전송 가능한 상태가 된다.
  • CharSequence 는 문자열을 다루기 위한 클래스라는 것을 명시적으로 나타내는데 사용한다.
  • StringBuilder 와 StringBuffer 도 이 CharSequece 인터페이스를 구현해 놓았다.
  • EUC-KR 은 한글 두글자를 표현하기 위해서 4 바이트를 사용하지만, UTI-16 은 6 바이트를 사용한다느 점이다. 즉 즐자 수와 상관 없이 무조건 2바이트의 차이가 발생하는 것이다.
    • 예시로, TCP 전문 통신할 때 전문 내용을 토대로 글자 수 대로 잘라서 써야하는 이슈가 있었는데, 이 때 해당 글자가 한글인 경우 2바이트 차이로 인해 한글인 경우 글자를 더 잘라야 하는 이슈가 있었다.\
  • 문자열이 같은지 비교하는 메서드
    • boolean equals(Object anObject)
    • boolean equalsIgnoreCase(String anotherStr)
    • int compareTo(String anotherStr)
    • int copareToIgnoreCase(String str)
    • boolean contentEquals(CharSequence cs)d
    • boolean contentEquals(StringBuffer sb)

 

String text = "Check Value";
String text2 = "Check Value";

if (text == text2) {
	return true;
}

 

를 했을 때 true 가 리턴된다.

그 이유는, 자바에 Constant Pool 이라는 것이 존재하기 때문이다. 이 풀은 간단히, 자바에서는 객체들을 재사용하기 위해서 Constatant Pool 이라는 것이 만들어져 있고, String 의 경우 동일한 값을 갖는 객체가 있으면, 이미 만든 객체를 재사용한다.

  • Constant Pool 이란?

 

contentEquals()

  • 변수로 넘어오는 CharSequence 와 StringBuffer 객체가 STring 객체와 같은지를 비교하는 사용

 

특정 조건에 맞는 문자열이 있는지를 확인하는 메소드

  • boolean startWith(String prefix)
  • boolean startWith(String prefix, int tooffset)
  • boolean endsWith(String suffix)
  • boolean contains(CharSequence s)
  • boolean matches(String regex)
    • contains 와 유사하지만 정규 표현식으로 되어 있어야만 한다.
  • boolean regionMatches(boolean ignoreCase, int tooffset, String other, int ooffset, int len)
  • boolean regionMatches(int offset, STring other, int ooffset, int len)

 

indexOf() 의 단점은 모든 문자열을 다 확인해 봐야한다느 점이다.

 

String 에서 문자열 위치를 찾는 메서드

  • indexOf(int ch)
  • indexOf(int ch, int fromIndex)
  • indexOf(String str)
  • indexOf(String str, int fromIndex)
  • lastIndexOf(int ch)
  • lastIndexOf(int ch, int fromIndex)
  • lastIndexOf(String str)
  • lastIndexOf(String str, int fromIndex)

 

char 단위의 값을 추출하는 메서드

  • charAt(int index)

 

char 를 String 으로 변환하는 메서드

  • copyValueof(char[] data)

 

String 을 char 배열로 변환하는 메서드

  • char[] toCharArray()

 

내용을 교체하는 메서드

  • String replace(char oldChar, char newChar)
  • String replaceAll();
  • STring replaceFirst(STring regex, String replacement)

 

String 에서 intern() 메서드는 native 메서드이기 때문에 절대 사용해서는 안된다.

  • String 은 기본적으로 block 내에서는 문자열 pool 에 해당하는 값이 있으면 기존에 있는 객체를 참조하고, new String 을 하면 같은 문자열의 풀에 있던 말든 새로운 객체를 생성한다. 그래서 == 가 먹히고, new 는 안먹힌다.
  • intern() 은 new String 을 하더라도 pool 에 있으면 그것을 참조해서 == 가 가능하게 한다.
  • == 가 equals 보다 훨씬 빠르다. 하지만, intern() 메소드를 사용하면 억지로 문자열 풀에 값을 할당하도록 만드는 것이므로, 저장되는 영역은 한계가 있기 때문에 그 영역에 대해서 별도로 메모리를 청소하는 (GC) 단계를 거치게 된다 (즉, content pool 을 계속해서 사용하게 되므로 메모리가 가득차서 GC 를 발생시킨다.) 따라서, 작은 연산 하나를 빠르게 하기 위해서 전체 자바 시스템의 성능에 악영향을 주게 된다.

 

 

String, StringBuilder, StringBuffer 의 차이

String 은 immutable 한 객체다. 즉, 불변의 객체라는 것이다.(final 이 선언된 클래스)

String 객체는 변하지 않는다. 만약 String 문자열을 더하면 새로운 String 객체가 생성이되고 기존 객체는 버려진다. 그러므로, 계속 하나의 String 을 만들어 계쏙 더하는 작업을 한다면 계쏙 쓰레기를 만들게 된다.

String text = "Hello";
text = text + "world";

의 경우 Hello 라는 단어를 갖고 있는 객체는 더 이상 사용할 수 없게 되며 GC 의 대상이 된다.

이를 위해 나온게 StringBuffer, StringBuilder 이다

 

StringBuffer 는 Thread safe 하다.

하지만 속도는 StringBuilder 가 더 빠르다.

 

핵심!!!

  • 하지만, JDK 5 이상에서는 String 의 더하기 연산을 할 경우, 컴파일할 때 자동으로 해당 연산을 StringBuilder 로 변환해 준다. 따라서, 일일이 더하는 작업을 변환해 줄 필요는 없으나, for 루프와 같이 반복 연산을 할 때에는 자도응로 변환을 해주지 않으므로, 꼭 필요하다

 

공통점

  • 모두 문자열을 다룬다.
  • CharSequence 인터페이스를 구현했다. 즉, 세 가지 중 하나의 클래스를 사용하여 매개 변수로 받는 작업을 할 때 String 이나 StringBuilder 타입으로 받는 것보다는 CharSequence 타입으로 받는 것이 좋다.(DIP 원칙 지킴)

 

 

언제 StringBuffer, StringBuilder?

  • 일반적으로 하나의 메소드 내에서 문자열을 생성하여 더할 경우에는 StringBuilder 를 사용해도 전혀 상관 없다. 그런데, 어떤 클래스에 문자열을 생성하여 더하기 위한 문자열을 처리하기 위한 인스턴스 변수가 선언되었고, 여러 쓰레드에서 이 변수를 동시에 접근하는 일이 있을 경우에는 반드시 StringBuffer 를 사용해야 한다.

 

Nested Class

  • Nested class < static netsted class, inner class < local inner class, anonymous inner class

 

  • 사용 이유
    • 한 곳에서만 사용되는 클래스를 논리적으로 묶어서 처리할 필요가 있을 때 - Static Nested
    • 캡슐화가 필요할 때 (A라는 클래스에 private 변수가 있다. 이 변수에 접근하고 싶은 B 라는 클래스를 선언하고, B 클래스를 외부에 노출시키고 싶지 않을 경우에 속한다.) 즉, 내부 구현을 감추고 싶을 때 사용한다.
    • 소스의 가독성과 유지보수성을 높이고 싶을 때

     

     

    익명 클래스의 정리
  • 익명 클래스를 사용하는 경우는 만약 인터페이스를 포함하고 있는 객체를 구현할 때 해당 인터페이스를 상속받은 구현체의 인스턴스를 생성해서 사용해야 한다. 하지만, 익명 클래스를 사용하면 별도의 인터페이스의 구현체를 구현 및 생성할 필요 없이 그냥 바로 생성해주면 된다. 비록 재사용은 하지 못하지만, 별도의 메모리에 올라가지 않기 때문에 애플리케이션을 시작할 때 시간이 덜 소요된다. 람다도 결국 이러한 방식인 것이다. 즉, 람다에서 파라미터로 받는 것들은 전부 인터페이스이기 때문에 별도로 해당 Supplier 같은 인터페이스의 구현체를 만들지 않고 익명 클래스를 사용하여 바로 구현하는 방식이다.

 

 

어노테이션이란

  • 어노테이션은 영어로 Annotation 이며, 메타데이터라고 불리기도 한다. JDK 5부터 등장했다.

     

  • 사용하는 곳
    • 컴파일러에게 정보를 알려주거나
    • 컴파일할 때와 설치시의 작업을 지정하거나
    • 실행할 대 별도의 처리가 필요할 때

     

  • 자바 언어에는 사용하기 위해서 정해져 있는 어노테이션을 3개가 있고, 어노테이션을 선언하기 위한 메타 어노테이션이라는 것은 4개가 있다. 하지만 이 메타 어노테이션은 선언을 위해서 존재하기 때문에 일반적으로 사용 가능한 어노테이션은 다음의 3개 뿐이다.
    • @Override
    • @Deprecated
    • @Supress Warnings

     

    @Tartget() 괄호 안에 적용 대상을 지정한다.

    • CONSTRUCTOR : 생성자 선언시
    • FILED : enum 상수를 포함한 필드 값 선언시
    • LOCAL_VALRIABLE : 지역 변수 선언시
    • METHOD : 메소드 선언시
    • PACKAGE : 패키지 선언시
    • PARAMETER : 매개 변수 선언시
    • TYPE : 클래스, 인터페이스 enum 등 선언시

     

    @Retention

    • 얼마나 오래 어노테이션 정보가 유지되는지를 다음과 같이 선언한다.
    • SOURCE - 어노테이션 정보가 컴파일 시 사라짐
    • CLASS - 클래스 파일에 있는 어노테이션 정보가 컴파일러에 의해서 참조 가능함. 하지만, 가상머신에서는 사라짐
    • RUNTIME - 실행시 어노테이션 정보가 가상 머신에 참조 가능

@Documented

  • 해당 "어노테이션에 대한 정보가 Javadocs(API) 문서에 포함된다는 것"을 선언한다.

 

@Inherited

  • 모든 자식 클래스에서 부모 클래스의 어노테이션을 사용 가능하다는 것을 선언한다.

 

@interface

  • 어노테션을 선언할 때 사용한다.

 

java.lang 을 별도로 선언하지 않아도 되는 이유는 클래스 로더는 부모에 있는지(Bootstrap class loader) 확인을 하기 때문에 별도로 선언하지 않아도 사용할 수 있다.

 

어노테이션의 용도

  • 제약사항 등을 선언하기 위해 : @Deprecated, @Override, @NotNull
  • 용도를 나타내기 위해 : @Entity, @TestCase, @WebService
  • 행위를 나타내기 위해 : @Statefull, @Transcational
  • 처리를 나타내기 위해 : @Column, @XmlElment

 

 

정리

객체지향 관련 용어 목록

  • 클래스
    • 상태와 행위를 갖는 자바의 기본 단위
  • 상태와 행위
  •  
  • 캡슐화
    • 연관된 상태와 행위를 결정하는 기능을 묶어주는 것을 의미.
    • 기능을 클래스 밖에서 접근 가능한 대상을 제한하는 정보 은닉이 가능
    • 하나의 객체를 위한 코드가 다른 객체를 위한 코드와 무관하게 수행할 수 있는 모듈화가 가능해진다.
    • 이처럼 묶여 있는 가장 작은 단위를 클래스라고 보면 된다.
  • 메시지
  • 객체
  • 상속
  • Overriede
  • 다형성
  • overloading

 

 

참조 자료형과 기본 자료형의 차이

  • 초기화 할 떄 : 기본 자료형은 값을 바로 지정하면 되지만, 참조 자료형은 일반적으로 new 와 생성자를 지정하여 객체를 생성한다.
  • 메소드를 호출할 때의 매개 변수 : 기본 자료형 및 참조 자료형 모두 값을 전달하지만, 참조 자료형 안에 있는 변수들은 참조 주소를 전달한다.
  • 특수 클래스 : String

 

 

 

2권

  • T academy - 영환님 JPA 강의
  • jre 만 설치하면, 자바를 컴파일하는 등의 각종 프로그램이 제외된 상태로 설치된다.
  • JDK 는 JRE 에서 java, javac, javadoc, jar, security, deploy, monitoring 등 Development Tools & APIs, Java Language 등을 추가적으로 제공한다.

 

  • 자바의 5가지 언어적 특징
    1. 자바는 " 단순하고 객체지향적이며 친숙"해야 한다.
    1. 자바는 "견고하며 보안상 안전"하다.
    1. 자바는 "아키텍처에 중립적이어야 하며 포터블" 해야 한다.
    1. 자바는 "높은 성능"을 제공해야 한다.
      • 자바는 낮은 우선순위의 쓰레드로 동작하기 때문에 보다 높은 성능을 낼 수 있다.
    1. 자바는 "인터프리트 언어이며, 쓰레드를 제공하고 동적인 언어이다.
      • 자바 인터프리터는 자바 바이트 코드를 어떤 장비에서도 수행할 수 있도록 해준다. 따라서, 기존에 사용하던 무거운 컴파일과 링크와 테스트 사이클을 거쳐야 하는 개발 환경보다 빠른 환경을 구축할 수 있다.

       

       

  • JDBC 는 JDK1.1 에서 추가된 것으로 Java Database Connectivity 의 약자로, 자바에서 데이터베이스라는 저장소에 데이터를 담기 위한 API 를 의미한다.
  • JDK1.2
    • JIT 라는 컴파일러가 Sum JVM 에 처음 추가되었다.
    • Collections 프레임웍 추가
    • JIT 는 Just-In-Time 의 약자로 어떤 메소드의 일부 혹은 전체 코드를 네이티브 코드로 변환하여 JVM 에서 변역하지 않도록 함으로써 보다 빠른 성능을 제공하는 기술이다.
  • JDK1.3
    • JNDI가 코어 라이브러리에 추가
      • 어떤 객체를 쉽게 찾을 수 있도록 도와주는 이름을 지정한 후, 나중에 그 이름으로 객체를 찾아가는 것을 의미한다.

       

  • JDK1.4
    • assert 예약어 추가
    • PErl 언어의 정규 표현식을 따르는 정규 표현식 추가
    • NIO 라는 non-blocking 추가
    • logging API 추가
  • JDK 1.5
    • 안전하게 컬렉션 데이터를 처리할 수 있는 제네릭 추가
    • 어노테이션ㅇ이라고 불리는 메타데이터 기능 추가
    • autoboxing 및 unboxing 추가
    • enum 추가
    • 매개 변수의 개수를 가변적으로 선언할 수 있는 varags 추가
    • concurrent 패키지 추가
    • Scanner 클래스 추가
  • JAVA 6
    • 안정성과 확장성
  • JAVA 8
    • 람다와 스트림
    •  
  • 마이크로서비스 패턴
  • 마이크로서비스 아키텍처 구축
  • 스프링 5.0 마이크로서비스 2/e
  • Code 코드
  • 지탱하는 기술 시리즈
  • 리눅스 커널 이야기
  • 카프카 데이터 플랫폼
  • 자바 성능 튜닝이야기

 

JIT

  • JIT 를 좀더 쉬운 말로 하자면, 동적 변환이라고 보면 된다.
  • JIT 의 목적은 프로그램 실행을 보다 빠르게 하기 위해서다
  • 명칭은 컴파일러지만, 실행시에 적용되는 기술이다.
  • 컴퓨터 프로그램을 실행하는 방식은 두 가지로 나눌 수 있는데, 인터프리트 방식과 정적 컴파일 방식이다. 인터프리터는 프로그램을 실행할 때마다 변환하는 작업을 수행하는 건데, 간편하지만 느릴 수 밖에 없다. 이를 둘 다 합친게 JIT 다.
  • 변환 작업은 인터프리터에 의해서 지속적으로 수행되지만, 필요한 코드의 정보는 캐시에 담아두었다가(메모리에 올려두었다가) 재사용하게 된다.
  • javac 는 정적 컴파일 방식이 아니라 class 의 바이트 코드르 만드는 것이다. 이 바이트 코드는 어떤 OS 에도 실행할 수 있다. 자바의 모토인 "Compile once, Run anywhere" 이다.
  • 즉, 컴퓨터가 알아먹으려면 다시 변환 작업이 필요하다. 이 변환작업을 JIT 컴파일러에서 한다.
  • 자바 소스코드 → 자바 컴파일러 → 컴파일된 bytecode → JVM → 기계 코드 → 하드웨어 및 OS
  • JVM → 기계코드 여기서 JIT 에서 수행되는 것이다.

 

HOTSPOT

  • HotSpot 클라이언트 컴파일러와 HotSpot 서버 컴파일러 의 두 가지 컴파일러를 제공한다.
  • CPU 코어가 하나뿐인 그런 사용자를 위해 만들어진 것이 HotSpot 클라이언트 컴파일러다.
  • 애플리케이션 시작 시간을 빠르게 하고, 적은 메모리를 점유하도록 하는 것이다.
  • 자바가 시작할 때 알아서 클라이언트 장비인지 서버 장비인지 확인한다.
    • 2 개 이상의 물리적인 프로세서
    • 2GB 이상의 물리적 메모리
  • 명시적으로 선언 가능 java -clinet, java -server
  • 윈도우는 기본적으로 클라이언트 컴파일러

 

 

GC

  • Java 7 부터 공식적으로 사용할 수 있는 GI(Garbage First) 라는 가비지 컬렉터를 제외한 나머지 JVM 은 다음과 같이 영역을 나누어 힙이라는 공간에 객체들을 관리한다.
  • Young 영역에는 말 그대로 젊은 객체들이 존재하며 Old 영역에는 늙은 객체들이 자리잡게 된다.
  • Perm 이라느 영역에는 클래스나 메소드에 대한 정보가 쌓인다. (더 많은 정보가 쌓이긴 한다.)
  • 객체를 생성하자마 저장되는 장소는 Eden 이다.
  • 마이너 GC 혹은 영GC의 메모리가 살아가는 과정(가비지 컬렉터가 아니라 가비지 컬레션이다)
    1. Eden 영역에서 객체가 생성된다.
    1. Eden 영역이 꽉 차면 살아있는 객체만 Survivor 영역으로 복사되고, 다시 Eden 영역을 채우게 된다.
    1. SUrvivor 영역이 꽉 차게 되면 다른 Survivor 영역으로 객체가 복사된다. 이때, Eden 영역에 있느 ㄴ객체들 중 살아잇는 객체들로 다른 Suvivor 영역으로 간다. 즉, Suvivor 영역의 둘 중 하나는 반드 비어 있어야만 한다.

     

  • 오래 살아있는 객체는 Old 영역으로 이동한다. 지속적으로 이동하다가 Old 영역이 꽉 차면 GC 가 발생하는데 이것을 메이저 GC, 풀 GC 라고 부른다.
  • 영 GC 가 풀 GC 보다 빠른데, 그 이유는 일반적으로 더 작은 공간이 할당되고, 객체들을 처리하는 방식도 다르기 때문이다. 그렇다고 전체 영역을 영 영역으로 만들면 장애로 이어질 확률이 매우 높아진다.
  • 오라클 자바 7부터추가된 GI 를 포함한 총 5가지의 가비지 컬렉터가 존재한다.
    • Serial GC
    • Paralle Young Generation Collector
    • Parallel Old Generation Collector
    • ConcurrentMark & Sweep Colector (CMS)
    • GI (Garbage First)
  • WAS 로 사용하는JVM 에서는 Serial GC 를 사용해서는 안된다. 이 GC 는 -client 옵션을 지정했을 때 사용된다. 즉, 클라이언트에 최적화된 GC 이기 때문에 만약 WAS 에서 이 방식을 사용하면GC 속도가 매우 느려 웹애플리케이션이 엄청 느려진다.

 

java.lang

  • 유일하게 import 하지 않아도 사용할 수 있다.
  • 제공하는 인터페이스, 클래스, 예외 클래스 등
    • 언어 관련기본
    • 문자열 관련
    • 기본 자료형 및 숫자 고나련
    • 쓰레드 관련
    • 예외 관련
    • 런타임 관련
  • 종류는 P.552 에 있음
  • 기본 자료형은 자바의 힙에 저장되지 않고 스택에 저장된다.
  • Character 와 Boolean 을 제외하고는 Wrapper 클래스라고 불리며 모두 Number 라는 abstract 클래스를 extend 한다.
  • 겉으로 보기엔 참조 자료형이지만, 기본 자료형처럼 사용할 수 있다. 왜냐면, 자바 컴파일러에서 자동으로 형 변환을 해주기 때문이다
  • Byte, Short, Integer, Long, Float, Doblue 은 기본 자료형처럼 쓸 수 있기 때문에 별도의 new 가 필요 없다.
  • Wrapper 를 만든이유
    • 매개 변수를 참조 자료형으로 받는 메소드를 처리하기 위해서
    • 제네릭과 같이 기본 자료형을 사용하지 않는 기능을 사용하기 위해서
    • MIN_VALUE, MAX_VALUE 와 같이 클래스에 선언된 상수 값을 사용하기 위해서
    • 문자열을 숫자로, 숫자를 문자열로 쉽게 변환하고, 2, 8, 10, 16 진수 변환을 쉽게 처리하기 위해서

     

  • Sytem 클래스
    • 시스템 속성값 관리
    • 시스템 환경 값 관리
    • GC 수행(실행하면 안됨)
    • JVM 종료(실행하면 안됨)
    • 현재 시간 조회
    • 기타 관리용 메소드들
  • System Properties 를 관리할 수 있는데 Hashtable 의 상속을 받은 클래스다.
  • String.valueOf() 는 null 을 "null" 로 변환하고, OBject.toString() 은 Nullpoint 예외난다.

 

제네릭

  • 타입을 꼭 점검해야 하는 걸까 라는 의문문이 들 때 사용
  • 살펴본 타입 형 변환에서 발생할 수 있는 문제점을 "사전"(컴파일 할 때)에 없애기 위해서 만들어졌다.
  • Object 를 하게되면 get 할 때 어떤 타입인지 알 수 없어 형변환을 해야한다. 하지만, 정확한 type 을 알기 위해서는 넣는 부분을 봐야하는 문제가 발생한다.
  • 하지만 제네릭을 사용하면 get 할 때 타입이 명시되기 때문에 소스 단계에서 타입을 알 수 있다.
  • 제네릭 단어 규칙
    • E : 요소
    • K : 키
    • N : 숫자
    • T : 타입
    • V : 값
    • S, U, V : 두 번째, 세 번째, 네 번째 선언된 타입

     

  • 하지만, 제네릭으로 타입을 지정하게 되면 매개변수 등을 사용할 때 타입이 고정되기 때문에 추상화가 어려워진다. 예를들어서 dto<String> 으로 매개변수를 지정해놓으면 String 제네렉 타입의 dto 만 받을 수 있다. 하지만, dto<?> 형태의 와일드 카드를 받으면 어떤 객체든 받을 수 있다.
  • 어떤 객체로 wildcard 로 선언하고, 그 객체의 값을 가져올 수는 있지만, 와일드 카드로 객체를 선언했을 때에는 이 예제와 같이 특정 타입으로 값을 지정하는 것은 불가능하다.
  • 따라서 조회용 매개변수로 wildcard 를 사용해야 한다.
  • 하지만 wildcard 처럼 너무 추상화가 되는 것보다 method 제네릭처럼 명시적으로 타입을 선언하는 것이 더 견고한 코드를 작성할 수 있다.
pubilc <T> void genericMethod(WhildcardGeneric<T> c, T addValue) {
	c.setWildcard(addValue);
  T value = c.getWildcard();
}

 

 

 

컬렉션

  • 목록성 데이터를 처리하는 자료 구조
  • 자료구조는 영어로 Data Source 이며 여러 데이터를 담을 때 사용한다.
  • 크게 다음과 같이 구분된다.
    • 순서가 있는 목록형 - List
    • 순서가 중요하지 않은형 - Set
    • 먼저 들어온 것이 먼저 나가는 큐형 - Queue
    • 키-값 으로 저장되는 맵형 - Map
  • List, Set, Queue 는 Collection 인터페이스를 구현하지만 Map 은 아니다.
    • public interface Collection<E> extends Iterable<E>

     

    List
    • List 에서 Vector 는 thread safe 하고 ArrayList thread safe 하지 않다.
    • Stack 은 Vector 클래스를 확장한 것
    • LinkedList 는 List, Queue 의 구현체다
    • Object → AbstractCollection<E> → AbstractList<E> → ArrayList<E>
    • ArrayList 가 구현안 인터페이스
      • Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
    • ArrayList 는 초기에 10으로 잡고 늘어나는 구조, 예측가능하면 크기를 잡고 하는 것도 좋음
    • 객체의 주소값만 할당하는 것이 shallow copy, 객체의 모든 값을 복사하여 복제된 객체에 있는 값ㅇ르 변경해도 원본에 영향이 없도록 할 때에는 Deep copy(arrayCopy()

     

    • list 를 vector 와 같이 threadsafe 하게 하려면
    • List list = Collections.synchronizedList(new ArrayList<>()); 처럼 만들어야 한다.

     

    Stack
    • Stack 은 LIFO 이다.
    • ArrayDeque 도 Stack 과 같은 기능이지만 빠르다. 하지만 thread safe 하지 않다.
    • Object → AbstractCollection → AbstractList → Vector → Stack
    • ArrayList 와 구현 목록 동일

     

    Set
    • HashSet: 순서가 전혀 필요 없는 데이터를 해시 테이블에 저장한다. Set 중에 가장 성능이 좋다.
    • TreeSet : 저장도니 데이터의 값에 따라서 정렬되는 셋이다. red-black 이라는 트리 타입으로 값이 저장되며 HashSet 보다 약간 성능이 느리다.
    • LinkedHashSet : 연결된 목록 타입으로구현된 해시 테이블에 데이터를 저장한다. 저장된 순서에 따라 값이 정렬된다. 대신 성능이 이 셋 중에서 가장 나쁘다.
    • red-black : 이진트리

     

    • HashSet
      • Object → AbstractCollection → AbstractSet → HashSet
      • 순서가 없기 때문에 get 과 indexOf 가 없다.
      • 로드 팩터는 (데이터의 개수)/(저장 공간) 을 의미한다. 만약 데이터의 개수가 증가하여 로드 팩터보다 커지면, 저장 공간의 크기는 증가되고 해시 재정리을 해야만 한다. 데이터가 해시 재정리 작업에 들어가면 내부에 갖고 있느 ㄴ자료 구졸르 다시 생성하느 ㄴ단계를 거쳐야 하므로 성능에 영향이 발생한다.
      • 로드팩터라는 값이 클수록 공간은 넉넉해지지만, 데이터를 찾는 시간은 증가한다.

       

       

    • Queue
      • LinkedList 는 List 인터페이스 뿐만 아니라, Queue 와 Deque 인터페이스도 구현하고 있다.
      • Deque 는 Double Ended Queue 이다.
      • Deque는 Queue 인터페이스를 확장하였으며 맨 앞에 값을 넣거나 빼는 작업, 맨 뒤에 값을 넣거나 빼는 작업을 수행하는데 용이하다.

       

    • LinkedList
      • Object → AbstractCollection → AbstractList → AbstractSequentialList → LinkedList
      • Serializable, Cloneable, Iterable, Collection, Deque, List, Queue 를 구현
      • List 도 되고 Queue 도 되고 Deque 도 된다.
      • offer, put, 등 어떤 걸 써도 결국 add, addLast 를 호출한다.

       

    • Map
      • key-value 이고 key 는 중복되지 않는다.
      • HashMap, TreeMap, LinkedHashMap 등이 가장 유명하고, HashTable 이라는 클래스도 있다.
      • Hashtable 클래스는 Map 인터페이스르 구현하긴 했지만 일반적인 Map 인터페이스를 구현한 것과 다르다.
        • Map 은 컬렉션 뷰를 사용하지만, Hashtable 은 Enumeration 객체를 통해서 데이터를 처리한다.
        • Map 은 키, 값, 키-값 싸응로 데이터를 순환하여 처리할 수 있지만, Hashtable 은 이중에서 키-값 쌍으로 데이터를 순환하여 처리할 수 없다.
        • Map dㅡㄴ 이터레이션을 처리하는 도중에 데이터를 삭제하는 안전한 방법을 사용하지ㅏㅁㄴ, Hashtable 은 그러한 기능을 제공하지 못한다.
        • 키나 값에 null 저장 가능 여부가 HashMap 은 가능하고 HashTable 은 안된다.
        • 여러 쓰레드 안전 여부는 HasMap 은 불가능하고 Hashtlabe 은 가능하다.
        • JDK 1.2 부터 Concurrent 가 포함되어 있어야만 쓰레드에 안전하게 사용할 수 있다.
        • ConcurrentHashMap, CopyOnWriteArrayList 등이 java.util.concurrent 패키지 소속으로 제공된다.

         

      • HashMap
        • 담을 데이터의 개수가 많은 경우 초기 크기를 지정해 주는 것을 권장한다.
        • 어떤 클래스르 만들어서 Hash에 넣을 때 equlas 와 hashcode 를 잘 구현해야 한다.
        • 그 이유는, 객체가 들어가면 hashCode() 결과 값에 따른 버켓이라는 목록형태의 바구니가 만들어진다. 만약 서로 다른 키가 저장되었는데, hashCode() 메소드의 결과가 동일하다면, 이 버켓에 여러 개의 값이 들어갈 수 있다. 따라서 get() 메서드가 호출되면 hashcode() 의 결과를 확인하고 버켓에 들어간 목록에 데이터가 여러개일 경우 equals() 메소드를 호출하여 동일한 값을 찾게 된다. 따라서 hashCode() 와 equals() 를 잘 구현해야 한다.
        • java map buckets
        • key 와 value 를 따로 가져올 때는 keySet, valueOf
        • 둘 다 같이 가져와야 할때는 entrySet 으로 Set<Map.Entity<>> 를 리턴받아 둘다 get

         

      • TreeMap
        • 정렬된 Map
        • SortedMap 이라는 인터페이스를 구현
        • firstKey, lastKey, higherKey, lowerKey 등의 메서드제공

       

       

      쓰레드
      • JVM 은 기본적으로 아무런 옵션 없이 실행하면 OS 마다 다르지만, 적어도 32MB ~ 64MB 의 물리 메모리를 점유한다. 그에 반해서 쓰레드를 하나 추가하면 1MB이내의 메모리를 점유한다. 그래서, 쓰레드를 "경량 프로세스" 라고도 부른다.

       

      • 쓰레드를 생성하는 방법
        1. Runnable 인터페이스
          • run() 메서드 만 존재
          RunnableSample runnable = new RunableSample();
          new Thread(runable).start();
        1. Thread 클래스
          • Thread 클래스는 Runnable 인터페이스를 구현한 클래스
          • 다양한 메소드 제공
          ThreadSample thread = new ThreadSample();
          thread.start();
        • 둘 다 java.lang
        • 쓰레드를 구현한 메소드는 run() , 쓰레드를 실행하는 메서드는 start()
        • 이러한 두 가지 방식을 제공하는 이유는 자바는 다중 상속이 되지 않기 때문에 인터페이스를 제공하는 것이다.
        • 쓰레드 클래스가 다른 클래스를 확장할 필요가 있을 경우에는 RUnnable 인터페이스르 구현하면 되며, 그렇지 않은 경우에는 쓰레드 클래스르 사용하는 것이 편하다.
        • AOP 의 경우 성능에 관한 로깅 처리할 때 사용할 수 있다.
        • 새로 생성한 쓰레드는 run() 메소드가 종료되면 끝난다.
        • 쓰레드에 이름을 지정하지 않으면 Thread-n 이라고 생각된다.
        • n 은 쓰레드가 생성된 순서에 따라 증가한다.
        • ThreadGroup 으로 묶을 수도 있고, size 로 stackSize 를 지정할 수 있지만 OS 에 따라 무시될 수도 있다.
        • Thread 에 있는 static 메서드는 JVM 에 있는 쓰레드를 관리하기 위한 용도로 사용된다.
        • 데몬 쓰레드는 해당 쓰레드가 종료되지 않아도 다른 실행중인 일반 쓰레드가 없다면 멈춰버린다.
        • 데몬 쓰레드는 부가적인 작업을 수행하는 쓰레드를 선언할 때 만든다.
        • 쓰레드에 안전하려면 synchronized 를 사용해야 한다.
        • 매개변수나 메소드에서만 사용하는 지역변수는 스택에 관리되기 때문에 공유되지 않아 문제가 되지 않지만 heap 과 같은 공유되는 자원에서 사용되는 멤버변수는 수정할 때 여러 쓰레드가 공유하면 문제가 발생한다.
        • synchronized 사용 방법
          • 메소드 자체를 synchronized 로 선언하는 방법
          • 다른 하나는 메소드 내의 특정 문장만 synchronized 로 감싸는 방법(synchronized statements)
        • synchronized 로 선언하면 하나의 쓰레드에서 사용한다.
        public synchronized void plus(int value) {
        }
        는 이 메소드 전체가 걸리기 때문에 메소드가 길면 이슈가 되지만
        
        public void plus(int value) {
        	synchronized(this) {
        	}
        }
        
        는 해당 블록만 걸리기 때문에 좀 더 빠를 수도 있다.
        this 에는 별도로 선언한 객체를 사용할 수 있다.

        같은 객체를 여러 쓰레드에 동시에 사용될때만 이러한 쓰레드 공유가 된다.

        하지만, 따로 생성한 인스턴스를 별도의 쓰레드에 담아 실행하는 건 공유되지 않는다.

        필요한 add 나 수정부분에 sychronized 로 감싸면 멀티 쓰레드 환경에 유용하다.

        • StringBuffer 는 synchronized 블럭을 필요한 부분에 감싸서 쓰레드 세이프하지만 느리고,
        • StringBuilder 는 그러한 부분이 없어서 빠르지만 쓰레드세이프 하지 않다.
        • join() - 수행중인 쓰레드가 중지할 때까지 대기한다. , 별도의 매개변수 없으면 무한정 기다린다.
        • interrupt() - 수행중인 스레드가 중지 요청을 한다. InterruptedException 발생시키면서 중지

         

        쓰레드 상태

        • NEW - 객체는 생성됐지만 아직 시작되지 않음
        • RUNNALBE - 쓰레드가 싫행 중
        • BLOCKED - 실행 중지 상태이며, monitor lock 이 풀리기를 기다리느 ㄴ상태
        • WAITING - 대기중
        • TIMED_WAITING - 특정 시간만큼 스레드 대기 상태
        • TERMINATED - 쓰레드 종료
        • NEW → 상태 → TERMINATED

       

      • ThreadGroup
        • 쓰레드는 운영체제의 폴더처럼 뻗어나가는 트리 구조를 가진다.
      • 쓰레드와 프로세스의 차이는 무엇인가요
        • 프로세스는 하나의 애플리케이션을 실행하는 단위입니다. 즉, 하나의 애플리케이션은 하나의 프로레스라고 할 수 있습니다. 쓰레드는 이러한 프로세스의 실행 단위입니다. 즉, 하나의 프로세스에 멀티 쓰레드가 존재할 수 있습니다. 또한, 프로세스는 JVM 하나에 64MB 같은 무겁지만 쓰레드는 경량 프로세스라고 하여 1MB 정도 밖에 되지 않습니다. 또한, 쓰레드는 각자의 스택 메모리를 가지며 그 외의 프로세스의 힙이나 클래스 메모리는 공유하게 됩니다. 따라서, 멀티 쓰레드 환경에서는 멤버변수와 같은 힙 메모리에 저장되는 변수가 원하지 않게 공유되는 문제를 주의해야 합니다.(싱글턴)
      • sleep() 에 InterrueptException 을 잡아야하는 이유는 중간에 interrupt() 로 스레드가 중지될 수 있기 때문에

       

      Object
      • wait(), wait(time), notify(), notifyAll()
      • wait 는 WAITING STATUS 를 만들고 notify() 로 깨울 수 있다.

 

IO

  • JVM 기준으로 output, input 으로 칭한다.
  • 바이트 기반의 데이터를 처리하기 위해서 읽는 작업은 Stream 기반의 InputStream, 쓰는 작업은 OutputStrem - abstract
  • char 기반의 문자열 은 Reader, Writer
  • JDK 1.4 부터는 I/O 처리하기 위해서 NIO라는 것이 추가되었다.
  • NIO 는 스트림 기반이 아니라 버퍼와 채널 기반으로 데이터를 처리한다.
  • NIO 의 Files 가 File 클래스 있는 메소드들을 대체하여 제공한다.
  • File 클래스는 객체를 생성하여 데이터를 처리하는데 반하여, Files 클래스는 모든 메소드가 static 으로 선언되어 있기 때문에 별도의 객체를 생성할 필요가 없다는 차이가 있다.
  • File 클래스느 생성한 파일 객체가 가리키고 있는 것이
    • 존재하는지, 파일인지 경로인지, 읽거나, 쓰거나, 실행할 수 있는지, 언제 수정되었는지
  • 를 확인하는 기능과 해당 파일의
    • 이름을 바꾸고, 삭제하고, 생성하고, 전체 경로를 확인하는 등의 기능을 제공한다.
    • 자바에서는 \\ 로 해야되며 UNIX 에서는 / 로 디렉터리를 구분하기 때문에 File.separator 로 사용하는 게 안전한다.
  • 파일 유형 확인 : isDirectory(), isFile(), isHidden()
  • 파일 권한 확인 : canWrite, canRead, canExcute()
  • getPAth() 는 경로와 파일 이름 모두 나온다.
  • 만약 경로만 보고 싶다면, getPAranet() 로 하면 된다.
  • list() 메서드는 FileFilter 와 FileNameFilter 로 원치않는 것을 제외할 수 있다.
  • FileFilter, FileNameFilter 인터페이스의 accept 메서드를 구현하면 원하는 형태로 file 을 걸러낼 수 있다.

 

  • public abstract class InputStream extends Object Closeable
  • InputStream 을 확장한 클래스 중 주요한 클래스
    • FileInputStream
      • 생성자 public
    • FilterInputStream
      • 생성자가 protected 이기 때문에 상속객체에서만 생성할 수 있다.
    • ObjectInputStream

     

  • pubilc abstract class OutputStream extends Objct implements Closeable, Flushable
  • flush() 는 현재 버퍼에 있는 내용을 기다리지 말고 무조건 저장해 라는 것

 

  • Reader
    • public abstract class Reader extends Object implemnts Readable, Closeable
    • BuffredReader, InputStreamReader 가 많이 사용된다.

 

  • Writer
    • pubilc abstract class Writer extends Object implements Appendable, Closeable, Flushable
    • BufferedWriter 는 Wrtier 객체를 매개변수로 받아 버퍼라는 공간에 저장할 데이터를 보관해 두었다가 버퍼가 차개되면 데이터를 저장하도록 도와준다.

P.731

  • Sacnner
    • 텍스트 기반의 기본 자료형이나 문자열 데이터를 처리하기 위한 클래스

     

     

Serializable

  • java.io 에 있는 인터페이스로 별도의 선언된 변수나 메서드가 없다.
  • 클래스가 파일에 읽거나 쓸 수 있도록 하거나, 다른 서버로 보내거나 받을 수 있도록 하려면 반드시 이 인터페이스를 구현해야만 한다. 그러면 JVM 에서 해당 객체는 저장하거나 다른 서버로 전송할 수 있도록 해 준다.
  • 그 후 serialVersionUID 라는 값을 지정해 주는 것을 권장한다.
  • 별도로 지정하지 않으면 자바 소스가 컴파일될 때 자동으로 생성해준다.
static final long serialVersionUID = 1L;
  • 해당 객체의 버전을 명시하는데 사용한다.
  • A 라는 서버에서 B 라는 서버로 SerialDTO 라는 클래스의 객체를 전송한다고 가정하면정송하는 A 서버에 SerialDTO 라는 클래스가 이썽야 하고, 전송받은 B 서버에도 SerialDTO 클래스가 있어야만 한다. 그래야만 그 클래스의 객체임을 알아차리고 데이터를 받을 수 있다.
  • 그런데 만약 A 서버가 갖고 있는 SerailDTO 에는 변수가 3개 있고, B 서버의 SerialDTO 에는 변수가 4개 있는 상황이 발생하면 자바에서는 제대로 처리하지 못하게 된다. 따라서, 각 서버가 쉽게 해당 객체가 같은지 다른지를 확인할 수 있도록 하기 위해서는 serialVersionUID 로 관리해줘야 한다.즉, 클래스 이름이 같더라도 ID 가 다르면 다른 클래스라고 인식한다. 게다가, 같은 UID 라고 할지라도 변수의 개수나 타입 등이 다르면 이 경우에도 다른 클래스로 인식한다.
  • ObjectOutputStream 과 ObjectIputerStream 으로 객체를 읽고 쓴다.
  • serialVersionUID 를 지정하면 멤버변수 개수가 달라도 오류가 나진 않는다. 따라서 객체가 변경되었을 때 serialVersionUID 를 변경하지 않으면 오류가 발생하지 않기 때문에 버전은 변경해주는 것이 좋다.
  • 하지만 만약 변수 앞에 transient 를 선언하면 JVM 으로 보낼 때 Serializable 의 대상에서 제외시킨다.
  • 패스워드 같은 보안상 중요한 문제는 노출시키지 않도록 할 수 있다.

 

NIO

  • NIO 는 속도 때문에 나왔다.
  • 채널과 버퍼를 사용한다.
  • 채널은 도매상이고 버퍼는 도매상에서 물건을 사고, 소비자에게 물건을 파는 소매상으로 생각하면 된다.
  • ByteBuffer 와 FileChannel 을 사용해서 간단한 파일 데이터를 처리할 수 있다.

 

 

Buffer

  • ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer 등이 있다.
  • 0 < position ≤ limit ≤ 크기(capacity())
  • 현재의 위치를 나타내는 메소드가 position()
  • 일거나 쓸 수 없는 위치를 나타내는 메소드가 limit()
  • 버퍼의 크기를 나타내는 것이 capacity()

 

 

Java 7 에서 변경된 것들

  • JSR 336 에서 확인 가능
  • 변경된 목록
    • 숫자 표시 방법 보완
      • 16, 10, 8 진수 표현이 가능해짐
      • 숫자 사이에 _ 사용 가능 숫자만!
    • switch 문에서 String 사용
    • 제네릭을 쉽게 사용할 수 있는 Diamond
      • 제네릭 생성자 타입 시 주입해주는 부분은 넣어주지 않아도 된다.
      • 다음과 같은 제약 이 따름
        • 다이아몬드 미 지정시 컴파일 경고 발생
        • 다이엄몬드 생성 시 유의 사항 1 - 메소드 내에서 객체 생성시
        • 다이아몬드 생성 시 유의 사항 2 - 제네릭하면서도 제네릭하지 않은 객체 생성시
        • 생성자에 있는 new 클래스 이름 사이에 타입 이름을 명시적으로 두려면, 다이아몬드를 사용하면 안된다는 것을 기억해야 한다.
    • 예외 처리시 다중 처리 가능
    • @SafeVarargs
      • 가변 매개 변수를 사용하고
      • final 이나 static 으로 선언되어 있는 경우 사용 가능
      • 가변 매개 변수가 reifiable 타입이고, 메소드 내에서 매개 변수를 다른 변수에 대입하는 작업을 수행하는 경우

      에는 컴파일러에서 경고가 발생하게 된다.

    • try - catch -resource 에서는 AutoCloseable 을 상속 받았기 때문에 별도로 close() 해주지 않아도 된다.

     

    Java 7 에 추가된 것들
    • Fork/Join 은 CPU 를 더 쉽게 효율적으로 사용하기 위해서 만들어진 것이다.
    • Work stealing 이 추가되었는데, Dequeue 에서 바쁜 작업에 대기하고 있는 것을 가져가서 해주느 ㄴ것
    • Fork / Join 은 ava.util.concurrent 패키지의 RecursiveAction 과 RecursiveTask 라는 abstract 클래스를 사용해야 한다.
    • RecursiveAction
      • publci abstaract class RecursiveAction extends ForJoinTask<Void>
    • RecursiveTask
      • public abstract class RecursiveTask<V> extends ForkJoinTask<V>

       

    • ForJoinTask 라는 클래스는 FUture 라는 인터페이스를 구현했다. Future 라는 인터페이스는 Java 5 부터 추가된 인터페이스로 비동기적인 요청을 하고 응답을 기다릴 때 사용 된다.
    • 계산을 수행하기 위해서 쓰레드를 관리할 필요가 없다는 것이 Fok/Join 의 핵심이다.
    • NIO 2 가 추가되었다.
      • Java 6 까지는 다음과 같은 단점이 있었다.
        • 심볼릭 링크, 속성, 파일의 권한 등에 대한 기능이 없음
        • 파일을 삭제하는 delete() 메소드는 실패시 아무런 예외를 발생시키지 않고 boolean 타입의 결과만 제공해줌
        • 파일이 변경되었는지 확인하는 방법은 lastModified() 라느 넴소드에서 제공해주는 long 타입의 결과로 이전 시간과 비교하는 수 밖에 없었음. 이 메소드가 호출되면 연계되어 호출되는 클래스가 다수 존재하여 성능상 문제도 많음

         

      • NIO2 에서는 다음과 같은 기능으로 대체됨
        • Paths
        • Files
        • FileSystems
        • FileStore
      • lastModifed() 를 대체하기 위해서 WatchService 라는 인터페이스를 제공함.
      • NIO2 에서 추가된 클래스
        • SeekableByteChannel(random access)
        • NetworkChannel 및 MulticastChannel
        • Asynchronous I/O
          • AsynchronousChannelGroup 은 비동기 처리르 하는 쓰레드 풀을 제공하여 보다 안정적으로 비동기적인 처리가 가능하다.
      • 그 외 추가된 것들
        • JDBC4.1
          • RowSetFactory 와 RowSetProvider 라는 클래스가 추가되었다.
          • Connection 및 Statement 생성할 필요 없이 SQL Query 를 수행할 수 있다.
          • RowSetFactory 와 RowSetProvider 는 아주 쉽게 RowSet 의 객체를 생성할 수 있다.
        • TransFerQueue 추가
        • Objects 클래스 추가
          • Object 에서 제공하는 메소드들을 null 이라고 할지라도 예외를 발생시키지 않도록 구현해 놨다.

     

Java 8 에서 추가된 것들

  • Lamda 표현식
  • Functional(함수형) 인터페이스
  • Stream(스트림)
  • Optional
  • 인터페이스의 기본 메소드
  • 날짜 관련된 클래스 추가
  • 병렬 배열 정렬
  • StringJoiner 추가

 

 

Optional

  • optional 은 Funtional 언어인 HAskell 과 Scala 에서 제공하는 기능을 따온 것이다.
  • null 처리를 보다 간편하게 하기 위해서 만들어졌다.
  • public final class Optional<T> extends Object
  • final 로 생성되었기 때문에 자식 클래스를 만들 수 없다.
  • 생성 방법은 empty(), of(), ofNullable() 메소드 들이 존재한다.
    • empty() : 데이터가 없는 Optioanl 객체를 생성한다.
    • ofNullable() : 만약 null 이 추가될 수 있는 상황이라면 사용한다.
    • of() : 반드시 데이터가 들어갈 수 있는 상황에는 of() 메소드를 사용한다.
  • 리턴하는 방법은 get(), orElse(), orElseGet(), orElseThrow() 다
    • get() : 만약 데이터가 없을 경우에는 null 이 리턴된다.
    • orElse() : 만약 값이 없을 경우에는 기본 값을 지정할 수 있다.
    • orElseGet() : Supplier<T> 라는 인터페이스를 활용할 수 있다.
    • orElseThrow() : 만약 데이터가 없을 경우에 예외를 발생시키고 싶다면 사용한다. Exception 도 Supplier<T> 인터페이스를 사용한다.

 

Default Method

  • 만들어진 이유는 하위 호환성 때문이다.

 

날짜 관련 클래스들

  • Date 나 SimpleDateFormatter 는 쓰레드 세이프 하지 않다.
  • 불변 객체도 아니어서 지속적으로 값이 변경되었다.
  • API 구성도 복잡하게 되어 있어서 연도는 1900년부터 시작하도록 되어 있고 달은 1부터, 일은 0부터 시작한다.
  • 이러한 여러 가지 이슈들 때문에 java.time 이라는 패키지를 만들었다.
  • https://docs.oracle.com/javase/tutorial/datatime/iso/index.html

 

병렬 배열 정렬

  • paralleSort() 라는 정렬 메소드가 제공되며, For-Join 프레임웍이 내부적으로 사용된다.
  • CPU 는 많이 사용되지만 속도는 빠르다.
  • 따라서, 5000개 정도부터 parallelSort() 성능이 빨라지기 때문에 CPU 가 좋으면 사용한다.

 

StringJoiner

  • 순차적으로 나열된느 문자열을 처리할 때 사용한다.
public void joinStringOnly(String[] stringArray) {
	StringJoinner joiner = new STringJoiner(",");
	for (String string : stringArray) {
		joiner.add(string);
	}
}

public void withCollector(STring[] stringArray) {
	List<String> strings = Arrays.asList(stringArray);
	STring result = strings.stream().collect(Collectors.joining(","));
}

 

 

Lamda

  • 익명 클래스를 사용하면 가독성도 떨어지고 불편한데, 이러한 단점을 보완하기 위해서 람다 표현식이 만들어졌다. 대신, 이 표현식은 인터페이스에 메소드가 하나 인 것들만 적용 가능하다. 그래서 람다 표현식은 익명 클래스로 전환이 가능하며, 익명 클래스는 람다 표현식으로 전환 가능하다.
  • 하나만 있는 인터페이스
    • java.lang.Runnable
    • java.util.Comparator
    • java.io.FileFilter
    • java.util.concurrent.Callable
    • java.security.PrivilegedAction
    • java.nio.file.PathMatcher
    • java.lang.reflect.InvocationHandler

     

private void calculateClassic() {
	Calculate calculateAdd = new Calculate() {
		@Override
		public int operation(int a, int b) {
			return a + b;
		}
	};
}

->

private void calculate() {
	Calculate calculateAdd = (a, b) -> a + b;
	calculateAdd.operation(1, 2);
}

 

  • 람다 표현식을 사용하기 위해서는 interface 에 추상 메소드가 1개뿐이여야 되는데, 즉 Funcional 인터페이스이어야 하는데, 만약 그러한 것을 알지 못하고 메소드를 하나 더 추가하면 오류가 발생한다. 이러한 것을 방지하기 위해서 @FunctionalInterface 를 선언해 놓을 수 있다.

 

private void runCommonThread() {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getContextClassLoader());
        }
    };
    
    new Thread(runnable).start();
}

->

private void runCommon() {
    new Thread(() -> System.out.println(Thread.currentThread().getContextClassLoader())).start();
}

 

 

java.util.funcion 패키지

  • Predicate
    • test() 라는 메소드가 있으며, 두 개의 객체를 비교할 때 사용하고 boolean 을 리턴한다. 추가로 and(), negate(), or() 이라는 default 메소드가 구현되어 있으며, isEquals() 이라는 static 메소드도 있다.
  • Suplier
    • get() 메소드가 있으며, 리턴값은 generic 으로 선언된 타입을 리턴한다. 다른 인터페이스들과는 다르게 추가적인 메소드는 선언되어 있지 않다.
  • Comsumer
    • accept() 라는 매개 변수를 하나 갖는 메소드가 있으며, 리턴값이 없다. 그래서 출력을 할 때처럼 작업을 수행하고 결과를 받을 일이 없을 때 사용한다.
    • 추가로 andThen() 이라는 default 메소드가 구현되어 있는데, 순차적인 작업을 할 때 유용하게 사용될 수 있다.
  • Function
    • apply() 라는 하나의 매개 변수를 갖는 메소드가 있으며, 리턴값도 존재한다. 이 인터페이스는 Function<T, R> 로 정의되어 있어, Generic 타입을 두 개 갖고 있다. 앞에 있는 T 는 입력 타입, 뒤에 있는 R 은 리턴 타입을 의미한다. 즉, 변환을 할 필요가 있을 때 이 인터페이스를 사용한다.
  • UnaryOperator: A unary operator from T → T
    • apply() 라는 하나의 매개 변수를 갖는 메소드가 있으며, 리턴값도 존재한다. 단, 한 가지 타입에 대하여 결과도 같은 경우일 경우 사용한다.
  • BinaryOperator : A binary operator from (T, T) → T
    • apply() 라는 두개의 매개 변수를 갖는 메소드가 있으며, 리턴값도 존재한다. 단, 한 가지 타입에 대하여 리턴ㄱ밧도 같은 타입일 경우 사용한다.

     

Stream

  • 뭔가 연속된 정보를 처리하는 데 사용된다.
  • 스트림 생성 : 컬랙션의 목록을 스트림 객체로 반환한다.
  • 중개 연산 : 생성된 스트림 객체를 사용하여 중개 연산 부분에서 처리한다. 아무런 결과를 리턴하지 못한다. 반드시 있어야 하는 것은 아니다. (parallelStream() 은 병렬로 처리하지만 CPU 도 많이 사용하고, 몇개의 쓰레드로 처리할지가 보장되지 않는다. 따라서, 일반적인 것은 stream() 을 쓴다.)
  • 종단 연산 : 마지막으로 중개 연산에서 작업된 내용을 바탕으로 결과를 리턴해 준다.

 

  • 중간 연산
    • filter(pred)
    • map(mapper) / mapToInt() / mapToLong() / mapToDobule()
    • flatMap(flat-mapper) / 위와 유사
    • distinct()
    • sorted(comparator)
    • peak()
    • limit()
    • skip()

     

  • 종단 연산
    • forEach() / forEachOrdered()
    • toArray(array-factory)
    • any / all / noneMath(pred)
    • findFirst / Any(pred)
    • cumulate(binop)
    • reduce(binop) / reduce(base, binop)
    • collect(collector)
    • min(), max(), count()
    • anyMatch(), allMatch(), noneMatch9)

메소드 참조

  • static 메소드 참조
  • 특정 객체의 인스턴스 메소드 참조
  • 특정 유형의 임의의 객체에 대한 인스턴스 메소드 참조
  • 생성자 참조

 

 

Violate

  • JIT 컴파일러가 optimization 을 적용해서 CPU 캐시 를 사용해서 메인 메모리에 저장되지 않고 CPU 캐시에 저장되고 참조하도록 한다.
  • 이러한 것이 Thread 가 여러개 일때 캐싱이 되므로 문제가 될 수 있다.
  • 이럴 때 violate 를 변수에
  • private violatile double instanceVariable = 0;
  • 쓰레드에 선언된 인스턴스 변수를 선언할 일이 있을 때, 이처럼 violatile 로 선언함녀 해당 변수 값이 바뀌면 "내가 갖고 있는 volatile 변수가 바뀌었는데, 너도 이거 쓰니가 값을 바꿔" 라고 얘기해준다. 그러므로, 같은 객체에 이쓴ㄴ 변수는 모든 쓰레드가 같은 값을 바라보게 된다.
  • 하지만, 모든 변수에 이렇게 volatile 이라고 써 줄 필요는 없다.
  • 남발하면 성능상으로 저하가 발생한다.
  • 또한 volatile 이라고 적어주지 않았다고 데이터가 꼬이는 일이 발생하는 것이 아니다.
  • 최적화가 되지 않아 캐시간에 데이터가 다른 값을 보지 않음녀 volatile 을 사용할 필요가 없다.
  • 데이터가 문제가 있을 경우 사용하면 좋다.