내가 보려고 만드는 자바 공부한 개념 통합본
프로그램이 실행되기 위해서 windows나 linux같은 운영체제(OS)가 메모리를 제어할 수 있어야 하는데, java 이전의 c같은 언어로 만들어진 프로그램은 이런 이유등으로 OS에 종속되어 실행되었다. java 프로그램은 JVM만 있으면 실행이 가능하며, JVM이 OS에게서 메모리 사용권한을 할당받고 JVM이 자바 프로그램을 호출하여 실행하게 된다.
자바 바이트 코드를 실행할 수 있는 주체로, 자바 코드를 컴파일해서 얻는 바이트 코드를 해당 운영체제가 이해할 수 있는 기계어로 바꿔 실행시킨다
1. Class Loader
자바에서 소스를 작성하면 Person.java 처럼 .java파일이 생성된다.
.java 소스를 자바컴파일러가 컴파일하면 Person.class 같은 .class파일(바이트코드)이 생성된다. (컴퓨터가 이해할 수 있는 코드로 변환)
이렇게 생성된 클래스파일들을 엮어서 JVM이 운영체제로부터 할당받은 메모리영역인 Runtime Data Area로 적재하는 역할을 Class Loader가 한다. (자바 애플리케이션이 실행중일 때 이런 작업이 수행된다.)
2. Execution Engine
Class Loader에 의해 메모리에 적재된 바이트 코드들을 기계어로 변경해 명령어 단위로 실행하는 역할을 한다.
명령어를 하나 하나 실행하는 인터프리터(Interpreter)방식이 있고 JIT(Just-In-Time) 컴파일러를 이용하는 방식이 있다.
JIT 컴파일러는 적절한 시간에 전체 바이트 코드를 네이티브 코드로 변경해서 Execution Engine이 네이티브로 컴파일된 코드를 실행하는 것으로 성능을 높이는 방식이다.
(네이티브 코드: CPU와 OS가 직접 실행할 수 있는 코드. .exe, .dll ...)
3. Garbage Collector
Garbage Collector(GC)는 Heap 메모리 영역에 생성(적재)된 객체들 중에 참조되지 않는 객체들을 탐색 후 제거하는 역할을 한다.
또 다른 특징은 GC가 수행되는 동안 GC를 수행하는 쓰레드가 아닌 다른 모든 쓰레드가 일시정지된다.
특히 Full GC가 일어나서 수 초간 모든 쓰레드가 정지한다면 장애로 이어지는 치명적인 문제가 생길 수 있다.
4. Runtime Data Area
JVM의 메모리 영역으로 JVM이 운영체제로부터 할당받은 메모리 공간이다. 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다.
이 영역은 크게 Method Area, Heap Area, Stack Area, PC Register, Native Method Stack로 나눌 수 있다.
1. Method area (메소드 영역)
클래스 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보같은 필드 정보와 메소드의 이름, 리턴 타입, 파라미터, 접근 제어자 정보같은 메소드 정보, Type정보(Interface인지 class인지), Constant Pool(상수 풀), 메소드에 대한 각종 정보, static 변수등이 생성되는 영역이다.
모든 스레드에서 정보가 공유된다.
2. Heap area (힙 영역)
new 키워드로 인스턴스가 생성되어 동적으로 메모리가 할당되는 영역이다. (또한 Array와 같은 동적으로 생성된 데이터가 저장되는 공간이다.)
메소드 영역에 로드된 클래스만 인스턴스로 생성이 가능하다. 스택 영역은 메서드 실행이 끝나면 자동으로 반환되지만, 힙 영역은 Garbage Collector가 참조되지 않는 메모리를 확인하고 제거한다.
모든 스레드에서 정보가 공유된다.
3. Stack area (스택 영역)
지역 변수, 매개 변수 등 연산에 사용되는 임시 값이 생성되는 영역이다. 메소드를 호출할 때마다 개별적으로 스택이 생성되고, 메서드 실행이 끝나면 자동으로 반환되는 메모리이다.
int a = 10; 이라는 소스를 작성했다면 정수값이 할당될 수 있는 메모리공간을 a라고 잡아두고 그 메모리 영역에 값이 10이 들어간다. 즉, 스택에 메모리에 이름이 a라고 붙여주고 값이 10인 메모리 공간을 만든다.
클래스 Person p = new Person(); 이라는 소스를 작성했다면 Person p는 스택 영역에 생성되고 new로 생성된 Person 클래스의 인스턴스는 힙 영역에 생성된다. 즉, 객체가 선언되면 이 객체는 스택 영역에 생성되고, 그 객체가 가지고 있는 내용들(attribute)은 힙 영역에 생성된다.
그리고 스택영역에 생성된 p의 값으로 힙 영역의 주소값을 가지고 있다. 즉, 스택 영역에 생성된 p가 힙 영역에 생성된 객체를 가리키고(참조하고) 있는 것이다.
이때, 같은 클래스의 다른 객체들이 여러개 생성되면 각각의 내용들이 독립적으로 힙 영역에 쌓인다.
스레드마다 하나씩 존재한다.
4. PC Register (PC 레지스터)
스레드가 생성되면서 생기는 공간. 각 스레드별로 PC Register가 존재하며 JVM 머신이 가장 최근에 실행한 명령어의 주소와 명령을 저장한다. 이것을 이용해서 쓰레드를 돌아가면서 수행할 수 있게 한다.
JVM이 실행하고 있는 현재 위치를 저장하는 역할
5. Native method stack
자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역이다.
보통 C/C++등의 코드를 수행하기 위한 스택이다. (JNI)
쓰레드가 생성되었을 때 기준으로
1,2번인 메소드 영역과 힙 영역을 모든 쓰레드가 공유하고,
3,4,5번인 스택 영역과 PC 레지스터, Native method stack은 각각의 쓰레드마다 생성되고 공유되지 않는다.
JVM에서는 GC의 스케줄링을 담당하여 자바 개발자에게 메모리 관리의 부담을 덜어준다. GC는 더 이상 사용되지 않는 객체들을 메모리에서 제거하여 효율적인 메모리 사용을 돕는다.
객체는 힙 영역에 저장되고 스택 영역에 이를 가르키는 주소값이 저장되는데 Garbage Collector는 참조되지 않는 객체를 메모리에서 제거한다.
JVM의 Heap 영역은 객체의 생존 기간에 따라 물리적인 Heap 영역을 Young, Old 두 영역으로 나누어 설계되었다.
- 위 과정을 계속 반복, survivor2영역까지 꽉차기 전에 계속해서 Old로 비움
Old 영역의 메모리가 부족해지면 발생.
- Minor GC보다 시간이 훨씬 많이 걸리고 실행중에 GC를 제외한 모든 쓰레드가 중지한다.
힙 영역은 우선 5개의 영역(eden, survivor1, survivor2, old, permanent)으로 나뉜다.
Young 영역(Young generation)
Old 영역(Old generation)
Young 영역과 Old 영역은 서로 다른 메모리 구조로 되어 있기 때문에, 세부적인 동작 방식은 다르다. 하지만 기본적으로 가비지 컬렉션이 실행된다고 하면 다음의 2가지 공통적인 단계를 따르게 된다.
GC 처리하는 동안 GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업이 중단되는 현상이다.
Major GC(Full GC)가 일어나면, Old영역에 있는 참조가 없는 객체들을 표시하고 그 해당 객체들을 모두 제거하게 된다.
그러면서 Heap 메모리 영역에 중간중간 구멍(제거되고 빈 메모리 공간)이 생기는데 이 부분을 없애기 위해 재구성을 하게 된다. (디스크 조각모음처럼 조각난 메모리를 정리함)
따라서 메모리를 옮기고 있는데 다른 쓰레드가 메모리를 사용해버리면 안되기 때문에 모든 쓰레드가 정지하게 되는 것이다.
Old 영역이 가득 차면 major GC 가 동작하는데 이때 STW 상태가 되므로 이를 최소화 하는 것이 중요하다.
Stop The World를 통해 모든 작업을 중단시키면, GC는 스택의 모든 변수 또는 Reachable 객체를 스캔하면서 각각이 어떤 객체를 참고하고 있는지를 탐색하게 된다. 그리고 사용되고 있는 메모리를 식별하는데, 이러한 과정을 Mark라고 한다. 이후에 Mark가 되지 않은 객체들을 메모리에서 제거하는데, 이러한 과정을 Sweep라고 한다.
-> JDK7까지는 permanent영역이 heap에 존재했습니다. JDK8부터는 permanent 영역은 사라지고 일부가 "meta space 영역"으로 변경되었습니다.(위의 그림 JDK7 기준입니다.) meta space 영역은 Native stack 영역에 포함되었습니다.
(survivor영역의 숫자는 의미없고 두 개로 나뉜다는 것이 중요하다)
힙 영역을 굳이 5개로 나눈 이유는 효율적으로 GC가 일어나게 하기 위함이다. 자세한 것은 GC가 일어나는 프로세스를 보면서 설명한다.
출처
https://jeong-pro.tistory.com/148
https://12bme.tistory.com/142
GC 동작원리
2진수: 앞에 0B 붙여서 사용한다.
8진수: 앞에 0 붙여서 사용한다. 이진수 3bit를 한번에 표현 가능함(0B111 = 07)
16진수: 앞에 0X 붙여서 사용한다. 이진수 4bit를 한번에 표현 가능함(0B1111 = 0XF)
int num = 10;
int bNum = 0B1010; // 10
int oNum = 012; // 10
int hNum = 0XA; // 10
변수: 프로그램에서 사용되는 자료를 저장하기 위해 할당 받은 메모리의 주소 대신 부르는 이름. 자바에서는 보통 camelCase를 사용한다.
프로그램에서 직접 표현한 값으로, 숫자(정수, 실수), 문자, 논리, 문자열 리터럴이 있다. (ex. 10, 3.14, 'A', true)
int num = 10; //여기서 10은 상수 풀에 저장되어 있는 숫자(리터럴)
long num = 1234567890L; //32비트를 초과하는 숫자를 사용하려면 long으로 처리하도록 뒤에 식별자를 써서 명시해줘야 한다.
변하지 않는 값. C에서는 const, 자바에서는 final 키워드를 사용해 선언한다.
final int MAX_NUM = 100;
final float PI = 3.14f;
PI = 3.15f; //오류남
문자를 위한 코드 값(숫자 값) 들을 정해 놓은 세트
지역 변수 자료형 추론(local variable type inference):
변수에 대입되는 값을 보고 컴파일러가 자료형을 추론한다. (자바 10부터 제공하는 기능)
지역 변수에만 사용할 수 있다.
한번 타입이 정해지면 중간에 바꿀 수 없다.
var num = 10; //컴파일러가 정수로 추론해서 int로 저장된다.
num = 3.14; //이미 int로 저장되었기 때문에 실수로 저장하는 것은 불가능함
Stack Memory: 함수가 호출될 때 사용하는 메모리로, 함수에서 사용하는 지역 변수, 파라미터 등 연산에 사용되는 임시 값이 생성되는 영역이다. 함수의 기능 수행이 끝나면 자동으로 반환되는 메모리이다.
클래스(Class)
객체(Object)
인스턴스(Instance)
클래스를 객체로 만들어서 메모리에 할당한 것.
인스턴스는 객체에 포함된다고 볼 수 있다.
oop의 관점에서 객체가 메모리에 할당되어 실제 사용될 때 ‘인스턴스’라고 부른다.
/* 클래스 */
public class Animal {
...
}
/* 객체와 인스턴스 */
public class Main {
public static void main(String[] args) {
Animal cat, dog; // 아직 메모리 할당은 안받았으니 그냥 '객체'
// 인스턴스화
cat = new Animal(); // cat은 Animal 클래스의 '인스턴스'(객체를 메모리에 할당)
dog = new Animal(); // dog은 Animal 클래스의 '인스턴스'(객체를 메모리에 할당)
}
}
Q. 클래스 VS 객체
Q. 객체 VS 인스턴스
추상화 기법
public Person() {
this("이름없음",1);
// default constructor가 호출되면 name = "이름없음", age = 1 으로 설정
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// this를 이용해 다른 생성자를 호출할 때는 그 이전에 어떠한 statement도 사용할 수 없다
// 위와 같이 생성자가 여러 개이고 파라미터만 다른 경우 constructor overloading이라고 한다.
non-static 멤버
static 멤버
여러 개의 인스턴스가 같은 메모리의 값을 공유하기 위해 사용
공간적 특성: 멤버는 클래스당 하나가 생성된다.
시간적 특성: 클래스 로딩 시(프로그램이 메모리에 로드될 때)에 멤버가 생성된다.
공유의 특성: 동일한 클래스의 모든 객체들에 의해 공유된다.
static 메소드에서는 인스턴스 변수를 쓸 수 없음
https://gmlwjd9405.github.io/2018/08/04/java-static.html
어떤 클래스에 대해 프로그램에서 단 하나의 인스턴스만 생성해야 할 때 사용하는 패턴이다.
// Company 인스턴스가 단 하나여야 하는 경우
public class Company {
// 유일하게 생성되는 인스턴스
private static Company instance = new Company();
private Company() { }
public static Company getInstance(){
if(instance == null)
instance = new Company();
return instance;
}
}
자바에서 배열은 new 생성자를 사용해 선언한다.
Book[] library = new Book[5];
int[][] arr = new int[2][3];
// length = 3 짜리 배열 초기화
int[] numbers = new int[] {0,1,2};
int[] numbers = new int[3] {0,1,2}; //오류남
int[] numbers = {0,1,2};
System.arraycopy(library, 0, librarycopy, 0, 5)
for(String lang:strArray){
System.out.println(lang);
}
기존 배열은 길이를 정하여 선언해야 하고, 중간에 요소를 삭제하거나 삽입하는 것도 어려워서 자바에서는 ArrayList 클래스를 제공한다. 여러 메서드와 속성을 사용해 객체 배열을 편리하게 관리 할 수 있다.
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.get(0); // A 반환. list[0]은 안됨
상위 클래스로의 형 변환(Upcasting)
Parent parent = new Child();
하위 클래스로의 형 변환(Downcasting)
((Child)parent).childMethod();
if(parent instanceof Child){
// parent가 Child 인스턴스인지 확인할 수 있음
}
Customer vc = new VIPCustomer();
// Customer이 상위 클래스, VIPCustomer이 하위 클래스
vc.calcPrice();
// 하위 클래스에서 오버라이딩된 메서드의 경우 객체 타입의 메서드가 아닌 인스턴스인 VIPCustomer 클래스의 calcPrice() 메서드가 호출된다.
가상 메서드(virtual method): 타입과 상관 없이 실제 생성된 인스턴스의 메서드가 호출된다. C++에서는 virtual 키워드를 명시해줘야 하지만 자바는 모든 메서드가 가상 메서드임.
abstract 키워드와 함께 원형만 선언되고, 코드는 작성되지 않은 메서드
public abstract String getName(); // 추상 메서드
public abstract String fail() { return "Fail"; } // 추상 메서드 아님. 컴파일 오류 발생
추상 클래스는 new(인스턴스화) 할 수 없음
개념: abstract 키워드로 선언된 클래스
a. 추상 메서드를 최소 한 개 이상 가지고 abstract로 선언된 클래스
최소 한 개의 추상 메서드를 포함하는 경우 반드시 추상 클래스로 선언하여야 한다.
b. 추상 메서드가 없어도 abstract로 선언한 클래스
추상 메서드가 하나도 없는 경우라도 추상 클래스로 선언할 수 있다.
추상 클래스의 구현
추상 클래스의 목적
객체(인스턴스)를 생성하기 위함이 아니며, 상속을 위한 부모 클래스로 활용하기 위한 것이다.
여러 클래스들의 공통된 부분을 추상화(추상 메서드) 하여 상속받는 클래스에게 구현을 강제화하기 위한 것이다. (메서드의 동작을 구현하는 자식 클래스로 책임을 위임)
즉, 추상 클래스의 추상 메서드를 자식 클래스가 구체화하여 그 기능을 확장하는 데 목적이 있다.
/* 개념 a의 예시 */
abstract class Shape { // 추상 클래스
Shape() {...}
void edit() {...}
abstract public void draw(); // 추상 메서드
}
/* 개념 b의 예시 */
abstract class Shape { // 추상 클래스
Shape() {...}
void edit() {...}
}
/* 추상 클래스의 구현 */
class Circle extends Shape {
public void draw() { System.out.println("Circle"); } // 추상 메서드 (오버라이딩)
void show() { System.out.println("동그라미 모양"); }
}두시반
개념: 추상 메서드와 상수만을 포함하며, interface 키워드를 사용하여 선언한다.
인터페이스의 구현
인터페이스의 목적
인터페이스의 특징
인터페이스는 상수 필드와 추상 메서드만으로 구성된다.
모든 메서드는 추상 메서드로서, abstract public 속성이며 생략 가능하다.
상수는 public static final 속성이며, 생략하여 선언할 수 있다.
인터페이스를 상속받아 새로운 인터페이스를 만들 수 있다.(다중 상속 가능)
interface MobilePhone extends Phone { }
/* 인터페이스의 개념 */
interface Phone { // 인터페이스
int BUTTONS = 20; // 상수 필드 (public static final int BUTTONS = 20;과 동일)
void sendCall(); // 추상 메서드 (public abstract void sendCall();과 동일)
abstract public void receiveCall(); // 추상 메서드
}
/* 인터페이스의 구현 */
class FeaturePhone implements Phone {
// Phone의 모든 추상 메서드를 구현한다.
public void sendCall() {...}
public void receiveCall() {...}
// 추가적으로 다른 메서드를 작성할 수 있다.
public int getButtons() {...}
}
Client는 어떻게 구현되었는지 상관없이 interface 정의만을 보고 사용할 수 있다.
추상 클래스와 인터페이스의 공통점
추상 클래스와 인터페이스의 차이점
서로 다른 목적을 가지고 있다.
추상 클래스는 추상 메서드를 자식 클래스가 구체화하여 그 기능을 확장하는 데 목적이 있다. (상속을 위한 부모 클래스)
인터페이스는 서로 관련이 없는 클래스에서 공통적으로 사용하는 방식이 필요하지만 기능을 각각 구현할 필요가 있는 경우에 사용한다. (구현 객체의 같은 동작을 보장)
추상 클래스는 클래스이지만 인터페이스는 클래스가 아니다.
public class CompleteClass implements Interface1, Interface2
추상 클래스는 “is a kind of” 인터페이스는 “can do this”
public final void run(){
start();
going();
stop();
}
// 세부적인 메서드에 대한 구현은 하위 클래스에서 하도록 하되, 이 시나리오는 변할 수 없게 함
https://gmlwjd9405.github.io/2018/08/06/java-final.html
a==b -> true
a.equlas(b) -> true
a==c -> false
a.equals(c) -> true
인스턴스의 힙메모리 저장 주소를 반환하는 메서드이다. (힙메모리에 인스턴스가 저장되는 방식이 hash)
hash: 정보를 저장, 검색하기 위해 사용하는 자료구조로, 자료의 특정 값(키 값)에 대해 저장 위치를 반환해주는 해시 함수를 사용한다.
index = hash(key)
// index는 저장위치
String, Integer 등의 클래스에서는 서로 다른 메모리의 두 인스턴스의 값이 동일하면 hashCode() 값도 동일하게 반환된다.(실제 힙메모리 주소는 다름)
String을 선언할 때 힙메모리에 인스턴스로 생성되는 경우와 상수 풀(constant pool)에 있는 주소를 참조하는 방법 두 가지가 있다. 이때 상수 풀의 문자열을 참조하면 모든 문자열이 같은 주소를 가리킨다.
String str1 = new String("abc"); //생성자의 매개변수로 문자열 생성
String str2 = "test"; //문자열 상수를 가리키는 방식
String
StringBuilder, StringBuffer
내부적으로 가변적인 char[] 배열을 가지고 있는 클래스이다. memory에 append 하는 방식으로 문자열을 변경할 수 있다. 따라서 문자열의 추가, 수정, 삭제가 빈번하게 발생할 경우라면 String 클래스가 아닌 StringBuffer/StringBuilder를 사용하는 것이 좋음.
StringBuilder
변경가능한 문자열
비동기 처리
싱글 쓰레드에서는 성능이 더 좋음
StringBuffer
컴파일 후 생성된 class 파일에서 객체의 정보(멤버변수, 메서드, 생성자 등)를 가져올 수 있다.
Class 클래스를 이용하여 클래스의 정보를 가져오고 이를 활용해서 인스턴스를 생성하고, 메서드를 호출하는 등의 프로그래밍 방식
String s = new String();
Class c = s.getClass();
Class c = String.class;
Class c = Class.forName("java.lang.String"); //클래스 이름은 패키지까지 풀네임
모든 종류의 타입을 다룰 수 있도록 일반화된 타입 매개 변수(generic type)로 클래스나 메서드를 선언하는 기법이다. 클래스 내부에서 사용하는 데이터 타입을 클래스의 인스턴스를 생성할 때 결정한다.
처리 방법: 타입 제거(type erasure)라는 개념에 근거한다.
복수 제네릭도 가능하다.
특징
컴파일 시 강한 체크 타입 가능
제네릭 타입으로 특정 클래스나 자식 클래스 타입만 오도록 타입 매개변수를 제한할 수 있다.
class Stack<T extends 클래스>
개념: 템플릿은 하나의 클래스를 서로 다른 여러 타입에 재사용할 수 있도록 하는 방법
처리 방법: 컴파일러는 인자로 주어진 각각의 타입에 대해 별도의 템플릿 코드를 생성한다.
예를 들어 MyClass가 MyClass와 정적 변수(static variable)를 공유하지 않는다.
하지만 java에서 정적 변수는 제네릭(Generic) 인자로 어떤 타입을 주었는지에 관계없이 MyClass로 만든 모든 객체가 공유한다.
Map
Collection
List
Set
HashSet
TreeSet
LinkedHashSet
클래스 내부에 있는 클래스
자바는 객체지향 프로그램 언어이기 때문에 자바에서 함수형 프로그래밍을 구현하는 방식으로 람다식을 제공한다. 클래스를 생성하지 않고 함수의 호출만으로 기능을 수행하는 방식이다.
순수 함수(pure function)를 구현하고 호출함.
순수 함수
병렬 처리가 가능하다. (외부 자료에 영향을 미치지 않기 때문에 side effect가 없어 여러 함수가 동시에 수행될 수 있다.)
함수 이름과 반환 형을 없애고 ->를 사용한다. {}까지가 실행문을 의미
(int x, int y) -> {return x + y;}
함수형 인터페이스
자바는 객체 지향 언어로 객체를 생성해야 메서드가 호출된다. 따라서 람다식으로 메서드를 구현하고 호출하면 내부에서 익명 클래스가 생성된다. 이 람다식에서 외부 메서드의 지역변수는 상수로 처리 됨(지역 내부 클래스와 동일한 원리)
@FunctionalInterface
interface StringConcat {
public void makeString(String s1, String s2);
}
public class TestStringConcat {
public static void main(String[] args) {
StringConcat concat = (s1, s2) -> System.out.println(s1 + ", " + s2);
concat.makeString("hello", "java");
// 내부적으로 생성되는 구현부
StringConcat concat = new StringConcat(){
@Override
public void makeString(String s1, String s2){
System.out.println(s1 + ", " + s2);
}
};
}
}
일관성 있는 연산을 처리할때, 자료를 더 효율적으로 연산할 수 있도록 제공되는 객체이다.
정의된 연산이 아닌 프로그래머가 직접 지정하는 연산을 적용할 수 있다.
최종 연산으로 스트림의 요소를 소모하여 연산을 수행
String[] greetings = {"안녕", "hello", "Good morning", "hi"};
// 직접 람다식 구현하는 방법
System.out.println(Arrays.stream(greetings).reduce("", (s1, s2) -> {
if(s1.getBytes().length >= s2.getBytes().length)
return s1;
else return s2;
}));
// 연산을 구현한 클래스를 사용하는 방법
String str = Arrays.stream(greetings).reduce(new CompareString()).get();
대상 기준
자바에서는 입력과 출력 스트림을 동시에 쓰지 않고 구분해서 쓴다.
입력 스트림/출력 스트림
자료의 종류
기능
class Person implements Serializable {
String name;
transient String title;
public Person(){}
public Person(String name, String title){
this.name = name;
this.title = title;
}
public String toString(){
return name + ", " + title;
}
}
public class SerializationTest {
public static void main(String[] args) {
Person personLee = new Person("Lee", "Manager");
try(FileOutputStream fos = new FileOutputStream("serial.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos)){
oos.writeObject(personLee);
} catch(IOException e){
System.out.println(e);
}
}
}
자바 직렬화는 자바 시스템에서 개발에 최적화되어 있다. 복잡한 데이터 구조의 클래스의 객체라도 직렬화 기본 조건만 지키면 큰 작업 없이 바로 직렬화가 가능하다. 물론 역직렬화도 마찬가지.
FileInputStream fis = null;
try{
fis = new FileInputStream("a.txt");
} catch (FileNotFoundException e){
System.out.println(e);
return;
} finally{ //finally 구문은 try 구문이 실행되면 무조건 실행된다.
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Finally");
}
try-with-resources
try(FileInputStream fis = new FileInputStream("a.txt")) {
} catch (FileNotFoundException e){
System.out.println(e);
}
throws로 예외 처리를 미루면 예외가 발생한 메서드에서 예외 처리를 하지 않고 이 메서드를 호출한 곳에서 예외 처리를 한다는 의미이다.
main() 에서 throws를 사용하면 가상머신에서 처리 됨
다중 예외 처리 시 주의 사항
try {
ex.loadClass("b.txt", "java.lang.String");
} catch (FileNotFoundException e) {
System.out.println(e);
} catch (ClassNotFoundException e) {
System.out.println(e);
}catch (Exception e){
System.out.println(e);
}