지구정복
[JAVA] 11/17 | Calendar클래스, Random클래스, StringTokonizer클래스, Scanner클래스, StringJoin클래스, 추상클래스, 인터페이스 본문
[JAVA] 11/17 | Calendar클래스, Random클래스, StringTokonizer클래스, Scanner클래스, StringJoin클래스, 추상클래스, 인터페이스
nooh._.jl 2020. 11. 17. 17:25ㅇCalendar 클래스
import java.util.Calendar;
public class Ex1117_CalendarEx01{
public static void main(String[] args) {
//현재 날짜 정보를 가져와서 출력하기
Calendar c = Calendar.getInstance();
System.out.println(c.getTime());
//현재 일에다가 1일을 더한 뒤 날짜정보 출력
c.add(Calendar.DATE, 1);
System.out.println(c.getTime());
//현재 달에서 6달을 뺀 다음 날짜정보 출력
c.add(Calendar.MONTH, -6);
System.out.println(c.getTime());
}
}
-달력 만들기
import java.util.Calendar;
public class Ex1117_CalendarEx02 {
public static void main(String[] args) {
int year = 2020;
int month = 11;
int startDayOfWeek = 0;
int endDay = 0;
Calendar sDay = Calendar.getInstance();
Calendar eDay = Calendar.getInstance();
sDay.set(year, month-1, 1); //월은 0부터 시작
eDay.set(year, month, 1-1); //12월에 1일에서 -1일을하면11월 마지막날이다.
//Date 클래스에서 요일 0: 일요일
//Calendar 클래스에서 요일 1: 일요일
startDayOfWeek = sDay.get(Calendar.DAY_OF_WEEK);
endDay = eDay.get(Calendar.DATE);
System.out.println(startDayOfWeek);
System.out.println(endDay);
System.out.println(" SU MO TU ME TH FR SA");
//1일 이전의 공백 나타내기, startDayOfWeek는 현재 첫 날의 요일이다.
for (int i=1; i<startDayOfWeek; i++) {
System.out.print(" ");
}
//달력출력
for(int i=1, n=startDayOfWeek; i<=endDay; i++, n++) {
//삼항연산자를 이용해서 한자리수이면 공백 2개, 두자리수면 공백 1개
System.out.print( (i<10) ? " "+i : " "+i);
//토요일이면 다음 행으로 넘어감
if (n%7 == 0) { System.out.println(); }
}
}
}
ㅇRandom 클래스
Math.random은 0.0~1 사이의 double난수를 얻는다면 random클래스를 이용하면
boolean, int, long, float, double 난수를 얻을 수 있다.
생성자 | 설명 |
Random() | 호출 시마다 다른 종자값(현재시간 이용)이 자동 설정 |
Random(long seed) | 매개밧으로 주어진 종자값이 설정 |
리턴값 | 메소드(매개변수) | 설명 |
boolean | nextBoolean() | boolean 타입의 난수를 리턴 |
double | nextDouble() | double 타입의 난수를 리턴 (0.0 <= ~ < 1.0) |
int | nextInt() | int 타입의 난수를 리턴 ( -2^31 <= ~ < 2^31-1 ) |
int | nextInt(int n) | int 타입의 난수를 리턴 ( 0 <= ~ < n ) |
이제 로또의 여섯숫자를 얻는 예제를 해보자.
실행할 때마다 값이 달라지는 것을 알 수 있다.
import java.util.Random;
public class Ex1117_Random01 {
public static void main(String[] args) {
int[] selectNum = new int[6];
Random random = new Random();
for(int i=0; i<6; i++) {
selectNum[i] = random.nextInt(45) + 1;
System.out.print(selectNum[i]+ " ");
}
System.out.println();
}
}
ㅇStringTokenizer 클래스
프로그래밍중 문자열 데이터의 분석이 필요한 상황이나 어떤 문자열을 특정 조건에 따라 문자를 나누고 싶을 때
토큰화를 이용한다.
만약 "08 : 45" 라는 문자열이 있을 때 토큰을 나누기 위한 구분자는 : 가 될 수 있다.
토큰이란 이러한 구분자를 기준으로 나눠지는 문자열 정보를 의미한다.
import java.util.StringTokenizer;
public class Ex1117_StringTokenizer01 {
public static void main(String[] args) {
String data1 = "사과 배 복숭아";
StringTokenizer st1 = new StringTokenizer(data1);
//토큰의 개수 출력
System.out.println(st1.countTokens() );
//각 토큰의 값을 출력
//다음 토큰 여부 판별하기 true or false
System.out.println(st1.nextToken() );
System.out.println(st1.nextToken() );
System.out.println(st1.hasMoreTokens());
System.out.println(st1.nextToken() );
System.out.println(st1.hasMoreTokens());
System.out.println(st1.nextToken() );
}
}
간단하게 아래와 같이 작성한다.
import java.util.StringTokenizer;
public class Ex1117_StringTokenizer01 {
public static void main(String[] args) {
String data1 = "사과 배 복숭아";
StringTokenizer st1 = new StringTokenizer(data1);
//토큰의 개수 출력
System.out.println(st1.countTokens() );
//각 토큰의 값을 출력
//다음 토큰 여부 판별하기 true or false
while ( st1.hasMoreTokens() ) {
System.out.println(st1.nextToken() );
}
}
}
만약 문자열이 구분자로 구분되어 있으면 아래와 같이 생성자에 구분자를 추가한다.
import java.util.StringTokenizer;
public class Ex1117_StringTokenizer01 {
public static void main(String[] args) {
String data1 = "사과&배&복숭아";
StringTokenizer st1 = new StringTokenizer(data1, "&"); //생성자에 & 구분자 추가
//토큰의 개수 출력
System.out.println(st1.countTokens() );
//각 토큰의 값을 출력
//다음 토큰 여부 판별하기 true or false
/*
System.out.println(st1.nextToken() );
System.out.println(st1.nextToken() );
System.out.println(st1.hasMoreTokens());
System.out.println(st1.nextToken() );
System.out.println(st1.hasMoreTokens());
System.out.println(st1.nextToken() );
*/
while ( st1.hasMoreTokens() ) {
System.out.println(st1.nextToken() );
}
}
}
만약 아래와 같이 사칙연산 문자열이면 다음과 같이 생성자에 추가한다.
import java.util.StringTokenizer;
public class Ex1117_StringTokenizer01 {
public static void main(String[] args) {
String data1 = "사과&배&복숭아";
StringTokenizer st1 = new StringTokenizer(data1, "&");
String data2 = "x=100*(200+300)/2";
StringTokenizer st2 = new StringTokenizer(data2, "=*+/()");
//토큰의 개수 출력
System.out.println(st2.countTokens() );
//각 토큰의 값을 출력
//다음 토큰 여부 판별하기 true or false
/*
System.out.println(st1.nextToken() );
System.out.println(st1.nextToken() );
System.out.println(st1.hasMoreTokens());
System.out.println(st1.nextToken() );
System.out.println(st1.hasMoreTokens());
System.out.println(st1.nextToken() );
*/
while ( st2.hasMoreTokens() ) {
System.out.println(st2.nextToken() );
}
}
}
만약 토큰화와 split를 비교하면 어떻게 될까? 아래 예제를 확인하자.
split는 공백이 2개 생기는 데 이는 &를 하나씩 나눈 뒤 값이 없기때문에 공백으로 저장되고
토큰화는 &가 1개가 있건 3개가 있건 &를 하나의 구분자로 인식하기 때문에 문자정보만 저장된다.
import java.util.StringTokenizer;
public class Ex1117_StringTokenizer02 {
public static void main(String[] args) {
String data1 = "사과&배&&&복숭아";
StringTokenizer st = new StringTokenizer(data1, "&");
String[] datas1 = data1.split("&");
System.out.println( st.countTokens() );
System.out.println( datas1.length );
System.out.println( "---토큰 사용---" );
int i = 1;
while ( st.hasMoreTokens() ) {
System.out.println(i+" : "+ st.nextToken() );
i++;
}
System.out.println( "---split() 메소드 사용---" );
i = 1;
for(String d : datas1) {
System.out.println( i+" : "+ d);
i++;
}
}
}
ㅇScanner 클래스
scanner 클래스는 정수, 실수, 문자열을 읽어올 수 있다.
실제 입력은 System.in에서 하지만 이를 편리하게 사용하려고 Scanner 클래스를 이용한다.
import java.util.Scanner;
public class Ex1117_Scanner01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("입력 : ");
String msg = sc.next(); //실제입력
System.out.println("입력된 메세지 : "+msg); //스페이스바를 치면 그 전까지만 저장된다.
sc.close();
}
}
next()를 쓴만큼 스페이스바를 기준으로 나눠진 문자열을 읽을 수 있다.
import java.util.Scanner;
public class Ex1117_Scanner01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("입력 : ");
String msg1 = sc.next();
String msg2 = sc.next();
System.out.println("입력된 메세지 : "+msg1);
System.out.println("입력된 메세지 : "+msg2);
sc.close();
}
}
nextLine()을 이용하면 엔터키를 치기 전까지는 모두 하나의 문자열로 인식한다.
import java.util.Scanner;
public class Ex1117_Scanner02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("입력 : ");
String msg1 = sc.nextLine();
System.out.println("입력된 메세지 : "+msg1);
sc.close();
}
}
-문제) 사용자에게 단수를 입력받고 구구단 출력하기 / q를 입력하면 프로그램 종료시키기
java Gugudan
단수 입력 : 2
2단출력
단수 입력 : 3
3단출력
단수 입력 : q
프로그램 종료
import java.util.Scanner;
public class Ex1117_Gugudan {
public static void main(String[] args) {
while ( true ) {
Scanner sc = new Scanner(System.in);
System.out.print("단수 입력(2~9까지만 입력) : ");
String dansu = sc.next();
//입력값 검증
if ( dansu.equals("q") ) {
System.out.print("프로그램 종료");
System.exit(0);
}
if ( (int)dansu.toCharArray()[0] <= 49 || (int)dansu.toCharArray()[0] > 57 ) {
System.out.println("범위를 맞지않습니다. 다시입력하세요.");
continue;
}
//계산하기
int dansu1 = Integer.parseInt(dansu);
System.out.println(dansu1+"단 출력");
for(int i=1; i<=9; i++) {
System.out.println(dansu1+" X "+i+"= "+i*dansu1);
}
}//while끝
}
}
여기서 (int)dansu.toCharArray()[0] 는 string인 dansu를 아스키코드값으로 바꿔준다.
string을 아스키코드로 바꾸려면 먼저 string을 char형으로 바꾼 다음에 int로 바꿔줘야 한다.
ㅇStringjoin 클래스
문자열 연결하는 방법은 총 4가지가 있다. 아래예시참고
import java.util.StringJoiner;
public class Ex1117_StringJoiner01 {
public static void main(String[] args) {
//문자열 연결하기 + 연산자
System.out.println("--- +연산자 사용 ---");
String str1 = "apple" +", banana"+", pineapple";
System.out.println(str1);
System.out.println("\n--- format 메소드 사용 ---");
String str2 = String.format("%s,%s,%s", "apple","banana","pineapple");
System.out.println(str2);
System.out.println("\n--- String.join()메소드 사용 ---");
String[] arr = {"apple", "banana", "pineapple"};
String str3 = String.join(",", arr);
System.out.println(str3);
System.out.println("\n--- StringJoiner클래스 사용 ---");
StringJoiner sj = new StringJoiner(",");
sj.add("apple");
sj.add("banana");
sj.add("pineaaple");
System.out.println(sj);
}
}
sj.add를 아래와 같이 붙여서 사용해도 된다.
sj.add("apple").add("banana").add("pineaaple");
ㅁ추상클래스
(p329)
추상(abstract)은 실체 간에 공통되는 특성을 추출한 것을 의미한다. 객체를 직접 생성할 수 있는 클래스를 실체 클래스,
실체클래스들의 공통적인 특성을 추출해서 선언한 클래스를 추상 클래스라고 한다.
추상 클래스와 실체 클래스는 상속의 관계를 가지고 있다. 추상 클래스가 부모이고, 실체 클래스가 자식이다.
추상클래스는 실체 클래스의 공통되는 필드와 메소드를 추출해서 만들었기 때문에 객체를 직접 생성하지 못한다.
즉, 추상 클래스는 new 연산자를 사용해서 인스턴스를 생성하지 못한다.
추상클래스 만드는 용도
1. 실체 클래스들의 공통된 필드와 메소드의 이름을 통일할 목적
같은 기능을 하는 실체 클래스들의 필드와 메소드들이 만약 이름이 다 다르다면 메모리낭비뿐만 아니라 사용하기 헷갈린다. 이때 공통필드와 메소드를 추상클래스에서 정의하고 실체 클래스가 이를 상속받은 다음 재정의해서 사용하면 된다.
2. 실체 클래스를 작성할 때 시간 절약
공통적인 필드와 메소드를 추상 클래스에서 정의하니깐 이를 상속받기만해서 사용하면되니깐 시간 절약이 된다.
추상 클래스는 abstract 를 클래스명 앞에 붙이고 만들면 된다.
추상클래스를 객체화하려면 상속을 통해서 실체클래스로부터 객체를 만들어야 한다.
class Parent1 {
Parent1() {
System.out.println("Parent1 생성");
}
}
//추상 클래스 만들기
abstract class Parent2 {
Parent2() {
System.out.println("Parent2 생성");
}
}
class Child extends Parent2 {
Child() {
System.out.println("Child 생성");
}
}
public class Ex1117_Abstract01 {
public static void main(String[] args) {
Parent1 p1 = new Parent1();
//Parent2 p2 = new Parent2(); //생성할 수 없다.
Child c1 = new Child(); //추상클래스인 부모생성자와 실체클래스인 자식생성자를 불러온다.
}
}
ㅇ추상 메소드와 오버라이딩
일반 클래스에서는 추상메소드를 정의할 수 없고 추상메소드는 오로지 추상클래스에서만 정의할 수 있다.
class Parent1 {
Parent1() {
System.out.println("Parent1 생성");
}
}
abstract class Parent2 {
Parent2() {
System.out.println("Parent2 생성");
}
//추상메서드 선언 - 이름만 만들고 중괄호가 없다.
abstract void viewParent1();
}
public class Ex1117_Abstract02 {
public static void main(String[] args) {
Parent1 p1 = new Parent1();
}
}
이제 추상클래스로부터 실체클래스를 만들고 추상클래스의 추상 메소드를 실체클래스에서 재정의한다음에 사용해보자.
abstract class Parent1 {
Parent1() {
System.out.println("Parent1 생성");
}
//추상메서드 선언 - 이름만 만들고 중괄호가 없다.
abstract void viewParent1();
}
class Child extends Parent1 {
Child() {
System.out.println("Child 생성");
}
//추상클래스에 추상메소드가 있으므로 추상메소드 재정의를 해주어야 한다.
void viewParent1() {
System.out.println("viewParent1() 호출");
}
}
public class Ex1117_Abstract02 {
public static void main(String[] args) {
Child c = new Child();
c.viewParent1();
}
}
만약 첫 번째 만든 실체클래스가 추상클래스의 추상메소드를 구현하지 않고 추상클래스가 된다음에
자식의 자식클래스(손자)를 상속해준다면
손자 클래스에서 추상메소드를 재정의해줘야한다.
//추상화된 부모클래스
abstract class Parent1 {
Parent1() {
System.out.println("Parent1 생성");
}
//추상메서드 선언 - 이름만 만들고 중괄호가 없다.
abstract void viewParent1();
}
//추상화된 자식클래스
abstract class Child extends Parent1 {
Child() {
System.out.println("Child 생성");
}
}
//손자클래스
class GrandChild extends Child {
GrandChild() {
System.out.println("GrandChild 생성");
}
//첫 번째 자식클래스에서 추상메소드를 재정의안했으므로 손자클래스에서 메소드 재정의를 해준다.
void viewParent1() {
System.out.println("viewParent1() 호출");
}
}
public class Ex1117_Abstract02 {
public static void main(String[] args) {
GrandChild gc = new GrandChild();
gc.viewParent1();
}
}
추상클래스에 실체클래스들의 공통적인 필드와 메소드를 선언하고 실체클래스들마다 실행내용이 달라야하는 경우가 있다.
예를 들어 동물들은 모두 울음소리를 내니깐 '운다' 라는 추상메소드를 만들고
강아지의 '운다' 메소드는 "멍멍" 이 돼야하고,
고양이의 '운다' 메소드는 "야옹"이 돼야한다.
이를 위해서 실체클래스에서 추상클래스의 추상메소드를 각 클래스의 입맛에 맞게 재정의해야 한다.
아래 예제를 살펴보자.
abstract class Animal {
public String kind;
public void breathe() {
System.out.println("숨을 쉽니다.");
}
//추상메소드
public abstract void sound();
}
class Dog extends Animal {
public Dog() {
this.kind = "포유류";
}
//추상메소드 재정의 - Dog클래스에 맞게
@Override
public void sound() {
System.out.println("멍멍");
}
}
class Cat extends Animal {
public Cat() {
this.kind = "포유류";
}
//추상메소드 재정의 - Cat클래스에 맞게
@Override
public void sound() {
System.out.println("야옹");
}
}
public class Ex1117_AnimalEx {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.sound();
cat.sound();
}
}
ㅁ인터페이스
인터페이스는 객체의 사용방법을 정의한 타입이다. 이는 객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할을 한다.
인터페이스는 개발코드와 객체가 서로 통신하는 접점역할을 한다.
개발코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출시킨다.
그렇기 때문에 개발 코드는 객체의 내부 구조를 알 필요가 없고 인터페이스의 메소드만 알고 있으면 된다.
그러면 왜 중간에 인터페이스를 사용하는가?
그 이유는 개발 코드를 수정하지 않고, 사용하는 객체르르 변경할 수 있도록 하기 위해서이다.
인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라서 실행 내용과 리턴값이 다를 수 있다. 따라서 개발 코드 측면에서는 코드 변경없이 실행 내용과 리턴값을 다양화할 수 있다는 장점을 가진다.
인터페이스는 물리적 형태는 클래스와 동일하지만 선언 방법이 다르다.
[public] interface 인터페이스명 { 내용 }
인터페이스의 구성 멤버는 상수와 메소드가 있다.
인터페이스는 객체를 생성할 수 없기때문에 생성자를 가질 수 없다.
메소드는 추상메소드, 디폴트 메소드, 정적 메소드만 선언이 가능하다.
-인터페이스 사용
인터페이스를 선언한다음 인터페이스를 구현해보자. 인터페이스를 구현하는 클래스를 구현 클래스라고 한다.
구현할 때는 아래와 같이 구현한다.
class 클래스명 implement 인터페이스명 { 내용 }
interface InterA {
//상수
public static final String STR1 = "홍길동";
//추상메소드
public abstract void method1();
}
interface InterB {
//상수
public static final String STR1 = "이몽룡";
//추상메소드
public abstract void method3();
}
class ClassA implements InterA, InterB { //다중상속
public void method1() {
System.out.println("method1 호출");
}
public void method2() {
System.out.println("method2 호출");
}
public void method3() {
System.out.println("method3 호출");
}
}
public class Ex1117_InterfaceEx01 {
public static void main(String[] args) {
ClassA a = new ClassA();
a.method1();
a.method2();
a.method3();
System.out.println(InterA.STR1);
System.out.println(InterB.STR1);
}
}
-추상 클래스와 인터페이스 차이점
추상클래스와 인터페이스의 공통점은 추상메서드를 사용할 수 있다는 점이다.
그럼 왜 굳이 2가지로 나눠서 사용할까?
가장 큰 차이점은 사용용도이다.
1. 사용의도 차이점
추상클래스는 IS - A : "~이다."
인터페이스는 HAS - A : "~을 할 수 있는"
즉, 다중상속의 가능 여부에 따라 용도가 나뉜다. 자바의 특성상 한 개의 클래스만 상속이 가능하기 때문에
추상클래스는 단 하나만 상속하는 반면
인터페이스는 여러개의 인터페이스를 구현받을 수 있다.
따라서 자바에서 인터페이스의 주된 역할은
-개발자 사이의 코드 규약을 정한다.
-다중상속이 가능하므로 여러 구현클래스에서 공통적인 부분을 추상화한다(다형성)
-인터페이스끼리 상속
interface InterA {
String STR1 = "홍길동";
void methodA();
}
//인터페이스끼리 상속
interface InterB extends InterA {
String STR2 = "박문수";
void methodB();
}
class Child implements InterB {
public void methodA() {
System.out.println("methodA 호출");
}
public void methodB() {
System.out.println("methodB 호출");
}
}
public class Ex1117_InterfaceEx02 {
public static void main(String[] args) {
Child c = new Child();
c.methodA();
c.methodB();
System.out.println(c.STR1);
System.out.println(c.STR2);
}
}
-상속과 구현 동시에 받기
interface InterA {
String STR1 = "홍길동";
void methodA();
}
//인터페이스끼리 상속
interface InterB extends InterA {
String STR2 = "박문수";
void methodB();
}
class Parent {
Parent() {
System.out.println("Parent 생성자");
}
}
//상속과 구현을 동시에 받는 클래스
class Child extends Parent implements InterB {
Child() {
System.out.println("Child 생성자");
}
public void methodA() {
System.out.println("methodA 호출");
}
public void methodB() {
System.out.println("methodB 호출");
}
}
public class Ex1117_InterfaceEx02 {
public static void main(String[] args) {
Child c = new Child();
c.methodA();
c.methodB();
System.out.println(c.STR1);
System.out.println(c.STR2);
}
}
이번에는 인터페이스의 구성멤버들을 모두 정의해보자.
먼저 디폴트메소드는 인터페이스에 선언되지만 사실은 구현객체가 가지고 있는 인스턴스 메소드라고 생각해야 한다.
이는 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위해서이다.
디폴트메소드는 추상 메소드와 달리 실행문{}이 있다. 접근제어자는 public이며 생략가능하다.
디폴트메소드는 인터페이스에 이미 구현되어 있으므로 구현클래스에서 구현할 필요가 없다.
또한 구현클래스에서 재정의도 가능하다.
정적메소드는 디폴트 메소드와 달리 객체가 없어도 인터페이스만으로 호출이 가능하다.
//인터페이스 선언
interface RemoteControl {
//상수
int MAX_VOLUME = 10;
int MIN_VOLUME = 10;
//추상메소드
void turnOn();
void turnOff();
void setVolmn(int volumn);
//디폴트 메소드
default void setMute(boolean mute) {
if(mute) {
System.out.println("무음 처리합니다.");
} else {
System.out.println("무음 해제합니다.");
}
}
//정적 메소드
static void changeBattery() {
System.out.println("건전지를 교체합니다.");
}
}
//구현클래스
class Television implements RemoteControl {
//필드
private int volumn;
//실체메소드 작성
public void turnOn() {
System.out.println("TV를 켭니다.");
}
public void turnOff() {
System.out.println("TV를 끕니다.");
}
public void setVolmn(int volumn) {
if (volumn > RemoteControl.MAX_VOLUME) {
this.volumn = RemoteControl.MAX_VOLUME;
} else if (volumn < RemoteControl.MIN_VOLUME) {
this.volumn = RemoteControl.MAX_VOLUME;
} else {
this.volumn = volumn;
}
System.out.println("현재 TV 볼륨: "+this.volumn);
}
}
public class Ex1117_RemoteControlEx {
public static void main(String[] args) {
RemoteControl rc;
rc = new Television();
rc.turnOn();
rc.setVolmn(11);
RemoteControl.changeBattery();
rc.setMute(true);
rc.setMute(false);
rc.turnOff();
}
}