| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- 메가바이트스쿨
- MVC
- Java
- github
- tomcat
- 내일배움카드
- 게시판 리뷰 만들기
- Sts
- Interface
- 국비지원교육
- side project
- 클래스 상속
- array
- #javaStudy
- GIT
- AWS
- Spring
- MegabyteSchool
- 개발자취업부트캠프
- spring boot
- 패스트캠퍼스
- #패스트캠퍼스 #국비지원교육 #메가바이트스쿨 #MegabyteSchool #개발자취업부트캠프 #내일배움카드
- 클래스 class
- crud
- group study
- View
- Algorism study
- MVC 패턴
- 게시판 만들기
- Entity
- Today
- Total
tuter77
2023.01.17 TIL 본문
<목차>
<공부계획표>
13:00 ~ 14:00 그룹스터디 피드백
14:00 ~ 18:00 (3시간) 온라인강의 시청.
14:00 ~ 15:00 그룹스터디 수정작업
15:00 ~ 16:00 그룹스터디 수정작업
17:00 ~ 18:00 그룹스터디 수정작업
18:00 ~ 19:00 식사시간
19:00 ~ 22:00 (3시간) 온라인강의 시청.
19:00 ~ 20:00 java 온라인 강의시청
20:00 ~ 21:00 java 온라인 강의시청
21:00 ~ 22:00 java 온라인 강의시청
22:00 ~ 23:00 블로그 작성 및 프로그래머스 문제 풀기.
<Java>
● 자바의 유용한 클래스
▷ Object 클래스
- 모든 클래스의 최상위 클래스로 import 하지 않아도 자동으로 import 된다.
- import.java.lang.*;- 많이 사용하는 기본 클래스들이 속한 패키지이다.(String, Integer, System...)- 모든 클래스는 Object를 상속받고 Object 클래스의 메서드 중 일부는 재정의해서 사용할 수 있다.- 컴파일러가 extends Object를 추가한다.(class Student => class Student extends Object)
▷Object 클래스의 toString 메서드 오버라이딩
예제)
package ch01;
class Book{
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
//오버라이딩.
@Override
public String toString() {
return title + "," +author;
}
}
public class BookTest {
public static void main(String[] args) {
Book book = new Book("데미안", "헤르만 헤세");
System.out.println(book); //ch01.Book@5ca881b5 메모리에 위치의 16진수 가상값이다.
String str = new String("test");
System.out.println(str); // 이경우엔 문자열의 값이 나온다.
// 이유는 str이 str.toString()으로 오버라이딩 되었기 때문
}
}
예제 코드에서 알 수 있듯이 toString은 문자열의 경우 자동으로 오버라이딩 된다.
때문에 특정 클래스의 생성자로 인스턴스를 만들 경우 해당 인스턴스의 값을 인스턴스명으로 불러오려면, toString을 재정의 해야한다.
위의 클래스 Book에서 toString을 오버라이딩하여 리턴값을 변경해주면 book이 JVM에서 제공한 가상 메모리 값이 아닌, 오버라이딩한 toString의 반환값을 출력할 수 있게된다.
▷Object 클래스의 메서드 활용.
- equals() 메서드는 두 인스턴스의 주소 값을 비교하여 true/false를 반환한다.
- 재정의하여 두 인스턴스가 논리적으로 동일함의 여부를 구현한다.- 인스턴스가 다르더라도 논리적으로 동일한 경우 true를 반환하도록 재정의 할 수있다.(같은 학번, 같은 사번, 같은 아이디의 회원 등)
-hashCode() 메서드는 인스턴스의 저장 주소를 반환한다.
- 힙메모리에 인스턴스가 저장되는 방식이 hash방식이며, 이는 정보를 저장 및 검색하는 자료구조이다.
- 자료의 특정 값(키 값)에 대한 저장 위치를 반환해주는 해시 함수를 사용한다.
- 때문에 equals() 메서드와 이 메서드는 페어여서 위의 메서드를 재정의하면, hashCode() 메서드의 반환값을 재정의한 equals() 의 키 값으로 설정해 주면된다.
index(저장위치) = hash(해시함수)(key(객체정보))
예제) Student 클래스
package ch02;
public class Student {
private int studentNum;
private String studentName;
public Student(int studentNum, String studentName) {
this.studentNum = studentNum;
this.studentName = studentName;
}
public String toString() {
return studentNum + "," +studentName;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Student) {
Student std = (Student)obj;
if(this.studentNum == std.studentNum) {
return true;
}else {
return false;
}
}
return false;
}
@Override
public int hashCode() {
return studentNum;
}
}
이 예시에서 toString, equals, hashCode를 재정의 했다.
이는 아래 test 클래스에서 출력했다.
package ch02;
public class EqualsTest {
public static void main(String[] args) {
Student std1 = new Student(100, "Lee");
Student std2 = new Student(100, "Lee");
System.out.println(std1 == std2); //주소는 다르지만
System.out.println(std1.equals(std2)); //논리적으로 같은 학생.
System.out.println(std1.hashCode());
System.out.println(std2.hashCode()); // 해시코드를 바꾸지 않았기 때문에 다른 주소값 출력.
System.out.println(System.identityHashCode(std1));
System.out.println(System.identityHashCode(std2)); //해시코드 오버라이딩 후 진짜 주소값 찾는 메서드.
}
}
같은 값을 입력한 두 인스턴스 std1과 std2는 주소는 다르지만 논리적으로 같은 학생이기 때문에, equals와 hashCode를 재정의했다.
기존 System.out.println(std1.equals(std2)); 코드를 실행하면 주소값만 나오지만 앞서 본 Student클래스에서 studentNum가 같으면 true를 반환하게끔 재정의했기때문에 두 인스턴스는 equals 메서드로 실행해보면 같은값이라는 true가 반환된다.
그러나 해시코드 값을 찍어보면 다른 주소값이 나오는데 Student클래스에서 hashcode값을 studentNum으로 반환하게 했기때문에 다시 찍어보면 같은 값이 출력된다.
마지막으로 진짜 주소값을 알고싶을땐, identityHashCode() 메서드로 출력하면 주소값을 확인할 수 있다.
결과창.

▷clone() 메서드
- 객체의 원본을 복제하는데 사용하는 메서드로 생성과정의 복잡한 과정을 반복하지 않고 복제할 수 있다.
- clone() 메서드를 사용하면 객체의 정보(멤버 변수 값 등)가 동일한 또 다른 인스턴스가 생성되는 것이므로, 객체 지향프로그램에서의 정보 은닉, 객체 보호의 관점에서 위배될 수 있다.
- 해당 클래스의 clone() 메서드의 사용을 허용한다는 의미로 cloneable 인터페이스를 명시해주어야한다.
public class Student implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
이 형태로 clone 메서드를 재정의하고 test 클래스에서 인스턴스를 생성해 아까 재정의된 toString으로 찍어보면,
Student copyStudent = (Student)std1.clone();
System.out.println(copyStudent);

똑같은 객체 정보를 가져올 수 있다.
만약 복제 대상의 인스턴스의 값이 변경되면, 복제된 인스턴스의 값도 그대로 변경된다.
▷ String 클래스
- String 선언하기
String str1 = new String("abc"); //힙메모리
String str2 = "abc"; //상수풀 주소만 가리킴.
- 힙 메모리에 인스턴스로 생성되는 경우와 상수 풀에 잇는 주소를 참조하는 두 가지 방법이 있다.
- 힙 메모리는 생성될 때마다 다른 주소값을 가지지만, 상수 풀의 문자열은 모두 같은 주소값을 가진다.
- 한번 생성된 String은 불변이며(선언된 스트링은 자동으로 final로 선언되어있다.), String을 연결하면 기존의 String에 연결되는 것이 아닌 새로운 문자열이 생성된다.(메모리 낭비가 발생할 수 있다.)
- 즉, concat() 메서드를 사용하여 두개의 스트링 변수를 연결하면, 새로운 변수가 생성되는 것이다.
String str = "abc";
String str2 = "def";
java = str.concat(str2);
java는 새로운 변수이다.
- 때문에 StringBuilder, StringBuffer를 활용하는 것을 권유하고 있다.
▷ StringBuilder, StringBuffer 활용하기
- 내부적으로 가변적인 char[]를 멤버변수로 갖는다.
- 문자열을 여러번 연결하거나 변경할 때 사용하면 유용하다.
- 새로운 인스턴스를 생성하지 않고 char[]를 변경한다.- StringBuffer 는 멀티 쓰레드 프로그래밍에서 동기화를 보장한다.- 단일 쓰레드 프로그램에서는 StringBuilder 사용을 권장한다.- toString() 메서드로 String을 반환한다.
예제)
public class StringTest {
public static void main(String[] args) {
String java = new String("java");
String android = new String("android");
System.out.println(System.identityHashCode(java));
java = java.concat(android);
System.out.println(System.identityHashCode(java)); //두 개의java는 다른 변수이다.
System.out.println(java);
}
}

두 java 변수의 주소값이 다르다.
public class StringBuilderTest {
public static void main(String[] args) {
String java = new String("java");
String android = new String("android");
StringBuilder buffer = new StringBuilder(java);
System.out.println(System.identityHashCode(buffer));
buffer.append(android);
System.out.println(System.identityHashCode(buffer)); //같은 메모리 주소를 갖는 것을 알 수 있다.
String test = buffer.toString(); // 스트링 인자로 사용하기 위해
System.out.println(test);
}
}

위의 예제에서 보면, concat을 활용한 변수 연결은 다른 주소값을 가진 새로운 인스턴스를 생성한다는 것을 알 수 있다.
때문에 StringBuilder를 사용하면 같은 주소값을 확인할 수 있으므로, 연결되기 전후가 같은 변수라는 것을 알 수 있다.
StringBuilder는 배열의 형태이기때문에 toString으로 스트링인자로 값을 형변환 해주어야한다.
▷ text block 사용하기(java 13 이상)
- 문자열을 """ """ 사이에 이어서 만들 수 있다. - html, json 문자열을 만드는데 유용하게 사용할 수 있다.
예제)
package ch03;
public class TextBlockTest {
public static void main(String[] args) {
String textBlocks = """
Hello,
hi,
how r u""";
System.out.println(textBlocks);
}
}

이와 같이 사용할 수 있다.
▷ Class 클래스
- 자바의 모든 클래스와 인터페이스는 컴파일 후 class 파일이 생성된다.
- Class 클래스는 컴파일 된 class 파일을 로드하여 객체를 동적 로드하고, 정보를 가져오는 메서드가 제공된다.
- Class.forName("클래스 이름") 메서드로 클래스를 동적으로 로드한다.
Class c = Class.forName("java.lang.String");
- 클래스 이름으로 직접 Class 가져오기
Class c = String.class;
- 생성된 인스턴스에서 Class 클래스 가져오기.
String s = new String();
Class c = s.getClass(); //Object 메서드.
* 동적로딩
- 컴파일 시에 데이터 타입이 binding 되는 것이 아닌, 실행(runtime) 중에 데이터 타입을 binding 하는 방법이다.
- 프로그래밍 시에는 문자열 변수로 처리했다가, 런타임시에 원하는 클래스로 로딩하여 binding 할 수 있다는 장점이 있다.
- 컴파일 시에 타입이 정해지지 않으므로 동적 로딩시 오류가 발생하면 프로그램의 심각한 장애가 발생 가능하다.
- Class의 newInstance() 메서드로 인스턴스 생성을 할 수 있다. new 키워드를 사용하지 않고도 클래스 정보를 활용하여 인스턴스를 생성할 수 있다.
▷ 클래스 정보
- reflection 프로그래밍: Class 클래스를 사용하여 클래스의 정보(생성자, 변수, 메서드)등을 알 수 있고, 인스턴스를 생성하고 메서드를 호출하는 방식의 프로그래밍이다.
- 로컬 메모리에 객체가 없는 경우, 원격 프로그래밍, 객체의 타입을 알 수 없는 경우에 사용한다.
- java.lang.reflect 패키지에 있는 클래스를 활용하여 프로그래밍 하며, 일반적으로 자료형을 알고 있는 경우엔 사용하지 않는다.
<Group Study>
● Controller 및 Service 클래스 수정
▷ 저장 및 수정.
컨트롤러 내에서 repository를 그대로 이용하는 것은 service클래스를 쓰는 의미가 없다는 것을 잊고 작업하였다가 이번에 수정하게 되었다.
또한 엔티티에서 Setter를 사용하는것은 안좋다고 하여 Setter 어노테이션을 제거했는데, 데이터값이 전달되지않아 다시 넣었다.(이 부분에서 시간을 다 잡아먹었다.)
또 시간이 많이 들었던 부분은 modify 기능이다. 해당 기능을 controller에 Setter로 구현해놓았을때는 괜찮았으나, Service로 옮기면서 수정기능이 실행되지않았다.
수정된 저장
- 컨트롤러
@GetMapping("/write") // 저장 페이지.
public String write(){
return "writer";
}
@PostMapping("/write/post") //저장 기능 구현.
public String writerPost(HealthInfo healthInfo){
homeService.write(healthInfo);
return "redirect:/home"; //prg패턴.
}
- 서비스
@Override //저장기능
public void write(HealthInfo healthInfo) {
healthRepository.save(healthInfo);
}
- 수정하지 못한 업데이트
@PostMapping("/home/modified/update/{id}")
public String postUpdate(@PathVariable Long id, HealthInfo healthInfo) {
HealthInfo healthInfoTemp = homeService.healthView(id);
healthInfoTemp.setBrand_name(healthInfo.getBrand_name());
healthInfoTemp.setLand_number(healthInfo.getLand_number());
healthInfoTemp.setRoad_number(healthInfo.getRoad_number());
healthInfoTemp.setCategory(healthInfo.getCategory());
homeService.write(healthInfo);
return "redirect:/home";
}
/* @Override //수정기능 값이 저장이 안됨.
public void modifiedUpdate(Long id) {
HealthInfo healthInfotmp = healthView(id);
healthInfotmp.setBrand_name(healthInfo.getBrand_name());
healthInfotmp.setLand_number(healthInfo.getLand_number());
healthInfotmp.setRoad_number(healthInfo.getRoad_number());
healthInfotmp.setCategory(healthInfo.getCategory());
write(healthInfotmp);
}*/
엔티티값을 setter로 사용하면 안되는데, 아직 id값을 전달하지 못해서 헤메고 있다.
▷ 페이징 기능 구현.
기존 home.html 파일을 두고 페이징 처리한 html 파일을 새로 만들었다.
그리고 서비스 클래스에서는 pageable 객체를 매개변수로 받아 데이터를 모두 찾아 페이지 타입으로 반환했다.
컨트롤러에서는 서비스의 메서드로 찾아온 값들을 페이지 타입의 변수에 저장하고, 해당 변수를 model 객체에 add해준다.
- 컨트롤러
@GetMapping("/page")
public String pageHome(Model model, Pageable pageable) {
Page<HealthInfo> healthInfoPage = homeService.healthList(pageable);
model.addAttribute("pages",healthInfoPage);
model.addAttribute("maxPage",5);
return "page";
}
- 서비스
@Override
public Page<HealthInfo> healthList(Pageable pageable){
return healthRepository.findAll(pageable);
}
- 페이지 html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>헬스장 정보 페이지</title>
</head>
<body>
<table border="1">
<thead>
<tr>
<h1>Homepage</h1>
</tr>
<button type="submit" onclick="location.href='/write'">글쓰기</button>
</thead>
<tbody>
<tr th:each = "healthInfo:${pages}">
<td th:text="${healthInfo.id}"></td>
<td>
<a th:text="${healthInfo.brand_name}"
th:href="@{/home/view(id=${healthInfo.id})}">
</a>
<a th:href="@{/home/delete(id=${healthInfo.id})}">삭제</a>
</td>
</tr>
</tbody>
</table>
<div class="page-num"
th:with="start=${(pages.number/maxPage)*maxPage + 1},
end=(${(pages.totalPages == 0) ? 1 : (start + (maxPage - 1) < pages.totalPages ? start + (maxPage - 1) : pages.totalPages)})">
<ul>
<li th:if="${start > 1}">
<a th:href="@{/page?(page=0)}" th:text="'<<'"></a></li>
</li>
<li th:if="${start > 1}">
<a th:href="@{/page?(page=${start - maxPage})}" th:text="'<'"></a>
</li>
<li th:each="page: ${#numbers.sequence(start, end)}">
<a th:href="@{/page?(page=${page-1})}" th:text="${page}"></a></li>
</li>
<li th:if="${end < pages.totalPages}">
<a th:href="@{/page?(page=${start + maxPage})}" th:text="'>'"></a>
</li>
<li th:if="${end < pages.totalPages}">
<a th:href="@{/page?(page=${pages.totalPages-1})}" th:text="'>>'"></a></li>
</li>
</ul>
</div>
</body>
</html>
html 파일에서 번호를 지정하는 계산식은 모두 긁어왔는데, 아직은 계산방법을 온전히 이해하지 못했다.
테스트 결과 페이지가 정상작동했다.
'TIL' 카테고리의 다른 글
| 2023.01.20 TIL (0) | 2023.01.20 |
|---|---|
| 2023.01.19 TIL (0) | 2023.01.19 |
| 2023.01.16 TIL (0) | 2023.01.16 |
| 2023.01.13 (0) | 2023.01.13 |
| 2023.01.12 TIL (0) | 2023.01.12 |