
JDK : "JAVA 개발 키트" = "SDK(Software Development Kit) 키트" = "라이브러리 + javac + javadoc"
JRE : JAVA Runtime Enviroment, JVM과 자바 프로그램을 실행시킬 때 필요한 라이브러리.
Java프로그램을 개발 -> JDK 필요, 컴파일 된 Java프로그램을 실행 -> JRE 필요.
객체지향 프로그래밍 언어로, 객체간 비즈니스 로직을 이용해서 도메인 문제를 해결을 한다.
JVM 가상 머신에 작동하며, GC에 의해서 메모리 관리가 C언어보다 유용하다.
절차지향적 언어로, 함수형 프로그래밍 언어다.
메모리를 직접 malloc, free를 통해서 관리를 해야만 한다.
절차지향 언어는 주로 트랜잭션 스크립트 방식으로 구성됩니다. 즉, 프로그램의 흐름을 순차적인 함수 호출로 구성하며, 로직이 코드 상에 나열되는 형태입니다. 모든 처리는 데이터를 중심으로 함수가 작동하는 구조이며, 데이터와 로직이 분리되어 있습니다.
반면, 객체지향 언어는 객체에 상태와 행위를 함께 부여하고, 각 객체가 책임을 가지고 상호작용함으로써 문제를 해결합니다. 즉, 비즈니스 로직을 객체에 담아 도메인 중심으로 모델링할 수 있어, 코드의 재사용성과 확장성, 유지보수가 유리합니다.
SRP(Single Responsibility Principle), 단일 책임 원칙
한 클래스는 하나의 책임만 가져야 한다.
OCP(Open Closed Principle), 개방 폐쇄 원칙
확장에는 열려있고, 수정에는 닫혀있다.
LSP(Listov Substitution Principle), 리스코프 치환 원칙
하위 타입은 항상 상위 타입을 대체 할 수 있어야 한다.
ISP(Interface Segregation Principle), 인터페이스 분리 원칙
인터페이스 내에 메소드는 최소한 일수록 좋다.(하나의 일반적인 인터페이스보다 여러 개의 구체적인 인터페이스와 낫다.)SRP와 같은 문제에 대한 두 가지 다른 해결책이다.
DIP(Dependency Inversion Principle), 의존관계 역전 원칙
구체적인 클래스보다 인터페이스에 의존한다.
추상화 ex). 추상 클래스, 인터페이스
공통성과 본질을 모아 추출 = 모듈화
상속
기존의 클래스를 재활용하여 새로운 클래스 작성.
( 상위 클래스를 상속한 모든 하위 클래스는 상위 클래스의 속성을 사용할 수 있음. )
다형성 ex). 오버라이딩, 오버로딩
어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있음.
캡슐화
서로 연관 있는 속석와 기능들을 하나의 캡슐로 만들어 외부로부터 보호
캡슐화는 클래스 안에다가 데이터와 데이터를 처리하는 행위를 묶어 놓는 것
코드의 중복을 피할 수 있다는 점과, 데이터를 처리하는 동작 방식을 외부에서 알 필요가 없다는 점
데이터 보호
데이터 은닉
OS에 종속받지 않고 Java를 실행할 수 있다.

.java 원시코드를 컴파일러가 .class의 바이트 코드로 변환을 한다.
그 이후 JVM이 .bytecode를 기게어로 바꿔준다.
( 컴파일 + 인터프리터 )
그러나 요즘은 JIT Compiler가 추가되었다.

.class 파일을 로드한다. 클래스와 클래스 로더의 참조가 더 이상 존재하지 않아야만 GC의 대상이 된다.
ContextClassLoader를 사용하면,
부모 클래스 로더부터 계층적으로 탐색하는 기본 방식(Parent Delegation)을 따르지 않고,
내가 설정한 ContextClassLoader부터 탐색을 시작할 수 있게 됩니다.
클래스 내의 멤버를 호출하게 되면 그때서야 클래스가 동적으로 메모리에 로드된다.
로딩
: 클래스 파일을 가져와서 JVM의 메소드 영역에 로드
링킹
: 클래스 파일을 사용하기 위해 검증
초기화
: 클래스 변수들을 적절한 값으로 초기화를 한다.
( static 변수에 코드로 작성된 값 할당, static 블록 실행. )
-> 초기화 시점은 클래스가 실제로 '사용될 때' 발생.
클래스의 인스턴스 생성
클래스의 정적 메소드 호출
클래스의 정적 변수 할당
클래스의 정적 변수 사용(final x)
클래스 초기화 작업은 오직 한번만 이행된다.
(멀티 스레드 환경에서도 클래스 초기화는 오직 한 번만 수행!)
이를 통해서 클래스 초기화 동작 자체는 스레드 세이프함!
동적으로 할당된 메모리 영역 중에서 필요가 없어진 메모리 영역을 회수하여 메모리를 관리해주는 기법
새롭게 생성된 객체가 할당
대부분의 객체는 금방 Unreachable -> 많은 객체가 Young에서 사라진다.
Young에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역
Young 보다 크며, GC는 적게 발생한다.
Stop the world
GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업이 중단되고, GC가 완료되면 작업이 재개된다.
(튜닝은 주로 stop-the-world를 줄이는 작업으로 진행한다.)
Mark and Sweep

Mark
사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업
Sweep
사용되지 않음으로 식별된 메모리를 해제하는 작업
Stop The World를 통해 모든 작업을 중단시키면, GC는 스택의 모든 변수, Reachable 객체를 스캔하면서 각각이 어떤 객체를 참고하고 있는지를 탐색한다.
여기서 Mark가 되지 않는 객체들을 메모리에서 제거하는데, 이를 Sweep이라고 한다.
: 기존 Mark and sweep은 참조하지 않는 객체를 다 찾아야 해서 비용이 비쌈.
그래서, active와 in-active로 구분지어서 참조하는 것을 active로 옮긴 다음, inactive 내 garbage를 다 지운다.
연속된 메모리 공간에 차곡차곡, 재배열이 되기에 캐시 효율이 높아진다.
Young = Eden×1 + Survivor×2
Eden : 새로 생성된 객체가 할당되는 영역,
Survivor : 최소 1번의 GC 이상 살아남은 객체가 존재하는 영역
새로 생성된 객체가 Eden영역에 할당
Eden 영역이 가득찬 경우, Minor GC가 발생
Eden 영역에서 사용되지 않는 객체의 메모리가 해제됨 ->
Eden 영역에서 살아남은 객체는 1개의 Survivor 영역으로 이동
(반복)
1~2 과정이 반복되다가 Survivor 영역이 가득 차면, Survivor 영역의 살아남은 객체를 다른 Survivor 영역으로 이동시킨다.
또한, Survivor 영역을 왕래하면서 age가 증가하는데, 임계치를 넘거야 old로 넘어간다.
(1개의 Survivor 영역은 반드시! 빈 상태가 되어야 한다.)
이 과정이 반복되어도 살아남은 객체는 Old로 이동한다.
Young에서 오래 살아남은 객체는 Old 영역으로 승급한다.
Major GC는 Old 영역의 메모리가 부족해지면 발생.
(Minor GC는 0.5~1s에 끝나서 빠름.)

메소드(static) 영역
JVM이 .class를 읽은 후 생성한 클래스에 대한 정보(바이트 코드)를 메서드 영역에 저장한다.
클래스 변수(static 변수), 멤버 변수(필드), 생성자, 메소드 등이 저장된다.
(프로그램의 시작~종료까지 메모리에 남아있다.)
스택(Stack) 영역, LIFO
메소드가 호출시, 스택 영역에 스택 프레임이 생기고, 그안에 메소드를 호출
원시타입, 지역변수, 매개 변수가 저장
메소드가 호출 시, 메모리에 할당되고 종료되면 메모리에서 사라짐
참고로, 스택 프레임은 하나의 메소드당 1개씩 생성이 된다.
힙 메모리는 애플리케이션 모든 부분에 사용되지만, 스택 메모리는 스레드당 1개씩 개별적인 영역을 할당 받는다.
객체가 생성되면 힙 공간에 저장되며, 스택 메모리는 힙 공간의 객체를 참조만 한다.(참조 변수)
스택 메모리 사이즈는 힙 메모리에 비해 매우 작으나, LIFO를 써서 힙 메모리보다 빠르다.
정수형
byte(1)
short(2)
int(4)
long(8)
실수형
float(4)
double(8)
문자형
char(2)
논리형
boolean(1)
클래스는 객체를 생성하기 위한 설계도이고, 이 객체를 메모리에 할당을 한 것이 객체(인스턴스)이다.
오버라이딩은 하위 클래스에서 상위 클래스의 메소드를 재정의 하는 것을 의미하고,
오버로딩은 매개변수의 개수,타입을 달리하여 같은 이름의 메소드를 여러 개 정의하는 것을 의미한다.
클래스로 객체를 생성하기 위해서는 생성자를 이용하는데,
기본 생성자를 이용하며, 오버로딩을 통해서 매개변수 생성자도 만들 수 있다.
추상 메소드를 가지고 있어야 한다.
new 생성자 사용 X
구현체를 통해서 인스턴스 생성이 가능하다.
인터페이스와 추상클래스를 구현한 클래스는 추상 메소드를 반드시 구현해야 한다.
인터페이스(자유로운 타입 묶음)
다중 상속이 가능하다
동작이 필요한 클래스에만 따로 상속에 구애받지 않고 묶음이 가능하다.
클래스와 별도로 구현 객체가 같은 동작을 한다는 것을 보장
기능 구현
그때 그때 필요에 따라 구현해서 자유롭게 알맞게 사용
추상클래스(논리적인 타입 묶음)
단일 상속
클래스간의 연관 관계를 구축
자신의 기능들을 하위 클래스로 확장
"공통" 중복 멤버 통합
미리 논리적인 클래스 상속 구조를 만들어 놓고 사용
단 하나의 유일한 객체를 만들기 위한 코드 패턴이며, 똑같은 인스턴스를 새로 만들지 않고 기존의 인스턴스를 가져와 활용한다.
주로 그 객체가 리소스를 많이 차지하는 경우에 사용을 한다.
(디스크 연결, 네트워크 통신, 커넥션풀, 스레드풀, 캐시 등...)

메소드 내에서 동일한 문자열을 생성할 경우 해당 객체들은 String Constan Pool 내의 동일한 객체를 바라본다.
하지만, new 연산자를 사용할 경우 Heap에서 서로 다른 객체를 생성한다.
String은 immutable(불변)하다.
( 단순한 경우, 굳이 StringBuffer, StringBuilder를 사용할 필요없이 +연산자를 사용하면 된다. )
+연산자를 사용할 경우, 기존 문자열에 새로운 문자열을 붙이는 것이 아니라, 새로운 String 객체를 만든 후, 새 String 객체에 연결된 문자열을 저장하고, 그 객체를 참조하도록 한다.
따라서, 문자열 연산이 많은 경우 최악이지만, Thread-safe하다.
StringBuffer는 각 메소드별로 Synchronized Keyword가 존재하여, 멀티스레드 환경에서도 동기화를 지원하지만,
StringBuilder는 동기화를 보장하지 않는다.
그러므로 멀티스레드 환경이라면 값 동기화 보장을 위해 StringBuffer를 사용하고,
단일스레드 환경이라면 StringBuilder를 사용하는 것이 좋다.
리터럴 String의 경우 Heap의 String Pool에 저장이 되는데, 문자열 재사용이 가능하다.
또한 불변이므로, Thread-Safe하다.
강제로 변경이 또한 불가능해서 보안상 우수하다.
ex) string
객체 생성 이후 내부의 상태가 변하지 않는 객체
final로 생성이 가능하다.
방어적 복사
참조를 통해서 값을 수정하면 내부의 상태가 변하기 때문에 내부를 복사하여 전달.
얕은 복사(=)
객체의 주소 값만 복사.
복사된 객체와 원본 객체는 같은 객체를 참조하게 되어, 하나의 객체를 수정하면 다른 객체에도 영향을 미친다.
방어적 복사
객체의 주소를 복사하지 않고, 객체의 내부 값을 참조하여 복사하는 방법
(내부의 값들은 동일하지만, 객체의 주소가 달라졌다.)
특히 컬렉션을 복사하는 경우 유용하다.
복사본이 원본의 주소를 그대로 참조하여 사용하지는 않지만, 복사복 객체 내부에 있는 객체들은 원본과 동일한 주소를 참조하게 된다.
public class Member {
private final String name;
public Member(String name) {
this.name = name;
}
}
public class VIPMembers {
private final List<Member> members;
public VIPMembers(List<Member> members) {
//new ArrayList 로 새로운 객체를 만들어 방어적 복사를 수행한다.
this.members = new ArrayList<>(members);
}
}
public class Application {
public static void main(String[] args) {
List<Member> memberList = new ArrayList<>();
memberList.add(new Member("김씨"));
memberList.add(new Member("쿨씨"));
VIPMembers vipMembers = new VIPMembers(memberList);
memberList.add(new Member("아저씨"));
}
}

( https://dev-cool.tistory.com/25 )
Thread-safe(안전한 병렬 프로그래밍 및 동기화 고려x)
항상 동일한 값을 반환하므로, 동기화를 고려하지 않아도 된다.
실패 원자적인 메소드 작성 가능
Cache, Map, Set 가능
한 번 데이터가 저장된 이후에 다른 작업들을 고려하지 않아도 된다.
부수 효과를 피해 오류가능성 최소화
Setter를 남발할 경우 유지보수성을 상당히 떨어뜨린다.
따라서, 불변 객체를 통해서 안전하게 재사용하도록 유도를 한다.
다른 사람의 함수 예측가능
가비지 컬력션 성능 높임
public class ImmutableObject {
private final Object value;
public ImmutableObject(Object o) { value = o; }
private Object getValue() { return value; }
}
생성과정은 다음과 같다.
Object 타입의 value 객체가 생성
ImmutableObject의 생성자를 통해 ImmutableObject 객체가 생성
ImmutableObject 객체가 value 객체를 참조
ImmutableObject가 reachable하다 -> 그 내부 객체도 자동으로 reachable 하다.
결과적으로, GC가 객체의 참조 관계를 복잡하게 추적할 필요가 줄어들어, 스캔해야 할 객체의 수가 감소하고 스캔해야 하는 메모리 영역과 빈도도 줄어든다.
또한 객체의 생성 비용보다 GC의 스캔 범위 축소 비용이 더 크다.
Programmers are often reluctant to employ immutable objects,
because they worry about the cost of creating a new object as opposed to updating an object in place.
The impact of object creation is often overestimated,
and can be offset by some of the efficiencies associated with immutable objects.
These include decreased overhead due to garbage collection,
and the elimination of code needed to protect mutable objects from corruption.
public class Something{
private final String someValue;
// String, Integer는 한번 할당되면 안 바뀐다.
}
배열일 경우, 배열을 받아 copy해서 저장하고, getter를 clone으로 반환하도록 한다.
(배열을 그대로 참조하거나, 반환할 경우 외부에서 내부 값을 변경할 수 있음.
따라서 clone을 반환해 외부에서 값 변경하지 못하게 함.)
리스트인 경우에도 배열과 마찬가지로 새로운 List를 만들어 값을 복사하도록 해야 한다.
public
접근 제한이 없다.( 같은 프로젝트 내 어디서든 사용 가능 )
가능 : 클래스 내부, 동일 패키지, 하위 클래스, 그 외의 영역
protected
상속받은 클래스 또는 같은 패키지에서만 접근이 가능하다.
가능 : 클래스 내부, 동일 패키지, 하위 클래스(다른 패키지에 있어도, 해당 클래스를 상속한 하위 클래스는 가능하다.)
default
해당 패키지 내에서만 접근 가능
가능 : 클래스 내부, 동일 패키지
private
해당 클래스에서만 접근 가능
가능 : 클래스 내부
| 접근 위치 | private | default (package) | protected | public |
|---|---|---|---|---|
| 동일 클래스 | ✅ | ✅ | ✅ | ✅ |
| 같은 패키지 내 다른 클래스 | ❌ | ✅ | ✅ | ✅ |
| 다른 패키지 하위 클래스 | ❌ | ❌ | ✅ | ✅ |
| 다른 패키지 비하위 클래스 | ❌ | ❌ | ❌ | ✅ |
클래스가 메모리에 올라갈 떄 자동으로 생성되며, 클래스 로딩이 끝나면 바로 사용이 가능하다.
(인스턴스 생성 없이 바로 사용 가능하다.)
모든 객체가 메모리를 공유하고, GC 관리 영역 밖에 있기 때문에 프로그램이 종료될 때까지 메모리에 값이 유지된 채로 존재한다.
인스턴스에 공통적으로 사용해야 하는 것에 static을 붙인다.
(인스턴스들이 공통적인 값이 유지되어야 하는 경우에는 static을 붙인다.)
static이 붙은 멤버변수는 인스턴스를 생성하지 않아도 사용 가능하다.
(클래스가 메모리에 올라갈 때 이미 자동적으로 생성이 된다.)
static 메소드는 인스턴스 변수를 사용할 수 없다.
( 인스턴스 변수, 메소드에선 static이 붙은 멤버들을 사용하는 것은 가능)
메소드 내에서 인스턴스 변수를 사용하지 않는다면, static 붙이는 것을 고려한다.
(메소드 호출 시간이 짧아지기 때문이다.)
클래스 설계시 static 사용지침
클래스의 멤버변수 중 모든 인스턴스에 공통된 값을 유지해야 하는 것이 있는지 보고 있다면 static을 사용한다.
작성한 메소드 중 인스턴스 변수를 사용하지 않는 메소드에 대해서 static을 붙일 것을 고려한다.
자주 변하지 않는 값이나 공통으로 사용되는 값 같은 공용자원에 대한 접근에 있어서 매번 메모리에 로딩하거나 값을 읽어들이는 것보다
전역변수와 같은 개념을 통해 접근하는 것이 비용도 줄이도 효율을 높일 수 있다.
인스턴스 없이 생성을 하므로, 공통으로 사용되는 데이터 관리에 유용하다.
내부 클래스에서 외부 클래스의 멤버에 손쉽게 접근
서로 관련 있는 클래스를 논리적으로 묶어서 표현함으로써, 캡슐화를 증가시키고, 코드의 복잡성을 낮출 수 있음.
외부에서는 내부 클래스에 접근할 수 없으므로, 코드의 보안성을 높일 수 있음.
(캡슐화와 정보 은닉 측면에서)

import java.util.ArrayList;
class Outer_Class {
// 외부 클래스 객체의 크기를 불리기 위한 배열 변수
private int[] data;
// 내부 클래스
class Inner_Class {
}
// 외부 클래스 생성자
public Outer_Class(int size) {
data = new int[size]; // 사이즈를 받아 배열 필드의 크기를 불림
}
// 내부 클래스 객체를 생성하여 반환하는 메소드
Inner_Class getInnerObject() {
return new Inner_Class();
}
}
public class Main {
public static void main(String[] args) {
// inner_Class 객체를 저장할 리스트
ArrayList<Object> al = new ArrayList<>();
for (int counter = 0; counter < 50; counter++) {
// inner_Class 객체를 생성하기 위해 Outer_Class를 초기화하고 메서드를 호출하여 리스트에 넣는다.
// 이때 Outer_Class 객체는 메소드 호출용으로 일회용으로 사용되고 버려지기 때문에 GC 대상이 되어야 한다.
al.add(new Outer_Class(100000000).getInnerObject());
System.out.println(counter);
}
}
}
// Outer_Class의 data 배열의 크기가 400MB인데, Inner Class가 참조를 하고 있기 때문에(외부 참조), GC가 되지 않는다.
/*
메소드 호출용으로 쓰인 일회용 객체 Outer_Class는 바로 GC 수거 대상이 되어 제거되어야 하지만,
내부 클래스에서 외부 클래스를 참조하고 있기 때문에, 내부 클래스가 살아있는 한 외부 클래스 데이터도 계속 살아있다.
*/
public class Outer_Class {
int field = 10;
// static inner class
static class Inner_Class {
int inner_field = 20;
}
}

외부 참조를 하고 있지 않음.

따라서, this 기능도 사용 불가능하다.
import java.util.ArrayList;
class Outer_Class {
private int[] data;
// static 내부 클래스
static class Inner_Class {
}
public Outer_Class(int size) {
data = new int[size];
}
Inner_Class getInnerObject() {
return new Inner_Class();
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Object> al = new ArrayList<>();
for (int counter = 0; counter < 50; counter++) {
al.add(new Outer_Class(100000000).getInnerObject());
System.out.println(counter);
}
}
}
/*
정적 멤버 클래스는 '외부 참조'를 하지 않기 때문에,
일회용으로 사용된 외부 클래스 객체는 정상적으로 GC 수거 대상이 되어 메모리 관리가 잘 된다.
*/
resources란 외부의 데이터(DB, Network, File)을 일컫는다.
( 자바 내부에 위치한 요소들이 아님.
그리고, 사용후 반드시 resource 반납을 해야한다. )
그런데 try-catch-finally를 할 경우 가독성이 매우 안좋다.
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
FileWriter file = null;
try {
file = new FileWriter("data.txt");
file.write("Hello World");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// close()에서 발생하는 예외를 처리하기 위해서 아래와같이 바꿀수도 있지만 코드가 복잡해져서 좋지않다.
try {
file.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
그래서 아래와 같이 수정
try (파일을 열거나 자원을 할당하는 명령문) {
...
}
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try (FileWriter file = new FileWriter("data.txt")) { // 파일을 열고 모두 사용되면 자동으로 닫아준다
file.write("Hello World");
} catch (IOException e) {
e.printStackTrace();
}
}
}
try-catch-finally의 문제점을 보완하기 위해 나온 개념
try(...) 안에 자원 객체를 전달하면, try 블록이 끝나고 자동으로 자원 해제 해주는 기능
finally 구문이나 모든 catch 구문에 종료 처리를 하지 않아도 된다.
Error는 실행 중 일어날 수 있는 치명적 오류
(컴파일 시점에 체크할 수 없고, 오류가 발생하면 프로그램 비정상 종료) = UncheckedException
Exception은 Error보다 비교적 경미한 오류이며, try-cath로 비정상 종료를 막을 수 있다.
CheckException은 실행하기 전에 예측 가능한 예외를 말하고, 반드시 예외 처리.
(I/O, ...)
UnCheckedException은 실행하고 난 후에 알 수 있는 예외를 말하고, 따로 예외처리를 하지 않아도 됨.
(NullPointerException,...)
RuntimeException은 UnCheckedException을 상속할 클래스이고, RuntimeException이 아닌 것은 CheckException을 상속한 클래스이다.
final 변수
한 번 초기화되면 그 이후에 변경 x
final 메소드
다른 클래스가 이 클래스를 상속시, 오버라이딩 금지
final 클래스
다른 클래스에서 이 클래스를 상속 x
기본 자료형(원시 타입)에 대한 객체 표현 = Wrapper Class
기본 자료형 -> Wrapper class(Boxing)
Wrapper class -> 기본 자료형(UnBoxing)
구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드 ,타입, 변수들에 접근할 수 있도록 해주는 자바 API
코드를 작성할 시점에는 어떤 타입의 클래스를 사용할지 모르지만,
런타임 시점에 지금 실행되고 있는 클래스를 가져와서 실행해야 하는 경우 사용된다.
( 스프링의 어노테이션 )
다수의 데이터를 효과적으로 관리할 수 있는 표준화된 방법을 제공하는 클래스의 집합.
( 자료 구조 종류의 형태들을 자바 클래스로 구현한 모음집 )
List, Set, Map, Stack, Queue 인터페이스가 존재한다.

List는 순서가 있는 데이터의 집합이며, 중복을 허용
Set은 순서가 없는 데이터의 집합이며, 중복을 허용x
(LinkedHashSet은 순서를 보장한다.)
Map은 key:value로 구성, key를 기준으로 중복을 허용하지 않으며, 순서가 없다.
(LinkedHashMap은 key의 순서를 보장한다.)
Stack은 직접 new로 사용할 수 있고, Queue인터페이스는 LinkedList에 new 키워드로 적용해 사용할 수 있다.
: hashCode()를 오버라이딩해서 리턴된 해시코드 값이 같은지를 보고, 만약 다르면 다른 객체이다.
벡터는 데이터 삽입시 원소를 밀어내지마, 리스트는 노드를 연결만 하므로, 삽입 삭제에서 리스트가 더 빠르다. ( 연결 리스트만 해당 )
벡터는 랜덤부분접근이 가능하지만, 리스트는 더블링크드리스트이므로, 랜덤 접근이 안되므로, 검색적인 측면에서 벡터가 우위.
벡터는 동기화가 되는데, 멀티쓰레드에서는 안전하게 객체 추가/삭제가 되지만, 단일쓰레드에서도 동기화가 되므로, 이때는 List보다 성능이 느리다.
둘다 모두 동적 배열 클래스인데, 최대 인덱스를 초과할 때 추가되는 인덱스 수는 vector는 현재 배열크기 100%, arraylist는 50%증가한다.
ArrayList vs LinkedList

(ArrayList vs LinkedList : https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-ArrayList-vs-LinkedList-%ED%8A%B9%EC%A7%95-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90#arraylist%EC%9D%98_%EB%AC%B8%EC%A0%9C%EC%A0%90 )

근데, LinkedList 잘 안씀.