본문 바로가기
개발일지/일간회고 (TIL)

Java 정리 / TIL(22-11-17)

by 윤승임 2022. 11. 17.

변수

데이터를 저장하기 위한 메모리 공간의 이름이며, 안의 값을 변경할수도 있다.

변수를 선언할 때는 [접근제어자] [변수의 자료형] [변수의 이름] 형식을 사용한다.

변수를 선언할 때, 값도 같이 선언하려면 [변수의 자료형] [변수의 이름] = [값] 형식을 사용한다.

int num = 5; //정수형 변수 선언
System.out.println(num);

String aa = "Hello!"; // 문자형 변수 선언
System.out.println(hello);

aa = "Goodbye"; // 이미 선언된 변수에 다른 값을 할당
System.out.println(aa);

 


상수

초기에 설정한 값이 변하지 않음.

final int num2 = 10; // 상수형 변수 선언 (final로 선언 가능)
System.out.println(num2);
num2 = 50; // 이미 위에서 상수로 선언했기 때문에 실행하면 오류가 난다.

 


1. 기본 자료형 

1) 숫자

short s = 1;
System.out.println(a);

int a = 3; // 정수형 변수 선언
System.out.println(a);

long b = 1234567890L; // Long 정수형 변수 선언
System.out.println(b);

float c = 5.5F; // float 실수형 변수 선언
System.out.println(c);

double d = 9.12345678901234567890d; // double 실수형 변수 선언
System.out.println(d);

// 다음처럼 각 자료형의 MAX, MIN 값을 가져올 수 있습니다.
System.out.println(Short.MAX_VALUE);
System.out.println(Short.MIN_VALUE);
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
System.out.println(Long.MAX_VALUE);
System.out.println(Long.MIN_VALUE);
System.out.println(Float.MAX_VALUE);
System.out.println(Float.MIN_VALUE);
System.out.println(Double.MAX_VALUE);
System.out.println(Double.MIN_VALUE);

 


2) 문자

char alphabet = 'A'; // 문자형 변수 선언
System.out.println(alphabet);
// char type은 default 값이 없다.

 


3) 문자열

String hi = "hello";
System.out.println(hi);

 


4) 논리

boolean fact = true; // 논리형 변수는 true, false의 값을 가진다.
System.out.println(fact);
// boolean type은 default 값이 없다.

 


5) 바이트

byte data = 'd'; 
System.out.println(data); // 알파벳 d는 ASCII code 에서 십진법으로 100이기 때문에 100이라는 글자가 출력

 


2. 참조 자료형 (Reference Type)

기본 자료형이 아닌 모든 것을 참조 자료형이라고 한다.

인스턴스를 가리킬 수 있는 자료형이다.

종류는 배열, 클래스, 인터페이스가 있다.

String hi = "hello !!";
System.out.println(hi);

int[] intArray = new int[] {1,2,3,4,5}; // int 배열을 선언과 동시에 초기화
System.out.println(Arrays.toString(intArray));

 


배열

동일한 자료형의 데이터를 연속된 공간에 저장하기 위한 자료구조

자료형[] 변수 = new 자료형 [배열의 크기] 의 형태로 선언한다.

데이터를 순차적으로 저장하여 0부터 시작하는 인덱스를 통해 자료에 접근 가능! 

배열은 선언과 동시에 크기를 지정받으며 이에 따라 고정된 크기를 갖는다!

int[] intEmptyArray = new int[5]; // int의 5자리 배열 선언
System.out.println(Arrays.toString(intEmptyArray)); // int의 default 값 0으로 채워짐

int[] intArray = new int[] {1,2,3,4,5}; // int 배열을 선언과 동시에 초기화
System.out.println(Arrays.toString(intArray));

String[] stringEmptyArray = new String[5]; // 참조자료형 String의 5자리 배열 선언
System.out.println(Arrays.toString(stringEmptyArray)); // 참조자료형은 값이 없을 경우 null(아무것도 없다) 이라는 표현으로 표시

String[] months = {"1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"}; 
System.out.println(Arrays.toString(months));

int[] scores = new int[4]; // 배열 선언
scores[0] = 5; //인덱스를 통해 배열에 값 입력
scores[1] = 10;
System.out.println(scores[1]); //인덱스를 통해 배열의 특정 값 출력

String[] months = {"1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"};
// 이렇게 선언과 동시에 값을 입력할 수도 있습니다.
System.out.println(months[7]); //인덱스를 통해 배열에 접근하여 특정 값 출력

int[][] arr = new int[4][3]; //배열을 활용하여 2차원의 배열도 만들 수 있습니다

 


연산자

여러 변수들 간의 계산, 비교를 위해 사용되며

산술 / 대입 / 관계 / 논리 / 비트 연산자 등이 있다.

 


조건문

1. if

if (조건식 1){
    실행문 1; // 조건식 1이 true일 경우에 실행
} else if(조건식 2){
	실행문 2; // 조건식 1이 false고, 조건식 2가 true일 경우에 실행
} else {
	실행문 3; // 조건식 1과 조건식 2가 false일 경우에 실행
}

 


2. switch

switch (입력 변수){
    case 입력값1 : 실행 구문
        break;
    case 입력값2 : 실행 구문
        break;
    case 입력값3 : 실행 구문
        break;
    default: 기본 실행 구문
        break;
}

// break는 해당하는 case의 코드를 실행한 후, switch문 밖으로 나가는 역할이다.
// 하지않으면 다음 코드도 실행된다.

char score = 'A';
switch (score) {
    case 'A':
        System.out.println("A등급입니다.");
        break;
    case 'B':
        System.out.println("B등급입니다.");
        break;
    case 'C':
        System.out.println("C등급입니다.");
        break;
    default:
        System.out.println("C등급보다 낮은 등급입니다.");
        break;
}

 


3. 삼항연산자

(조건식) ? A : B // ?를 기준으로 조건식이 true일 경우에는 A를, false일 경우에는 B를 수행

int a = 5;
String reuslt = (a < 10) ? "10보다 작습니다." : "10보다 큽니다.";
System.out.println(reuslt);

 


반복문

1. for

for(초기값 ; 조건식 ; 증감식){	//조건식에 위배되지 않는다면 실행문을 한번 돈다.
    실행문
}


// 1부터 10까지의 합을 구하는 예제
int sum = 0;

for (int i = 0; i < 10; i++) {
    sum += (i + 1);
}
System.out.println(sum);

 


2. for-each

public class Main {
    public static void main(String[] args) {
		// write your code here
		String[] days = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
		for (String day : days) { // 배열의 값들을 하나하나 변수에 담아서 출력
        System.out.println(day);
		}
    }
}

 


3. while

while(조건식){
    실행문
}

// 1부터 10까지의 합을 구하는 예제
int i = 0;
int sum = 0;
while (i < 10) {
    sum += i + 1;
    i += 1;
}
System.out.println(sum);

 


3-1. break

int i = 0;
while (i < 10){
    if (i==5){
        break; // 반복문을 더이상 실행하지 않고 끝내는 역할
    }
    i += 1;
}
System.out.println(i);

 


3-2. continue

for(int i=0;i<10;i++){
  if (i==5){
      continue; // 코드를 실행하지 않고 다시 반복문의 시작 부분으로 돌아감
  }
  System.out.println(i);
}

 


4. do-while

int i = 1;
int result = 0;
do {				// do내부의 코드들을 먼저 실행한 후
    result += i;
    i += 1;
} while (i < 2);	// while문의 조건을 비교
System.out.println(result);

 


반복문, 조건문 퀴즈

1부터 100 더하기

public class Main {
    public static void main(String[] args) {
        // write your code here
        int sum = 0;
        for (int i = 0; i < 100; i++){
            sum += i + 1;
        }
        System.out.println(sum);
    }
}

 


5초부터 카운트 다운

#내가 한거
public class Main {
    public static void main(String[] args) {
        // write your code here

        for(int i = 0; i < 6; i++){
            int five = 5;
            five = five - i;
            System.out.println(five);
        }
    }
}

#다른 방법
public class Main {
    public static void main(String[] args) {
        // write your code here

        for(int i = 5; i >= 0; i--){
            System.out.println("카운트다운: " + i);
        }
    }
}

 


1부터 30까지 홀수의 합과 짝수의 합을 더하고 각각 출력하기

public class Main {
    public static void main(String[] args) {
        // write your code here
        int 짝수 = 0;
        int 홀수 = 0;
        for(int i = 1; i < 31; i++){
            if(i % 2 == 0){
                짝수 += i;
            }else {
                홀수 += i;
            }
        }
        System.out.println("홀수: " + 홀수);
        System.out.println("짝수: " + 짝수);
    }
}

#결과 
홀수: 225
짝수: 240

 


객체지향언어

클래스

표현하고자 하는 대상의 공통 속성을 한 군데에 정의해 놓은 것.

즉, 객체의 속성을 정의해 놓은 것이다.

클래스 내부의 정보를 멤버 변수라고 함.

 

인스턴스

어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스라고 한다.

인스턴스의 멤버변수에 접근할 때는 [생성된 인스턴스.멤버 변수] 의 형식을 사용한다.

class Phone {		// 클래스
    String model;	// 멤버 변수
    String color;	// 멤버 변수
    int price;		// 멤버 변수
}

public class Main {
    public static void main(String[] args) {
        Phone galaxy = new Phone(); 	// 인스턴스
        galaxy.model = "Galaxy10";	// 생성된 인스턴스.멤버변수
        galaxy.color = "Black";		// 생성된 인스턴스.멤버변수
        galaxy.price = 100;		// 생성된 인스턴스.멤버변수
        
        Phone iphone =new Phone();	// 인스턴스
        iphone.model = "iPhoneX";	// 생성된 인스턴스.멤버변수
        iphone.color = "Black";		// 생성된 인스턴스.멤버변수
        iphone.price = 200;		// 생성된 인스턴스.멤버변수
        

        System.out.println("철수는 이번에 " + galaxy.model + galaxy.color + " 색상을 " + galaxy.price + "만원에 샀다.");
        System.out.println("영희는 이번에 " + iphone.model + iphone.color + " 색상을 " + iphone.price + "만원에 샀다.");
    }
}

 


메소드(method)

어떤 작업을 수행하는 코드를 하나로 묶어놓은 것.

메소드를 쓰면 재사용이 가능하다, 중복된 부분을 없앨 수 있다, 프로그램의 구조화.

 

int[] heights = new int[5]; // 키가 들어가 있는 배열

initHeight(heights); // 1. 키에 대한 초기화
sortHeight(heights); // 2. 키를 오름차순으로 정렬
printHeight(heights); // 3. 정렬된 키를 출력

readability의 기본 품질을 위해서 Java로 메소드를 만들 때 지켜야 하는 기본 약속

  1. 동사로 시작해야한다.
  2. camel case로 작성해야한다. (첫 단어는 소문자로, 이후 단어의 구분에 따라서 첫 글자만 대문자인 단어가 이어집니다. 중간에 띄어쓰기나 특수문자는 들어가지 않습니다.)
// 선언과 구현

반환타입 메소드이름 (타입 변수명,타입 변수명, ...){ 
    수행되어야 할 코드
}

int add(int x, int y) {
    int result = x + y;
    return result;
}
// 메소드의 반환타입은 int이며 이는 반환되어지는 변수인 result와 일치하여야 한다.
// 반환타입중 void는 '아무 것도 없음'을 의미한다.
// 메소드내에서 출력을 할 경우 사용

// 예제

class Calculation {
    int add(int x, int y) {
        int result = x + y;
        return result;
    }	// 두 값을 더한 결과

    int subtract(int x, int y) {
        int result = x - y;
        return result;
    }	// 두 값을 뺀 결과 
}
// 여기서 두 메소드의 x와 y변수가 중복되어 사용되고 있지만
// 메소드 내의 변수는 지역변수로써 메소드 내부에서만 사용할 수 있다.

public class Main {
    public static void main(String[] args) {
        Calculation calculation = new Calculation();
        int addResult = calculation.add(100, 90);
        int subResult = calculation.subtract(90, 70);

        System.out.println("두 개를 더한 값은 " + addResult);
        System.out.println("두 개를 뺀 값은 " + subResult);
    }
}

 


생성자

인스턴스가 생성될 때 사용되는 인스턴스 초기화 메소드

'new'와 같은 키워드로 해당 클래스의 인스턴스가 새로 생성될 때 자동으로 호출되는 메소드

생성자를 이용하여 인스턴스가 생성될 때 수행할 동작을 코드로 짤 수 있다.

대표적으로 인스턴스 변수를 초기화 하는 용도

클래스이름 (타입 변수명, 타입 변수명, ...){
    인스턴스 생성 될 때에 수행하여할 코드
    변수의 초기화 코드
}

// 생성자의 이름은 클래스명과 같아야한다.
// 생성자는 리턴 값이 없다.

// 예시
class Phone {
    String model;
    String color;
    int price;

    Phone(String model, String color, int price) { //생성자
        this.model = model;	
        this.color = color;	// 생성자에서 사용된 this는 생성된 객체 자신을 가리키며
        this.price = price;	// 생성자의 매개변수의 값을 객체의 해당하는 데이터에 넣어주게 된다.
    }
}

public class Main {
    public static void main(String[] args) {
        Phone galaxy = new Phone("Galaxy10", "Black", 100);

        Phone iphone =new Phone("iPhoneX", "Black", 200);

        System.out.println("철수는 이번에 " + galaxy.model + galaxy.color + " + 색상을 " + galaxy.price + "만원에 샀다.");
        System.out.println("영희는 이번에 " + iphone.model + iphone.color + " + 색상을 " + iphone.price + "만원에 샀다.");
    }
}

 


상속

기존의 클래스를 재사용하는 방식 중 하나로, 변경사항만 코드로 작성하므로 상대적으로 적은 양의 코드를 작성할 수 있다.

상속을 통해 클래스간의 계층구조를 만들게 된다.

extends를 이용하여 사용할 수 있다.

상속의 특징으로는

  1. 부모 클래스로에서 정의된 필드와 메소드를 물려 받는다.
  2. 새로운 필드와 메소드를 추가 가능.
  3. 부모 클래스에서 물려받은 메소드를 수정 가능.
  4. 오직 하나의 클래스만을 상속받을 수 있다.
class Animal{} 			// 부모클래스, 조상클래스라고 부름
class Dog extends Animal{}	// 자식클래스, 자손클래스라고 부름
class Cat extends Animal{}

// 예시
class Animal {
    String name;

    public void cry() {
        System.out.println(name + " is crying.");
    }
}

class Dog extends Animal {

    Dog(String name) {
        this.name = name;
    }

    public void swim() {
        System.out.println(name + " is swimming!");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("코코");
        dog.cry();
        dog.swim();
        
        Animal dog2 = dog;
        dog2.cry();
	//dog2.swim();	// compile error 
    }			
}

자식(Dog) 객체는 자식(Dog) 타입으로 선언된 변수에도 할당할 수 있고, 부모(Animal) 타입으로 선언된 변수에도 할당할 수 있습니다. 단, 부모(Animal) 타입의 변수로 사용할 때는, 실제 객체를 만들(new) 때 사용한 자식(Dog) 타입에 있는 함수 를 호출할 수 없습니다. 컴파일 에러입니다. (여기서는 swim())

 


오버로딩

한 클래스 내에 동일한 이름이지만 매개변수의 개수나 타입이 다른 메소드를 여러개 정의하는 것.

int add(int x, int y, int z) {
    int result = x + y + z;
    return result;
}

long add(int a, int b, int c) {
    long result = a + b + c;
    return result;
}
// 반환타입은 다르지만 매개변수의 자료형과 개수는 같기에 오버로딩이 아닙니다.

int add(int x, int y, int z) {
    int result = x + y + z;
    return result;
}

long add(int a, int b, long c) {
    long result = a + b + c;
    return result;
}

int add(int a, int b) {
    int result = a + b;
    return result;
}
// 오버로딩의 조건에 부합하는 예제입니다.

 


오버라이딩

부모 클래스로부터 상속받은 메소드의 내용을 변경하는 것.

조건

  1. 부모 클래스의 메소드와 이름이 같아야 합니다.
  2. 부모 클래스의 메소드와 매개변수가 같아야 합니다.
  3. 부모 클래스의 메소드와 반환타입이 같아야 합니다.
class Animal {
    String name;
    String color;

    public void cry() {
        System.out.println(name + " is crying.");
    }
}

class Dog extends Animal {

    Dog(String name) {
        this.name = name;
    }
	
    //오버라이딩
    public void cry() {	
        System.out.println(name + " is barking!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("코코");
        dog.cry();
    }
}

접근 제어자

멤버 변수/함수나 클래스에서 사용하며 외부에서의 접근을 제한하는 역할을 한다.

  • private : 같은 클래스 내에서만 접근이 가능합니다
  • default(nothing) : 같은 패키지 내에서만 접근이 가능합니다.
  • protected : 같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능합니다.
  • public : 접근 제한이 전혀 없습니다.

객체지향 프로그래밍이란 객체들 간의 상호작용을 코드로 표현하는 것인데, 이때 객체들간의 관계에 따라서 접근 할 수 있는 것과 아닌 것, 권한을 구분할 필요가 생기는데 이런 것을 캡슐화(encapsulation)라고 한다. 접근 제어자는 캡슐화가 가능할 수 있도록 돕는 도구.

// ModifierTest.java
package pkg;

public class ModifierTest {
    private void messageInside() {
        System.out.println("This is private modifier");
    }

    public void messageOutside() {
        System.out.println("This is public modifier");
        messageInside();
    }

    protected void messageProtected() {
        System.out.println("This is protected modifier");
    }
}

// Main.java
import pkg.ModifierTest;

class Child extends ModifierTest {
    void callParentProtectedMember() {
        System.out.println("Call my parent's protected method");
        super.messageProtected();
    }
}

public class Main {
    public static void main(String[] args) {
        ModifierTest modifierTest = new ModifierTest();

        modifierTest.messageOutside();
//        modifierTest.messageInside(); // compile error
//        modifierTest.messageProtected(); // compile error

        Child child = new Child();
        child.callParentProtectedMember();
    }
}

 


추상클래스

추상메소드를 선언할 수 있는 클래스.

보통 클래스와는 다르게 상속받는 클래스 없이 그 자체로 인스턴스를 생성할 수 없다.

추상메소드란, 추상메소드는 설계만 되어있으며 수행되는 코드에 대해서는 작성이 안된 메소드이다. 미완성으로 남겨두는 이유는 클래스마다 반드시 동작이 달라지는 경우에 상속받는 클래스 작성자가 반드시 작성하도록 하기 위함이다.

// 추상메소드 형식
abstract 리턴타입 메소드이름();
abstract class Bird {	
    private int x, y, z;

    void fly(int x, int y, int z) {
        printLocation();
        System.out.println("이동합니다.");
        this.x = x;
        this.y = y;
        if (flyable(z)) {
            this.z = z;
        } else {
            System.out.println("그 높이로는 날 수 없습니다");
        }
        printLocation();
    }
    # fly(x, y, z) 함수는 Bird 를 상속받는 모든 클래스에서 동일한 동작을 합니다.
    # 다만, 그 안에서 호출된 flyable(z)의 동작만 그것을 구현하는 자식 클래스에서 구현한대로 동작
    
    abstract boolean flyable(int z); // 추상메소드

    public void printLocation() {
        System.out.println("현재 위치 (" + x + ", " + y + ", " + z + ")");
    }
}

class Pigeon extends Bird {
    @Override
    boolean flyable(int z) {
        return z < 10000;
    }
}

class Peacock extends Bird {
    @Override
    boolean flyable(int z) {
        return false;
    }
}

public class Main {
    public static void main(String[] args) {
        Bird pigeon = new Pigeon();
        Bird peacock = new Peacock();
        System.out.println("-- 비둘기 --");
        pigeon.fly(1, 1, 3);
        System.out.println("-- 공작새 --");
        peacock.fly(1, 1, 3);
        System.out.println("-- 비둘기 --");
        pigeon.fly(3, 3, 30000);
    }
}

 


인터페이스

객체의 특정 행동의 특징을 정의하는 간단한 문법

함수의 특징(method signature)인 접근제어자, 리턴타입, 메소드 이름만을 정의하고 함수의 내용은 없다.

인터페이스를 구현하는 클래스는 인터페이스에 존재하는 {함수의 내용}을 반드시 구현해야한다.

// 인터페이스 형식
interface 인터페이스명{
    public abstract void 추상메서드명();
}
interface Bird {
    void fly(int x, int y, int z);
}

class Pigeon implements Bird{
    private int x,y,z;

    @Override
    public void fly(int x, int y, int z) {
        printLocation();
        System.out.println("날아갑니다.");
        this.x = x;
        this.y = y;
        this.z = z;
        printLocation();
    }
    public void printLocation() {
        System.out.println("현재 위치 (" + x + ", " + y + ", " + z + ")");
    }
}

public class Main {

    public static void main(String[] args) {
        Bird bird = new Pigeon();
        bird.fly(1, 2, 3);
//        bird.printLocation(); // compile error
    }
}

#interface인 Bird 타입으로 선언한 bird 변수는 실제로 Pigeon 객체이지만
#interface인 Bird 에 선언되지 않은 printLocation() 이라는 함수는 호출할 수 없다.
#interface type 으로 선언되어있는 부분에서는 실제객체가 무엇이든지, interface에 정의된 행동만 할 수 있다.

 


추상클래스와 인터페이스의 비교

  • 인터페이스
  1. 구현하려는 객체의 동작의 명세
  2. 다중 상속 가능
  3. implements를 이용하여 구현
  4. 메소드 시그니처(이름, 파라미터, 리턴 타입)에 대한 선언만 가능
  • 추상클래스
  1. 클래스를 상속받아 이용 및 확장을 위함
  2. 다중 상속 불가능 , 단일 상속
  3. extends를 이용하여 구현
  4. 추상메소드에 대한 구현 가능

객체지향 퀴즈

abstract class Human {
    String name;
    int age;
    int x;
    int y;
    int speed;
    String type;


    void walk(int x, int y){
        this.x = x;
        this.y = y;
        System.out.println("걸어서 " + x + ", " + y + " 도착!");
    }
    abstract boolean runnable(String type);

    void run(int x, int y) {
        if (runnable(type)){
                this.x = x;
                this.y = y;
                this.speed += 2;
                System.out.println("뛰어서 " + x + ", " + y + " 도착! 현재 속도: " + this.speed);
        }else{
            System.out.println("불가능!");
        }
    }
    abstract boolean ableswim(String type);
    void swim(int x, int y) {
        if (ableswim(type)){
            this.x = x;
            this.y = y;
            this.speed += 1;
            System.out.println("수영해서 " + x + ", " + y + " 도착! 현재 속도: " + this.speed);
        }else{
            System.out.println("불가능!");
        }
    }
}



class Child extends Human {
    Child(String name, int age) {
        this.name = name;
        this.age = age;
        this.x = 0;
        this.y = 0;
        this.speed = 5;
        this.type = "자식";
        System.out.println("이름: " + name + " / 나이: " + age + " / 위치: " + x + ", " + y + " / 속도: " + speed);
    }


    @Override
    boolean runnable(String type) {
        return true;
    }

    @Override
    boolean ableswim(String type) {
        return true;
    }
}


class Parent extends Human {
    Parent(String name, int age) {
        this.name = name;
        this.age = age;
        this.x = 0;
        this.y = 0;
        this.speed = 3;
        this.type = "부모";
        System.out.println("이름: " + name + " / 나이: " + age + " / 위치: " + x + ", " + y + " / 속도: " + speed);
    }

    @Override
    boolean runnable(String type) {
        return true;
    }

    @Override
    boolean ableswim(String type) {
        return false;
    }
}



class Grandparents extends Human {
    Grandparents(String name, int age) {
        this.name = name;
        this.age = age;
        this.x = 0;
        this.y = 0;
        this.speed = 1;
        this.type = "조부모";
        System.out.println("이름: " + name + " / 나이: " + age + " / 위치: " + x + ", " + y + " / 속도: " + speed);
    }

    @Override
    boolean runnable(String type) {
        return false;
    }

    @Override
    boolean ableswim(String type) {
        return false;
    }
}





public class Main {
    public static void main(String[] args) {
        Child child = new Child("김자식", 24);
        Parent parent = new Parent("김부모", 52);
        Grandparents grandparents = new Grandparents("김조부모", 70);
        child.walk(1,1);
        parent.walk(1,1);
        grandparents.walk(1,1);
        child.run(2,2);
        parent.run(2,2);
        grandparents.run(2,2);
        child.swim(3,-1);
        parent.swim(3,-1);
        grandparents.swim(3,-1);


    }
}

 


예외, 에러 처리

자바에서는 상속을 이용해 예외를 표현.

모든 예외 클래스는 Throwable의 자손 클래스이다.

Throwable의 자손 클래스는 크게 Error 와 Exception이 있다.

Error - 프로그램이 종료되어야 하는 심각한 문제
Exception - 프로그램이 종료되지는 않지만 예외나 문제상황을 표현하기 위해 사용

try-catch(-finally) 형식

try {
    // 예외가 발생할 가능성이 있는 코드를 구현합니다.
} catch (FileNotFoundException e) {
    // FileNotFoundException이 발생했을 경우,이를 처리하기 위한 코드를 구현
} catch (IOException e) {
    // FileNotFoundException이 아닌 IOException이 발생했을 경우,이를 처리하기 위한 코드를 구현
} finally {
    // 예외의 발생여부에 관계없이 항상 수행되어야하는 코드를 구현
    // 필수는 아니다
}

// 0으로 나눠지는 경우 catch문이 실행되는 예제
public class Main {
    public static void main(String[] args) {
        int number = 10;
        int result;

        for (int i = 10; i >= 0; i--) {
            try {
                result = number / i;
                System.out.println(result);
            } catch (Exception e) {
                System.out.println("Exception발생: " + e.getMessage());
            } finally {
                System.out.println("항상 실행되는 finally 구문");
            }
        }
    }

}

 

try-with-resource 형식

입출력과 자주 쓰이는 구문

기존의 try-catch(-finally)문은 자원을 닫을 때 close()를 사용

try-with-resource문은 try문을 벗어나는 순간 자동적으로 close()가 호출

try()안의 입출력 스트림을 생성하는 로직을 작성할 때 해당 객체가 AutoClosable 인터페이스를 구현한 객체여야한다.

import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {

        try (FileOutputStream out = new FileOutputStream("test.txt")) {
            // test.txt file 에 Hello Sparta 를 출력
            out.write("Hello Sparta".getBytes());
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

 

메소드에서의 예외 선언

catch문을 이용해서 예외처리를 하지 않은 경우, 메소드에 throws로 예외가 발생할 수 있다는 것을 알려주어야 한다.

void method() throws IndexOutOfBoundsException, IllegalArgumentException {
    // 메소드의 내용
}

// caller 쪽에서 catch와 관련된 코드를 작성

 


예외, 에러 처리 퀴즈

다음 스니펫에 있는 divide() 함수는 매개변수(parameter)에 들어오는 값에 따라서 ArithmeticExceptionArrayIndexOutOfBoundsException이 발생할 수 있습니다.

  1. throws 키워드를 통해서 divide() 함수에서 발생할 수 있는 exception의 종류가 무엇인지 알게 해주세요.
  2. Main 함수에서 try-catch 문을 이용해서, 다음 동작을 구현하세요.
    • ArithmeticException이 발생할 때는 잘못된 계산임을 알리는 문구를 출력하세요.
    • ArrayIndexOutOfBoundsException이 발생할 때는 현재 배열의 index범위를 알려주는 문구를 출력하세요.
class ArrayCalculation {

    int[] arr = { 0, 1, 2, 3, 4 };

    public int divide(int denominatorIndex, int numeratorIndex)
            throws ArithmeticException, ArrayIndexOutOfBoundsException {
        return arr[denominatorIndex] / arr[numeratorIndex];
    }
}

public class Main {
    public static void main(String[] args) {
        ArrayCalculation arrayCalculation = new ArrayCalculation();

				System.out.println("2 / 1 = " + arrayCalculation.divide(2, 1));
        try {
            System.out.println(
                    "1 / 0 = " + arrayCalculation.divide(1, 0));
        } catch (ArithmeticException arithmeticException) {
            System.out.println("잘못된 계산입니다. " + arithmeticException.getMessage());
        }
        try {
            System.out.println("Try to divide using out of index element = "
                               + arrayCalculation.divide(5, 0)); 
        } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) {
            System.out.println(
                    "잘못된 index 범위로 나누었습니다. 타당 index 범위는 0부터" + (arrayCalculation.arr.length - 1) + "까지 입니다.");
        }
    }

}

 


날짜와 시간 다루기

java.time패키지를 통해 날짜와 시간을 사용하자.

public class Main {
    public static void main(String[] args) {
        System.out.println("now()를 활용하여 생성"); // 현재의 날짜와 시간
        LocalDate date = LocalDate.now();
        LocalTime time = LocalTime.now();
        LocalDateTime dateTime = LocalDateTime.now();

        System.out.println(date);
        System.out.println(time);
        System.out.println(dateTime);

        System.out.println("of()를 활용하여 생성"); // 지정하는 값
        LocalDate newDate = LocalDate.of(2021, 03, 29);
        LocalTime newTime = LocalTime.of(22, 50, 55);

        System.out.println(newDate);
        System.out.println(newTime);
    }
}

// DateTimeFormatter 클래스를 이용하여 형식 수정
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
String shortFormat = formatter.format(LocalTime.now());
System.out.println(shortFormat);

// 사용자 지정 형식
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String myDate = newFormatter.format(LocalDate.now());
System.out.println(myDate);

 


컬렉션

다수의 데이터를 다루기 위한 자료구조를 표현하고 사용하는 클래스의 집합

  1. List : 순서가 있는 데이터의 집합. 데이터의 중복을 허용. → ArrayList, LinkedList, Stack 등
  2. Set : 순서를 유지하지 않는 데이터의 집합. 데이터의 중복을 허용하지 않는다. → HashSet, TreeSet 등
  3. Map : 키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합. 순서는 유지되지 않으며 키는 중복을 허용되지 않고 값은 중복을 허용. → HashMap, TreeMap 등
  4. Stack : 마지막에 넣은 데이터를 먼저 꺼내는 자료구조. LIFO(Last In First Out) → Stack, ArrayDeque 등
  5. Queue : 먼저 넣은 데이터를 먼저 꺼내는 자료구조. FIFO(First In First Out) → Queue, ArrayDeque 등

 


List

순서가 있는 나열된 데이터

ArrayList는 배열을 이용하여 데이터를 저장하는 List 인터페이스이다.

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList(10);
        list.add(1);
        list.add(5);
        list.add(4);
        list.add(11);
        list.add(10); // ArrayList에 값 한개씩 입력
        System.out.println(list); // [1,5,4,11,10]

        Collections.sort(list); // list 정렬
        System.out.println(list); // [1,4,5,10,11]

        System.out.println(list.size()); // arrayList의 크기 출력

        arrayList.remove(4); // 인덱스를 활용하여 해당하는 값 제거
        System.out.println(list);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i)); // get을 이용하여 값 1개씩 출력
        }
		for (int current : list) {
			System.out.println(current);
        }

    }
}

 


Set

순서를 유지하지 않는 데이터의 집합이며 데이터의 중복을 허용하지 않는다.

HashSet은 Set 인터페이스를 구현한 대표적인 컬렉션

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<Integer> integerSet = new HashSet<>(); // Collection의 자료형에는 primitive 타입은 올 수 없습니다. primitive 타입에 해당하는 class 가 존재하니 그것을 사용하세요.
        integerSet.add(1);
        integerSet.add(3);
        integerSet.add(2);
        integerSet.add(9);// 하나씩 값을 삽입합니다.
        System.out.println(integerSet); // 출력을 해보면 순서가 지켜지지 않는 것을 알 수 있습니다.

        Set<String> stringSet = new HashSet<>();
        stringSet.add("LA");
        stringSet.add("New York");
        stringSet.add("LasVegas");
        stringSet.add("San Francisco");
        stringSet.add("Seoul");
        System.out.println(stringSet);

        stringSet.remove("Seoul"); //Seoul을 HashSet에서 제거해보겠습니다.
        System.out.println(stringSet);

        ArrayList<String> target = new ArrayList<String>();
        target.add("New York");
        target.add("LasVegas");//제거할 항목을 ArrayList에 삽입하겠습니다.
        stringSet.removeAll(target);//제거항목에 삽입된 도시들을 삭제하겠습니다.
        System.out.println(stringSet);

        System.out.println("LA가 포함되어있나요? " + stringSet.contains("LA"));
        System.out.println("LA가 포함되어있나요? " + stringSet.contains("LasVegas"));
        //LA가 HashSet에 포함되어있으면 true를, 그렇지 않으면 false를 반환합니다.

        System.out.println("현재 HashSet의 크기는 : " + stringSet.size() + "입니다.");
        //HashSet의 크기를 반환합니다.

        stringSet.clear();//HashSet의 모든 아이템들을 삭제합니다.
        System.out.println(stringSet);
    }
}

 


Map

HashMap은 키(key)와 값(value)을 하나의 데이터로 저장하는 특징을 가진다.

public class Main {
    public static void main(String[] args) {
				Map<Integer, String> map = new HashMap<>();
        map.put(1, "apple");
        map.put(2, "berry");
        map.put(3, "cherry");

        System.out.println(map);

        System.out.println("1st in map: " + map.get(1));

        map.remove(2);
        System.out.println(map);
        System.out.println(map.containsKey(2));
        System.out.println(map.containsValue("cherry"));
        
        map.clear();
        System.out.println(map);
    }
}

 


Stack

마지막에 저장한 데이터를 가장 먼저 꺼내는 자료구조 LIFO(Last In First Out)

public class Main {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(1);
        stack.push(3);
        stack.push(5);
        stack.push(7);
        System.out.println(stack); // Stack을 출력합니다

        System.out.println(stack.peek()); // Stack의 가장 상단 값을 출력합니다.(삭제는 하지 않습니다.)
        stack.pop(); // Stack의 가장 상단 값을 제거합니다.
        System.out.println(stack);
        System.out.println(stack.size()); // Stack의 크기를 반환합니다.
        System.out.println(stack.contains(1)); // Stack에 1이라는 값이 있으면 true를, 그렇지 않으면 false를 반환합니다.
        System.out.println(stack.empty()); // STack이 비어있으면 true를, 그렇지 않으면 false를 반환합니다.
        System.out.println(stack);
    }
}

 


Queue 

처음에 저장한 데이터를 가장 먼저 꺼내게 되는 FIFO(First In First Out) 구조

public class Main {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(1);
        queue.add(3);
        queue.add(5);//Queue에 값 삽입합니다.
        System.out.println(queue);//Queue 출력합니다.
        System.out.println(queue.poll()); // Queue에서 객체를 꺼내서 반환합니다.
        queue.add(7);
        queue.add(11);
        queue.add(9);
        System.out.println(queue);
        System.out.println(queue.peek()); //Queue에서 삭제 없이 요소를 반환합니다.
        System.out.println(queue);
    }
}

 


ArrayDeque

기본 Stack, Queue의 기능을 모두 포함하면서도 성능이 더 좋다

public class Main {
    public static void main(String[] args) {
        ArrayDeque<Integer> arrayDeque = new ArrayDeque<>(); // ArrayDeque를 이용한 선언(제네릭스 이용)
        arrayDeque.addFirst(1);
        arrayDeque.addFirst(2);
        arrayDeque.addFirst(3);
        arrayDeque.addFirst(4); // arrayDeque의 앞에 값을 삽입
        System.out.println(arrayDeque);

        arrayDeque.addLast(0); // arrayDeque의 끝에 값을 삽입
        System.out.println(arrayDeque);

        arrayDeque.offerFirst(10); // addFirst와 비슷하지만 큐의 크기 문제가 생길 때, offerFirst는 false를,
        // addFrist는 exception을 반환합니다.
        System.out.println(arrayDeque);

        arrayDeque.offerLast(-1); // arrayDeque의 끝에 값을 삽입
        System.out.println(arrayDeque);
        System.out.println(arrayDeque.size()); // 7


        System.out.println(arrayDeque.removeFirst()); // 첫번째 값을 제거하면서 그 값을 리턴
        System.out.println(arrayDeque.removeLast()); // 마지막 값을 제거하면서 그 값을 리턴
		    System.out.println(arrayDeque);
				System.out.println(arrayDeque.size()); // 5

        System.out.println(arrayDeque.pollFirst()); // 첫번째 값을 반환 및 제거하면서 그 값을 리턴
        System.out.println(arrayDeque);
				System.out.println(arrayDeque.size()); // 4

        System.out.println(arrayDeque.pollLast()); // 마지막 값을 반환 및 제거하면서 그 값을 리턴
        System.out.println(arrayDeque);
				System.out.println(arrayDeque.size()); // 3

        System.out.println(arrayDeque.peekFirst()); // 첫번째 값을 반환, 제거하지 않음
        System.out.println(arrayDeque.peekLast()); // 마지막 값을 반환, 제거하지 않음
				System.out.println(arrayDeque.size()); // 3
    }
}

 


제네릭스

다양한 타입의 객체들을 다루는 메소드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능

// 형식
public class 클래스명<T> {...}
public interface 인터페이스명<T> {...}

// 자주 사용되는 타입인자 약어
- <T> == Type
- <E> == Element
- <K> == Key
- <V> == Value
- <N> == Number
- <R> == Result

 


람다

보다 단순하게 표현하는데 유용한 개념

식별자 없이 실행 가능한 함수, 익명 함수라고도 부른다.

하지만 익명 함수는 재사용이 불가능하다, 중복해서 쓰기에는 불리하다.

[기존의 메소드 형식]
반환타입 메소드이름(매개변수 선언) { 수행 코드 블록 }

[람다식의 형식]
반환타입 메소드이름(매개변수 선언) -> { 수행 코드 블록 }
public class Main {
    public static void main(String[] args) {
        ArrayList<String> strList = new ArrayList<>(Arrays.asList("korea", "japan", "china", "france", "england"));
        Stream<String> stream = strList.stream();
        stream.map(str -> str.toUpperCase()).forEach(System.out::println);
    }
}

::(이중 콜론 연산자) 매개변수를 중복해서 사용하고 싶지 않을 때 사용

public class Main {
    public static void main(String[] args) {
        List<String> cities = Arrays.asList("서울", "부산", "속초", "수원", "대구");
        cities.forEach(System.out::println);
        // cities.forEach(x -> System.out.println(x)); 와 똑같은 의미
    }
}

 


스트림 (Stream)

스트림은 람다를 활용할 수 있는 기술 중 하나

데이터의 흐름

컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자

스트림을 활용해서 필터링, 데이터 변경, 다른 타입이나 자료구조로 변환 등을 할 수 있다.

데이터 소스를 변경하지 않는다.

작업을 내부적으로 반복 처리한다.

컬렉션의 요소를 모두 읽고 나면 닫혀서 재사용이 불가능하기 때문에 필요할 경우 재생성 해야한다.

 

생성

Stream<T> Collection.stream() 을 이용하여 해당하는 컬렉션을 기반으로하는 스트림을 생성

 

중간 연산

데이터의 형변환 혹은 필터링, 정렬 등 스트림에 대한 가공

map(변환) / sorted(정렬) / skip(스트림 자르기) / limit(스트림 자르기) 등

 

최종 연산

스트림의 요소를 소모해서 결과를 반환하는 단계

최종 연산 이후에는 스트림이 닫히게 되고 더 이상 사용할 수 없다.

최종 연산의 결과값은 단일 값일 수도 있으며 배열 혹은 컬렉션일 수도 있다.

 

구조

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(); // 문자열 리스트 형식
        list.add("서울");
        list.add("부산");
        list.add("속초");
        list.add("서울");

        System.out.println(list); // [서울, 부산, 속초, 서울]
        List<String> result = list.stream() // 스트림 생성
                                  .limit(2) //중간 연산
                                  .collect(Collectors.toList()); // 최종 연산
        System.out.println(result); // [서울, 부산]

        System.out.println("list -> transformation -> set");
        Set<String> set = list.stream()
                              .filter("서울"::equals)
                              .collect(Collectors.toSet());
        set.forEach(System.out::println);
    }
}

// Array를 Stream으로 변환
public class Main {
    public static void main(String[] args) {
        String[] arr = {"엑셀보다 쉬운 SQL", "웹개발 종합반",
                "알고보면 알기쉬운 알고리즘", "웹개발의 봄,Spring"};
        Stream<String> stringStream = Arrays.stream(arr);
        stringStream.forEach(className -> System.out.println("수업명 : " + className));
        System.out.println();
    }
}

 

Stream.collect()

데이터의 중간 처리 후 마지막에 원하는 형태로 변환해 주는 역할을 한다.

Stream 요소들을 List, Set, Map 자료형으로 변환 / 결합 / 통계(최소, 최댓값 등등) / 그룹화와 분할 등의 기능을 제공

 

Collectors.toList()

모든 Stream의 요소를 List 인스턴스로 수집

Collectors.toMap()

모든 Stream의 요소를 Map 인스턴스로 수집

Collectors.toSet()

모든 Stream의 요소를 Set 인스턴스로 수집

 


네트워킹 - Retrofit, OpenAPI

Retrofit 라이브러리를 활용하여 API 호출하기

TCP 와 UDP