2. Field/ Parameter / Argument/ this
A: package com.practice // 본인의 경로를 적으면 된다. B: import com.practice.model.User C: ?? 그러길래 뭐냐? D: import는 실제 클래스 파일을 불러오는 것이 아니라 주어진 경로부터 클래스와 메서드를 불러오는 것 E: package는 import 할 주소 지정, import는 실제로 클래스와 메서드를 불러오는 명령어
- User : 클래스, user: 참조 변수명, new : 새 객체 생성 User("민형") : 생성자, "민형" : parameter 에 들아가는 arguement
- a : 필드 , b : macbook, 2000000 , c: 생성자에 들어가는 arguement d: 필드의 값을 지정하는 parameter e: 클래스를 생성하고 생성한 클래스를 바탕으로 참조변수명을 선언하고, 선언한 클래스에 argumen를 넣어서 새로운 인스턴스를 생성한다.
- a : User 객체는 2개 생성, b : 참조하는 객체가 같아서 true, c : false, 참조하는 객체가 다르다. d: 2. name의 경우에는 인스턴스마다 별개로 저장되지만, count는 공통 클래스의 경우에는 공통 필드 값을 사용한다. 따라서 user 클래스를 호출하면 공통 필드에 +1이 더해진다. f: 공통해서 써야하는 필드값이나 메서드, 클래스를 반복해서 사용해야 할 때 사용한다. g : 가능하다. 새롭게 선언하는 건 괜찮다. h: 불가능하다. 일단 처음 선언값이외에는 변경이 불가능하다.
- Package와 Import A: package com.practice // 본인의 경로를 적으면 된다. B: import com.practice.model.User C: ?? 그러길래 뭐냐? D: import는 실제 클래스 파일을 불러오는 것이 아니라 주어진 경로부터 클래스와 메서드를 불러오는 것 E: package는 import 할 주소 지정, import는 실제로 클래스와 메서드를 불러오는 명령어
- Interface, 추상화, 구현, 다형성 a : 인터페이스의 역할 필수로 구현해야 할 형식을 지정해주는 것이다. b : Dog가 makeSound()를 구현하지 않으면 컴파일 에러가 난다. c : abstract로 지정해서 구현하지 않아도 되게 만든다. d : 변수의 타입은 Dog, 실제 객체의 타입은 animal이다. e : Dog의 구현이 실행된다. f : 추상화는 implemetns, 다형성은 클래스 지정에서 나타난다. g : 부모 클래스인 Animal을 만들고, 그 하위 클래스인 Dog, cat을 만들고 부모 클래스를 상속 받는다. 각 클래스는 makesound()를 구현하고, 각각 a,b에 클래스를 부여하고 인스턴스를 생성한다.
- Generic
a. Generic -> ArrayList에는 string만 들어갈 수 있다. b. names.add(100) -> 컴파일 시점이다. 애초에 arraylist에 string을 지정해서, 100은 리스트에 넣을 수 없다. c. Generic을 사용하면 지정된 데이터 형식만 처리하므로, 추후에 데이터를 처리할 때 형변환 횟수가 줄어들 게 된다. d. int가 아닌 Integer 이다. 근데 이유는 모르겠다. 설명 좀 e. int -> Integer f. 특정 타입만 사용하도록 고정하기 때문에 데이터 타입을 예측할 수 있으며, 재사용성은 복제 어쩌고 저쩌고 였는데. 추후에 데이터를 추출할 때 데이터 형변환을 진행할 필요가 줄어들기 때문에 저장 용량과 프로세스를 절약할 수 있다.
- ArrayList, Iterator, Stream API
a : 순서를 바꾸기 쉽다? b : 모르겠는데 c : hasNext() 와 next() ㄹㅇ 뭐냐 걍 모르겠는데? d : 스트림 생성 -> + 포함한 레코드 필터링 -> 추출된 요소 라인별로 출력 e : filter는 말 그대로 조건에 맞는 요소만 추출하는 명령어, map은 데이터를 조작하는 명령어 f : 데이터흐름을 쉽게 볼수 있게 하는 API
- Reflection과 Annotation
a : 클래스, 필드, 메서드 b : 실행중에도 메서드를 호출할 수 있다. c : 작동시간이 지연될 수 있으며, 데이터가 꼬일 수 있다.(무슨 말이 있었는데 잘 모르겠음) d : Annotation이란 컴파일러에게 참조를 보내는 사인이야 e : 실행하지는 않는데 컴파일러에게 상속받은 부분을 다시 쓴다고 알려줘 f : annotation으로 서술해놓으면 reflection이 참조해서 들여다볼 수 있아
근데 annotation은 컴파일 과정이 어캐 되는거임? 아무위치에나 쓰지는 않을 거 아냐
- JVM 실행 구조
다음 단어를 전부 사용해서 Java 프로그램이 실행되는 과정을 설명해라
.java javac .class Bytecode ClassLoader JVM Stack Heap Interpreter JIT Compiler Garbage Collector
public static void main(String args) { User user = new User("민형"); }
a. 참조변수는 stack에 저장된다. b. 메서드 호출마다 생성되는 실행단위는 ..??? c. new User() 객체를 아무도 참조하지 않으면 Garbage Collector 가 메모리를 회수한다. d. 컴파일러로 바이트코드를 미리 만들어놓고, 인터프리터로 해석한다..? 잘 모르겠는데
- Spring 종합 문제
a. @repository DB API, @service 처리 API, @RestController ?? b. Spring이 관리하는 객체는 bean c. Spring API d. IcU e. 읽고, 처리하고, DB로 보내 f. ? g. 메타데이터를 해석해서 동작해 h. SPring을 자바를 이용해서 API를 관리하는 도구, Spring Anootation은 지정한 annotation을 읽고 작동하는 것
--- 평가 점수
- Class / Object / Reference / Constructor
문제 : User user = new User("민형");
내 답
User: 클래스 user: 참조 변수명 new: 새 객체 생성 User("민형"): 생성자 "민형": parameter에 들어가는 argument
답
User → 클래스 타입
user → 참조 변수
new → 새로운 객체를 생성하는 키워드 (키워드라는 것을 잊지 말자)
User("민형") → 생성자 호출
"민형" → 생성자에 전달되는 argument
User 클래스를 기반으로 새로운 객체를 생성하고, "민형"을 생성자의 argument로 전달한 뒤, 생성된 객체의 참조를 user 변수에 저장한다.
고쳐야 할 점 :
- class -> 이미 존재하는 설계도
- new -> 그 설계도로 객체 생성
2. Field/ Parameter / Argument/ this
문제
class Product {
String name;
int price;
Product(String name, int price) {
this.name = name;
this.price = price;
}
}
Product p = new Product("MacBook", 2000000);
정확한 구분
| 코드 | 정확한 용어 |
|---|---|
String name; | Field |
int price; | Field |
String name | Parameter |
int price | Parameter |
"MacBook" | Argument |
2000000 | Argument |
this.name | 현재 객체의 Field |
name | 생성자의 Parameter |
this는 생성자로 전달받은 name parameter의 값을 현재 객체의 name field에 저장한다.
Product 클래스를 기반으로 새로운 객체를 생성하면서 "MacBook"과 2000000 을 생성자의 argument로 전달하고, 생성자는 해당값을 객체의 name, price field에 저장한다.
3. Static / final / 참조
final 에서 정확히 반대로 이해했다.
User a = new User("민형"); User b = a; User c = new User("철수");
a == b : true : 같은 객체 참조 a == c : false : 다른 객체 참조
Static
내 답 : 공통해서 써야하는 필드값이나 메서드, 클래스를 반복해서 사용해야 할 때 정확한 답 : static은 특정 인스턴스가 아니라 클래스 자체에 소속되는 멤버를 선언하며, 모든 인스턴스가 하나의 값을 공유할 수 있도록 한다.
instance field -> 객체마다 존재 static field -> 클래스에 하나 존재
final
static final User user = new User("민형");
-> final 참조 변수를 다른 객체로 재할당이 불가능하다 -> user.name = "철수"; 는 가능. 참조 변수를 다른 객체로 재할당이 안되는 것이지, 객체 내부 값 변경 자체를 막는 것이 아니다.
final user ───→ User 객체 name = 민형 ↓ 철수
- 요약 : static은 소속과 공유의 문제, final은 재할당의 문제이다.
4. Package / import
내 답 : package는 import 할 주소 지정, import는 실제로 클래스와 메서드를 불러오는 명령어
package
- package com.practice.model;
현재 클래스가 com.practice.model 이라는 이름 공간에 속한다고 선언한다. (진짜 포장해주는 것)
package → 현재 클래스의 소속
내가 적은 답인 단순한 물리적 폴더 주소도 아니고, 라이브러리를 만드는 기능도 아니다.
import
- import com.prcatice.model.User;
뜻 : com.practice.model.User 라는 클래스 이름을 현재 소스 코드에서 User라고 간단하게 사용할 수 있다.
-> 실제로 파일을 가져오거나 메모리에 로딩하는 기술이 아니다.
사실 임포트 없이도 쓸 수는 있다
com.practice.model.User user = new com.practice.model.User();
그런데 귀찮잔슴?
import com.practice.model.User; 때리고
User user = new User(); 라고 쓰는 것
요약 : package는 현재 클래스의 소속 namespace를 정의하고, import는 다른 package의 타입을 현재 소스에서 간단한 이름으로 사용할 수 있게 된다.
5. Interface / 추상화 / 다형성
Interface
내 답 : 인터페이스는 필수로 구현해야 할 형식을 지정 더 정확하게는 : 인터페이스는 구현 클래스가 제공해야 하는 공퉁 기능의 계약을 정의한다.
Animal a = new Dog();
참조 변수 타입 -> Animal 실제 객체 타입 -> Dog
Animal a = new Dog();
추상화
implements 자체가 추상화는 아니다. 추상화를 가능하게 하는 명령어일뿐
모든 Animal은 makeSound() 라는 기능이 있어야 한다.
라는 공통 규칙만 정의하고 실제 구현 방법을 숨긴 것이 추상화
다형성
Animal a = new Dog(); Animal b = new Cat();
같은 Animal 타입으로 서로 다른 구현체를 구현하는 것 (Dog,Cat)
Animal 인터페이스가 공통 기능을 추상화하고, Dog 와 Cat이 이를 각각 구현하며, 두 구현체를 Animal 타입으로 동일하게 처리함으로써 다형성을 활용한다.
6. Generic
ArrayList
그런데 int은 안 되고 Integer인가?
Generic은 기본적으로 참조 타입(reference type)을 사용한다.
int, double, boolean, char -> 이것들은 객체가 아니라 Primitive type
그래서 ArrayList
그 대신에 Wrapper Class인 ArrayList
int -> Integer double -> Double boolean -> Boolean char -> Character
Generic의 목적
내 답 : 저장 용량과 프로세스를 절약한다
핵심 : Generic은 사용할 타입을 컴파일 시점에 명확하게 지정하여, 타입 안정성을 확보하고, 불필요한 형변환을 줄이고, 하나의 클래스나 메서드를 여러 타입에 재사용할 수 있게 한다.
특히 재사용성은
class Box
-> Box
7, ArrayList / Iterator/ Stream
ArrayList
일단 배열의 사용방법부터 알아보자 : String history = new String100; -> 크기를 미리 정한다.
ArrayList
그래서 h history.add(...) history.remove(...)
같은 Collection 기능을 편하게 쓸 수 있다.
iterator
Iterator
while (iterator.hasNext()) { System.out.println(iterator.next()); }
hasNext(): 다음요소가 존재하는지 확인 next() : 다음 요소를 반환하고 iterator를 다음 위치로 이동
현재 위치 -> hasNext() -> 다음 데이터 있나?
있음 -> next() -> 데이터 가져오기 + 위치 이동
Stream
요약 : Stream API는 Collection 데이터를 filter, map등의 연산으로 선언적인 파이프라인 형태로 가공할 수 있도록 하는 제공하는 API이다.
8. Reflection / Annotation
Reflection으로 알 수 있는 것
클래스 이름과 타입 정보 필드 메서드 생성자 Annotation 접근 제어자 상속 및 인터페이스 정보
일반 메서드 호출과의 차이
내 답 : 실행 중에도 메서드를 호출할 수 있다.
Reflection의 특징 : 컴파일 시 코드에 직접 지정하지 않고도, 런타임에 클래스 구저를 조사해서 메서나 필드를 동적으로 찾아 접근할 수 있다는 것
일반적으로
user.hello(); 코드 작성 시점에 hello()를 알고 있다.
Reflection:
Method method = User.class.getDeclaredMethod("hello") method.invoke(user);
실행하면서 "hello" 라는 이름을 찾아 실행할 수 있다.
남용하면 안되는 이유
캡슐화 우회 가능 컴파일 타임 타입 검사 약화 런타임 오류 가능성 증가 코드 추적 어려움 성능 비용
번외 : Annotation에 대한 내 질문
Annotation은 컴파일 과정이 어떻게 되는 것?
- 코드에 붙이는 메타데이터
@Override public void run(){ }
@Override는 메서드에 붙인다
반면 :
@Service public class UserService{}
@Service는 클래스에 붙인다. 이걸 제한하는 게 @Target
@Target(ElementType.METHOD) -> 이러면 메서드에만 사용가능 @Target(ElementType.TYPE) -> 클래스나 인터페이스 같은 타입 선언에 사용 가능
Annotation의 생명주기
@Retention 이라는 게 있다.
SOURCE -> 소스 코드에서만 존재 -> 컴파일 후 사라질 수 있다
CLASS -> .class까지 저장 -> 런타임 Reflection에 반드시 노출되는 것은 아니다.
RUNTIME -> .class에 저장 -> JVM 실행 중 Reflection 으로 조회 가능
@Override는 컴파일러가 주로 확인하는 Annotation
반면 Spring의:
@Service @RestController
같은 Annotation은 런타임에서 Spring이 확인할 수 있어야 하므로 런타임 메타데이터로 남는다.
.java -> javac -> Annotation 문법 검사 -> @target 검사 -> 필요한 compile-time 처리 -> .class -> Retention 정책에 따라 Annotation 정보 보존 -> JVM 실행 -> RUNTIME Annotation이면 Reflection / Spring Framework등이 조회 가능
9. JVM
전체 실행 구조
Main.java -> javac -> Main.class -> Java Bytecode -> ClassLoader -> JVM에 클래스 로딩 -> Runtime Memory
- Heap
- Stack -> Execution Engine
- Interperter
- JIT Compiler -> 실행
실행 도중에 Garbage Collector -> Heap의 사용되지 않는 객체 매모리 회수
Stack / Heap User user = new User("민형"); 단순화하면: Stack Heap
user ───────────────→ User 객체 name="민형"
메서드 호출마다 만들어지는 것
정답: Stack Frame
예를 들어: main() 실행: Stack
┌─────────────────┐ │ main Stack Frame│ │ user │ └─────────────────┘ 그 안에서: calculate() 호출: Stack
┌─────────────────────┐ │ calculate Frame │ ├─────────────────────┤ │ main Frame │ └─────────────────────┘
Interpreter와 JIT
Interperter: Bytecode를 바로 해석하며 실행
- 장점 : 시작이 빠르다
- 단점 : 반복 실행하면 계속 해석해야 해서 비료 JIT
- 자주 실행되는 Bytecode를 Native Machine Code로 컴파일
즉 :
JVM은 빠른 초기 실행을 위해 Interperter를 사용하고, 반복적으로 실행되는 hot code는 JIT Compiler가 네이티브 코드로 컴파일해 성능을 높인다.
10. Spring
HTTP Request -> Controller -> Service -> Repsoitory -> Database
@RestController
@RestController class UserController{}
HTTP 요청을 받는 REST Controller 역할
@Service
@Service class UserService{}
비즈니스 로직 처리
- 회원가입 가능 여부
- 가격 계산
- 주문 검증
- 결제 처리
@Repository
@Repository class UserRepository{}
데이터 저장소 접근 계층
- DB 조회
- DB 저장
- DB 수정
- DB 삭제
Bean
Spring Container가 생성하고 관리하는 객체를 Bean이라고 한다.
IoC
Inversion of Control
객체 생성과 관리의 제어권을 개발자 대신 Spring container가 가지는 것
일반 Java
개발자 -> new 객체
Spring
Spring Container -> 객체 생성 및 관리
DI
DI : Dependency Injection
public UserController(UserService userService) { this.userService = userService; }
spring이 필요한 UserService Bean을 넣어주는 것
요청 흐름
GET /users/10
-> @GetMapping("/users/{id}") : 컨트롤러가 받음 -> @PathVariable Long id : 10을 id로 받음. -> userService.getUserName(10L) : Service 호출. -> userRepository.findName(10L) : Repository 호출. DB 또는 데이터 저장소 조회. -> Repository -> Service -> Controller -> HTTP Response
- Spring은 Java 객체의 생성과 생명주기, 의존 관계를 관리하고 웹, 데이터 접근, 트랜젝션등의 애플리케이션 기능을 제공하는 프레임워크이다.
Spring Annotation 한 문장
- Spring Annotation은 클래스나 메서드에 역할과 설정에 대한 메타데이터를 부여하고, Spring이 이를 해석하여 Bean 등록, HTTP 요청 매핑, DI 등의 기능을 적용할 수 있도록 한다.
COMMENTS
GitHub 계정으로 로그인하여 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions에 공개 저장되며, 작성 내용과 GitHub 프로필 정보가 다른 방문자에게 보일 수 있습니다.