자바 스크립트와 Node.js

- 비동기 프로그래밍으로 이벤트 주도 방식을 사용하기 때문에 콜백 함수 사용

- 콜백 함수 : 매개변수를 통해서 다른 함수의 내부로 전달되는 함수

- ES2015부터 콜백 대신 API들이 프로미스 기반으로 재구성

 

프로미스(Promise)

- 자바스크립트 비동기 처리에 사용되는 객체

- ECMAScript6- 비동기 처리 : 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성- 프로미스를 사용하면, 비동기 작업들을 순차적으로 진행하거나, 병렬로 진행하는 컨트롤이 수월해진다.

- new Promise로 프로미스 생성

- resolve, reject를 매개변수로 갖는 콜백 함수를 넣는 방식 

- resolve가 호출되면 then, reject가 호출되면 catch가 실행 된다.

- resolve 호출 -> resolve('성공') -> message에 '성공'이 들어가 출력

- reject 호출 -> resolve('실패') -> message에 '실패'가 들어가 출력

const condition = true;
 
const promise = new Promise((resolve, reject) => {
    if (condition){
        resolve('성공');
    } else {
        reject('실패');
    }
});
 
promise
    .then((message) => {
        console.log(message);
    })
    .catch((error) => {
        console.log(error);
    });

 

Promise.all

- 프로미스를 여러개 한꺼번에 실행

- 모든 프로미스가 resolve 상태여야 then 실행

const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
 
Promise.all([promise1, promise2])
    .then((result) => {
        console.log(result);
    })
    .catch((error) => {
        console.error(err);
    });

 

async/await

  • ES2017에 추가된 기능
  • Node version 7.6
  • 비동기 프로그래밍을 할 때 유용하게 사용되고, 콜백의 복잡성을 해결하기 위한 프로미스를 조금 더 깔끔하게 만들어주는 역할
  • 기존 프로미스 사용 코드 
function findAndSaveUser1(Users) {
    Users.findOne({})
        .then((user) => {
            user.name = 'kim';
            return user.save();
        })
        .then((user) => {
            return Users.findOne({gender: 'm'});
        })
        .then((user) => {
            // 생략
        })
        .catch(err => {
            console.error(err);
        });
}
  • async/await 문법 사용
    • function 앞에 async를 붙여주고
    • 프로미스 앞에 await를 붙여준다.
async function findAndSaveUser(Users) {
    try{
        let user = await Users.findOne({});
        user.name = 'kim';
        user = await user.save();
        user = await Users.findOne({gender: 'm'});
        // 생략
 
    } catch(err) {
        console.error(err);
    } 
}

자바스크립트 작동 원리

  • 자바스크립트?
    • 싱글 스레드 기반으로 동작하는 자바스크립트
    • 이벤트 루프를 기반으로 동작하는 싱글 스레드 Node.js

 

JS Engine 종류

- 클라이언트에서 JavaScript를 실행하려면 JavaScript 엔진이 필요하다 -> 브라우저에 내장되어 있다.

- Chrome : V8

- Edge : Chakra

- Safari : Webkit

- Firefox : SpiderMonkey

 

JS Engine vs Rendering Engine

  • Rendering Engine : HTML, CSS으로 작성된 마크업 관련된 코드를 웹 페이지에 rendering하는 역할
  • Javascript Engine : Javascript으로 작성한 코드를 해석하고 실행하는 인터프리터

 

이벤트 핸들러

  • 이벤트 : 브라우저에서 사용자의 조작이나 환경의 변화로 벌어진 사건
  • 핸들러 : 이벤트에 대한 즉각적인 반응을 할 수 있도록 해주는 것

 

 

JS Engine의 3가지 영역

  • Call Stack
    • 하나의 call stack 사용 -> 하나의 함수가 실행되면 이 함수의 실행이 끝나기 전까지 다른 task 수행 불가
  • Task Queue(event Queue)
    • JS의 런타임 환경에서 처리해야하는 Task들을 임시 저장하는 대기큐
    • call stack이 비어졌을 때 먼저 대기열에 들어온 순서대로 수행
    • 자바스크립트에서 이벤트에 의해 실행되는 함수(핸들러)들은 비동기로 실행된다.
    • 비동기로 호출되는 함수는 call stack이 아닌 task queue에 enqueue
  • heap
    • 동적으로 생성된 객체(인스턴스)는 힙에 저장된다.
  • Event Loop
    • event loop와 queue를 이용해서 동시에 여러 작업이 처리되는 것처럼 비동기 작업을 수행한다.
    • 반복해서 call stack과 queue 사이의 작업들을 확인해서, call stack이 비워있는 경우 queue의 작업을 꺼내고 call stack에 넣는다.
function three() {
  console.log('3')
}

function two() {
  console.log('2')
}

function one() {
  console.log('1') // 1 출력
}

one() // one 실행

setTimeout(() => { // 익명함수는 event queue
  two()
}, 1000)

three() // three 실행

 

JS 엔진이 코드를 실행하기 위해 기동되면

1. 빌트인 객체(생성자 함수 포함) 생성

2. 실행 환경에 맞는 global 객체 생성

  • browser 환경 : window 객체
  • node.js 환경 : global 객체
  • 자바스크립트 엔진이 소스코드를 불러와서 기동되면, 기본적인 빌트인 객체들을 만들어서 글로벌 객체(전역 객체)를 만들게 된다.
  • 실행되는 환경에 따라 달라진다.

 


this 

  • 자바스크립트의 모든 함수는 실행될 때 마다 함수 내부에 this 객체가 추가된다.
  • arguments(유사 배열 객체)와 함께 함수 내부로 암묵적 전달
  • 함수가 호출된 상황에 따라 this가 달라진다.

1. 객체의 메소드 호출을 할 경우

  • 메소드 : 객체의 프로퍼티가 함수인 경우
  • 메소드를 호출할 경우 메소드를 호출한 객체를 바인딩 -> 현재 사용하는 객체

2. 함수를 호출할 경우

  • 함수 내부 코드에 사용된 this는 전역 객체에 바인딩 -> window

3. 생성자 함수를 통해 객체를 생성할 때

  • new 연산자 사용
  • this는 객체 자신 
    • 빈 객체 생성 -> this 바인딩 -> 함수를 통해 생성된 객체, 부모 프로토타입 객체와 연결되어 있다.
  • return문이 명시되어 있지 않은 경우 this는 바인딩 된 새로 생성한 객체가 리턴된다.
var myObject = {
  name: "foo",
  sayName: function() {
    console.log(this);
  }
};
myObject.sayName();
// console> Object {name: "foo", sayName: sayName()}
var value = 100;
var myObj = {
  value: 1,
  func1: function() {
    console.log(`func1's this.value: ${this.value}`);

    var func2 = function() {
      console.log(`func2's this.value ${this.value}`);
    };
    func2();
  }
};

myObj.func1();
// console> func1's this.value: 1
// console> func2's this.value: 100
var Person = function(name) {
  console.log(this);
  this.name = name;
};

var foo = new Person("foo"); // Person
console.log(foo.name); // foo

 

 

Spring WeFlux

  • Spring 5에 추가된 모듈
  • 클라이언트, 서버에서 reactive 스타일의 어플리케이션 개발을 도와주는 모듈
  • async + non-block + reactive stream 지원
    • reactive Stream : Non-blocking backpressure를 통한 비동기 스트림 처리 표준을 제공하기 위한 명세
  • 장점 : 고성능, netty 지원, 비동기 non-blocking 메시지 처리

 

Back Pressure

  • 옵저버 패턴 : 발행자(publisher)가 구독자(subscriber)에게 데이터를 밀어넣는 방식
  • 발행자의 데이터 전송이 더 빠를 경우, 큐를 사용해서 대기중인 이벤트를 저장해야 한다. -> 오버플로우(overflow)
  • 고정 길이 버퍼 : 신규로 수신된 메시지 거절 -> 거절된 메시지를 재요청 하는 CPU 연산 비용 낭비
  • 가변 길이 버퍼 : out of memory 에러로 서버 크래시(crash) 
  • 백 프레셔 기본 원리 : 풀 방식 사용
    • 구독자가 처리할 수 있는 만큼의 데이터를 발행자에게 요청한다.
    • Dynamic pool 방식의 데이터를 요청해서 구독자가 수용할 수 있는 만큼의 데이터를 요청하는 방식

 

Netty

  • 프로토콜 서버 및 클라이언트와 같은 네트워크 응용 프로그램을 빠르고 쉽게 개발할 수 있는 NIO(Non-Blocking Input Output) 클라이언트 서버 프레임워크

 

Spring WebFlux vs Spring MVC

  • MVC : Servlet stack 기반으로 요청 한 개에 대해 하나의 스레드가 처리하고 Sync + blocking
  • WebFlux : Reative stack 기반으로 다수의 요청에 대해 1개의 스레드 처리 Async + non blocking

 

Spring WebFlux 사용이유

  • 하나의 요청에 대해 여러 외부 시스템에 대한 요청이 필요한 시스템에서 가장 빠른 응답을 주기 위해
  • 수 많은 요청을 처리하는 상황에서 스레드 지옥에서 벗어나기 위해
  • MVC는 1:1 요청처리 방식이기 때문에 트래픽이 몰리면 많은 스레드가 발생하고, 이 때 스레드가 전환될 때도 context switching 비용이 발생해 스레드가 많을 수록 비용이 커지게 된다.
  • WebFlux는 Event-Driven과 Async + Non-Blocking I/O를 통해 리소스를 효율적으로 사용한다.
  • MVC도 servlet 버전이 올라가면서 비동기 / 논블로킹이 가능하다.
    • MVC servlet 3.2 이후
    • 1. reqeust를 받은 서블렛 스레드는 pool에게 반납
    • 2. 특정 이벤트 발생 -> 서블릿 스레드 풀에 슬데ㅡ 요청
    • 3. 서블렛 스레드를 할당받고 작업 수행 후 사용자에게 response
    • 하지만 DB 접근, API call 등 모든 부분이 non blocking으로 구현하기에는 부족하다.

Class(클래스)

  • 객체를 정의하는 툴, 또는 설계도와 같은 의미로 사용된다.
  • 클래스는 멤버(member)로 속성을 표현하는 필드(field)와 기능을 표현하는 메소드(method)를 가진다.
  • 필드 : 객체의 상태
  • 메소드 : 객체의 행동
  • 인스턴스(Instance) 
    • 클래스를 사용하기 위해 해당 클래스 타입의 객체(Object)를 선언해야 한다.
    • 클래스의 인스턴스화 : 클래스로부터 객체를 선언하는 과정
    • 인스턴스는 선언된 해당 클래스 타입의 객체이다. -> 인스턴스는 메모리에 할당된 객체
  • 메소드(method)
    • 메소드 : 특정 작업을 수행하기 위한 명령문의 집합
    • 중복 코드 최소화 -> 가독성 향상
    • 유지보수 용이
  • 생성자(consructor)
    • 생성된 객체의 필드를 초기화해주는 메소드
    • 객체의 생성과 동시에 인스턴스 변수를 원하는 값으로 초기화할 수 있다.
    • 생성자도 메소드 이기 때문에, 메소드 오버로딩 가능 하지만, return X
    • 1. class는 무조건 생성자를 1개 이상 갖는다. : 없으면 default constructor 자동 생ㅅ어
    • 2. class 이름과 동일
    • 3. instance 초기화 담당
  • 필드(field)
    • 클래스에 포함된 변수(variable)
class { -> instance 생성 

1) fields

2) constructors

3) methods

}
public class Student{
	// fields (변수)
	String stuName; // String -> class -> reference Type -> 문자
	String stuNum; // 학번(00231123) : 연산이 필요하면 int, 아니면 사용하기 편한 String
	int stuAge; // 나이 - 숫자
	String stuDept; // 학과 - 문자열
	
	// 생성자 - 클래스 이름과 동일, 메소드와 유사(메소드는 Return이 있음)
	// 인스턴스를 초기화해주는 역할 
	// default constructor 
	Student() {
		
	}
	
	// methods (함수)
	public static void main(String args[]) {
		Student stu = new Student(); // instance 생성 : new + constructor
		// . operator ( . 연산자 )
		stu.stuName = "홍길동"; 
		stu.stuNum = "00231123";
		stu.stuAge = 20;
		stu.stuDept = "컴퓨터";
		
		System.out.println();
	}
}

 

 

상속(inheritance)

  • 부모 class가 가지는 내용을 확장해서 자식 class를 만드는 방식
  • 기존 클래스에 기능을 추가하거나 재정의해서 새로운 클래스를 정의하는 것
  • 장점
    • 기존 클래스를 재활용 가능
    • 중복 멤버를 미리 부모 클래스에 작성하면 자식 클래스에서 작성하지 않아도 된다.
    • 클래스 간의 계층 관계를 구성함으로써 다형성의 문법적 토대를 마련한다.
  • 자바의 다중 상속이 없는 이유?
    • 두 곳에서 동시에 상속을 받으면 같은 변수가 올 수 있는 문제점을 배제
class Human {
    // String name
}
class Person {
    String name; // 이름
    String mobile; // 전화번호
}

// tightly coupled
class Student extends Person{
    String dept; // 학과
}

class Teacher extends Person{
    String subject; // 과목
}

class Staff extends Person{
    int salary; // 월급
}

 

Object 클래스

  • 모든 클래스의 부모 클래스
  • 기존 Object에 있는 메소드들을 오버라이딩해서 사용
    • equals() : 해당 인스턴스를 매개변수로 전달받는 참조 변수와 비교하여 결과 반환
    • toString() : 인스턴스에 대한 정보를 문자열로 반환
    • clone() : 해당 인스턴스를 복제해, 새로운 인스턴스를 생성하고 반환한다.
      • clone()은 필드만 복제하기 때문에, 배열같은 경우 오버라이딩을 통해 재정의해야한다.

 

Constructor 상속은 어떻게?

  • 기본적으로 생성자는 상속되지 않는다.
  • super와 super()
    • super : 현재 사용하는 객체의 referenct -> type이 상위 타입
      • this와 마찬가지로, 부모 클래스 멤버와 자식 클래스 멤버 이름이 같을 경우 super 키워드로 구별
    • super() : 상위 클래스의 생성자 호출
      • 부모 클래스의 멤버를 초기화하려면, 자식 클래스의 생성자에서 부모 클래스의 생성자까지 호출해야 한다.
      • 기본 생성자가 아닌 다른 생성자를 선언했으면, 컴파일러가 자동으로 부모 클래스의 멤버를 초기화줄 수 없다.
      • 생성자를 따로 선언하고, 기본 생성자가 없으면 자식 클래스에서 super()호출 시 에러 발생
      • 되도록 기본 생성자까지 명시적으로 선언해주기
class Parent {
    int a;
    Parent() { a = 10; }
    Parent(int n) { a = n; }
}
 
class Child extends Parent {
    int b;
    Child() {
        super();
        b = 20;
    }

 

 

동적 바인딩(dynamic binding)

  •  객체에 대한 type이 상위 타입이라 할지라도, 만약 override된 method에 하위에 존재한다면 overriding 된 method를  사용
  • 다형성을 사용하여 메소드를 호출할 때 발생하는 현상
  • 실행 시간(runtime), 파일을 실행하는 시점에 결정된다.
  • 실제 참조 객체는 서브 클래스 -> 서브 클래스 메소드 호출

정적 바인딩(static binding)

  • 컴파일(compile) 시간에 의해 결정된다.
  • 변수의 타입이 super 클래스여서 super클래스의 메소드 호출
class SuperClass {

    // static method
    static int staticCall(String msg){
        System.out.println(msg);
        return 100;
    }

    // fields
    int a = staticCall("1번입니다."); // 4번째 출력 : line 3 실행
    static int b = staticCall("2번입니다."); // 1번째 출력
    // static : 프로그램 시작 시 최초에 한 번 생성만 되고 초기화된다.
    // 인스턴스를 생성하지 않아도 바로 사용 가능

    // constructor
    public SuperClass(){  // 생성자 전에 필드 공간이 만들어 져야 한다. -> line 10
        staticCall("3번입니다."); // 5번째 출력 : 필드 공간 생성 후 실행 -> line 23 복귀
    }

    // constructor overloading
    public SuperClass(int i){
        this(); // 자신의 클래스가 가지고있는 다른 생성자를 호출 -> line 16
        staticCall("4번입니다."); // 6번째 출력 -> line 40으로 복귀
    }

    // method
    public void myFunc(){
        System.out.println("5번입니다."); // -> 9번째 출력
    }
}

class InheritanceTest extends SuperClass {
    // fields
    int c = staticCall("6번입니다."); // -> 7번째 출력 -> line 40 복귀
    static int d = staticCall("7번입니다."); // 2번째 출력

    // constructor
    public InheritanceTest(){ // 객체 생성
        super(100); // 상위 클래스의 생성자(100) 호출 -> line 21
        staticCall("8번입니다."); // 생성자 전에 필드 공간 생성 -> line 34 -> 8번째 출력
        super.myFunc(); // -> 상위 클래스의 myFunc 메소드 실행
    }

    @Override
    public void myFunc(){
        System.out.println("9번입니다."); // 10번째 출력
    }

    public static void main(String[] args) {
        System.out.println("10번입니다."); // 3번째 출력 : static -> main 실행
        SuperClass obj = new InheritanceTest(); // 객체 생성 -> line 38
        obj.myFunc(); // obj의 타입 SuperClass 이지만 동적바인딩으로 line 45

        // 객체에 대한 type이 상위 타입이라 할지라도
        // 만약 override된 method에 하위에 존재한다면
        // method는 overriding 된 method를 사용한다.
        // 동적 바인딩(dynamic binding)
    }
}

// 2->7->10->1->3->4->6->8->5->9

 

abstract 제어자

  • 추상 메소드를 만들 때 사용

 

추상 클래스(Abstract Class)

  • 추상 메소드가 1개라도 클래스 내부에 존재하면, 해당 클래스는 추상 클래스
  • 추상 메소드는 선언만 -> 메소드가 하는 일이 정의되지 않았다.
  • 추상 클래스는 인스턴스를 생성할 수 없다.
  • 자식 클래스에서 오버라이딩 해서 사용
public abstract class UpperClass {
    // field
    String name;
    int age;

    // method
    public abstract void printAll();
        // abstract method
}

class subClass extends UpperClass{

    @Override
    public void printAll(){
        // TODO Auto-generated method stub
    }
}

 

인터페이스(Interface)

  • 추상 클래스의 특별한 케이스
  • 모든 메소드가 추상 메소드이다.
  • 모든 필드는 public static final -> 안에 들어가 있는 필드는 모두 상수
    • public 어디에서나 접근 가능, static 전역, final 상수화
  • 추상 메소드와는 다르게, 다중 implements 가능
interface myInterface {
    // fields
    //public static final
    int kk=0;
    String aa = "Hello";

    // abstract methods
    public abstract void printAll();
    public abstract void myPrint();
}

class MyClass implements myInterface{
    // 상속이랑 같은 개념을 사용한다.
    // is - A 관계 성립!!
    // 인터페이스 안에 있는 값들을 확장해서 새로운 클래스(인터페이스)를 구현한다는 의미
    // 인터페이스 안에서 내가 구현한 해당 인터페이스의 모든 내용들을 오버라이딩 해줘야한다.
    // generate 사용 -> 오버라이드 메소드
    @Override
    public void printAll() {

    }

    @Override
    public void myPrint() {

    }
}

 

 

인터페이스, 클래스 구현 예시

abstract class Myclass extends Human implements Aniaml
// Myclass에서 Human 상속, Animal interface를 구현한다는 뜻

abstract class Myclass extends Human implements Aniaml, MyInterface
// 인터페이스는 다중 구현 가능

형변환(type conversion)

  • 묵시적 형변환 : (컴파일러) 자동 변환
    • double num1 = 10
    • System.out.print(num1) // 10.0
  • 명시적 형변환(type casting) : 캐스트 연산자(괄호) 사용
    • int result = 1 / 4; // int(0.25) = 0
    • System.out.print(double(result)) // 0.0

 

삼항 연산자(ternary operator)

  • true : 반환값1
  • false : 반환값2
조건식 ? 반환값1 : 반환값2

 

 

루프와 제어

  • continue : 루프 내에서 사용하여 해당 루프의 나머지 부분은 건너 뛰고, 바로 다음의 조건식으로 넘어가게 해준다.
  • break : 루프 내에서 해당 반복문을 완전 종료

 

static 제어자

  • 해당 변수를 클래스 변수로 만들어준다.
  • 메소드에 사용하면 해당 메소드를 클래스 메소드로 만들어준다.
  • 초기화 블록에도 사용 가능
  • 프로그램 시작 시 최초에 한 번 생성만 되고 초기화된다.
  • 인스턴스를 생성하지 않아도 바로 사용 가능
  • 해당 클래스의 모든 인스턴스가 공유

 

클래스의 필드(field)

  • 필드 : 클래스에 포함된 변수(variable)
  • 선언 위치에 따라 구분된다.
  • 1. 클래스 변수(static variable) : 클래스 영역에서 static 키워드를 가지는 변수
    • 해당 클래스의 모든 인스턴스가 공유해야 하는 값을 유지하기 위해 사용
  • 2. 인스턴스 변수(instance variable) : static 키워드를 가지지 않은 변수
    • 인스턴스마다 가져야하는 고유의 값을 유지하기 위해 사용
  • 3. 지역 변수(local variable) : 메소드나 생성자, 초기화 블록 내에 위치한 변수
  • 클래스 변수 + 인스턴스 변수 : 컴파일러가 자동으로 초기화
  • 지역 변수는 초기화하지 않으면 오류 발생
  • 필드의 초기화 
    • 1. 명시적 초기화 : 필드 선언과 동시에 초기화
    • 2. 생성자를 이용한 초기화 : 객체 생성과 동시에 필드 초기화
      • 인스턴스 생성 전까지 필드 초기화 불가능
    • 3. 초기화 블록을 이용한 초기화 (초기화 블록 : main 메소드 호출 이전에 실행)
      • 초기화 블록은 생성자 보다 먼저 호출
      • 1. 인스턴스 초기화 블록 : 생성자와 거의 차이 없어서 잘 사용 X
      • 2. 클래스 초기화 블록 : 처음 메모리에 로딩될 때 단 한 번만 실행 
        • 필드의 초기화 순서
        • 클래스 변수 : 기본값 -> 명시적 초기화 -> 클래스 초기화 블록
        • 인스턴스 변수 : 기본값 -> 명시적 초기화 -> 인스턴스 초기화 블록 -> 생성자
class Car {
    static int modelOutput; // 클래스 변수
    String modelName;       // 인스턴스 변수
 
    void method() {
        int something = 10; // 지역 변수
    }
}
변수 생성 시기 소멸 시기 저장 메모리 사용 방법
클래스 변수 클래스가 메모리에 올라갈 때 프로그램 종료 메소드 영역 클래스 이름.변수이름
인스턴스 변수 인스턴스가 생성될 때 인스턴스 소멸 힙 영역 인스턴스이름.변수이름
지역 변수 블록 내에서 변수 선언문이 실행될 때 블록을 벗어날 때 스택 영역 변수이름
public class MyClass {
    // field
    int aaa;
    static int bbb = staticCall();

    // default constructor : class 확장성과 관계성이 있음.
    public MyClass(){

    }

    // static block
    static {
        // main이 실행되기 직전에 다른 library 같은 것들을 loading할 필요가 있을 때 사용한다.
        System.out.println("static block");
    }

    static int staticCall(){
        System.out.println("호출되었어요!");
        return 100;
    }

    public static void main(String[] args){
        System.out.println("main 호출");
    }
}

 

 

클래스 메소드와 인스턴스 메소드

  • 1. 클래스 메소드(static method) : static 키워드를 가지는 메소드
    • 인스턴스를 생성하지 않아도 바로 사용 가능
    • 메소드 내부에서 인스턴스 변수를 사용할 수 없다.
  • 2. 인스턴스 메소드(instance method) : static 키워드가 없는 메소드
class Method {
    int a = 10, b = 20;                            // 인스턴스 변수
    int add() { return a + b; }                    // 인스턴스 메소드

    static int add(int x, int y) { return x + y; } // 클래스 메소드
}
 
public class Member02 {
    public static void main(String[] args) {
        System.out.println(Method.add(20, 30)); // 클래스 메소드의 호출
        Method myMethod = new Method();         // 인스턴스 생성
        System.out.println(myMethod.add());     // 인스턴스 메소드의 호출
    }
}

ouput 
50
30

this와 this()

  • this : 현재 사용하는 객체의 reference (자기자신 참조)
    • 생성자의 매개변수 이름과 인스턴스 변수를 구분하기 위해 사용한다.
    • this를 사용해서 인스턴스 변수에 접근한다.
  • this() : 자신의 클래스가 가지고 있는 다른 생성자 호출 
    • 생성자 내부에서만 사용 가능
class Car {
    private String modelName;
    private int modelYear;
    private String color;
    private int maxSpeed;
    private int currentSpeed;
 
    Car(String modelName, int modelYear, String color, int maxSpeed) {
        this.modelName = modelName;
        this.modelYear = modelYear;
        this.color = color;
        this.maxSpeed = maxSpeed;
        this.currentSpeed = 0;
    }
    ...
}
    Car() {
        this("소나타", 2012, "검정색", 160); // 다른 생성자를 호출함.
    }

상수(constant)

  • final 키워드 사용
    • final은 다른 주소를 가리킬 수 없는 것이기 때문에
    • reference type의 변수에서는 그 대상 인스턴스 객체 값 수정 가능
    • final한 참조 타입 변수는 다른 주소를 가리킬 수 없을 뿐, 그 대상 인스턴스 객체의 값을 수정하지 못한다는 의미가 아니다.
    • final 배열은 값 변경, 정렬 가능
  • final int score = 00;
  • 선언과 동시에 초기화 되어야 한다.
  • 상수는 전부 대문자 사용한다 + Snake notation(_ 사용)
    • final int MY_SCORE=100;
  • 1. 변수 앞에 붙으면 상수 처리
  • 2. class 앞에 붙으면 상속 불가능
  • 3. method 앞에 붙으면 오버라이딩 불가능
final class Car { // 이 클래스는 상속을 통해 서브 클래스를 생성할 수 없음.
    final int VAR; // 이 필드는 상수화되어 값을 변경할 수 없음.
    final void brake() { // 이 메소드는 오버라이딩을 통해 재정의할 수 없음.
        final double MAX_NUM = 10.2; // 이 지역 변수는 상수화되어 값을 변경할 수 없음.
    }
}

 

String

  • String class : reference type 
  • primitive type 처럼 사용 가능
// 실행 fn + command + shift + f11
public class Student{
	// fields (변수)
	String stuName; // String -> class -> reference Type -> 문자
	String stuNum; // 학번(00231123) : 연산이 필요하면 int, 아니면 사용하기 편한 String
	int stuAge; // 나이 - 숫
	
	// methods (함수)
	public static void main(String args[]) {
		System.out.println("hello");
	}
}

 

StringBuffer, String, StringBuilder

  • String : 힙 영역에 있는 String Constant pool에 문자열이 저장된다. += 연산을 통하여 새로운 값으로 정의될 때, 기존의 값을 메모리에 유지하고 새로운 메모리 주소에 저장된다.
    • string은 immutable하기 때문에 멀티스레드 환경에서 안전성 보장
  • StringBuffer : 내부적으로 버퍼(buffer)라고 하는 독립적인 공간을 가진다. append를 통하여 새로운 값으로 정의되면, 기존의 값이 있는 주소에 버퍼에 있는 값을 메모리에 저장한다.
    • method별로 동기화 지원 -> 멀티스레드 환경에 적합
  • StringBuilder : StringBuffer와 내부 동작은 동일하지만, 동기화 보장 X
@Override
@IntrinsicCandidate
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

@Override
@IntrinsicCandidate
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

 

String 생성 방식 

  • 1. new 연산자
  • 2. 리터럴
    • String Constant pool(상수 풀)
      • Java 8부터 Perm(Permanent Generation) 영역에서 Heap 영역으로 이동
      • Java 8부터 Perm 영역 삭제 후 MetaSapce 영역이 대신하고 있다.
      • Perm 영역은 실행 시간(Runtime)에 가변적으로 변경할 수 없는 고정된 사이즈이기 때문에 intern 메소드 호출은 저장할 공간이 부족하게 만들 수 있었다. -> out of memory
        • intern() : String 클래스의 메소드. String과 동등한 객체가 이미 상수풀에 존재하면 그 객체를 그대로 리턴한다.
      • heap 영역으로 들어가게 된 상수풀은 Garbage Collection의 대상이 되었다.

 

자바 프로그램 실행 과정

  • 1.자바 소스코드를 컴파일러가 JVM이 이해할 수 있는 코드(byte code)로 변환
  • 2.바이트 코드를 JVM의 클래스 로더에게 전달
  • 3.클래스 로더는 동적로딩(Dynamic loading)을 통해 필요한 클래스들을 로딩 및, 링크하여 런타임 데이터 영역(JVM의 메모리)에 올린다.
    • 클래스 로더 세부 동작 
      • 로드 : 클래스 파일을 가져와서 JVM의 메모리에 로드
      • 검증 : 자바 언어 명세 및 JVM 몇세에 명시된 대로 구성되어 있는지 검사
      • 준비 : 클래스가 필요로 하는 메모리를 할당
      • 분석 : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 래퍼런스로 변경
      • 초기화 : 클래스 변수들을 적절한 값으로 초기화한다. (static 필드)
  • 4.실행엔진(Execution engine)은 JVM 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행한다. 
    • 자바 인터프리터 : 바이트 코드 명령어를 한줄씩 해석하고 실행  
    • JIT 컴파일러 : 바이트 코드 전체를 컴파일하고 바이너리코드로 변경해서 직접 실행하는 방식
  • 5. 해석된 바이트 코드는 메모리 영역에 배치되어 실질적 수행이 이루어진다. JVM은 필요에 따라 스레드 동기화나 가비지 컬렉션같은 메모리 관리 작업을 수행한다.

 

자바 컴파일러(Java compiler)

  • 자바를 가지고 작성한 자바 소스 코드를 자바 가상 머신이 이해할 수 있는 자바 바이트 코드로 변환한다.
  • javac.exe

 

자바 바이트 코드(Java bytecode)

  • 자바 가상 머신이 이해할 수 있는 언어로 변환된 자바 소스 코드
  • 명령어 크기 1 byte

 

자바 가상 머신(JVM)

  • 자바 바이트 코드를 실행하기 위한 가상 기계
  • 각 운영체제에 맞는 JVM을 설치해야 한다.

 

 

자바 가상 머신 구성요소

  • 자바 인터프리터 : 자바 컴파일러에 의해 변환된 자바 바이트 코드를 읽고 해석하는 역할
  • 클래스 로더(class loader)
    • 자바는 동적으로 클래스를 읽어오기 때문에, 동적으로 클래스를 로딩해주는 역할
  • JIT 컴파일러(Just In Time Compiler)
    • 프로그램이 실행 중인 런타임에 실제 기계어로 변환해 주는 컴파일러
    • 동적 번역을 통해, 프로그램의 실행 속도를 향상시켜 준다.
    • JIT 컴파일러는 자바 컴파일러가 생성한 바이트 코드를 바로 런타임에 기계어로 변환
  • 가비지 컬렉터(Garbage Collector)
    • 더 이상 사용하지 않는 메모리를 자동으로 회수
    • 개발자가 메모리 관리를 하지 않아도 되는 편의성
    • 실행 순서 : 참조되지 않은 객체들 탐색 후 삭제 -> 삭제된 객체의 메모리 반환 -> 힙 메모리 재사용

 


JVM이 관리하는 메모리 구조

  • 자바가 실행되면, JVM은 운영체제로 부터 해당 프로그램을 수행할 수 있도록 필요한 메모리를 할당 받는다.
  • 용도에 따라
    • 1. 메소드(method) 영역
      • 클래스에 대한 정보와 함께 클래스 변수(static variable)가 저장되는 영역
    • 2. 힙(heap) 영역
      • 모든 인스턴스 변수가 저장되는 영역
      • new 키워드를 사용한 인스턴스가 생성되면, 해당 인스턴스의 정보를 힙 영역에 저장한다.
      • 힙 영역은 메모리의 낮은 주소에서 높은 주소의 방향으로 할당된다.
    • 3. 스택(stack) 영역
      • 메소드가 호출될 때 메소드의 스택 프레임이 저장되는 영역
        • 스택 프레임(stack frame) : 메소드의 호출 정보
      • 메소드가 호출되면, 메소드 호출과 관계되는 지역변수와 매개변수를 스택에 저장
      • 메소드가 호출되면 함께 할당되고, 호출이 완료되면 소멸한다.

 

JVM - Runtime Data Areas

  • JVM이 운영체제 위에서 실행되면서 할당받는 메모리 영역

1. PC 레지스터(register)

  • 프로그램 실행 포인터를 관리하는 영역

 

2. Native method stack(네이티브 메서드 스택)

  • 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역

 

3. Method area

  • Class 자체에 대한 정보
  • Class를 처음 사용하는 시점에 memory에 올라간다.
  • class의 static field

 

4. heap Area

  • new keyword으로 생성되는 모든 인스턴스가 저장된다.
  • String constant pool : string pool을 heap 안에 가지고 있고, 여기에서 문자열을 관리한다.

 

5. call stack area

  • method가 호출되면 method를 위한 공간이 스택에 쌓인다.
  • 그리고 메소드 안에 사용하는 local variable(지역 변수) 같은 것들이 이 공간에 위치한다.

 

자바의 탄생

  • 썬 마이크로시스템즈(Sun Microsystems)에서 제임스 고슬링(James Gosling)이 고안
  • 가전제품에서 사용할 목적으로 만들었지만, 인터넷과 웹의 출현으로 인터넷을 목적으로 개발
  • 제임스 고슬링의 목표 : Write Once, Run Anywhere(한 번 쓰고 어느 곳에서도 실행)

 

자바의 특징

  • 객체지향 언어
    • 객체들을 먼저 만들고, 하나씩 조립, 연결해서 전체 프로그램 완성
    • 객체를 만들기 전, 클래스 작성
    • 캡슐화, 상속성, 다형성
  • 높은 이식성, OS에 독립적
    • 서로 다른 실행환경에서 프로그램을 옮겨 실행 가능
    • 자바 실행환경(JRE)이 설치되어 있는 모든 운영체제에서 실행 가능

  • 인터프리터 언어
    • C언어는 컴파일 언어 // C -> 컴파일 -> machine code
    • Javascript는 인터프리터 언어 // 소스코드 -> (js)엔진 -> 중간코드 생성 -> 한줄씩 machine code
    • 자바 = 컴파일 언어 + 인터프리터 언어
    • ex. Student.java - 컴파일(javac.exe Student.java) - Student.class(byte code)
      • interpret(java.exe) Student 실행 - JVM 기동
      • 1. class loader 호출
      • 2. class file 검사 + 악성코드 check
      • 3. main() 호출 // 없으면 Main method not found

  • 메모리 자동 관리
    • 개발자가 직접 메모리 접근이 불가능하고, 자바가 메모리 직접 관리
    • 객체 생성 시 자동으로 메모리 영역을 찾아서 할당하고, 사용이 완료되면 가비지 컬렉터(Garbage Colletor)를 실행시켜 자동으로 사용하지 않는 객체를 제거한다.
    • 높은 안정성
  • 쉬운 멀티 스레드 구현
    • 자바는 스레드 생성 및 제어와 관련된 라이브러리 API를 제공하기 때문에 운영체제와 상관없이 멀티 스레드를 쉽게 구현 가능하다.
  • 동적 로딩(Dynamic Loading) 지원
    • 실행시 모든 클래스 로딩이 아닌, 필요한 시점에 클래스를 로딩할 수 있는 장점
    • 메모리의 효율적인 사용과 속도를 위해 필요한 클래스를 동적 로딩
  • 분산 환경 지원
    • TCP/IP 라이브러리가 포함되어 있다.
    • http, http 프로토콜을 지원한다.
  • 풍부한 오픈소스 라이브러리
    • 자바는 오픈소스(Open source) 언어이고, 사용하는 라이브러리나 오픈소스의 양이 당야하다.

JRE(Java Runtime Environment) 자바 실행 환경

  • Java class Library + JVM

 

JDK(Java Developement Kit) 자바 개발 도구

  • 유틸리티(개발 도구)
  • JRE + utility
  • JDK 1.1 -> JDK -> 1.2 -> ... -> JDK 1.5(Java5) -> Java6 -> ... -> 자주 사용되는 Java 8과 Java 11
  • 점점 함수형 언어 관련 기능 업데이트 중

 

Java 8

  • 2014년 Java SE 8 업데이트1.
  • 1.람다 표현식(lambda expression) : 함수형 프로그래밍
  • 2.스트림 API(Stream API) : 데이터의 추상화
  • 3.java.time 패키지 : Joda-Time을 이용한 새로운 날짜와 시간 API
  • 4.나즈혼(Nashorn) : 자바스크립트의 새로운 엔진, 성능과 메모리 관리 면에서 개선

 

람다 표현식(labmda expression)

  • 메소드를 하나의 식으로 표현
  • 식별자 없이 실행 가능한 함수 표현식 -> 익명 함수(anonymous function)
  • 클래스와 객체를 생성하지 않아도 메소드 사용 가능
  • 코드의 가독성을 높여준다.
new Thread(new Runnable() {
    public void run() {
        System.out.println("전통적인 방식의 일회용 스레드 생성");
    }
}).start();
 
new Thread(()->{
    System.out.println("람다 표현식을 사용한 일회용 스레드 생성");
}).start();

 

스트림 API(Stream API)

  • 자바에서 많은 양의 데이터 저장에 배열, 컬렉션 사용
  • 배열, 컬렉션 등에 접근하기 위해 반복문이나 반복자(iterator) 사용
  • 가독성과 재사용성 단점, 정형화된 패턴 부재로 데이터마다 다른 방법으로 접근해야 한다는 단점
  • 스트림 API는 데이터를 추상화하여 다양한 방식으로 저장된 데이터를 읽고 쓰기 위한 공통된 방법 제공
String[] arr = new String[]{"넷", "둘", "셋", "하나"};
 
// 배열에서 스트림 생성
Stream<String> stream1 = Arrays.stream(arr);
stream1.forEach(e -> System.out.print(e + " "));
System.out.println();
 
// 배열의 특정 부분만을 이용한 스트림 생성
Stream<String> stream2 = Arrays.stream(arr, 1, 3);
stream2.forEach(e -> System.out.print(e + " "));

 

java.time 패키지

  • JDK 1.1부터 Calendar 클래스 제공. 하지만 문제점이 존재
    • Calendar 인스턴스는 불변 객체가 아니라서 값이 수정될 수 있는 단점
    • 윤초(leap second)와 같은 특별 상황 고려X

 


call by value VS call by reference

  • call by value : 값에 의한 호출
    • 함수 호출 시 전달되는 변수 값을 복사해서 함수 인자로 전달한다.
  • call by reference : 참조에 의한 호출
    • 인자로 전달되는 변수의 래퍼런스를 전달
  • 자바는 call by value으로 값을 넘긴다. 
  • reference type의 경우에는 해당 객체의 주소 값을 복사하여 사용한다.

 

Java Naming rule

  • Class Name : Parscal notation 사용
    • ex. SampleProject
  • Source code Name : (public) class 이름을 따서 만든다.
    • 하나의 java 파일 안에 여러 class가 나올 수 있다. -> public class는 1개

변수의 종류

  • 1. 기본형(primitive type) 변수
    • 실제 연산에 사용되는 변수
    • 자바에서 기본으로 제공해주는 타입들
    • 정수형, 실수형, 문자형, 논리형

 

  • 2. 참조형(reference type) 변수
    • primitive 변수를 이용해서 사용자가 직접 만들어 사용하는 변수
    • 클래스, 인터페이스, 배열, 열거 타입
    • 빈 객체를 의미하는 null 존재

 

+ Recent posts