Static Nested 클래스를 사용하는 이유 : 한 곳에서만 사용되는 클래스를 논리적으로 묶어서 처리할 필요가 있을 때
내부 클래스를 사용하는 이유 : 캡슐화가 필요할 때( 예를 들어 A라는 클래스에 private 변수가 있다. 이 변수에 접근하고 싶은 B라는 클래스를 선언하고, B클래스를 외부에 노출시키고 싶지 않을 경우가 여기에 속한다). 즉, 내부 구현을 감추고 싶을 때를 말한다.
소스의 가독성과 유지보수성을 높이고 싶을 때
Static nested 클래스의 특징
내부 클래스는 감싸고 있는 외부 클래스의 어떤 변수도 접근할 수 있다. 심지어 private로 선언된 변수까지도 접근 가능하다.
Static nested 클래스를 그렇게 사용하는 것은 불가능하다.
public class OuterOfStatic {
static class StaticNested {
private int value = 0;
public int getValue(){
return value;
}
public void setValue(int value){
this.value = value;
}
}
}
내부에 있는 Nested 클래스는 별도로 컴파일할 필요가 없다. OuterOfStatic이라는 감싸고 있는 클래스를 컴파일하면 자동으로 컴파일되기 때문이다.
Nested 클래스는 이처럼 감싸고 있는 클래스 이름 뒤에 $ 기호를 붙인 후 Nested 클래스의 이름이 나온다.
public class NestedSample {
public static void main(String[] args) {
NestedSample sample = new NestedSample();
sample.makeStaticNestedObject();
}
public void makeStaticNestedObject() {
OuterOfStatic.StaticNested staticNested = new OuterOfStatic.StaticNested();
staticNested.setValue(3);
System.out.println(staticNested.getValue());
}
}
객체 생성은 감싸고 있는 클래스 이름 뒤에 .(점)을 찍고 쓰면 된다. 객체를 생성한 이후에 사용하는 방법은 일반 클래스와 동일하다.
내부 클래스와 익명 클래스 (p. 419)
public class OuterOfInner {
class Inner {
private int value = 0;
public int getValue(){
return value;
}
public void setValue(int value){
this.value = value;
}
}
}
public class InnerSample {
public static void main(String[] args) {
InnerSample sample = new InnerSample();
sample.makeInnerObject();
}
public void makeInnerObject(){
OuterOfInner outer = new OuterOfInner();
OuterOfInner.Inner inner = outer.new Inner();
inner.setValue(3);
System.out.println(inner.getValue());
}
}
Inner 클래스의 객체를 생성하기 전에는 먼저 Inner클래스를 감싸고 있는 OuterOfInner 라는 클래스의 객체를 만들어야만 한다. 여기서는 outer라는 객체다. 이 outer 객체를 통해서 Inner 클래스의 객체를 만들어 낼 수 있다.
내부 클래스를 만드는 이유
캡슐화 때문이다. 하나의 클래스에서 어떤 공통적인 작업을 수행하는 클래스가 필요한데 다른 클래스에서는 그 클래스가 전혀 필요가 없을 때 이러한 내부 클래스를 만들어 사용한다. 내부 클래스는 GUI 관련 프로그램을 개발할 때 가장 많이 사용한다.
익명 클래스를 만드는 이유
내부 클래스를 만드는 것보다 더 간단한 방법
public class AnonymousSample { public static void main(String[] args) { AnonymousSample sample = new AnonymousSample(); sample.setButtonListener(); } private void setButtonListener() { MagicButton button = new MagicButton(); button.setListener(new EventListener(){ public void onClick(){ System.out.println("Magic Button Clicked!!!"); } }); button.onClickProcess(); } }
메소드를 호출하는 과정 내에 익명 클래스가 있는 것이기 때문에 이와 같이 소괄호를 닫고 세미콜론을 해 줘야만 한다.
클래스 이름도 없고, 객체 이름도 없기 때문에 다른 클래스나 메소드에서는 참조할 수가 없다. 만약 객체를 해당 클래스내에서 재사용하려면, 다음과 같이 객체를 생성한 후 사용하면 된다.
public void setButtonListenerAnonymousObject(){
MagicButton button = new MagicButton();
EventListener listener = new EventListener() {
@Override
public void onClick() {
System.out.println("Magic Button Clicked");
}
};
button.setListener(listener);
button.onClickProcess();
}
클래스를 만들고, 그 클래스를 호출하면 그 정보는 메모리에 올라간다. 즉, 클래스를 많이 만들면 만들수록 메모리는 많이 필요해지고, 애플리케이션을 시작할 때 더 많은 시간이 소요되므로 ㄹ자바에서는 이렇게 간단한 방법으로 객체를 생성할 수 있도록 하였다.
익명 클래스나 내부 클래스는 모두 다른 클래스에서 재사용할 일이 없을 때 만들어야 한다.
Nested 클래스의 특징
package chapter16;
public class NestedValueReference {
public int publicInt = 0;
protected int protectedInt = 1;
int justInt = 2;
private int privateInt = 3;
static int staticInt = 4;
static class StaticNested {
public void setValue(){
staticInt = 14;
}
}
class Inner {
public void setValue(){
publicInt = 20;
protectedInt = 21;
justInt = 23;
privateInt = 33;
staticInt = 24;
}
}
public void setValue(){
EventListener listener = new EventListener() {
public void onClick() {
publicInt = 20;
protectedInt = 21;
justInt = 23;
privateInt = 33;
staticInt = 24;
}
};
}
}
Static Nested 클래스에서는 감싸고 있는 클래스의 static 변수만 참조할 수 있다. StaticNedsted 클래스가 static 으로 선언되어 있기 때문에 부모 클래스에 static 하지 않은 변수를 참조할 수는 없다.
static nested 클래스와는 다르게, 내부 클래스와 익명 클래스는 감싸고 있는 클래스의 어떤 변수라도 참조할 수 있다.
public class ReferenceAtNested {
static class StaticNested{
private int staticNestedInt = 99;
}
class Inner {
private int innerValue= 100;
}
public void setValue(int value){
StaticNested nested = new StaticNested();
nested.staticNestedInt = value;
Inner inner = new Inner();
inner.innerValue = value;
}
}
감싸고 있는 클래스는 Static Nested 클래스의 인스턴스 변수나 내부 클래스의 인스턴스 변수로 접근 가능하다. 그 값이 private라고 할지라도 모두 접근할 수 있다.
17장
어노테이션이란?
@
Annotation
메타데이터(Metadata)
사용
컴파일러에게 정보를 알려주거나
컴파일할 때와 설치(deployment) 시의 작업을 지정하거나
실행할 때 별도의 처리가 필요할 때
클래스, 메소드, 변수 등 모든 요소에 선언할 수 있다.
미리 정해져 있는 어노테이션
@Override
@Deprecated
@SupressWarnings
@Override
해당 메소드가 부모 클래스에 있는 메소드를 Override 했다는 것을 명시적으로 선언
public class Parent {
public Parent(){
System.out.println("Parent Constructor");
}
public Parent(String name){
System.out.println("Parent(String) Constructor");
}
public void printName(){
System.out.println("printName() - Parent");
}
}
public class AnnotationOverride extends Parent{
@Override
public void printName(String args){ //--error
System.out.println("AnnotationOverride");
}
@Override
public void printName(){
System.out.println("AnnotationOverride");
}
}
@Deprecated
미리 만들어져 있는 클래스나 메소드가 더 이상 사용되지 않는 경우
public class AnnotationDeprecated {
@Deprecated
public void noMoreUse(){
}
}
public class AnnotationSample {
public void useDeprecated(){
AnnotationDeprecated child = new AnnotationDeprecated();
child.noMoreUse();//'noMoreUse()' is deprecated
}
}
컴파일 결과에는 "경고"만 있고 "에러"는 없다
하위 호환성을 위해서 Deprecated로 선언하는 것은 꼭 필요하다.
@ SupressWarnings
컴파일러가 경고를 알리는 경우가 있는데 일부러 이렇게 코딩한 거니까 경고를 해 줄 필요가 없음을 알려주는 경우 사용
public class AnnotationSample {
@SuppressWarnings("deprecation")
public void useDeprecated(){
AnnotationDeprecated child = new AnnotationDeprecated();
child.noMoreUse();
}
}
메타 어노테이션 (Meta annotaiton)
어노테이션을 직접 선언할 때 사용
@Target
어노테이션을 어떤 것에 적용할지를 선언할 때 사용한다.
Target 괄호 안에 적용 대상을 지정한다.
@Target(ElementType.METHOD)
요소 타입
대상
CONSTRUCTOR
생성자 선언시
FIELD
enum 상수를 포함한 필드(field) 값 선언시
LOCAL_VARIABLE
지역 변수 선언시
METHOD
메소드 선언시
PACKAGE
패키지 선언시
PARAMETER
매개 변수 선언시
TYPE
클래스, 인터페이스, enum 등 선언시
@Retentaion
얼마나 오래 어노테이션 정보가 유지되는지를 다음과 같이 선언한다.
괄호 안에 지정하는 적용 가능한 대상
@Retention(RetentionPolicy.RUNTIME)
대상
SOURCE
어노테이션 정보가 컴파일시 사라짐
CLASS
클래스 파일에 있는 어노테이션 정보가 컴파일러에 의해서 참조가 가능함. 하지만, 가상 머신(Virtual Machine)에서는 사라짐
RUNTIME
실행시 어노테이션 정보가 가상 머신에 의해서 참조 가능
@Documentated
해당 "어노테이션에 대한 정보가 Javadocs(API) 문서에 포함된다는 것"을 선언
@Inherited
모든 자식 클래스에서 부모 클래스의 어노테이션을 사용 가능하다는 것을 선언
@interface
이 어노테이션은 어노테이션을 선언할 때 사용한다.
어노테이션 선언
java.lang.annotation
어노테이션 관련 클래스들은 해당 패키지에 선언되어 있다.
@Target(ElementType.METHOD) // -- 1
@Retention(RetentionPolicy.RUNTIME) // -- 2
public @interface UserAnnotation { // -- 3
public int number();
public String text() default "This is first annotation"; // -- 4
}
메소드에 사용할 수 있다고 지정
클래스 선언시, 메소드 선언시에 어노테이션 사용할 수 있도록 하려면
@Target({ElementType.METHOD, ElementType.TYPE})
실행시에 이 어노테이션을 참조
@UserAnnotation으로 어노테이션이 사용 가능해진다.
default가 있으면 값을 지정하지 않아도 default 값으로 지정된다.
public class UserAnnotationSample {
@UserAnnotation(number=0)
public static void main(String args[]){
UserAnnotationSample sample = new UserAnnotationSample();
}
@UserAnnotation(number=1)
public void annotationSample1(){
}
@UserAnnotation(number=2,text="second")
public void annotationSample2(){
}
@UserAnnotation(number=3,text="third")
public void annotationSample3(){
}
}
어노테이션에 선언된 값 확인
public class UserAnnotationCheck {
public static void main(String[] args) {
UserAnnotationCheck sample = new UserAnnotationCheck();
sample.checkAnnotations(UserAnnotationSample.class);
}
public void checkAnnotations(Class useClass){
Method[] methods = useClass.getDeclaredMethods(); // -- 1
for(Method tempMethod:methods){
UserAnnotation annotation = tempMethod.getAnnotation(UserAnnotation.class); // -- 2
if(annotation!=null){
int number = annotation.number(); // -- 3
String text = annotation.text(); // -- 3
System.out.println(tempMethod.getName() + "() : number = " + number + ", text = "+ text);
}else {
System.out.println(tempMethod.getName()
+ "() : annotation is null.");
}
}
}
}
Class 클래스에 선언되어 있는 getDeclaredMethods() 메소드를 호출하면, 해당 클래스에 선언되어 있는 메소드들의 목록을 배열로 리턴한다.
Method 클래스에 선언되어 있는 getAnnotation()이라는 메소드를 호출하면, 해당 메소드에 선언되어 있는 매개 변수로 넘겨준 어노테이션이 있는지 확인하고, 있을 경우 그 어노테이션의 객체를 리턴해준다.
어노테이션에 선언된 메소드를 호출하면, 그 값을 리턴해준다.
어노테이션도 상속이 안돼요
미리 만들어놓은 어노테이션을 확장하는 것은 불가능하다.
extends 예약어 사용 불가
Lombok
롬복을 사용하면 간단히 선언만으로도 getter 메소드와 setter 메소드를 생성해준다.
19장
자바에서 사용되는 다른 용어들
JDK : Java Development Kit
Java SE : Java Standard Edition
JRE : Java Runtime Environment
자바를 실행할 수 있는 환경의 집합
자바에서 제공하는 라이브러리들이 칸칸이 쌓여 있음
자바 언어의 특징
It should be "simple, object-oriented and familiar" (자바는 "단순하고, 객체지향적이며, 친숙"해야 한다.)
It should be "robust and secure" (자바는 "견고하고, 보안상 안전"하다.)
자바는 컴파일할 때와 실행할 때 문법적 오류에 대한 체크를 한다.
It shoud be "architecture-netural and portable" (자바는 "아키텍처에 중립적이어야 하며 포터블" 해야 한다.)
자바는 아키텍처에 중립적인 바이트 코드를 생성한다. 자바의 버전만 동일하다면, 동일한 프로그램은 어떤 플랫폼에서도 동일한 결과가 나오며, 호환성 문제가 발생하지 않는다( JVM 덕분 )
It should execute wite "high performance" (자바는 "높은 성능"을 제공해야 한다. )
It should be "interpreted, threaded, and dynamic" (자바는 "인터프리트 언어이며, 쓰레드를 제공하고, 동적인 언어"이다.)
자바의 버전별 차이
JDK 1.0
JDK 1.1
JDK 1.2
- Collections라는 프레임웍 추가
- JIT
Just-In-Time 의 약자
어떤 메소드의 일부 혹은 전체 코드를 네이티브 코드로 변환하여 JVM에서 번역하지 않도록 함으로써 보다 빠른 성능을 제공하는 기술
동적 변환 (dynamic translation)
명칭이 컴파일러지만, 실행시에 적용되는 기술
인터프리터(interpret) 방식 + 정적(static) 컴파일 방식 = 두 가지를 혼합한 것
인터프리터 방식 : 프로그램을 실행할 때마다 컴퓨터가 알아 들을 수 있는 언어로 변환하는 작업
정적컴파일 방식 : 실행하기 전에 컴퓨터가 알아 들을 수 있는 언어로 변환하는 작업을 미리 실행
변환 작업은 인터프리터에 의해서 지속적으로 수행되지만, 필요한 코드의 정보는 캐시에 담아두었다가(메모리에 올려두었다가) 재사용
javac : 텍스트로 만든 java 파일을 어떤 OS에서도 수행될 수 있도록 바이트 코드라는 파일로 만든 것이고, 컴퓨터가 알아먹을 수 있도록 하려면 다시 변환 작업이 필요한데, 이 변환 작업을 JIT 컴파일러가 수행함
자바 프로그램이 수행되는 절차
자바 소스 코드 -> 자바 컴파일러 -> 컴파일 된 Bytecode -> JVM -> 기계 코드 -> 하드 웨어 및 OS
"JVM -> 기계 코드" 로 변환되는 부분을 JIT에서 수행하는 것
JDK 1.3
- HotSpot JVM
종류
HotSpot 클라이언트 컴파일러
CPU 코어가 하나 뿐인 사용자를 위해 만들어짐
HotSpot 서버 컴파일러
코어가 많은 장비에서 사용하기 위해 만들어짐
기준
기본적으로 자바가 시작할 때 알아서 클라이언트 장비인지 서버 장비인지를 확인한다.
아래 조건 만족시 JVM은 서버 컴파일러 선택
2개 이상의 물리적 프로세스
2GB 이상의 물리적 메모리
명시적 지정
클라이언트 JVM
java -client Calculator
서버 JVM
java -server Calculator
옵션 지정
JVM의 시작 메모리 크기를 지정 : -Xms
java -server -Xms512m Calculator
JDK 1.4
정규 표현식
NIO
JDK 5
보다 안전하게 컬렉션 데이터를 처리할 수 있는 제너릭(generic) 추가
어노테이션(annotation)이라고 불리는 메타데이터(metadata) 기능 추가
기본 자료형과 그 기본 자료형을 객체로 다루는 클래스 간의 데이터 변환이 자동으로 발생하는 autoboxing 및 unboxing 기능 추가
상수 타입을 나타내는 enum 추가
매개 변수의 개수를 가변적으로 선언할 수 있는 varargs 추가
for 루프에 세미콜론이 아닌 콜론으로 구분하여 배열이나 컬렉션 타입에 저장되어 있는 데이터를 순차적으로 꺼내는 향상된 for 루프 추가
import 앞에 static 붙여 정적 import가 가능하도록 해 주는 static import 추가
쓰레드 처리를 쉽게 할 수 있는 concurrent 패키지(java.util.concurrent) 추가
스트림이나 버퍼로 들어오는 데이터의 분석(parse)을 보다 간편하게 할 수 있는 Scanner 클래스 추가
JDK 6
JDK 7
JDK 8
람다 표현식
JVM
Java Virtual Machine
자바 가상 머신
자바 프로그램이 수행되는 프로세스
JVM 위에서 애플리케이션이 동작
JVM에서 작성한 프로그램을 찾고 실행하는 일련의 작업이 수행
GC
Garbage Collector
가비지 컬렉터
메모리 관리를 JVM이 알아서 하기 때문에 자바의 메모리 관리는 개발자가 하지 않아도 된다.
JVM 내에서 메모리 관리를 해주는 것이 바로 "가비지 컬렉터"이다.
Java7부터 공식적으로 사용할 수 있는 G1 라는 가비지 컬렉터를 제외한 나머지 JVM은 위와 같이 영역을 나누어 힙(Heap)이라는 공간에 객체들을 관리한다.
자바의 힙 영역
Young
Eden : 객체를 생성하자마자 저장되는 장소
2개의 Survivor 영역
자바에서 메모리가 살아가는 과정
마이너 GC or 영 GC(컬렉션)
Eden 영역에서 객체가 생성된다.
Eden 영역이 꽉 차면 살아있는 객체만 Survivor 영역으로 복사되고, 다시 Eden 영역을 채우게 된다.
Survivor 영역이 꽉 차게 되면 다른 Survivor 영역으로 객체가 복사된다. 이때, Eden 영역에 있는 객체들 중 살아있는 객체들도 다른 Survivor 영역으로 간다. 즉, Survivor 영여그이 둘 중 하나는 반드시 비어있어야 한다.
메이저 GC or 풀 GC
오래 살아있는 객체들은 Old 영역으로 이동한다.
Old 영역이 꽉차면 GC가 발생함.
영 GC 가 풀 GC 보다 빠르다
더 작은 공간이 할당되고, 객체들을 처리하는 방식도 다르기 때문이다.
전체의 힙 영역을 영 영역으로 만들면 장애로 이어질 확률이 매우 높다.
5가지 가비지 컬렉터
Serial GC
WAS로 사용하는 JVM에서 사용하면 안 되는 GC.
이 GC 방식은 -client 옵션을 지정했을 때 사용된다. 즉, 클라이언트용 장비에 최적화된 GC이기 때문에 WAS에서 이 방식을 사용하면 GC 속도가 매우 느려 웹 애플리케이션이 엄청 느려진다.
Parallel Young Generation Collector
Parallel Old Generation Collector
Concurrent Mark & Sweep Collector(CMS)
G1(Garbage First)
20장. Java.lang 패키지
java.lang 패키지에 정의되어 있는 "에러"
OutOfMemoryError(OOME)
메모리가 부족하여 발생하는 에러
StackOverflowError
호출된 메소드의 깊이가 너무 깊을 때 발생한다.
Stack 이라는 영역에 어떤 메소드가 어떤 메소드를 호출했는지에 대한 정보를 관리한다. 스택에 쌓을 수 있는 메소드 호출 정보의 한계를 넘어설 때 발생
참조 자료형 중에서 Byte, Short, Integer, Long, Float, Double은 필요시 기본 자료형처럼 사용할 수 있다. new를 사용하여 객체를 생성하지 않아도 값 할당 o.
public void numberTypeCheck2(){
Integer refInt1;
refInt1 = 100;
System.out.println(refInt1.doubleValue());
}
매개 변수를 참조 자료형으로만 받는 메소드 처리
제너릭과 같이 기본 자료형을 사용하지 않는 기능을 사용하기 위해서
MIN_VALUE(최소값)나 MAX_VALUE(최대값)와 같이 클래스에 선언된 상수 값을 사용하기 위해서(boolean 클래스 제외)
문자열을 숫자로, 숫자를 문자열로 쉽게 변환하고, 2,8,10,16 진수 변환을 쉽게 처리하기 위해서
public void numberMinMaxCheck(){
System.out.println("Byte min = "+ Byte.MIN_VALUE + " Byte max = " +Byte.MAX_VALUE);
System.out.println("Short min = "+ Short.MIN_VALUE + " Short max = " +Short.MAX_VALUE);
System.out.println("Integer min = "+ Integer.MIN_VALUE + " Integer max = " +Integer.MAX_VALUE);
System.out.println("Long min = "+ Long.MIN_VALUE + " Long max = " +Long.MAX_VALUE);
System.out.println("Float min = "+ Float.MIN_VALUE + " Float max = " +Float.MAX_VALUE);
System.out.println("Double min = "+ Double.MIN_VALUE + " Double max = " +Double.MAX_VALUE);
System.out.println("Character min = "+ (int)Character.MIN_VALUE + " Character max = " +(int)Character.MAX_VALUE);
/*
Byte min = -128 Byte max = 127
Short min = -32768 Short max = 32767
Integer min = -2147483648 Integer max = 2147483647
Long min = -9223372036854775808 Long max = 9223372036854775807
Float min = 1.4E-45 Float max = 3.4028235E38
Double min = 4.9E-324 Double max = 1.7976931348623157E308
Character min = 0 Character max = 65535
*/
}
직접 호출하지 않아도 JVM이 더 이상 필요 없는 객체를 처리하는 GC 작업과 finalization 작업을 실행함
JVM 종료(절대 사용 x)
리턴 타입
메소드 이름 및 매개 변수
설명
static void
exit(int status)
현재 수행중인 JVM을 멈춘다.
현재시간 조회
currentTimeMillis()
리턴 타입 : static long
현재 시간을 밀리초 단위로 리턴한다.
현재 시간을 나타낼 때 사용
UTC(Universal time 기준으로 1970년 1월 1일 00:00 부터 지금까지의 밀리초(1/1000) 단위의 차이를 출력한다.
nanoTime()
리턴 타입 : static long
현재시간을 알아내는 용도 아님
시간 차이를 측정하기 위한 용도임
나노초 (1/1,000,000,000초) 로 시간 제공
public void numberMinMaxElapseCheck(){
JavaLangNumber numberSample = new JavaLangNumber();
long startTime = System.currentTimeMillis();
long startNanoTime = System.nanoTime();
numberSample.numberMinMaxCheck();
System.out.println("Milli second = " + (System.currentTimeMillis()-startTime));//Milli second = 0
System.out.println("Nano second = " + (System.nanoTime() - startNanoTime));//Nano second = 10081100
}
System.out
print(), println(), printf(), format(), write()
print() , println()
차이점
println()만 줄바꿈
println()만 매개변수가 없는 메소드 존재
공통점
byte 타입이나 short 타입을 매개변수로 받는 메소드 선언되어 있지 않지만, 넘겨 주면 int 타입을 매개 변수로 받는 메소드에서 알아서 처리해줌
public class JavaLangSystemPrint {
public static void main(String[] args) {
JavaLangSystemPrint sample = new JavaLangSystemPrint();
sample.printStreamCheck();
}
public void printStreamCheck(){
byte b = 127;
short s = 32767;
System.out.println(b);
System.out.println(s);
printInt(b);
printInt(s);
/*
127
32767
127
32767
*/
}
public void printInt(int value){
System.out.println(value);
}
}
public void printNull(){
Object obj = null;
System.out.println(obj);// == String.valueOf(obj)
System.out.println(obj.toString());//NullPointerException
System.out.println(obj + "is object's value"); // == new StringBuilder().append(obj).append(" is object's value)
}