2023.03.23 : java (내부 클래스, 예외 처리, arrays, decimal 등)

이준영·2023년 3월 23일
0

UML(Unified Modeling Language) : 객체 모델링

객체지향 프로그래밍에 흔히 사용되는 표준화된 모델링 표기 체계 (객체 모델링에 대한 산출물)

객체 모델링 결과 - UML(학계 : 이론) -> 계속 버전 업이 된다.

클래스 다이어그램 : 클래스의 구성 요소(박스 모델 사용하여 클래스 이름 및 필드, 메서드 작성) 와 클래스 간의 관계(선을 사용하여 상속, 구현, 포함 등 표시)를 묘사하는 다이어그램

시퀀스 다이어그램 : 특정 행동이 어떤 순서로 어떤 객체와 상호작용 하는지 표현하는 행위

유스케이스 다이어그램 : 시스템과 사용자의 상호작용을 다이어그램으로 표현한 것으로 사용자의 관점에서 시스템의 서비스 혹은 기능 및 그와 관련한 외부 요소를 보여주는 것
...

UML 사용

  1. TOOL :
    Eclipse - UML 지원 툴 / 스타 UML (https://staruml.io)



UML 특징

가시화 : 설계 내용을 가시적인 그래프 형태로 작성, 의사소통 자료로 사용
명세화 : SW 개발 단계의 각 과정에 필요한 모델 정확하고 완전하게 명세
구축화 : 다양한 객체지향 프로그래밍 언어로 변환 가능(순공학, 역공학 적용)
문서화 : 프로젝트 참여자들 간의 통제, 평가 및 의사소통에 필요한 문서화 가능

순공학 - (요구사항 정의 - 분석 - 설계 -구현) 단계로 개발하는 것으로 일반적인 개발 절차



내부(중첩) 클래스

다른 클래스 내부에 작성되는 클래스

종류


인스턴스 내부 클래스

외부 클래스의 필드 선언 위치에 static 키워드 없이 선언된 클래스

내부 -> 외부 클래스의 멤버에 접근할 때 private 포함한 모든 멤버에 대해 자유롭게 참조 가능

static 멤버 가질 수 없다. 단 static final 멤버는 상수 취급되어 사용 가능

클래스

메인 클래스


내부 클래스 예

public class ex01 {
	//외부 클래스
	private int outerMember = 1;
	
	private void outerMethod() {
		Inner inner = new Inner();
		inner.innerMember = 100;
		inner.innerMethod();
	}
    //인스턴스 내부 클래스
	class Inner {
		int innerMember = 10;
		//static int staticMember = 10;
		static final int finalstaticMember = 20;
		
		private void innerMethod() {
			System.out.println("om : " + outerMember + ", im : " + innerMember);
		}
	}
	
    //메인 메소드
	public static void main(String[] args) {
		ex01 ex = new ex01();
		ex.outerMethod();
		//내부 클래스 접근 가능
		Inner inner = ex.new Inner();
		inner.innerMethod();
	}
}



클래스 내부 클래스

static 키워드가 붙은 내부 클래스, 인스턴스 내부 클래스와 달리 static 멤버 가질 수 있음

클래스

메인 클래스

메인 클래스에서 외부 클래스 객체를 생성하지 않아도 된다(static 객체 필요 없는 효과)


외부 클래스 예


public class ex01 {
	static class StaticInner {
		private int iMember = 10;
		private static int sMember = 0;
			
		private void innerMethod() {
			ex01 sinner = new ex01();
			System.out.println("om : " + sinner.oMember + 
					", im : " + iMember);	
			}
		}
	private int oMember = 1;
	
	private void outerMethod() {
		StaticInner inner = new StaticInner();
		inner.iMember = 100;
		inner.innerMethod();
	}
	
	public static void main(String[] args) {
		StaticInner inner = new StaticInner();
		inner.innerMethod();
	}
}



로컬 내부 클래스

클래스 영역 아닌 메서드나 생성자 또는 초기화 블록 내부에 선언된 클래스

static 변수 x, final static 변수 가질 수 있음

내부 클래스 -> 외부 클래스 멤버에 접근하는 것은 전혀 제약이 없고 호춯할 때 외부 객체에 대한 참조도 필요 없다

내부적으로 컴파일러가 final 키워드를 변수에 추가하여 내부에서는 값을 변경할 수 없다.

클래스

메인 클래스


로컬 내부 클래스 예

public class ex01 {
	int iMember = 1;
	static int cMember = 2;
	
	void method() {
		int localVar = 3;
		
		class LocalInner {
			int innerlocalVar = 4;
			
			void innerMethod() {
				System.out.println("외부 인스턴스 멤버 변수 : " + iMember);
				System.out.println("외부 클래스 멤버 변수 : " + cMember);
				System.out.println("외부 로컬 변수 " + localVar);
				System.out.println("내부 인스턴스 멤버 변수 " + innerlocalVar);
			
				iMember++;
				cMember++;
				//localVar++; //편집 불가능
				innerlocalVar++; //편집 가능하나 실행 x
			}
		}
		LocalInner lInner = new LocalInner();
		lInner.innerMethod();
	}
	
	public static void main(String[] args) {
		ex01 ex = new ex01();
		ex.method();
        System.out.println();
		ex.method();
	}
}



tip : 자바 버전 바꾸기

JRE System Library[java 버전~~] 우클릭 -> Build Path -> Configure Build Path -> JRE System Library[java 버전~~] 클릭하고 edit 눌러서 바꿔주기!



익명 내부 클래스

객체화가 이루어지지 않은 클래스( 이 클래스를 이용해 새로운 객체 만들 수 없고 선언과 동시에 바로 클래스를 구성하고 객체 생성함)
-> 주로 일회용으로 더 이상 재사용하지 않는 경우에 적합하게 사용(abstract 클래스나 인터페이스의 구현에 많이 사용됨)

로컬 내부 클래스의 한 종류로 기본 특성은 동일함


인터페이스 InnerB 생성

메인 클래스 생성( InnerB 선언과 동시에 바로 생성해버림)


익명 내부 클래스 예

인터페이스를 메서드 매개변수로 넘기고, 메인 메서드에서 호출 시에 매개변수에서 바로 만들어서 사용 가능

interface SomeInterface {
	void printInfo();
	}

public class ex01 {
	int iMember = 1;
	static int cMember = 2;
	
	void OuterMethod(SomeInterface si) {
		si.printInfo();
	}
	
	public static void main(String[] args) {
		ex01 ex = new ex01();
		int localVar = 3;
		ex.OuterMethod(new SomeInterface() {
			@Override
			public void printInfo() {
				//System.out.println("외부 인스턴스 멤버 변수 : " + iMember);
				System.out.println("외부 클래스 멤버 변수 : " + cMember);
				System.out.println("외부 로컬 변수 : " + localVar);
				
				cMember++;
				//localVar++;  편집 불가능
			}
		});
	}
}



람다식(Lambda)

익명 내부 클래스를 조금 더 편리하게 사용하는 것으로 생각해도 된다.



1. 익명 객체 선언
public class MyFunctionalInterMain {

	public static void main(String[] args) {
		new MyFunctionalInter1() {
			
			@Override
			public void method() {
				System.out.println("method 호출");
			}
		}.method();
	}
}



  1. 인터페이스를 객체 변수로 선언

public class MyFunctionalInterMain {

	public static void main(String[] args) {
		//인터페이스를 객체변수로 선언 , 이 때 람다식으로 선언 가능
		MyFunctionalInter1 f = new MyFunctionalInter1() {
		
			@Override
			public void method() {
				System.out.println("method 호출2");
			}
		};
	
		f.method();
	}
}



  1. 2번의 내용을 람다식으로 변경
public class MyFunctionalInterMain {

	public static void main(String[] args) {
		//람다식
		MyFunctionalInter1 f1 = () -> {
			System.out.println("method 호출3");
		};
	
		f1.method();
		
		//완전 축약 람다식
		MyFunctionalInter1 f2 
			= () -> System.out.println("method 호출4");
		
		f2.method();
	}
}



  1. 완전 축약 람다식 사용
public class MyFunctionalInterMain {

	public static void main(String[] args) {
    	//실행문이 하나일 경우
		//완전 축약 람다식
		MyFunctionalInter1 f2 = () -> System.out.println("method 호출4");
		
		f2.method();
	}
}




@FunctionalInterface

이 인터페이스는 람다식에서 사용할 함수형 인터페이스이므로 abstract가 하나만 있는지 검사하라 << 라고 요청하는 어노테이션

두 개를 선언하니 에러가 발생하는 모습



람다식 예

인터페이스 생성

package pack1;

//추상 메서드는 반드시 하나만 있어야 한다.(함수형 인터페이스(람다식)일 경우)
@FunctionalInterface
public interface MyFunctionalInter {
	void methodA();
	//void methodB();
}

메인 클래스 생성

package pack1;


public class MyFunctionalInterMain {
	public static void useFIMethod(MyFunctionalInter fi) {
		fi.methodA();
	}

	public static void main(String[] args) {
		useFIMethod(new MyFunctionalInter() {
			
			@Override
			public void methodA() {
				System.out.println("methodA 호출");
			}
		});

		useFIMethod( () -> {
			System.out.println("람다식 이용");
		});
		
		//실행문이 하나일 때
		useFIMethod(() -> System.out.println("람다식 이용2"));
}
}



매개변수가 있는 람다식 예

위 코드에서 매개변수만 적어주면 된다.

인터페이스 생성

package pack1;

//추상 메서드는 반드시 하나만 있어야 한다.(함수형 인터페이스(람다식)일 경우)
@FunctionalInterface
public interface MyFunctionalInter {
	void methodA(String msg);
	//void methodB();
}

메인 클래스 생성

package pack1;


public class MyFunctionalInterMain {
	public static void useFIMethod(MyFunctionalInter fi) {
		fi.methodA("홍길동");
	}

	public static void main(String[] args) {

		useFIMethod( (String msg) -> {
			System.out.println("람다식 이용1 : " + msg);
		});
		
		//실행문이 하나일 때
		useFIMethod(msg -> System.out.println("람다식 이용2 : " + msg));
}
}



리턴 타입이 있는 람다식



예외 처리와 디버깅

에러

컴파일 에러 : 문법 에러 / javac 명령을 사용할 때 나타나는 에러 / eclipse : 빨간줄

런타임(실행) 에러 : 시스템 에러(처리 불가) / 예외(if / exception : 고급 에러 처리) (java 명령을 사용할 때 나타나는 에러) / eclipse : run as 사용 시



try ~ catch 구문

예외 처리 방식
try - 예외가 발생할 수 있는 코드 작성
catch - 해당 예외가 발생했을 때 처리할 코드 작성

예외 발생 경우 :
try 블록에서 예외 발생 -> (try안의 나머지 코드는 수행하지 못함) ->JVM 동작하여 예외객체 생성(new)해서 던져버림(throw) -> catch
로 받아서 catch 안 내용 실행

public class Exception1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("시작");

		int num1 = 0;
		int num2 = 20;
		
		try {  //에러 발생하지 않으면 try 구문 실행
		int result = num2 / num1;
		System.out.println(result);
		}
		catch (ArithmeticException e) {  --> 
			System.out.println("exception 발생");
		}
		
		System.out.println("끝");	
	}
}



Exception 정보 얻기 위한 주유 메서드


getMessage()

발생된 예외에 대한 구체적인 메세지 반환


public class Exception1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("시작");

		int num1 = 0;
		int num2 = 20;
		
		try {
		int result = num2 / num1;
		System.out.println(result);
		}
		catch (ArithmeticException e) {
			System.out.println("exception 발생");
			System.out.println(e.getMessage());
			e.printStackTrace();
			
		}
		
		System.out.println("끝");	
	}
}



다중 예외 처리

ex 코드

public class exception2 {

	public static void main(String[] args) {
		System.out.println("시작");
		
		String name = null;
		int num1 = 0;
		int num2 = 10;
		
		System.out.println(name.length());
		System.out.println(num2/num1);
		
		System.out.println("끝");
	}
}

위 코드 경우 첫번째 에러에서 NullPointerException 발생
String name = ""; 일경우(정상) 두번째 에러인 ArithmeticException 발생


위 코드를 처리하기 위해 try ~ catch 사용


public class exception3 {

	public static void main(String[] args) {
		System.out.println("시작");
		
		String name = null;
		int num1 = 0;
		int num2 = 10;
		
		try {
		System.out.println(name.length());
		}
		catch(NullPointerException e) {
			System.out.println("객체 생성후 메서드 사용");
		}
		try {
			System.out.println(num2/num1);
		}
		catch(ArithmeticException e) {
			System.out.println("0으로 나눌 수 없음");
		}
		System.out.println("끝");
	}
}



불필요한 코드를 줄이기 위해 다중 예외 처리 사용 - 이 경우 if else 와 유사하게 실행(첫번째 catch 실행되면 빠져나감)

public class exception3 {

	public static void main(String[] args) {
		System.out.println("시작");
		
		String name = null;
		int num1 = 0;
		int num2 = 10;
		
		//다중 예외 처리, if ~ else if ~ else 와 유사
		try {
		System.out.println(name.length());
		System.out.println(num2/num1);
		}
		catch(NullPointerException e) {
			System.out.println("객체 생성후 메서드 사용");
		}
		catch(ArithmeticException e) {
			System.out.println("0으로 나눌 수 없음");
		}
		System.out.println("끝");
	}
}



에러를 통합적으로 처리할 때

public class exception3 {

	public static void main(String[] args) {
		System.out.println("시작");
		
		String name = null;
		int num1 = 0;
		int num2 = 10;
		
		//다중 예외 처리, if ~ else if ~ else 와 유사
		try {
		System.out.println(name.length());
		System.out.println(num2/num1);
		}
		catch(Exception e) {   //에러를 통합적으로 처리
			System.out.println("[에러] : " + e.getMessage());
		}
		System.out.println("끝");
	}
}



특정한 것은 처리해주고 나머지는 전부 처리할 때 (else 느낌)
String name에서 Exception 발생하면 첫 catch가 실행,
String name이 정상이면 나머지 Exception catch 실행
한 번에 여러개를 처리할 수 있는 장점이 있지만, 정확한 예외 처리가 어렵다


public class exception3 {

	public static void main(String[] args) {
		System.out.println("시작");
		
		String name = null;
		int num1 = 0;
		int num2 = 10;
		
		//다중 예외 처리, if ~ else if ~ else 와 유사
		try {
		System.out.println(name.length());
		System.out.println(num2/num1);
		}
		catch(NullPointerException e) {
			System.out.println("객체 생성후 메서드 사용");
		}
		catch(Exception e) {   --> 나머지는 여기서 처리, 순서 바꿔쓰면 안됨
			System.out.println("예외 처리!!");
		}
		System.out.println("끝");
	}
}



try ~ catch ~ finaally

try ~ catch 구문은 finally 블록 추가로 가질 수 있음
finally 블록은 예외 발생 여부와 상관없이 반드시 실행되어야 하는 내용을 작성
중간에 return 문을 만날 때에도 먼저 finally 블록을 실행 후 메서드 리턴 됨

import java.util.Random;

public class Finnaly1 {

	public static void main(String[] args) {
		int num = new Random().nextInt(2);
		
		try {
			System.out.println("code 1, num : " + num);
			int i = 1 / num;
			System.out.println("code 2 - 예외 없음");
			return;
		}
		catch(ArithmeticException e) {
			System.out.println("code 3 - 예외 처리 완료");
		}
		finally {   --> 항상 실행되는 구문
			System.out.println("code 4 - 언제나 실행");
		}
		System.out.println("code 5");
	}

}

num = 0으로 Exception 발생할 시

정상 처리될 시



throw : 강제 예외 발생

public class exception7 {
	public void method1(int num) {
		System.out.println("method 시작");
		
		//입력값 검사
		if(num >= 100) {
			System.out.println("100보다 크다");
		}
		else {
			//강제 예외 발생시키기(만들기)
			try {
				throw new Exception("100보다 작다");  -->강제로 예외 발생시킴
			}
			catch(Exception e) {
				System.out.println("[에러] : " + e.getMessage());
			}
		}
		
		System.out.println("method 끝");
	}
	
	public static void main(String[] args) {
		exception7 e = new exception7();
		
		e.method1(10);
		System.out.println();
		e.method1(200);
		
	}
}



throws : 위임

발생한 예외를 호출한 쪽으로 넘긴다(위임한다.)
코딩 중 Unhandled exception 뜨면 try ~ catch 처리를 해줘야 한다.
보통 저 문구가 뜨면 try ~ catch를 사용하고 싶은 영역을 잡고
우클릭 -> Surround With -> try/catch block 누르면 자동으로 생성된다.

public class exception7 {
	public void method2(int num) throws Exception { //호출하는 쪽으로 던짐
		System.out.println("method 시작");
		
		//입력값 검사
		if(num >= 100) {
			System.out.println("100보다 크다");
		}
		else {
			//강제 예외 발생시키기(만들기)
			throw new Exception("100보다 작다"); // 던져짐
		}
		
		System.out.println("method 끝");
	}
	
	public static void main(String[] args) {
		exception7 ee = new exception7();
        
		try {
		ee.method2(10);  // 던진 예외를 받음, 대리로 예외 처리해줌
		}
		catch (Exception e) {
			System.out.println("[에러] : " + e.getMessage());
		}
	}

}



사용자 정의 예외 작성 (예외 만들기)

  1. Exception을 상속한 클래스를 하나 만든다.



  1. 예외를 발생시키고 위임하는 클래스와 메서드를 하나 만든다.



  1. 메인 클래스에서 메서드를 호출하고, 위임받은 예외를 처리해준다.




응용 - 아이디와 비밀번호 처리

사용자 정의 예외 클래스 / getLocalizedMessage() 오버라이드 처리

public class LoginFailException extends RuntimeException {
	enum ErrorCode {
		INVALID_ID, INVALID_PASS
	}

	private ErrorCode errorCode;

	public LoginFailException(ErrorCode errorCode, String data) {
		super(data);
		this.errorCode = errorCode;
	}
	
	@Override
	public String getLocalizedMessage() {
		String msg = this.getMessage();
		switch(errorCode) {
		case INVALID_ID:
			msg += ", 아이디를 확인하세요";
			break;
		case INVALID_PASS:
			msg += ", 비밀번호를 확인하세요";
			break;
		}
		return msg;
	}
}



메인 클래스

public class UserManagerWithException {
	public boolean login(String id, String pass) {
		if(!id.equals("hong")) {  --> id가 hong이 아니면 예외 던지기
			throw new LoginFailException(LoginFailException.ErrorCode.INVALID_ID, id);
		}
		else if(!pass.equals("1234")) { --> pass가 1234가 아니면 예외 던지기
			throw new LoginFailException(LoginFailException.ErrorCode.INVALID_PASS, pass);
		}
		return true;
	}
	
	public static void main(String[] args) {
		UserManagerWithException uManager = new UserManagerWithException();
		try {
			//boolean result = uManager.login("hong", "1234");
			//boolean result = uManager.login("hong2", "1234");
			boolean result = uManager.login("hong", "5678");
			System.out.printf("로그인 성공 여부 : %b %n",result);
		}
		catch (LoginFailException e) {
			System.out.printf("예외 처리 : %s %n",e.getLocalizedMessage());
		}
	}
}

비밀번호 에러일 때





Arrays 객체

배열의 접근을 조금 더 쉽게 하기 위해 쓰임

Arrays.toString(객체명) 사용하여 문자열 형식으로 리턴 / Arrays.fill() 로 추가



Arrays.Equals 로 내용 비교하기


Arrays.sort() : 안의 내용 정렬

아스키 코드값 순으로 정렬


arraycopy 이용하기

import java.util.Arrays;

public class ArrayCopy {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] source = {1, 2, 3, 4, 5};
		int[] target = new int[10];
		for(int i = 0; i<source.length; i++) {
			target[i] = source[i];
		}
		System.out.println(Arrays.toString(target));
		
		int[] target2 = new int[10];
		System.arraycopy(source, 0, target2, 0, source.length);
		System.out.println(Arrays.toString(target2));
	}

}



List <-> 배열 형변환

list 타입으로 형변환 해준다.



배열, 컬렉션 같이 사용하기


이런 출력 결과를 가지고 싶을 때 배열과 컬렉션을 같이 써서 나타낼 수 있다.

  1. 배열과 ArrayList 사용 ( 뿐 아니라 hashmap등과도 사용 가능, 활용 잘하기)
import java.util.ArrayList;

public class DataEx1 {

	public static void main(String[] args) {
		Student s1 = new Student("1", "홍길동", "010-111-1111", "20", "서울시");
		Student s2 = new Student("2", "박문수", "010-222-2222", "22", "경기도");
		Student s3 = new Student("3", "이몽룡", "010-333-3333", "25", "강원도");
		
		ArrayList<Student> datas = new ArrayList<>();
		datas.add(s1);
		datas.add(s2);
		datas.add(s3);
		
		for(Student s : datas) {
			System.out.println(s.getSeq() + "\t" + s.getName() 
			+ "\t" + s.getPhone() + "\t" + s.getAge() + "\t" + s.getRegion());
		}
	}
}



형식화 클래스

java.text패키지에 포함

숫자, 날짜, 텍스트 데이터를 일정한 형식에 맞게 표현할 수 있는 방법을 객체지향적으로 설계하여 표준화

형식화에 사용될 패턴을 정의

데이터를 정의된 패턴에 맞춰 형식화 / 역으로 형식화된 데이터에서 원래의 데이터를 얻기 가능


DecimalFormat 클래스

숫자에 대한 형식화 클래스
지정된 기호들 조합하여 패턴을 만들고 그것을 이용해 데이터를 문자열로 만들거나 거꾸로 문자열을 데이터로 변환

사용

  • 데시벨 객체에서 지정한 기호대로 읽어준다, 크기를 넘어가면 알아서 잘라줌
  • 자를 때 반올림 해서 자른다.
  • #은 패턴 형식을 위해 쓰고, $는 예외처리 위해서 써줌




SimpleDateFormat

날짜 / 시간 정보에 대한 패턴을 지정해서 파싱과 포맷팅 처리
DecimalFormat과 동일한 메서드 사용

throws 처리

try ~ catch 처리



MessageFormat

+를 사용하여 문자열을 연산하면 지저분해 보이기 쉬운데, 이 때 이 클래스를 사용하면 데이터를 변수처럼 사용해서 메시지 패턴에 적용 가능

  • {} 인덱스를 지정해서 차례대로 출력함
  • split으로 src를 각각 나누고
  • for문 사용하여 패턴대로 문자열 넣고 다시 split으로 :로 나눠준다.

결과

profile
끄적끄적

0개의 댓글