반응형

자바의 Integer와 int 자료형 차이

 

1. 기본 데이터 타입 (Primitive Type) - int

 

정의: int는 기본 데이터 타입으로, 32비트 정수를 저장합니다.

특징:

메모리 효율적: 4바이트(32비트)를 사용하여 정수 값을 저장합니다.

값 저장: 단순히 값을 저장하고 연산을 수행합니다.

기본값: 0 (선언만 하고 초기화하지 않은 경우)

성능: 오토박싱/언박싱이 필요 없기 때문에 성능이 더 좋습니다.

 

2. 참조 데이터 타입 (Reference Type) - Integer

 

정의: Integer는 자바의 래퍼 클래스(wrapper class)로, int의 객체 표현입니다.

특징:

클래스 기반: Integer 클래스는 java.lang 패키지에 속하며, 객체로 취급됩니다.

기본값: null (선언만 하고 초기화하지 않은 경우)

기능: 추가 메서드와 기능 제공 (예: parseInt, valueOf, toString 등)

오토박싱 및 언박싱: 자바 컴파일러가 기본 타입과 래퍼 클래스 간의 자동 변환을 수행합니다.

 

Integer x = 10; // 오토박싱
int y = x;      // 언박싱
Integer z = Integer.valueOf(20);
int sum = x + z;

3. 차이점 요약

 

4. 언제 사용해야 하나?

int 사용 시점:

- 성능이 중요한 경우

- 단순히 숫자 값을 저장하고 연산할 때

Integer 사용 시점:

- 객체로 처리해야 할 때 (예: 컬렉션 프레임워크와 함께 사용)

- null 값을 허용해야 할 때

- 메서드와 추가 기능이 필요할 때

 

 

5. 기본 데이터 타입과 래퍼 클래스의 다른 예

 

자바에는 다양한 기본 데이터 타입과 그에 대응하는 래퍼 클래스가 있습니다. 각 래퍼 클래스는 기본 데이터 타입을 객체로 다룰 수 있도록 하며, 추가적인 메서드와 기능을 제공합니다.

 

 기본 데이터 타입과 그에 대응하는 래퍼 클래스

 

요약

 

기본 데이터 타입은 메모리 효율적이고 성능이 좋지만, 객체로 다룰 수 없으며 null 값을 가질 수 없습니다. 반면 래퍼 클래스는 추가적인 메서드와 기능을 제공하며 컬렉션과 같은 객체 기반 데이터 구조에서 사용할 수 있습니다. 따라서 상황에 따라 적절한 타입을 선택하는 것이 중요합니다.

반응형
반응형
  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

 

상속이란

기반이 되는 상위 클래스의 특성을 하위 클래스에게 적용하고, 거기에 더해 필요한 특성을 추가 확장하는 방식을 말한다.

상속의 목적은 기존 기능의 확장과 코드의 재사용이다.

 

상속의 장점

하위클래스에 상위 클래스를 extend 만 하여도 코드가 자동적으로 재사용된다는 것이며, 확장성을 위해 변경하여야 할 부분의 로직만 overiding을 사용하여 재정의도 가능하다.

 

상속 시 단점

상속을 사용하여 코드를 재사용하느 경우에는 기존 상위 클래스가 어떻게 작성되었는지 고려해야 한다.

상속의 특성상 상위 클래스와 하위 클래스가 결합상태로 묶여, 추후 코드의 수정으로 상위클래스의 변경 여파가 하위클래스에 전파될 수 있는 상황이 발생한다.

 

상속을 사용하는 경우

프로젝트 구조 자체가 상속을 위해 설계된 경우

is-a 관계가 명확한 경우 (동물 -> 포유류 -> 고래 등 상위 분류와 하위분류가 명확한 경우)

이미 존재하는 클래스의 메서드와 필드를 재사용할 수 있기때문에 사용한다. 하지만 단순히 재사용만을 위해 상속을 사용하게 되면 oop 의 목적과는 맞지 않는 클래스가 될 수 있다.

 

자바 상속의 특징

다중상속을 지원하지 않는다.

모든 클래스는 Object 클래스의 자식 클래스이다.

 

자바의 상속 구조

단일 상속 구조

public class 상위클래스 {

}

public class 하위클래스 extends 상위클래스 {

}

계층적 상속 구조

public class 상위클래스 {

}

public class 하위클래스A extends 상위클래스 {

}

public class 하위클래스B extends 상위클래스 {

}

자바는 다중 상속을 허용하지 않는다.

다중 상속을 허용하지 않는 이유는 하위 클래스의 코드를 복잡하게 만들기 떄문이다.

다중상속을 하려면 인터페이스를 이용한 다중 상속이 있다.

public class 상위클래스 {

}

public interface 인터페이스{

}

public class 하위클래스 extends 상위클래스 implements 인터페이스 {

}

 

Super 키워드

super 키워드는 자식클래스에서 부모 클래스를 가리키는 키워드이다.

super 키워드를 통해 자식클래스는 부모 클래스의 필드나 메서드를 호출할 수 있다.

super() 메서드는 부모클래스의 생성자를 호출하는 메서드이다.

자식클래스의 생성자에는 기본적으로 super() 를 호출해야 하는데 기입하지 않더라도 컴파일러가 기본으로 추가한다.

class Parent {
	
    public Parent() {
    	System.out.println("부모 클래스 생성자");
    }
    
}

class Child extends Parent {

    public Child() {
    //  super(); 내부적으로 동작한다.
    	System.out.println("자식 클래스 생성자");
    }
}

class Main {
    public static void main(String[] args) {
        Parent p = new Parent();
        
        System.out.println(">>");
        
        Child c = new Child();
    }
}

// 출력내용

부모 클래스 생성자  // 얘도 알고보면 앞에 Object의 생성자가 호출 되겠지.
>>
부모 클래스 생성자다.  // 부모클래스의 생성자가 호출되고 자식 클래스의 생성자가 호출된다.
자식 클래스 생성자다.  

자식클래스의 기본생성자를 만들때 부모 클래스의 기본 생성자가 없으면 컴파일 에러가 생긴다.

class Parent {  //  기본 생성자가 없는 부모 클래스

    String name;
    
    public Parent(String name) {
        this.name = name;
    }
    
}

class Child extends Parent {
    
    public Child() {    // 이 경우 부모 클래스에 기본 생성자가 없기 때문에
    // super();         // 컴파일 에러가 난다.
    }
    
    public Child() {   
        super("super class"); // 부모 클래스의 생성자가 정확히 호출 되므로 에러가 없다.
    }
    
}

 

메서드 오버 라이딩

오버라이딩은 부모클래스로부터 상속받은 메서드를 자식 클래스에서 재정의 하는 것이다.

class Parent {
    public void say() {
        System.out.println("I`m parent.");
    }
}

class Child extends Parent {
    
    @Override
    public void say() {
        System.out.println("I`m child.");
    }
}

class Main {
	public static void main(String[] args) {
        Parent parent = new Parent();
        Child child = new Child();
        
        parent.say();
        child.say();
    }
}

@Override 어노테이션은 없어도 되지만 개발자에게 부모클래스에 있는 메서드라는것을 명시하기 위해 쓰는 것이다.

부모클래스에 없는 메서드에 어노테이션을 붙일 경우 컴파일 오류가 난다.

 

다이내믹 메서드 디스패치 (Dynamic Method Dispatch)

메서드 디스패치란 메소드를 어떻게 호출할 지를 정해서 호출하는 것이다.

정적 메소드 디스패치 (static method dispatch) 는 메서드가 어떻게 실행될지가 컴파일 타임에 결정된다.

스태틱 메소드 디스패치 (Static Method Dispatch)

스태틱 메소드 디스패치는 구현 클래스를 통해 컴파일 시점에서 컴파일러가 어떤 메소드를 호출할지 명확하게 알고 있는 경우
컴파일 시 생성된 바이트코드에도 정보가 남아있으며 애플리케이션 실행 전에 호출할 메소드 결정
대표적으로, 메소드를 오버로딩(Overloading)하면 매개변수 타입과 개수에 따라 어떤 메소드를 호출할지 알 수 있는 경우(메소드 시그니처 변경)
상위 클래스가 있더라도 하위 클래스(구현 클래스)로 선언을 하고 하위 클래스의 인스턴스를 생성

다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

다이나믹 메소드 디스패치는 인터페이스나 추상 클래스에 정의된 추상 메소드를 호출하는 경우로, 호출되는 메소드가 런타임 시 동적으로 결정되는 것
인터페이스 또는 추상 클래스로 선언하고 구현/상속 받은 하위 클래스의 인스턴스를 생성
컴파일러가 알고 있는 타입에 대한 정보를 토대로 런타임 시 해당 타입의 객체를 생성하고 메소드를 호출

 

public interface Animal {
  String method();
}

public class Dog implements Animal {

  @Override
  public String method() { . . . }

  public void bark() { . . . }
}

public class Cat implements Animal {

  @Override
  public String method() { . . . }

  public void meow() { . . . }
}
// 예시 1
public static void main(String[] args) {
  // 다이나믹 메소드 디스패치
  Animal animal = new Dog();
  System.out.println(animal.method());
}

// 예시 2
public class Example {
  private Animal animal;

  public Example(Animal animal) {
    this.animal = animal;
  }

  public void print() {
    // 다이나믹 메소드 디스패치
    System.out.println(animal.method());
  }
}
  • 런타임 전에는 객체 생성이 되지 않기 때문에 Animal animal = new Dog();를 해도, 컴파일러는 Dog가 생성됨을 알 수 없으므로 Animal이 정의한 method() 메소드만 접근 가능
  • Example 클래스의 생성자 인자로 넘어오는 매개값의 타입이 Dog인지 Cat인지(혹은 그 외)는 실행 시 확인 가능.  실행 시 동적으로 확인해서 다이나믹 디스패치

 

더블 디스패치 (Double Dispatch)

다이나믹 메소드 디스패치가 2번 발생하는 것
디자인 패턴 중 방문자 패턴 (Visitor Pattern) 과 밀접한 관계

방문자 패턴 (Visitor Pattern)

일반적으로 OOP는 객체가 스스로 행위를 수행하게 하지만, 경우에 따라(ex. 확장성 고려, OCP 위배) 객체의 행위 수행을 외부 클래스에 위임

이 때 사용하는 디자인 패턴 종류는 전략 패턴, 커맨드 패턴, 방문자 패턴

  • 전략 패턴 (Strategy Pattern)
    • 하나의 객체가 여러 동작을 하게 하는 패턴 (1:N)
  • 커맨드 패턴 (Command Pattern)
    • 하나의 객체가 하나의 동작(+보조 동작)을 하게 하는 패턴 (1:1)
  • 방문자 패턴 (Visitor Pattern)
    • 여러 객체에 대해 각 객체의 동작들을 지정하는 패턴 (N:N)

 

추상클래스

추상(abstract)이란 실체 간에 공통되는 특성을 추출한 것
추상 클래스는 객체를 직접 생성할 수 있는 실체 클래스의 공통적인 특성(필드, 메소드)을 추출해서 선언한 클래스

추상 클래스는 인스턴스 생성 불가능

Animal animal = new Animal(); // (X)
Animal animal = new Dog();    // (O)

클래스 선언 시 abstract 키워드 추가

public abstract class 클래스 {
  // 필드
  // 생성자
  // 메소드
}
  • 추상 클래스를 상속받은 자식 객체가 생성될 때, 내부에서 super 메소드가 호출되어 추상 클래스 객체를 생성하기 때문에 생성자 필요

추상 메소드 오버라이딩

추상 클래스는 자식 클래스의 공통 기능을 추출해 구현한 클래스이지만, 경우에 따라 자식 클래스마다 구현 내용이 달라질 수 있기 때문에 필요 시 추상 메소드를 작성
추상 메소드는 추상 클래스에서만 선언 가능하며, 메소드 선언부만 있는 형태
자식 클래스가 반드시 실행 내용을 구현하게 하기 위해 추상 메소드 선언

public abstract class Animal {
  public abstract void sound();
}

구현

public class Dog {
  @Override
  public void sound() {
    System.out.println("bark");
  }
}
public class Cat {
  @Override
  public void sound() {
    System.out.println("meow");
  }
}

 

final 키워드

메소드에 붙는 경우 - 오버라이딩 불가

class Parent {

    final void run() { // 메소드에 final이 붙는 경우.
        System.out.println("run");
    }
    
}

class Child extends Parent {

    @Override  //  오버라이딩을 못함, 컴파일 에러.
    void run() {
    }
    
}

클래스에 붙는 경우 - 상속 불가

final class Parent { // 클래스에 final이 붙는 경우.
}


class Child extends Parent { // 상속받을 수 없음, 컴파일 에러.
}

 

Object 클래스

 자바에서 모든 클래스들의 상위 클래스이다. 내가 임의로 만든 클래스도 Object 클래스를 상속받고 있다. extends Object를 써넣지 않았는데도 어떻게 이게 되는 걸까? 이것은 컴파일러가 컴파일 타임에 쓱 끼워 넣어 준다.

 

 내가 만든 클래스는 다른 클래스를 상속받고 있는데, 다중 상속이 되지 않는 자바인데 어떻게 Object 상속 부분을 끼워 넣어줄까? 이것은 컴파일러가 똑똑하게 가상 상위 클래스를 찾아 거기에 Object 상속을 슥 끼워넣어 준다.

 

 이렇게 해서 직접적으로나 간접적으로나 모든 클래스는 Object 클래스를 상속받는다.

반응형
반응형
  • 클래스 정의하는 방법
  • 객체 만드는 방법 (new 키워드 이해하기)
  • 메소드 정의하는 방법
  • 생성자 정의하는 방법
  • this 키워드 이해하기

클래스란

클래스는 쉽게 설명하면 객체를 만들기 위한 틀

붕어빵은 하나의 객체로 이를 만들기 위한 붕어빵틀은 하나의 클래스로 설명할 수 있다.

이런 클래스는 class라는 키워드로 정의되며 각각 변수(속성)와 메서드(행위)를 가지게 된다.

 

 

클래스 요소

  • 필드(field) - 필드는 해당 클래스 객체의 상태 속성을 나타내며, 멤버 변수라고도 불린다. 여기서 초기화하는 것을 필드 초기화 또는 명시적 초기화라고 한다.

    • 인스턴스 변수 - 이름에서 알 수 있듯이 인스턴스가 갖는 변수이다. 그렇기에 인스턴스를 생성할 때 만들어진다. 서로 독립적인 값을 갖으므로 heap 영역에 할당되고 gc에 의해 관리된다.
    • 클래스 변수 - 정적을 의미하는 static키워드가 인스턴스 변수 앞에 붙으면 클래스 변수이다. 해당 클래스에서 파생된 모든 인스턴스는 이 변수를 공유한다. 그렇기 때문에 heap 영역이 아닌 static 영역에 할당되고 gc의 관리를 받지 않는다. 또한 public 키워드까지 앞에 붙이면 전역 변수라 볼 수 있다.
  • 메서드(method) - 메서드는 해당 객체의 행동을 나타내며, 보통 필드의 값을 조정하는데 쓰인다.

    • 인스턴스 메서드 - 인스턴스 변수와 연관된 작업을 하는 메서드이다. 인스턴스를 통해 호출할 수 있으므로 반드시 먼저 인스턴스를 생성해야 한다.
    • 클래스 메서드 - 정적 메서드라고도 한다. 일반적으로 인스턴스와 관계없는 메서드를 클래스 메서드로 정의한다.
  • 생성자(constructor) - 생성자는 객체가 생성된 직후에 클래스의 객체를 초기화하는 데 사용되는 코드 블록이다. 메서드와 달리 리턴 타입이 없으며, 클래스엔 최소 한 개 이상의 생성자가 존재한다.
  • 초기화 블록(initializer) - 초기화 블록 내에서는 조건문, 반복문 등을 사용해 명시적 초기화에선 불가능한 초기화를 수행할 수 있다.

    • 클래스 초기화 블록 - 클래스 변수 초기화에 쓰인다.
    • 인스턴스 초기화 블록 - 인스턴스 변수 초기화에 쓰인다.

      클래스 변수 초기화: 기본값 → 명시적 초기화 → 클래스 초기화 블록
      인스턴스 변수 초기화: 기본값 → 명시적 초기화 → 인스턴스 초기화 블록 → 생성자

클래스 정의

class Class {               // 클래스
    String constructor;
    String instanceVar;     // 인스턴스 변수
    static String classVar; // 클래스 변수

    static {                // 클래스 초기화 블록
        classVar = "Class Variable";
    }

    {                       // 인스턴스 초기화 블록
        instanceVar = "Instance Variable";
    }

    Class() {                // 생성자
        constructor = "Constructor";
    }

    void instanceMethod() {       // 인스턴스 메서드
        System.out.println(instanceVar);
    }

    static void classMethod() {   // 클래스 메서드
        System.out.println(classVar);
    }
}

 

 

제어자 키워드

접근 제어자 - public, protected, default, private
그 외 - static, final, abstract, transient, synchronized, volatile etc.

  • 접근 제어자 - 접근 제어자는 해당 클래스 또는 멤버를 정해진 범위에서만 접근할 수 있도록 통제하는 역할을 한다. 클래스는 public과 default밖에 쓸 수 없다. 범위는 다음과 같다. 참고로 default는 아무것도 덧붙이지 않았을 때를 의미한다.

  • static - 변수, 메서드는 객체가 아닌 클래스에 속한다.
  • final

    • 클래스 앞에 붙으면 해당 클래스는 상속될 수 없다.
    • 변수 또는 메서드 앞에 붙으면 수정되거나 오버라이딩 될 수 없다.
  • abstract

    • 클래스 앞에 붙으면 추상 클래스가 되어 객체 생성이 불가하고, 접근을 위해선 상속받아야 한다.
    • 변수 앞에 지정할 수 없다. 메서드 앞에 붙는 경우는 오직 추상 클래스 내에서의 메서드밖에 없으며 해당 메서드는 선언부만 존재하고 구현부는 상속한 클래스 내 메서드에 의해 구현되어야 한다. 상속과 관련된 내용은 6주차에 다룰 예정이다.
  • transient - 변수 또는 메서드가 포함된 객체를 직렬화할 때 해당 내용은 무시된다.
  • synchronized - 메서드는 한 번에 하나의 쓰레드에 의해서만 접근 가능하다.
  • volatile - 해당 변수의 조작에 CPU 캐시가 쓰이지 않고 항상 메인 메모리로부터 읽힌다.

객체 생성

public class Person {
    private int age = 10;
    private String name = "jun";

    Person() {}  // 기본 생성자

    Person(int age, String name) {
        setAge(age);
        setName(name);
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public static void main(String[] args) {
	Person p1 = new Person();
	Person p2 = new Person(15, "min");
}

new 연산자

new연산자는 클래스 타입의 인스턴스를 생성해주는 역할을 하는 연산자로 메모리(Heap 영역)에 공간을 할당받고 그 공간의 참조값을 객체에게 반환해주는 역할을 한다.

new 키워드는 새 객체에 메모리를 할당하고 해당 메모리에 대한 참조값을 반환하여 클래스를 인스턴스화한다. 

일반적으로 객체가 메모리에 할당되면 인스턴스라 부른다

 

생성자 정의

Person() {}  // 기본 생성자

Person(int age, String name) {
    setAge(age);
    setName(name);
}

 생성자를 명시하지 않으면 컴파일러가 자동으로 기본 생성자를 생성한다.

하지만 기본 생성자가 아닌 다른 형태의 생성자만 생성했다면 기본 생성자는 생성되지 않는다.

 

this 키워드

public void setAge(int age) {
    this.age = age;
}

public void setName(String name) {
    this.name = name;
}

this키워드는 인스턴스 자신을 가르킨다. 위 코드에서 this를 사용함으로써 지역변수 age, name와 구분할 수 있다. 당연한 말이지만 클래스 (static)메서드에서는 this를 쓸 수 없다. 왜냐하면 인스턴스가 생성되지 않았을 수도 있기 때문이다.

 

this()는 해당 클래스 생성자를 호출할 수 있다. 그렇기 때문에 생성자를 재사용하는 데 쓰인다. (생성자 체이닝)

 

public class Person {
    int age;
    String name;
    int height;

    Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    Person(int age, String name, int height) {
        this.age = age;
        this.name = name;
        this.height = height;
    }
}

 

super 키워드

super의 경우 this와는 다르게 자식클래스가 부모클래스로부터 상속받은 멤버로를 사용하고자 할때 사용된다.

class Parent{
   int a = 10;
}

class Child extends Parent{
    int a = 20;
    
    void childMethod(){
           System.out.println(a);         //20
           System.out.println(this.a);     //20
           System.out.println(super.a);   //10
    }
}

super 키워드와 this 키워드의 기능이 비슷하 듯이 this()와 super() 키워드 역시도 기능이 비슷하다. super() 키워드는 this()와 마찬가지로 생성자를 호출하는 함수로, 자기자신이 아닌 부모클래스의 생성자를 호출한다.

 

반응형
반응형

제어문

Java에서 코드는 위에서 아래 순으로 읽고 실행된다. 모든 일을 순차적으로 실행할 수 있다면 아무런 상관이 없겠지만, 어떤 코드를 반복해야 될 수도 있고 어떤 코드는 건너뛰는 등의 순서를 변경해야 되는 일이 발생한다. 이 때, 제어문을 사용하여 코드 실행 흐름을 제어할 수 있다.이러한 제어문은 선택문(if-then, if-then-else, switch)과, 반복문(for, while, do-while), 분기문(break, continue, return)으로 나뉜다.

선택문(Decision-making Statement)

if-then

if-then 문은 가장 기본적인 제어문중 하나로 지정한 조건이 만족할 시에 지정한 블록({}) 안에 있는 코드가 실행된다.

if(조건식) { // 한 줄일 경우 {} 생략 가능 
	조건이 참일 경우 실행되는 코드; 
}

만약 if-then문 안에 코드가 한 줄이라면 {}은 생략이 가능하다.

if-then-else

기본적인 if-then 문에서는 참일 경우만 실행이 됬다면, if-then-else 문은 거짓일 때도 실행할 수 있다.
즉, 조건식이 참일 경우와 거짓일 경우의 실행되는 코드들을 나눌 수 있다는 것이다.

if(조건식) { 
	참일 경우; 
} else { 
	거짓일 경우; 
}

이렇게 조건이 하나만 존재 할 수 있지만, 여러가지의 조건을 사용해야 할 경우가 생길 수 있다.(예 - 학점)
이 때는 else if()를 사용하여 또 다른 조건식을 사용할 수 있다.

switch

switch 문은 if-then과 if-then-else 문과 다르게 변수에 대해 평가를 하고 이를 분기할 수 있다.
평가 당하는 변수는 원시형 타입(int, byte, char...)일 수 있고, Enum형 혹은 String, Wrapper(Integer, Byte, Character...) 클래스도 가능하다.

switch(변수) { 
	case 값 A: 
		변수가 값 A에 해당하는 경우; 
		break; 
	case 값 B; 변수가 값 B에 해당하는 경우; 
		break; 
	default: 어떠한 값에도 해당하지 않는 경우; 
		break; 
}

위 예시는 다음과 같이 if-then-else 문으로 변경도 가능하다.

if(변수 == 값 A) { 
	변수가 값 A에 해당하는 경우; 
}else if(변수 == 값 B) { 
	변수가 값 B에 해당하는 경우; 
}else { 
	어떠한 값에도 해당하지 않는 경우; 
}

반복문(Looping Statement)

어떠한 코드가 반복적으로 사용 될 때 사용되는 구문이다.

for

프로그래머가 설정한 조건이 만족 될 때까지 지정한 코드 블럭이 계속해서 수행된다.
카운팅을 할 수도 있겠지만, 배열 혹은 컬렉션 안에 저장되어 있는 값을 순회 할 때도 많이 사용된다.

for(초기식; 조건식; 증감식) { 
	반복 될 코드; 
}

JDK 5.0 이상부터 배열 혹은 컬렉션의 순회시에 다음과 같이 조금 더 향상된 for 문을 사용할 수 있다.

for(타입 변수명 : 배열/컬렉션) { 
	반복 될 코드; 
}

foreach 스타일 for문

: 어떤 컬렉션이든 순회할 수 있음

 

Effetive java - item 46 : for문보다는 for-each를 사용하라

for-each문은 반복자나 인덱스 변수를 제거해 오류 가능성을 줄인다

 int[] nums = {1,2,3,4,5};
 for (int num : nums) {
	 System.out.println(num);
 }

while

특정 조건이 참일 경우에 지정한 코드 블럭이 계속해서 수행되는 구문이다.

while(조건식) { 
	조건식이 참일 경우 반복되는 코드; 
}

조건식이 항상 참일 경우에는 계속해서 해당 코드들이 실행되므로 다음 명령들을 수행할 수 없는 상태가 된다.
따라서 조건식을 잘 유의해서 사용해야 된다.

do-while

while 문이 조건식을 먼저 판별하고 코드를 수행했다면, do-while 문은 먼저 코드블럭을 수행하고 조건을 판별한다.

do { 
	조건식이 참일 경우 반복되는 코드; 
}while(조건식);

 

분기문(Branching Statement)

조건문에서 프로그래머의 의도에 의해 중간 흐름을 바꿀 수 있는 구문

break

break 문은 두가지 케이스로 나뉘는데, 이는 라벨링이 된 것과 안된 것이다.

먼저 라벨링이 되지 않는 break 문은 switch 문, 반복문에서 사용될 수 있으며, 해당 구문을 만났을 때 가장 가까운 조건문을 종료한다.

for(int i = 0; i <= 10; i++) { 
	if(i == 5) break;
    System.out.println(i); 
} // 출력 // 1 // 2 // 3 // 4

다음과 같은 예시가 존재할 때, i가 5가 되면 반복문을 종료하고 다음 코드로 진행하게 된다.

라벨링이 된 break 문은 똑같이 break를 만나면 제어문이 종료되지만, 해당 라벨링이 된 제어문 자체가 종료된다.
즉, 가장 가까운 제어문뿐만 아니라 자신이 표시한 위치안의 제어문을 종료하는 것이다.

int findIt = 10; 
search: 
for(int i = 0; i < arr.length; i++) { 
  for(int j = 0; j< arr[i].length; j++) { 
    if(arr[i][j] == findIt) { 
    	break search;
  	}
  } 
}

위의 예시에서는 arr의 값이 10일 경우, search: 안에 있는 두개의 for 문 모두 종료된다.

continue

continue 문은 반복문 안에서 사용되며, 조건에 해당할 경우에만 반복문을 건너뛴다.
continue 문도 break 문과 똑같이 라벨링이 된 경우와 안된 경우가 존재하고 똑같은 메커니즘으로 동작한다.

for(int i = 0; i <= 10; i++) { 
	if(i == 5) continue; System.out.println(i); 
}
// 출력 // 1 // 2 // 3 // 4 // 6 // 7 // 8 // 9 // 10

위의 예제에서는 i가 5일 경우에만 가장 가까운 반복문의 끝으로 건너뛴다.

int findIt = 10; 
search: 
for(int i = 0; i < arr.length; i++) {
  for(int j = 0; j< arr[i].length; j++) { 
    if(arr[i][j] == findIt) { 
      continue search; 
    } 
  } 
}

위의 예제의 경우, arr의 값이 10일 경우, search: 의 가장 바깥 for 문의 끝으로 건너뛰게 된다.

return

return 문은 현재 메소드를 종료시키고 해당 메소드를 호출한 위치로 돌아간다.
이 또한 메소드의 타입에 의해 뒤에 값이 올 수 있고 안올 수 있다.

int getAge(String name) { 
	if(name == "jongnan") return 28; 
	System.out.println("존재하지 않는 사람!"); 
	return -1; 
}

위의 예제는 메소드의 타입이 int 이므로 반환 값을 지정해주어야 한다.

void pringAge(String name) { 
	if(name == "jongnan") { 
    	System.out.println("28"); 
    	return; 
	} 
	System.out.println("존재하지 않는 사람!"); 
	return; 
}

 

Enumeration 

  • 초기 Collection만 지원
  • Snap-shot : 원본과 달리 사본을 남겨처리. 다른 처리가 발생하면 오류 가능성 생김
  • hasMoreElements(), nextElement()
Vector<Integer> vector = new Vector<>(Arrays.asList(1,2,3));
Enumeration elements = vector.elements();
while (elements.hasMoreElements()) {
	int e = (int)elements.nextElement();
	System.out.println(e);
}

Iterator

  • 모든 Collection 지원
  • enumeration에 비해 빠르다.
  • hasNext(), next(), remove()
Iterator<Integer> iterator = Arrays.asList(1, 2, 3).iterator();
while (iterator.hasNext()) {
	Integer number = iterator.next();
}

 

반응형
반응형

JUnit 5란 
문서에서는 JUnit 5는 크게 세가지 서브 프로젝트의 여러 모듈로 구성이 되어있다고 한다.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintag -e

  • Junit Platform

    JVM에서 테스트를 하기 위한 기초적인 역할을 담당

    • 테스트를 하기 위한 TestEngine API 제공

    • JUnit 4(하위 버전) 기반 테스트 제공
  • Junit Jupiter

    JUnit 5에서 테스트 및 확장하기 위한 프로그래밍 모델과 확장 모델의 조합이다.
    Platform에서 사용하는 TestEngine은 Jupiter를 통해 제공하는 것이다.

    Jupiter API 는 Junit5 에 새롭게 추가된 API 들을 포함하고 있다.
  • Junit Vintage

    하위 버전들(JUnit 3/4) 기반의 테스트를 실행시기키 위해 해당 TestEngine을 제공한다.

 

 문서 설명을 보면 Platform이 가장 상단에서 개발자들에게 JUnit의 전반적인 테스트 API들을 제공하며, 이를 실제적으로 구현한 Jupiter와 Vintage가 존재한다라고 할 수 있다.

JUnit 5를 사용하기 위해서는 런타임에 Java 8 이상이 필요하나, 이전 버전의 JDK로 컴파일 된 코드들 또한 테스트가 가능하다.

 

참고로 Spring Boot 2.2.0 버전부터 JUnit5가 기본으로 채택되었다.


Spring Initializer를 통해 프로젝트를 만들어보면 Test 디펜던시가 다음과 같이 되어있는것을 볼 수 있습니다....

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
  <exclusions>    
    <exclusion>
      <groupId>org.junit.vintage</groupId>
      <artifactId>junit-vintage-engine</artifactId>
    </exclusion>
  </exclusions>
</dependency>

junit-vintage-engine 모듈을 제외하고 있습니다. vintage-engine 자체가 JUnit4를 지원하기 위한 모듈이다보니 기본적으로는 제외하도록 설정되는 것입니다.

만약 JUnit4 테스트케이스를 같이 관리해주어야하는 상황이라면 위 exclusion 설정을 삭제하면 된다.

 

Annotations

Jupiter에서는 테스트와 확장된 기능을 제공하기 위해 여러가지 어노테이션들을 제공한다.

Annotaion설명

@Test 테스트 메소드임을 알림
Junit 4와는 다르게 속성을 정의 X
이는 Jupiter에선 이미 해당 어노테이션들이 존재하기 때문
@ParameterizedTest 여러가지 매개변수를 통해 다양한 테스트 진행
@RepeatedTest 반복 횟수만큼 테스트를 진행
@TestFactory 동적 테스트를 위한 테스트 팩토리
@TestTemplate 일반 테스트가 아닌 테스트 케이스의 템플릿
@TestMethodOrder 테스트 메서드의 실행 순서를 구성하는데 사용(Junit 4의 @FixMethodOrder와 유사)
@TestInstance 테스트 인스턴스 생명주기를 구성하는데 사용
@DisplayName 테스트 클래스 혹은 메소드에 대한 이름을 선언
@DisplayNameGeneration 테스트 클래스에 대한 Display name generator를 선언
@BeforeEach 현재 클래스에서 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 적힌 각각의 메소드들 보다 먼저 실행
(JUnit 4의 @Before와 동일)
@AfterEach 현재 클래스에서 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 적힌 각각의 메소드들 보다 나중에 실행
(JUnit 4의 @After와 동일)
@BeforeAll 현재 클래스에서 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 적힌 모든 메소드들 보다 먼저 실행
(JUnit 4의 @BeforeClass와 동일)
@AfterAll 현재 클래스에서 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 적힌 모든 메소드들 보다 나중에 실행
(JUnit 4의 @AfterClass와 동일)
@Nested 중첩된 테스트 클래스임을 알림
각 클래스의 테스트 인스턴스 생명주기를 사용하지 않는 한 @BeforeAll과 @AfterAll 메소드는 사용 X
@Tag 테스트 필더링을 위한 테그를 선언하는데 사용
@Disabled 테스트 클래스 혹은 메소드를 비활성하는데 사용(JUnit 4의 @Ignore와 유사)
@Timeout 주어진 시간을 초과할 경우, 테스트 실패를 나타내기 위해 사용
@ExtendWith 확장을 선언적으로 등록하는데 사용
@RegisterExtension 필드를 통해 프로그래밍 방식으로 확장을 등록하는데 사용
@TempDir 필드 주입 또는 매개변수 주입을 통해 임시 디렉토리를 제공하는데 사용

 

@DisplayName

기존에 테스트 결과가 보고될 때 메소드명이 테스트케이스 이름으로 사용되었는데요. 때문에 테스트케이스를 잘 설명하기 위해 한글로 하고는 했습니다.

JUnit5에서는 @DisplayName 어노테이션을 활용해 좀 더 깔끔하게 이름을 붙혀줄 수 있습니다.

public class MemberServiceTest {

  // 기존 방식
    @Test
    public void 멤버_프로필_조회() {
        ...
    }

  // JUnit5 방식
  @Test
  @DisplayName("멤버 프로필 조회")
  public void getMemberProfileTest() {    
        ...    
  }
}

 

 LifeCycle Hook

JUnit 테스트케이스 작성 시 개발자가 테스트 수행 전 후 특정 시점에 실행될 동작을 정의할 수 있다.

기존에는 @BeforeClass, @AfterClass, @Before, @After 어노테이션을 활용했는데.

JUnit5에서는 @BeforeAll, @AfterAll, @BeforeEach, @AfterEach 로 변경되었다. @Before와 @After는 기존에 (테스트케이스 마다)라는 의미가 없어서 혼동의 여지가 있었지만, 명확하게 네이밍이 변경되었다.

 

assertions

Assertion을 프로그래밍 관점에서 해석하면 표명, 가정 설정문으로 할 수 있으며, 이를 통해 자신의 로직이 정확한지 테스트 해보는 것이다.
Jupiter에서는 기존 버전에 존재했던 Assertion 메소드를 포함하고, 람다와 함께 사용하기 적합한 추가적인 메소드를 제공한다.
모든 Assertion은 정적 메소드로 정의되어 있다.

 

테스트에서 assertion종류가 많고 복잡한 경우 assertAll(Executable...executables)을 이용할 수 있습니다.
Executable은 파라미터와 반환값이 없는 함수형 인터페이스이기 때문에 assertAll로 그룹핑 + assertion을 람다로 작성할 수 있습니다.

assertAll("assertionGroup1",
  () -> assertEquals(expected, actual),
  ...    
)
@Test
public void shouldFailBecauseTheNumbersAreNotEqual_lazyEvaluation() {
    Assertions.assertTrue(
      2 == 3,
      () -> "Number 2 and 3 are not equal!");
}
@Test
public void shouldAssertAllTheGroup() {
    List<Integer> list = Arrays.asList(1, 2, 4);
    Assertions.assertAll("List is not incremental",
    () ->  Assertions.assertEquals(list.get(0).intValue(), 1),
    () ->  Assertions.assertEquals(list.get(1).intValue(), 2),
    () ->  Assertions.assertEquals(list.get(2).intValue(), 3));
 }

@ParameterizedTest

한 메소드에서 여러 종류의 파라미터를 받아 테스트를 수행할 수 있습니다.

@DisplayName("공백 문자열 테스트")
@ParameterizedTest(name = "문자열 {0}으로 조회")
@ValueSource(strings = " ", "")
public void isBlankStringTest(String source) {
  boolean isBlank = service.isBlank(source);
  assertTrue(isBlank);
}

참고로 @ParamterizedTest를 사용하려면 junit-jupiter-params 모듈을 추가해야합니다.

<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-params</artifactId>
  <version>VERSION_TO_USE</version>
  <scope>test</scope>
</dependency>

Assumptions

Assumption은 테스트를 진행할지 안할지에 대해 테스터가 작성한 가정을 기반으로 선택된다.
즉, 설정한 가정이 참일 경우에는 테스트를 실행하고 거짓일 겅우에는 테스트는 실행하지 않는다.
이러한 Assumption은 조그마한 단위 테스트보다는 통합 테스트에 더 적절히 사용할 수 있다.
Assertion과 똑같이 정적 메소드로 정의되어 있다.

@Test
void testOnlyOnDev() {
  assumeTrue("DEV".equals(System.getenv("ENV")));
  
  DEV 환경에서만 테스트할 코드;
}

@Test
void testAllEnv() {
  assumeTrue("DEV".equals(System.getenv("ENV")),
             () -> {
               DEV 환경에서 테스트할 코드;
             });

  모든 환경에서 테스트할 코드;
}

Tagging And Filtering

Junit 4 에서는 @Category Annotation 을 사용하여 테스트를 그룹화 할 수 있다. Junit 5 에서는 @Category Annotation 이 @Tag Annotation 으로 대체된다.

@Tag("annotations")
@Tag("junit5")
@RunWith(JUnitPlatform.class)
public class AnnotationTestExampleTest {
	/* */
}

 

 

 

참고문헌

 https://junit.org/junit5/docs/current/user-guide/#overview [공식문서]

 

반응형
반응형

목표

자바가 제공하는 다양한 연산자를 학습하세요.

학습할 것

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 논리 연산자
  • instanceof
  • assignment(=) operator
  • 화살표(->) 연산자
  • 3항 연산자
  • 연산자 우선 순위
  • (optional) Java 13. switch 연산자

 

 

  •  

산술 연산자

  • Binary Operator(이항)

    • +, -, *, /, %(Modulo)
    • ArithmeticException 발생 가능
      • Divide by zero
      • Modulo by zero 

비트 연산자

  • Binary Operator(이항)
    • &(AND)
    • |(OR)
    • ^(XOR)
    • >> (right SHIFT)
    • << (left SHIFT)
    • >>> (unsigned right SHIFT) : 비트값을 오른쪽으로 이동한 이후 왼쪽 공간은 모두 0로 채움, C/C++ 에 없음
  • Unary Operator(단일항)
    • ~(NOT, 비트 패턴 반전)
int a = 3 & 1;  // 0011 & 0001 = 1
int b = 2 | 1;  // 0010 | 0001 = 3
int c = 3 ^ 1;  // 0011 ^ 0001 = 2
int d = b >> 1; // 0011 에서 왼쪽으로 1칸 이동, 1(0001)
int e = b << 1; // 0011 에서 오른쪽으로 1칸 이동, 6(0110)
int f = ~a;     // 0001 -> 1111 1111 1111 1111 1111 1111 1111 1110
// 1000 0000 0000 0000 0000 0000 0000 0000
// ->
// 0100 0000 0000 0000 0000 0000 0000 0000
int g = -2147483648 >>> 1;

관계 연산자

  • Binary Operator(이항)
    • == : Equal to
    • != : Not equal to
    • > : greater than
    • < : less than
    • >= : greater than or equal to
    • <= : less than or equal to
  • primitive type 인 피연산자에서 연산 가능
  • 연산 결과 타입은 boolean

논리 연산자

  • Binary Operator(이항)
    • && (LOGICAL AND)
    • || (LOGICAL OR)
  • 피연산자의 타입은 boolean
  • 연산 결과 타입은 boolean

instanceof

  • Type introspection : 실행 시간에 객체의 타입이나 속성을 검사하는 프로그램의 능력을 뜻함

  • Java 에서 대표적인 type introspection

  • 객체의 타입을 비교할 때 사용

  • null 은 어떤 것의 instance 도 아님

class Test {
  ...
}

class Test2 extends Test{
  ...
}

class Operator{
    public static void main(String[] args) {
        Test obj1 = new Test();
        Test obj2 = new Test2();
        System.out.println(obj1 instanceof Test);    // true
        System.out.println(obj1 instanceof Test2);   // false
        System.out.println(obj2 instanceof Test);    // true, 상속 관계이기 때문?
        System.out.println(obj2 instanceof Test2);   // true
        System.out.println(null instanceof Object);  // false
    }
}

Assignment(=) operator

  • Binary Operator(이항)
    • = (ASSIGN)
    • += (ADD and ASSIGN)
    • -= (SUBTRACT and ASSIGN)
    • *= (MULTIPLY and ASSIGN)
    • /= (DIVIDE and ASSIGN)
    • %= (MODULO and ASSIGN)
    • &= (AND and ASSIGN)
    • ^= (XOR and ASSIGN)
    • |= (OR and ASSIGN)
    • <<= (LEFT SHIFT and ASSIGN)
    • >>= (RIGHT SHIFT and ASSIGN)
    • >>>= (UNSIGNED RIGHT SHIFT and ASSIGN)
  • 객체에 값을 할당
  • 다른 연산자와 묶어서 사용 가능
  • declaration statement, assignment expression 에서 사용됨
  • Java 는 reference type 의 경우 주소값을 할당한다는것에 주의해야 함

화살표(->) 연산자

  • 8 버전부터 람다 표현식(lambda expression) 이 공식적으로 적용되었음

  • 함수형 프로그래밍(Functional programming) 표현

    • side-effect 발생을 최소화 하기 위함
    • 조건
      • Pure function
      • Anonymous function
      • Higher-order function
    • Java 에서는 하나의 메소드가 선언된 인터페이스
  • 화살표 연산자는 Java 에서 람다 표현식의 syntax 일부

    (argument, ...) -> {expression}
interface Test{
    int func(int a);
}

class Test2 {
    public void func(Test test){
        int value = test.func(3);
        System.out.println(value);
    }
}

class Operator{
    public static void main(String[] args) {
        Test2 test2 = new Test2();
				// lambda expression 사용 X 버전
        test2.func(new Test() {
            public int func(int a){
                return a + 2;
            }
        });
				// lambda expression 사용 버전
        test2.func((a) ->{
            return a + 2;
        });
    }
}

3항 연산자

  • Conditional Operator

    • ?:

    • 구조

      Conditional Expression ? Expression(true인 경우) : Expression(false 인 경우);
  • 3항 연산자 사용 VS if-else 사용

    • 3항 연산자: expression
    • if-else: statement
    • 문법적인 의미로 보자면 3항 연산자는 expression 이므로 statement 내부 expression이 위치할 수 있는 모든 곳에서 사용 가능, expression 이므로 3항 연산자 자체를 중첩해서 사용 가능

 

int a = 2;
// if-else statement 사용
if(a == 2){
a += 1;
}
else{
a += 2;
}

// 3항 연산 사용
a == 2 ? a += 1 : a+=2 ;

연산자 우선 순위

기본적으로 연산자에는 우선순위가 있으며, 괄호의 우선순위가 제일 높고, 산술 > 비교 > 논리 > 대입의 순서이며, 단항 > 이항 > 삼항의 순서이다. 연산자의 연산 진행방향은 왼쪽에서 오른쪽으로 수행되며, 단항 연산자와 대입 연산자의 경우에는 오른쪽에서 왼쪽으로 수행된다. 

OperatorsPrecedence

postfix expr++ expr--
unary ++expr --expr +expr -expr ~ !
multiplicative * / %
additive + -
shift << >> >>>
relational < > <= >= instanceof
equality == !=
bitwise AND &
bitwise exclusive OR ^
bitwise inclusive OR |
logical AND &&
logical OR ||
ternary ? :
assignment = += -= *= /= %= &= ^= |= <<= >>= >>>=

 

Ref. https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html

  • 동일 선상에 있는 연산자들은 동일한 우선 순위를 가짐
  • 할당 연산 제외한 Binary Operator 는 left ⇒ right 로 평가
  • 할당 연산은 right⇒ left 로 평가

(optional) Java 13. switch 연산자

switch(expression){
	case expression -> expression;
	...
	default -> expression
};

switch(expression){
	case expression:
		expression;
		yield expression;
	...
	default:
		expression;
		yield expression;
};
  • 차이점

    • case : 말고도 case -> 사용 가능
      • 화살표인 경우 branch fall-through 가 아님
      • 중괄호로 감싸야 하는 경우
        • multiple statement 를 적용
        • throw statement 사용, expression이 아닌 경우
    • break statement 대신 yield statement 사용
  • oracle 문서에서는 arrow case 라벨을 사용하는 것을 권장함

    • 이유:
      • colon case 라벨을 사용하면 헷갈릴 수 있음
      • 헷갈리게 되면 의도하지 않은 fall through 가 발생할 수 있음

 

반응형
반응형

목표

자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.

학습할 것

  • 프리미티브 타입 종류와 값의 범위 그리고 기본 값
  • 프리미티브 타입과 레퍼런스 타입
  • 리터럴
  • 변수 선언 및 초기화하는 방법
  • 변수의 스코프와 라이프타임
  • 타입 변환, 캐스팅 그리고 타입 프로모션
  • 1차 및 2차 배열 선언하기
  • 타입 추론, var

 

프리미티브 타입 종류와 값의 범위 그리고 기본 값

 

 

타입

 할당되는 메모리 크기

 기본값

 데이터의 표현 범위

 

 논리형

 boolean

 1 byte

 false

 true, false

 정수형

 byte

 1 byte

 0

 -128 ~ 127

 short

 2 byte

 0

 -32,768 ~ 32,767

 int(기본)

 4 byte 

 0

 -2,147,483,648 ~ 2,147,483,647
-2^{31} ~ 2^{31}-

 long

 8 byte

 0L

 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807

 실수형

 float

 4 byte

 0.0F

 (3.4 X 10-38) ~ (3.4 X 1038) 의 근사값

 double(기본)

 8 byte

 0.0  (1.7 X 10-308) ~ (1.7 X 10308) 의 근사값

 문자형

 char

 2 byte (유니코드)

 '\u0000' 

 0 ~ 65,535

 

자바 8부터 

unsigned int 및

unsigned long 이라는 개념이 추가됨.

 

1byte 는 8bit 이다. 1bit 가 추가 될때마다 표현가능한 숫자가 2배가 된다.

부호가 있는 자료형의 경우 1비트는 부호츨 표현하기 위해 사용하기 떄문에 1byte 의 경우 -128 ~ 127 까지 표현 가능하다.

양수는 0이 포함되기 떄문에 128이 아니다.

만약 0 ~ 255 까지 표현하고 싶다면, 부호 비트 자리도 데이터로 취급하려면 unsigned 자료형을 사용하면 된다. 음수는 표현하지 못하는 대신 양수 표현 범위가 두 배 늘어난다.

2비트로 표현 가능했던 모든 값 : 00, 01, 10, 11

 

자바에는 unsigned 라는 자료형은 제공하지 않고,

Integer.toUnsignedString() 메소드 등으로 제공한다. 

 

실수형의 경우 정수형과 비교했을 떄 메모리 크기는 비슷한데 값의 표현 범위가 훨씬 넓다.

 

실수는 부호(sign), 가수(Mantissa), 지수(exponent) 로 구성되며 부동 소수점 방식을 사용한다.

부동 소수점 방식을 사용하여 모든 기수를 0보다 크거나 같고 1보다 작은 값 범위의 값으로 만들고 원래 수를 표현하기 위해 10을 몇번 거듭 제곱해야 하는지 지수로 표현한다.

sign: 부호를 나타내기 위해 사용하는 비트

exponent: 지수를 사용하기 위해 사용하는 비트

Mantissa: 가수를 사용하기 위해 사용하는 비트

실수 표현 예제

-118.625를 IEEE 754(32비트 단정밀도)로 표현해 보겠습니다.

  1. 우선 음수이므로 부호비트는 1이 됩니다.
  2. 부호를 뺀 절대값을 이진법으로 나타내면 1110110.101(2)이 됩니다.
  3. 소수점을 왼쪽으로 이동시켜 왼쪽에는 1만 남도록 만듭니다. 1110110.101 = 1.110110101 * 2^6 으로 만듭니다. 이것을 정규화된 부동소수점 수라고 합니다.
  4. 가수부는 소수점의 오른쪽 부분으로, 부족한 비트 수 부분만큼 0으로 채워 23비트로 만듭니다.
  5. 지수는 6이며 Bias를 더해야 합니다. 32비트 IEEE754형식에서 Bias는 127이므로 133이되고 이를 이진법으로 변환하면 10000101이 됩니다.

java에서 기본적으로 실수리터럴을 사용하면 double 형식입니다.

그렇기 때문에 float형식의 실수는 마지막에 f를 붙여 float형식의 실수라고 알려주어야 한다. 하지만 float형식은 현재 사용하지 않는 것을 권장한다. 부동 소수점 형식은 정확한 실수값이 아닌 근사값을 저장하는 방식이기 때문에 32비트인 float는 오차가 생길 확률이 크다.

 

프리미티브 타입과 레퍼런스 타입

Java Data Type
ㄴ Primitive Type
    ㄴ Boolean Type(boolean)
    ㄴ Numeric Type
        ㄴ Integral Type
            ㄴ Integer Type(short, int, long)
            ㄴ Floating Point Type(float, double)
        ㄴ Character Type(char)
ㄴ Reference Type
    ㄴ Annotation
    ㄴ Array
    ㄴ String
    ㄴ Class
    ㄴ Enumeration
    ㄴ Interface
Primitive Type Reference Type
boolean, char, byte, short, int등 이미 정해진 유형 사용자가 무제한적으로 유형을 선언
값 저장 주소 저장
call by value call by reference
메모리에 저장되는 값이 실제값 저장되어 있는 값은 메모리의 주소
동일한 유형의 다른 변수에 값이 복사됨 다른 참조유형에 할당되면 동일한 객체를 가리킴
메소드에 전달되면 사본만 전달됨. 메서드에서는 Primitive Type의 원본에 접근할 수 없음. 메서드는 복사된 값을 변경 가능 객체가 메소드에 전단되면 메소드는 주소값을 전달받게되어 객체의 내용을 변경할 수 있음. 객체의 주소는 변경불가

위의 값들은 JVM 의 Runtime Data Area 에 저장된다. 

String name ="james";   // name 변수 와 주소값은 스택에, 실제값은 힙 영역에 생성
int age = 20; // age 변수 와 값은 스택영역에 생성
Person person = new Person(name, age);
stack Heap
name james  
age 20  
person   (주소값)---------------> Person

 

리터럴

리터럴은 실제값 그 자체입니다. 소스코드에서 고정된 값을 대표하는 용어이며 리터럴은 변수 초기화에 사용됩니다.

  • Integer Literals
    • long 타입은 L 로 끝나거나 l 로 끝나는 값
    • 그외 나머지 숫자는 int
    • 16진수: 0x 로 시작
    • 2진수: 0b 로 시작
  • Floating-Point Literals
    • float 타입은 F로 끝나거나 f 로 끝나는 값
    • 그 외 나머지는 double (optional, D 혹은 d 로 끝나는 값)
  • Character and String Literals
    • Unicode character
    • special escape character
      • \b : 백스페이스
      • \t : 탭
      • \n : new line
      • \f : form feed
      • \r : 캐리지 리턴
      • \" : 쌍따옴표
      • \' : 따옴표
      • \\ : 역슬래시

 

 

변수 선언 및 초기화하는 방법

선언 - Declaration?

변수를 선언한다는 것은, 저장공간을 확보하겠다는 의미로 해석할 수 있다.

int a;

위의 코드가 변수를 선언한 것인데, 해석하자면 다음과 같다.
int 타입의 값을 저장할 수 있는 공간을 확보했고(int 타입은 4byte) 그 공간을 지칭할 이름은 a 이다.

초기화 - Initialization?

변수를 초기화 한다는 것은, 저장공간에 원하는 값을 저장하는 것을 의미한다.
변수를 선언하고 나면, 해당 공간에는 아무런 의미 없는 쓰레기값이 들어가있게 된다. 그리고 그 상태에서 컴파일을 시도하면
Error:(10, 28) java: variable a might not have been initialized 와 같은 컴파일 에러코드를 볼 수 있을 것이다.
그러므로 변수는 선언 후에 초기화를 해야하며 위에 선언한 변수에 대한 초기화는 다음과 같이 할 수 있다

a = 10;

선언과 초기화

변수의 선언과 초기화를 한 줄에 하는 것도 가능하다.

int a = 10;

 

초기화 블록

클래스 초기화 블럭 : 클래스 변수의 복잡한 초기화에 사용된다. 클래스가 처음 로딩될 때 한번만 수행된다.

  - class 로딩시에 호출된다.

  - 인스턴스 변수나 인스턴스 메소드에 접근 못함. 

 

인스턴스 초기화 블럭 : 인스턴스 변수의 복잡한 초기화에 사용된다. 

  - 객체 생성할때마다 호출됨.

  - super 생성자 이후에 실행됨.

  - 생성자 보다 먼저 실행됨.

  - 모든 생성자의 공통 코드를 instance initializer block 에 넣으면 코드 줄일수 있다.

 

class InitBlock{
    static {
        /* 클래스 초기화 블럭 */
    }

    {   /* 인스턴스 초기화 블럭 */ }
} 

 

변수의 스코프와 라이프타임

클래스 변수는 static을 앞에 붙여서 선언해주어야 한다.

지역변수는 메소드 안에서 선언을 하면 지역변수로 된다.

인스턴스 변수는 클래스 내부에 생성된 변수이다.

public class scope_and_lifetime {
    int num1, num2;   		//Instance Variables
    static int result;  	//Class Variable
    int add(int a, int b){  	//Local Variables
        num1 = a;
        num2 = b;
        return a+b;
    }
}
클래스 변수(스태틱 변수) 클래스 영역 클래스가 메모리에 올라갈 때 클래스 전역(클래스 기반으로 생성된 각각의 인스턴스에 공유됨)
인스턴스 변수 클래스 영역 인스턴스가 생성될 때 각각의 인스턴스
지역 변수 메소드 영역 변수 선언문이 수행되었을 때 메소드 안

각각의 변수의 라이프타임은 클래스 변수의 경우 프로그램이 종료할때까지 적용되고 인스턴스 변수의 경우는 인스턴스가 참조 되고 있을때에는 유지되다가 객체를 참조하는 변수가 없을 경우 JVM의 Garbage Collector가 제거하게 된다.

지역변수의 경우 메소드가 끝나면 소멸되어 사용할 수 없게 됩니다.

인스턴스변수는 클래스가 로딩될떄 생성되서, 스태틱 함수에서는 인스턴스 변수 사용 불가
스태틱 변수에도 인스턴스메소드 사용 불가
인스턴스변수에는 반대로 스태틱 변수 참조가능 왜냐하면 스태틱변수가 먼저 생성되기 때문이다.

타입 변환, 캐스팅 그리고 타입 프로모션

타입 변환으로 종류로는 캐스팅(강제 형변환)과 타입 프로모션이 있습니다(자동 형변환).

타입캐스팅이란 크기가 더 큰 자료형을 크기가 더 작은 자료형에 대입하는 것을 의미.

데이터가 작은데서 큰 곳으로 옮기는 타입 프로모션의 경우 오류나 문법의 필요없이 형변환이 가능하지만,

반대로 타입캐스팅은 데이터 표현범위가 작은 곳으로 옮길 경우 표현범위를 넘어나게 되면 전혀 다른 값이 나올수도 있다.

 

타입 캐스팅

// 큰 범위에서 작은범위로 캐스팅
int a = 10;     
byte b = (byte)a;
System.out.println(b); //  -> 10
(byte 는 -256~255까지 표현할 수 있음으로 타입캐스팅을 했음에도 데이터 변형이나 
손실은 오지 않았다) 

// 작은 범위에서 큰 범위로 캐스팅	
int a = 10000;     
byte b = (byte)a;
System.out.println(b); //  -> 16
(표현범위를 벗어나는 값을 강제로 타입캐스팅해 데이터에 변형이 생겼다) 

 

타입 프로모션

byte a = 10;
int b = a;
System.out.println(b); //  -> 10

타입변환은 레퍼런스 타입에서도 가능하다.

 

부모클래스로의 타입변환은 자동적으로 가능하지만 자식클래스로의 타입변환은 타입캐스팅이 필요하다.

이유는 자식클래스는 부모클래스의 필드나 메소드를 물려받음으로 자식클래스타입의 객체를 부모클래스타입으로 바꾼다고 해서 데이터의 손실이나 변형이 일어나진 않기 때문이다.

  • Inheritance 관계에서만 가능
  • Upcast : subclass → superclass
    • 모든 subclass 는 superclass 의 컨텐츠를 가지고 있으므로 superclass 로의 casting 이 가능함
  • Downcast: superclass → subclass
    • 모든 superclass 는 subclass 의 컨텐츠를 가지고 있지 않을 수도 있음, 그러므로 오류 발생 가능성 ↑

 

1차 및 2차 배열 선언하기

class ArrayExample {
	public static void main(String[] args) {
        //1차원 배열
        int[] oneDimensionArrayEx1 = {1, 2, 3, 4, 5};
        int[] oneDimensionArrayEx2;
        oneDimensionArrayEx2 = new int[10];

        //2차원 배열
        int[][] twoDimensionArrayEx1 = {{1, 2}, {3, 4}};
        int[][] twoDimensionArrayEx2;
        twoDimensionArrayEx2 = new int[10][10];
    }
}

 

  • oneDimensionArrayEx1 은 Runtime Stack 영역의 힙 영역 주소값을 가짐
  • Heap 영역에 int 타입 크기의 요소 5개를 할당하여 사용됨

 

  • 2차원 배열

  • Runtime Stack 영역의 twoDimensionArrayEx1 은 2개의 요소 크기(2개 요소에 주소값을 가지고 있음)를 가진 힙 영역 주소값을 가짐
  • 힙 영역에는 실제 값이 들어있는 요소들과 주소값이 들어있는 요소들로 존재하게됨

 

타입 추론, var

Java 10부터 추가된 특징중 하나인 Local Variable Type Inference은 로컬 변수 선언을 var를 이용하여 기존의 엄격한 타입 선언방식에서 컴파일러에게 타입추론 책임을 위임할 수 있게 되었다.

var list = new ArrayList<String>(); //infers ArrayList<String> 
var stream = list.stream();//infers Stream<String>

 

Local Variable Type Inference 사용 조건

• 초기화된 로컬 변수 선언시

• 반복문에서 지역변수 선언 시(enhanced for loop포함)

var 활용

1. 지역변수 선언

var numbers = Arrays.asList(1, 2, 3, 4, 5); 
for (var i = 0; i < numbers.size(); i++) { 
	System.out.println("numbers = " + numbers.get(i)); 
}

2. forEach

var numbers = Arrays.asList(1, 2, 3, 4, 5); 
for (var number : numbers) { 
	System.out.println(number); 
}

 

⇒ 기존에는 Object타입으로 받아서 형변환을 하거나 IDE가 아닌 개발자가 직접 타입추론해 명시적 타입선언을 해줬는데 var를 사용하여 훨씬 편하게 타입선언이 가능해진다.

3. Lambda (Java 11)

IntBinaryOperator plus10 = (@NonNull var one, @NonNull var two) -> one + two + 10;

 

⇒ Java 11부터는 람다 인자에도 var사용이 가능해졌는데, 이를 통해 파라미터 어노테이션 사용까지 가능해졌다.

반응형
반응형

1. java.lang.UnsupportedClassVersionError 발생 원인 

 

Unsupported major.minor version 52.0

 

Java SE 15 = 59 (0x3B hex),
Java SE 14 = 58 (0x3A hex),
Java SE 13 = 57 (0x39 hex),
Java SE 12 = 56 (0x38 hex),
Java SE 11 = 55 (0x37 hex),
Java SE 10 = 54 (0x36 hex),
Java SE 9 = 53 (0x35 hex),
Java SE 8 = 52 (0x34 hex),
Java SE 7 = 51 (0x33 hex),
Java SE 6.0 = 50 (0x32 hex),
Java SE 5.0 = 49 (0x31 hex),
JDK 1.4 = 48 (0x30 hex),
JDK 1.3 = 47 (0x2F hex),
JDK 1.2 = 46 (0x2E hex),
JDK 1.1 = 45 (0x2D hex).

 

 

 


java.lang.UnsupportedClassVersionError는 높은 버전의 JDK에서 컴파일한 class 파일을 낮은 버전의 JDK에서 실행을 해서 발생하는 에러다.

 

위 에러가 없으려면 실행환경과 컴파일할 자바버젼을 맞춰줘야 한다.

컴파일시 자바버젼을 맞추기위해서는 jacac 에 -target 옵션을 줘야 한다.

javac -target 1.2 Helloworld.java

 

반응형

+ Recent posts