ArrayList와 람다식을 이용한 코드
[p1]
package p1;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class App {
public static void main(String[] args) {
// 람다식, stream API
List<StudentDto> studentDtoList = new ArrayList<>();
studentDtoList.add(new StudentDto("민수", 30, "비밀 정보"));
studentDtoList.add(new StudentDto("철수", 28, "비밀 정보"));
studentDtoList.add(new StudentDto("영수", 32, "비밀 정보"));
// 어쩌다 보니 위에 있는 목록이 생겼다.
// List<StudentDto> 를 List<ResponseStudentDto> 로 변환
// List<ResponseStudentDto> responseStudentDtoList = new ArrayList<>();
// for(StudentDto studentDto : studentDtoList) {
// ResponseStudentDto responseStudentDto = new ResponseStudentDto();
// responseStudentDto.setName(studentDto.getName());
// responseStudentDto.setAge(studentDto.getAge());
// responseStudentDtoList.add(responseStudentDto);
// } 옛날에 많이 쓰던 코드
// 위의 코드를 stream API를 이용해서 작성해보자.
List<ResponseStudentDto> responseStudentDtoList = studentDtoList.stream()
.map(ResponseStudentDto::from)
.toList();
// 1부터 100까지 구해보자.
IntStream.rangeClosed(1, 100).sum();
// 1부터 100까지 구하는데 3의 배수랑 5의 배수는 빼고 구하자
int sum = IntStream.rangeClosed(1, 100)
.filter(e -> e%3!=0)
.filter(e -> e%5!=0)
.sum();
}
}
class ResponseStudentDto {
private String name;
private int age;
public ResponseStudentDto() {}
public ResponseStudentDto(String name, int age) {
this.name = name;
this.age = age;
}
public ResponseStudentDto(StudentDto studentDto) {
// this.name = studentDto.getName(); // 아래코드랑 같은거긴 함
this.setName(studentDto.getName()); // 이건 setter쓰는거
this.setAge(studentDto.getAge());
}
public static ResponseStudentDto from(StudentDto studentDto) {
ResponseStudentDto responseStudentDto = new ResponseStudentDto();
responseStudentDto.setName(studentDto.getName());
responseStudentDto.setAge(studentDto.getAge());
return responseStudentDto;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class StudentDto {
private String name;
private int age;
private String secret;
public StudentDto(String name, int age, String secret) {
this.name = name;
this.age = age;
this.secret = secret;
}
// getter, setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSecret() {
return secret;
}
public void setSectet(String secret) {
this.secret = secret;
}
}
[StudentManagerV3] - 배열만 ArrayList로 바꾼 것.
[StudentManagerRepository]
package com.joongang.stm.repository;
import java.util.ArrayList;
import java.util.List;
import com.joongang.stm.dto.StudentDto;
// 이 부분이 제일 중요함. 인터페이스나 그런것보단 클래스 문법, ArrayList, 기초 등이 중요
public class StudentManagerRepository {
private List<StudentDto> studentList = new ArrayList<>();
// 앞으로 반복할 때는 배열이 아니라 ArrayList로. 다형성
public void save(StudentDto studentDto) {
studentList.add(studentDto);
}
public List<StudentDto> findAll() {
return studentList;
}
public int count() {
return studentList.size(); // size가 count를 대체 함
}
}
[StudentManagerService]
package com.joongang.stm.service;
import java.util.List;
import com.joongang.stm.dto.StudentDto;
import com.joongang.stm.repository.StudentManagerRepository;
import com.joongang.stm.util.IoUtil;
public class StudentManagerService {
private StudentManagerRepository repository = new StudentManagerRepository();
public void register() {
IoUtil.print("======= 학생 정보 등록 =======");
String name = IoUtil.input("이름 > ");
int age = Integer.parseInt(IoUtil.input("나이 > "));
int score = Integer.parseInt(IoUtil.input("점수 > "));
StudentDto studentDto = new StudentDto(name, age, score);
repository.save(studentDto);
IoUtil.print("============================");
}
public void list() {
IoUtil.print("======= 학생 정보 목록 =======");
// 배열을 Lits로 바꾼것. 배열은 크기가 고정되어야 할 때만 씀.
List<StudentDto> studentList = repository.findAll();
int count = repository.count();
for(StudentDto studentDto : studentList) {
String text = "";
text += "이름: " + studentDto.getName();
text += ", 나이: " + studentDto.getAge();
text += ", 점수: " + studentDto.getScore();
IoUtil.print(text);
}
IoUtil.print("총 " + count + "명이 존재합니다.");
IoUtil.print("============================");
}
}
이 두개만 바뀌었다.
[예외 처리]
결과물의 품질을 높이는 것. 지금까지 한것들은 코드의 품질(가독성)을 높이는 것이지, 결과물의 품질을 높이진 않는다. 그래서 결과물의 품질을 높이기 위해서는 모든 예외상황을 예측하고 처리 해야 한다. 사용자가 어떤 일들을 할지 모르기 때문에. 예외(Exception)란? runtime 오류를 말하는 것. 예외처리이 종류로는 checked와, unchecked가 있다. checked는 Java에만 있으며, 다른 언어에는 대부분 없다.
[try-catch문]
[p2]
package p2;
public class App {
public static void main(String[] args) {
// 예외처리
System.out.println("코드1");
System.out.println("코드2");
// 지금은 테스트를 위해 적어뒀지만 우리는 원래 이 input의 값을 모른다.
// 만약 여기 input 값에 사용자가 0을 넣는다면 컴파일 에러는 나지 않지만
// runtime 에러를 발생시켜서 위의 2줄만 실행되고 프로그램이 아예 뻗어버린다.
// 이러한 예외를 잘 처리한 프로그램이 안정성이 높은 프로그램이다.
// 1순위의 목적으로, 우리는 프로그램이 절대 뻗는 일이 생기지 않게 해야한다.
// exception이 발생하더라도 뻗으면 안되기 때문에 try catch문을 쓴다
try{
int input = 1;
System.out.println(10/input);
// try문에 있는 코드들이 실행되다가 예외 발생시 catch문으로 바로 넘어 간다.
System.out.println("코드3");
String name = "민수";
// 여가에 null값이 들어오는 경우도 있을 수 있기 때문에 try-catch를 해준다.
// 만일 try문을 쓰지 않으면 컴파일 에러는 아니지만 조심하라는 경고가 뜨며,
// 실행하면 역시나 문제가 생긴다. (runtime error - NullPointException)
// String이 null이라는 뜻 참조주소가 없다는 뜻이고, 참조주소가 없기 때문에
// 접근연산자(.)로 접근 자체를 할 수가 없다. - 에러코드로도 알려준다.
// Exception in thread "main" java.lang.NullPointerException:
// Cannot invoke "String.equals(Object)" because "name" is null
// 에러코드를 볼 줄 알아야 함.
System.out.println(name.equals("야호"));
System.out.println("야호!!");
System.out.println("야호!!");
System.out.println("야호!!");
} catch (NullPointerException e) {
// e는 변수명이라서 아무거나 해도 되지만 보통 e를 쓴다. 그리고 여기에 정확한
// Exception 이름을 넣어주면 그때만 catch해서 안의 실행문 실행.
// 예측 가능 - NullPointerException
// ... NullPointerException이 발생했을때 대응해야 할 코드
} catch (ArithmeticException e) {
// 예측 가능 - ArithmeticException
// ... ArithmeticException이 발생했을때 대응해야 할 코드
} catch (Exception e) {
// 예측 불가능... (혹은 대충 퉁쳐서 처리)
// 로그를 찍어서 어떤 때에 어떤 exception이 발생했는지..
e.printStackTrace();
// 예외 처리 정보 콘솔 출력: 어떤 exception이 발생했는지 알려줌. (많이 씀)
} finally {
// 여기는 어떠한 경우에도 실행 됨.
}
System.out.println("코드4");
System.out.println("코드5");
}
}
[p3]
package p3;
public class App {
public static void main(String[] args) {
// try-catch 구문의 finally
test(10);
}
public static void test(int value) {
try {
if(value < 0) {
return;
}
int result = 10/value;
System.out.println(result);
} catch (Exception e) {
System.out.println("예외 발생");
} finally {
// 여기는 어떠한 경우에도 실행 됨
System.out.println("여기는 무조건 실행됨!");
}
}
}
[checked, unchecked 예외]
[p4]
package p4;
public class App {
public static void main(String[] args) {
// throw
"ffff".charAt(0); // 이것도 마우스오버 해보면 마지막에 throw 있음
SumCalculator sumCalculator = new SumCalculator();
try {
int result = sumCalculator.sum(1, 10);
System.out.println(result);
} catch (StartNotBetterThenEndException exception) {
// 귀찮으면 catch (Exception e)로 함.
// Exception은 최상위 클래스라 모든 예외를 받을 수 있음
// ...
exception.printStackTrace();
// 로그 찍는것. 무슨 예외가 발생하는지 모르기 때문에 일단 찍는것이 좋다.
}
System.out.println("프로그램 종료.");
}
}
class SumCalculator {
public int sum(int start, int end) throws StartNotBetterThenEndException {
if(start > end) {
// 이러면 아예 말이 안되니까 return을 해주면 안됨. - 서비스 거부
throw new StartNotBiggEndException("start가 end보다 클 수 없다.");
// 여기에는 exception을 상속 받은것(직접만들어도ok)만 생성 시킬 수 있음.
// 그런건 보통 이름의 마지막에 exception이 들어감.
}
int sum = 0;
for(int i = start; i <= end; i++) {
sum += i;
}
return sum;
}
}
// exception의 이름을 지정 해주고 싶으면 Exception을 직접 만들 수 있다.
// 그냥 Exception을 분리하는거라 내부에 들어가는게 거의 없음
class StartNotBiggerThenEndException extends RuntimeException {
public StartNotBetterThenEndException(String message) {
super(message);
}
}
[p5]
package p5;
import java.io.File;
import java.io.FileOutputStream;
public class App {
public static void main(String[] args) {
// try문을 배운 이유 = 적어도 문법 오류는 막아야 되기에...
try {
Thread.sleep(1000);
} catch (Exception e) { // 모든 예외를 받을 수 있기 때문에 간단히 쓸때 좋다.
e.printStackTrace(); // 어떤 예외가 발생하는지 알기 위해서 로그 기록 필요.
}
File file1 = new File("C:\\temp\\aaa.jpg");
try {
FileOutputStream fos = new FileOutputStream(file1);
fos.write(10);
// 할거 다 하고...
fos.close(); // 자원 반납 (해제)
} catch (Exception e) {
// API중에 checked API가 꽤나 많고, 그 API마다 Exception의 종류가 다 달라서
// catch를 사용하는 API 수 만큼 짜야하는데, 그걸 하나하나 다 쓸 수 없으니까
// 정확한 Exception 이름을 넣는게 아니라 퉁치는거.
// 애초에 이런 API들은 예외가 거의 나지 않는다.
e.printStackTrace();
} finally {
}
// 예전에 했던 방법. 얼마나 힘들었는지 보여주신다고... 그래서 새로운 문법이 나옴
File file2 = new File("C:\\temp\\aaa.jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file2);
fos.write(10);
// 할거 다 하고...
fos.close(); // 자원 반납
} catch (Exception e) {
e.printStackTrace();
} finally {
if(fos != null) {
try {
fos.close(); // 자원 반납 (해제)
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
[p6]
package p6;
import java.io.File;
import java.io.FileOutputStream;
public class App {
public static void main(String[] args) {
// try with resources
File file = new File("C:\\aaa\\aaa.jpg");
// try 다음 괄호 안에 있는게 resources임
try(FileOutputStream fos = new FileOutputStream(file)) {
fos.write(10);
//... 할거 하고 알아서 close(); 해줌
} catch (Exception e) {
e.printStackTrace();
}
}
}
[p7]
package p7;
import java.util.Scanner;
public class App {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 나이 입력
while (true) {
try {
System.out.print("나이 입력 > ");
int age = Integer.parseInt(scanner.nextLine());
System.out.println("입력 받은 값: " + age);
break;
// 예외가 발생하면 break를 만나지 않고 catch로 가고,
// 예외가 없으면 break를 만나서 반복문 밖으로..
} catch (Exception e) {
System.out.println("숫자만 입력 할 수 있습니다.");
System.out.println("다시 입력 해주세요.");
}
}
System.out.println("프로그램 종료");
scanner.close();
}
}
[문법적 키워드 복습, 정리]
문법적 키워드: 기본타입 8가지(int, char..), null, package, true, try...
클래스 이름: App, System, Math, main... 등등
int는 문법적 키워드라 변수명으로 쓸 수 없지만, String는 문법적 키워드가 아니라 변수명으로 쓸 수 있다. 즉, 키워드인지 아닌지 알아보기 위해서는 변수 명으로 써보기.
까먹었을때 보기 위해 정리하는 vscode 다크 테마 기준 단어들 색의 의미