이 모든 클래스를 알 필요는 없지만 자주 쓰는 것 위주로 정리하겠다.
동등 비교를 할 때 쓰는 equals는 object의 소속이다.
자바의 모든 클래스는 암묵적으로 java.lang.Object를 상속 받는다.
그래서 모든 객체는 equals, hashCode, toString을 메소드로 가진다.
public class Member {
public String id;
public Member(String id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if(o instanceof Member target){
if(id.equals(target.id)){
return true;
}
}
return false;
}
}
이렇게 Member에서 equals를 재정의 해서 Member끼리 동등비교를 할 수 있다.
그런데 equals는 값을 비교하는 것이고 객체가 진짜 동등한 객체인지 확인 할 때는 equals와 hashCode를 같이 쓴다.
if(s1.hashCode() == s2.hashCode()){
if(s1.equals(s2)){
System.out.println("동등 객체");
}
이렇게 hashCode와 equals가 모두 같아야 동등하다고 보는 것이다.
public class Student {
private int no;
private String name;
public Student(int no, String name){
this.no = no;
this.name = name;
}
public int getNo(){ return no;}
public String getName(){return name;}
@Override
public int hashCode(){
int hashCode = no + name.hashCode();
return hashCode;
}
@Override
public boolean equals(Object obj){
if(obj instanceof Student target){
if(no == target.getNo() && name.equals(target.getName())){
return true;
}
}
return false;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
이렇게 hashCode, equals, toString을 모두 재정의 해주면 다음과 같이 응용이 가능하다.
package ch12.sec03.exam02;
import java.util.HashSet;
public class HashSetExample {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
Student s1 = new Student(1,"홍길동");
hashSet.add(s1);
Student s2 = new Student(1,"홍길동");
hashSet.add(s2);
System.out.println(hashSet.toString());
}
}
HashSet의 동등비교는 equals와 hashCode로 하기 때문에 동등한 객체라 중복 저장하지 않고
출력할 때도 toString()을 통해 미리 정의된 출력 포멧으로 볼 수 있다.
주로 키보드 입출력에서 활용된다.
public class InExample {
public static void main(String[] args) throws Exception{
int speed = 0;
int keyCode = 0;
while (true){
if(keyCode != 13 && keyCode != 10){
if(keyCode == 49){
speed++;
}else if(keyCode == 50){
speed--;
}else if(keyCode == 51){
break;
}
System.out.println("-------------------");
System.out.println("1. 증속 | 2. 감속 | 3. 중지");
System.out.println("-------------------");
System.out.println("현재 속도 : "+speed);
System.out.println("선택 : ");
}
keyCode = System.in.read();
}
System.out.println("프로그램 종료");
}
}
이렇게 System.in을 통해 읽을 수 있다.
System.exit(); 는 프로그램을 종료할 때 쓴다.
프로그램을 개발하다 보면 특히 네트워크쪽에서는 byte를 주로 쓰게 되는데, 이를 String으로 다뤄야 하는 경우가 많다.
String str = new String(byte[] bytes);
이런 식으로!
String data = "자바";
byte[] arr1 = data.getBytes(); // 아무것도 적지 않을 시 UTF-8
System.out.println(Arrays.toString(arr1));
String str1 = new String(arr1);
System.out.println(str1);
byte[] arr2 = data.getBytes("EUC-KR"); // EUC-KR 하면 2바이트로 변환됨
System.out.println(Arrays.toString(arr2));
String str2 = new String(arr2);
System.out.println(str2);
String.getBytes()
로 변환하고, 생성자를 통해 다시 String으로 만들 수 있다.
StringBuilder는 효율적인 문자열 관리를 위해 만들어진 클래스이다.
StringBuilder를 쓰는 것이 메모리 측면에서 더욱 효율적이다.
리턴타입 | 메소드(매개변수) | 설명 |
---|---|---|
StringBuilder | append(문자열) | 문자열을 끝에 추가 |
StringBuilder | insert(위치, 문자열) | 문자열을 위치에 추가 |
StringBuilder | delete(시작위치, 끝위치) | 문자열 일부를 삭제 |
StringBuilder | replace(시작위치, 끝위치, 문자열) | 문자열 일부를 대체 |
String | toString() | 완성된 문자열 리턴 |
String data = new StringBuilder()
.append("DEF")
.insert(0,"ABC")
.delete(3,4)
.toString();
System.out.println(data);
이렇게 활용할 수 있다.
그리고 특정 구분자로 연결되어 있을 때 분리해 배열로 만들어 주는 split()과 비슷한 StringTokenizer도 있다.
이 둘의 차이점은 split()은 비교의 기준이 정규식이고 StringTokenizer는 문자열을 기준으로 자른다는 것이다.
import java.util.StringTokenizer;
public class StringTokenizerExample {
public static void main(String[] args) {
String data1 = "홍딜동&이수홍,박연수";
String[] arr = data1.split("&|,");
for(String token : arr){
System.out.println(token);
}
String data2 = "홍길동/바언수/샌즈";
StringTokenizer st = new StringTokenizer(data2,"/");
while (st.hasMoreTokens()){
String token = st.nextToken();
System.out.println(token);
}
}
}
이런 차이점이 있다.
자바의 기본 타입(int, short, int, long, float, double, boolean)은 모두 어떤 값을 갖는 객체를 생성할 수 있다.
문자열로된 숫자를 정수로 바꿀 때 Integer를 쓴 것 처럼, 기본 타입들은 모두 포장 클래스를 가지고 있다.
기본 타입 | 포장 클래스 |
---|---|
byte | Byte |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
이걸 쓰는 이유는 컬렉션 프레임워크에서 기본 타입의 객체의 값은 저장할 수 없고 객체만 저장할 수 있기 때문에 객체 형태의 기본 타입이 존재하는 것이다.
이렇게 객체로 만들면 당연하게도 값 비교 ==, !=는 쓸 수 없다. 객체이므로 equals를 통해 비교해야 한다.
Integer obj = 100;
System.out.println("value: "+obj.intValue());
int value = obj;
System.out.println("value: "+value);
int result = obj + 100;
System.out.println("result: "+result);
Math 클래스는 각종 수학 연산을 해준다.
제곱, 루트, 절대값을 주로 쓴다.
Math.random();
Math.sqrt();
Math.pow();
Math.abs();
이런 것들이 있다.
자바는 컴퓨터의 날짜 및 시각을 읽을 수 있도록 java.util 패키지에서 Date와 Calendar 클래스를 제공하다 있다.
클래스 | 설명 |
---|---|
Date | 날짜 정보를 전달하기 위해 사용 |
Calendar | 다양한 시간대별로 날짜와 시간을 얻을 때 사용 |
LocalDateTime | 날짜와 시간을 조작할 때 사용 |
public class DateExample {
public static void main(String[] args) {
Date now = new Date();
String strNow1 = now.toString();
System.out.println(strNow1);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String strNow2 = sdf.format(now);
System.out.println(strNow2);
}
}
이런 식으로 활용할 수 있다.
public class CalendarExample {
public static void main(String[] args) {
Calendar now = Calendar.getInstance();
int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH)+1;
int day = now.get(Calendar.DAY_OF_MONTH);
int week = now.get(Calendar.DAY_OF_WEEK);
String strWeek = null;
switch(week){
case Calendar.MONDAY -> strWeek = "월";
case Calendar.TUESDAY -> strWeek = "화";
case Calendar.WEDNESDAY -> strWeek = "수";
case Calendar.THURSDAY -> strWeek = "목";
case Calendar.FRIDAY -> strWeek = "금";
case Calendar.SATURDAY -> strWeek = "토";
default -> strWeek = "일";
}
int amPm = now.get(Calendar.AM_PM);
String strAmPm = null;
if(amPm == Calendar.AM){
strAmPm = "오전";
}else{
strAmPm = "오후";
}
int hour = now.get(Calendar.HOUR);
int minute = now.get(Calendar.MINUTE);
int second = now.get(Calendar.SECOND);
System.out.print(year+"년 ");
System.out.println(month + "월 ");
System.out.println(day + "일 ");
System.out.print(strWeek+"요일 ");
System.out.println(strAmPm+" ");
System.out.print(hour+"시 ");
System.out.print(minute+"분 ");
System.out.println(second+"초");
}
}
캘린더 클래스로 이런 것도 된다!
정규 표현식은 자바 뿐만 아니라 컴퓨터밥좀 먹은 사람이라면 누구나 알고 있어야 하는 문자 검열식 이다. 정규표현식은 정확히 말하자면 무수한 가능성을 지닌 사용자의 입력을 정해져 있는 형식으로 가공하기 위한 식이다. 전화번호, 이메일 등 형식에 맞는지 확인하기 위한 규칙인 것이다.
정규 표현식 연습 사이트
문법 자체는 여기서 연습하면 된다.
Patten.matches(resExp, data)
이렇게 쓰면 된다~!
자바는 클래스와 인터페이스의 메타 정보를 Class 객체로 관리한다. 여기서 메타 정보란 패키지 정보, 타입 정보, 멤버(생서자, 필드, 메소드) 정보 등을 말한다. 이러한 메타 정보를 프로그램에서 읽고 수정하는 행위를 리플렉션(relection)이라고 한다.
1) Class claszz = 클래스이름.class;
2) Class clazz = Class.forName("패키지 ... 클래스이름");
3) Class clazz = 객체참조변수.getClass();
이렇게 쓰면 된다.
1과 2는 클래스 이름만 가지고 Class 객체를 얻는 방법이고, 3은 객체로부터 Class 객체를 얻는 방법이다.
package ch12.sec11.exam01;
public class Car {
}
이렇게 객체를 하나 만들고
package ch12.sec11.exam01;
public class GetClassExample {
public static void main(String[] args) throws Exception {
Class clazz = Car.class;
System.out.println("패키지 : "+clazz.getPackage().getName());
System.out.println("클래스 간단 이름 : "+clazz.getSimpleName());
System.out.println("클래스 전체 이름 : "+clazz.getName());
}
}
리플렉션으로 알아볼 수 있다.
코드중 @로 시작하는 요소를 어노테이션이라고 한다.
어노테이션의 용도는 다음과 같다.
대표적인 예로 @Override가 있다.
어노테이션은 자바 프로그램을 개발할 때 필수 요소가 되었다. 특히 웹 개발에 많이 사용되는 Spring Framework 또는 Spring Boot는 다양한 종류의 어노테이션을 활용해 웹 어플리케이션을 설정 하는데 사용된다.
어노테이션도 하나의 타입이므로 어노테이션을 사용하기 위해서는 먼저 정의 부터 해야 한다.
public @interface AnnotationName {
}
이렇게 정의한 어노테이션은 코드에서 다음과 같이 사용된다.
@AnnotationName
어노테이션은 속성을 가질 수 있따. 속성은 타입과 이름으로 구성되며 이름 뒤에 괄호를 붙인다.
속성의 기본값은 default 키워드로 지정할 수 있다. 예를 들어 String 타입 prop1과 int 타입의 prop2 속성은 다음과 같이 선언할 수 있다.
public @interface AnnotationName{
String prop1();
int prop2() defaultt 1;
}
이렇게 정의한 어노테이션은 코드에서 다음과 같이 사용할 수 있다.
prop1은 기본값이 없기 때문에 반드시 값을 기술해야 하고, prop2는 기본값이 있기 때문에 생략 가능하다.
@AnnotationName(prop1="값");
@AnnotationName(prop1= "값", prop2=3);
어노테이션은 기본 속성인 value를 다음과 같이 가질 수 있다.
public @interface AnnotationName{
String value();
int prop2() defalut 1;
}
value 속성을 가진 어노테이션 코드에서 사용할 때는 다음과 같이 값만 기술할 수 있다.
이 값은 value 속성에 자동으로 대입된다.
@AnnotationName("값");
하지만 value 속성과 다른 속성의 값을 동시에 주고 싶다면 value 속성 이름을 반드시 언급해야 한다.
@AnnotationName(value="값", prop2=3);
자바에서 어노테이션은 설정 정보라고 했다. 그렇다면 어떤 대상에 설정 정보를 적용할 것인지, 즉 클래스에 적용할 것인지, 메소드에 적용할 것인지를 명시해야 한다. 적용할 수 있는 대상의 종류는 ElementType 열거 상수로 정의되어 있다.
ElementType 열거 상수 | 적용 요소 |
---|---|
TYPE | 클래스,인터페이스,열거 타입 |
ANNOTATION_TYPE | 어노테이션 |
FIELD | 필드 |
CONSTRUCTOR | 생성자 |
METHOD | 메소드 |
LOCAL_VARIABLE | 로컬 변수 |
PACKAGE | 패키지 |
적용 대상을 지정할 때에는 @Target 어노테이션을 사용한다. @Target의 기본 속성인 value는 ElementType 배열을 값으로 가진다. 이것은 적용 대상을 복수 개로 지정하기 위해서이다. 예를 들어 다음과 같이 적용 대상을 지정했다고 가정해보자.
@Target( {ElementType.TYPE, ElementType.FILED, ElementType.METHOD} )
public @interface AnnotationName{
}
이 어노테이션은 다음과 같이 클래스, 필드, 메소드에 적용할 수 있고 생성자는 적용할 수 없다.
@AnnotationName
public class ClassName{
@AnnotationName
private String fieldName;
//@AnnotationName <- @Target에 CONSTRUCT가 없으므로 생성자에는 적용 못함
public ClassName() { }
@AnnotationName
public void methodName() { }
}
어노테이션을 정의할 떄 한 가지 더 추가해야 할 내용은 @AnnotationName을 언제까지 유지할 것인지를 지정하는 것이다. 어노테이션 유지 정책은 RetentionPolicy 열거 상수로 다음과 같이 정의 되어 있다.
RetentionPolicy 열거 상수 | 어노테이션 적용 시점 | 어노테이션 제거 시점 |
---|---|---|
SOURCE | 컴파일할 때 적용 | 컴파일 |
CLASS | 메모리로 로딩할 때 적용 | 메모리로 로딩된 후에 제거됨 |
RUNTIME | 실행할 때 적용 | 계속 유지됨 |
유지 정책을 지정할 때에는 @Retention 어노테이션을 사용한다. @Retention의 기본 속성인 value는 RetentionPolicy 열거 상수 값을 가진다. 다음은 실행 시에도 어노테이션을 설정 정보를 이용할 수 있도록 유지 정책을 RUNTIME으로 지정한 예이다.
@Target( {ElementType.TYPE, ElementType.FILED, ElementType.METHOD} )
@Retention( RetentionPolicy.RUNTIME )
public @interface AnnotationName{
}
4번
3번
4번
equals, hashCode
@Override
public boolean equals(Object obj) {
if(obj instanceof Student) {
Student student = (Student) obj;
if(studentNum.equals(student.getStudentNum())) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return studentNum.hashCode();
}
@Override
public String toString() {
return id + ": " + name;
}
3번
long start = System.nanoTime() 으로 처음에 측정하고
long end = System.nanoTime() 로 마지막줄에 측정해서
end - start 를 출력하면 됩니다.