
먼저 공통점부터 알아보자
둘 다 가지고있는 추상 메서드를 구현하도록 강제한다
그리고 인스턴스화가 불가능하다
추상 클래스 (is-a): "상속" 관계. 부모의 특징과 기능을 그대로 물려받아 확장할 때 사용한다. (다중 상속 불가능)
상속 관계이니까 extends 상속
인터페이스 (can-do): "구현" 관계. 클래스의 종류와 관계없이 "이러한 행위/기능을 할 수 있다"는 할 일 목록(규약)을 정의할 때 사용한다. (다중 구현 가능)
인터페이스는 implements로 구현
그럼 왜 이들은 인스턴스화를 막는건가?
추상 클래스는
컴파일러 입장에서 추상메서드는 구현부가 없으니까 애초에 객체를 만들어도 낭비니까, 그래서 추상 클래스를 상속받는 클래스가 미완성된 부분을 @Override시켜서 자식 객체만 생성해서 쓰도록 강제하기 위함이다
인터페이스는
객체를 만들기 위한 용도가아니라 규약의 모음이기때문에 이러한 메서드가 있어야된다라는걸 알려주는 용도지 객체화 용도가 아니니까.

선언부는 존재하지만 구현되어있지 않은 미완성 메서드
abstract 리턴타입 메서드명()
으로 선언한다.
추상메서드를 선언하려면 클래스도 반드시 abstract키워드를 붙여줘야한다.
StringBuilder는 가변(Mutable) 객체이다
String은 불변(UnMutable) 객체 => 한 번 만들어진 문자열은 수정이 불가능해
// String으로 덧붙이기
String result = "";
for (int i = 0; i < 10000; i++) {
result += i; // 💥 루프가 돌 때마다 새로운 String 객체가 10,000개 생성됨!
}
그래서 위 같은 반복문에서는 최악의 결과가 나오게된다
Heap에 쓸모없는 객체가 쌓이게되는거지.
반면 StringBuilder를 이용하게되면,
// StringBuilder로 덧붙이기
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i); // 🚀 객체 생성 없이 내부 버퍼만 변경하여 매우 빠름
}
String result = sb.toString(); // 마지막에만 String으로 변환
이런식으로 객체 생성없이 하나의 객체에서 문자열 변경이 가능하다
반복문(for, while) 안에서 문자열을 계속 덧붙일 때
콘솔 출력용 긴 텍스트나 영수증/결제 내역 포맷을 만들 때
예: 장바구니 목록 출력을 위해 여러 상품 정보를 조합할 때
JSON, SQL 쿼리문 등 복잡하고 긴 문자열을 동적으로 조립할 때
다만 코드가 길어지고 가독성이 떨어지는 문제점도 존재한다