변수
데이터를 저장하기 위한 메모리 공간의 이름이며, 안의 값을 변경할수도 있다.
변수를 선언할 때는 [접근제어자] [변수의 자료형] [변수의 이름] 형식을 사용한다.
변수를 선언할 때, 값도 같이 선언하려면 [변수의 자료형] [변수의 이름] = [값] 형식을 사용한다.
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로 메소드를 만들 때 지켜야 하는 기본 약속
- 동사로 시작해야한다.
- 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를 이용하여 사용할 수 있다.
상속의 특징으로는
- 부모 클래스로에서 정의된 필드와 메소드를 물려 받는다.
- 새로운 필드와 메소드를 추가 가능.
- 부모 클래스에서 물려받은 메소드를 수정 가능.
- 오직 하나의 클래스만을 상속받을 수 있다.
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;
}
// 오버로딩의 조건에 부합하는 예제입니다.
오버라이딩
부모 클래스로부터 상속받은 메소드의 내용을 변경하는 것.
조건
- 부모 클래스의 메소드와 이름이 같아야 합니다.
- 부모 클래스의 메소드와 매개변수가 같아야 합니다.
- 부모 클래스의 메소드와 반환타입이 같아야 합니다.
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에 정의된 행동만 할 수 있다.
추상클래스와 인터페이스의 비교
- 인터페이스
- 구현하려는 객체의 동작의 명세
- 다중 상속 가능
- implements를 이용하여 구현
- 메소드 시그니처(이름, 파라미터, 리턴 타입)에 대한 선언만 가능
- 추상클래스
- 클래스를 상속받아 이용 및 확장을 위함
- 다중 상속 불가능 , 단일 상속
- extends를 이용하여 구현
- 추상메소드에 대한 구현 가능
객체지향 퀴즈
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)에 들어오는 값에 따라서 ArithmeticException과 ArrayIndexOutOfBoundsException이 발생할 수 있습니다.
- throws 키워드를 통해서 divide() 함수에서 발생할 수 있는 exception의 종류가 무엇인지 알게 해주세요.
- 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);
컬렉션
다수의 데이터를 다루기 위한 자료구조를 표현하고 사용하는 클래스의 집합
- List : 순서가 있는 데이터의 집합. 데이터의 중복을 허용. → ArrayList, LinkedList, Stack 등
- Set : 순서를 유지하지 않는 데이터의 집합. 데이터의 중복을 허용하지 않는다. → HashSet, TreeSet 등
- Map : 키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합. 순서는 유지되지 않으며 키는 중복을 허용되지 않고 값은 중복을 허용. → HashMap, TreeMap 등
- Stack : 마지막에 넣은 데이터를 먼저 꺼내는 자료구조. LIFO(Last In First Out) → Stack, ArrayDeque 등
- 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
'개발일지 > 일간회고 (TIL)' 카테고리의 다른 글
자바 컬렉션, 제네릭스, 람다, 스트림, 네트워킹 / TIL (22-11-21) (0) | 2022.11.21 |
---|---|
자바 강의 / TIL(22-11-18) (0) | 2022.11.18 |
조졌다! JAVA 문제 풀이 / TIL (22-11-16) (0) | 2022.11.16 |
알고리즘 4주차 정리 - TIL (22-11-15) (0) | 2022.11.15 |
알고리즘 2주차, 3주차 내용 정리 / TIL (22-11-14) (0) | 2022.11.14 |