no image
[JAVA] 봉인된 클래스(sealed class)
이 게시글은 이것이 자바다(저자 : 신용권, 임경균)의 책과 동영상 강의를 참고하여 개인적으로 정리하는 글임을 알립니다. 기본적으로 final 클래스를 제외한 모든 클래스는 부모 클래스가 될 수 있다. 또한 부모가 자식을 선택하는 것이 아니라, 자식이 부모를 선택해서 상속을 받을 수 있다. 하지만 Java 15부터는 부모 클래스가 특정 자식 클래스에게만 상속을 허락할 수 있게 되었다. sealed 키워드를 사용하면 permits 키워드 뒤에 상속 가능한 자식 클래스를 지정해야 한다. public sealed class Person permits Employee, Manager{} 이런 식으로 Person 클래스를 봉인된 클래스로 지정하고 permits 옆에 상속을 허락할 자식 클래스를 지정하면 된다. 봉..
2023.07.14
no image
[C++] 백준 13단계 - 2751번 문제 (수 정렬하기 2)
문제설명 소스코드 #include #include using namespace std; int main() { ios_base::sync_with_stdio(false); //표준 스트림 동기화 해제 cin.tie(NULL); //입력과 출력 연결 끊기 int N; cin >> N; int* arr = new int[N]; for (int i = 0; i > arr[i]; sort(arr, arr+N); for (int i = 0; i < N; ++i)cout
2023.07.13
no image
[C++] 백준 13단계 - 2750번 문제 (수 정렬하기)
문제설명 소스코드 #include #include using namespace std; int main() { int N; cin >> N; int* arr = new int[N]; for (int i = 0; i > arr[i]; sort(arr, arr+N); for (int i = 0; i < N; ++i)cout
2023.07.13
no image
[JAVA] main() 메소드의 String[] 매개변수 용도
우리가 자바로 코드를 작성할 때, main함수를 선언하려면 매개변수로 args라는 String 타입 매개변수가 들어간다. 이 args 매개변수는 윈도우의 명령 프롬프트나 맥OS의 터미널에서 프로그램을 실행할 때 args에 원하는 값을 넣을 수 있다. 아래의 코드는 args의 값을 모두 출력하는 코드이다. public class Main { public static void main(String[] args) { for(int i = 0; i < args.length; ++i) { System.out.println(args[i]); } } } 이렇게 작성하고 저장을 한뒤, 명령 프롬프트로 컴파일을 한다. 이후 java 명령어를 이용하여 실행을 하되, 매개변수로 원하는 값을 넣어준다. 아래 예제에서는 매개변..
2023.07.13
no image
[JAVA] 오버플로우와 언더플로우
오버플로우(overflow)란 타입이 허용하는 최대값을 벗어나는 것을 말한다. 반대로 언더플로우(underflow)는 타입이 허용하는 최소값을 벗어나는 것을 말한다. 정수 타입 연산에서 오버플로 또는 언더플로가 발생하면 해당 정수 타입의 최소값 또는 최대값으로 되돌아간다. 예를 들어 byte타입일 경우 최대값 127에서 1을 더하면 128이 되어 오버플로가 발생하여 연산 결과는 최소값인 -128이 된다. 그리고 나서 다시 1을 더하면 -127이 된다. byte value = 127; ++value; System.out.println(value); //-128 마찬가지로 -128에서 1을 빼면 -129가 되어 언더플로가 발생하는데, 연산 결과는 최대값인 127이 된다. 그리고 다시 1을 빼면 128이 된다..
2023.07.12
no image
[JAVA] 기본 연산을 할 때 주의사항
리터럴 기본적으로 컴파일러는 정수 리터럴을 int타입으로 간주한다는 점이다. 따라서 int타입의 허용범위를 초과할 경우 long 타입임을 컴파일러에게 알려줘야 한다. 컴파일러에게 long타입임을 알려주는 방법은 정수 리터럴 뒤에 소문자 l이나 대문자 L을 붙이면 된다. long a = 10000000000; //컴파일 에러 long b = 10000000000L; //정상 또한 실수 리터럴을 기본적으로 double 타입으로 해석하기 때문에, 실수 리터럴을 float타입으로 저장하고 싶다면 리터럴 뒤에 소문자 f나 대문자 F를 붙여 컴파일러가 float 타입임을 알 수 있도록 해야 함 float a = 3.14; //컴파일 에러 float b = 3.14F; // 정상 컴파일 단계 연산과 JVM 연산 자바..
2023.07.12
no image
[C++] 백준 12단계 - 1018번 문제 (체스판 다시 칠하기)
문제설명 https://www.acmicpc.net/problem/1018 1018번: 체스판 다시 칠하기 첫째 줄에 N과 M이 주어진다. N과 M은 8보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다. www.acmicpc.net 소스코드 #include #include using namespace std; int** arr; //2차원 배열 동적할당을 위한 더블포인터 변수 int func(int x, int y) { int case1 = 0; int case2 = 0; for (int i = x; i < x + 8; i++) { for (int j = y; j < y + 8; j++) { if ((i + ..
2023.07.11
no image
[C++] 백준 12단계 - 1436번 문제 (영화감독 숌)
문제설명 소스코드 #include #include using namespace std; int main() { string str; int N; cin >> N; int i = 665; int count = 0; while (true) { ++i; str = to_string(i); //i를 문자열로 바꿈 if (str.find("666") == string::npos) continue; //문자열에 666이 들어가 있지 않다면 pass else //문자열에 666이 들어간다면 { ++count; //count 증가 if (count == N) break; } } cout
2023.07.11

이 게시글은 이것이 자바다(저자 : 신용권, 임경균)의 책과 동영상 강의를 참고하여 개인적으로 정리하는 글임을 알립니다. 


기본적으로 final 클래스를 제외한 모든 클래스는 부모 클래스가 될 수 있다.

또한 부모가 자식을 선택하는 것이 아니라, 자식이 부모를 선택해서 상속을 받을 수 있다.

하지만 Java 15부터는 부모 클래스가 특정 자식 클래스에게만 상속을 허락할 수 있게 되었다.

 

sealed 키워드를 사용하면 permits 키워드 뒤에 상속 가능한 자식 클래스를 지정해야 한다.

public sealed class Person permits Employee, Manager{}

이런 식으로 Person 클래스를 봉인된 클래스로 지정하고 permits 옆에 상속을 허락할 자식 클래스를 지정하면 된다.

 

봉인된 Person 클래스를 상속하는 Employee와 Manager 클래스는 final 또는 non-sealed 키워드로 선언하거나, sealed 키워드를 사용하여 또 다른 봉인 클래스로 선언해야 한다.

public final class Employee extends Person{}
public non-sealed class Manager extends Person{}

final은 더 이상 상속할 수 없다는 뜻이고, non-sealed는 봉인을 해제한다는 뜻이다.

따라서 Employee는 더 이상 자식 클래스를 만들 수 없지만 Manager는 자식 클래스를 만들 수 있다.

아래의 코드에서 Manager를 상속한 Director 클래스는 정상적으로 상속을 받고, Director 객체 또한 정상적으로 생성되는 것을 볼 수 있다.

 

person.java

public sealed class Person permits Employee, Manager{
	public String name;
	public void work()
	{
		System.out.println("백수입니다.");
	}
}

 

Employee.java

public final class Employee extends Person{
	public void work()
	{
		System.out.println("제품을 생산합니다.");
	}
}

 

Manager.java

public non-sealed class Manager extends Person{
	public void work()
	{
		System.out.println("생산 관리를 합니다.");
	}
}

 

Direcotr.java

public class Director extends Manager{
	public void work()
	{
		System.out.println("제품을 기획합니다.");
	}
}

 

Main.java

public class Main {
	public static void main(String[] args) {
		Person p = new Person();
		Employee e = new Employee();
		Manager m = new Manager();
		Director d = new Director();
		
		p.work();
		e.work();
		m.work();
		d.work();
	}
}
/*
백수입니다.
제품을 생산합니다.
생산 관리를 합니다.
제품을 기획합니다.
*/

문제설명

 

소스코드

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
	ios_base::sync_with_stdio(false); //표준 스트림 동기화 해제
	cin.tie(NULL); //입력과 출력 연결 끊기
	int N;
	cin >> N;
	int* arr = new int[N];
	for (int i = 0; i < N; ++i) cin >> arr[i];
	sort(arr, arr+N);
	for (int i = 0; i < N; ++i)cout << arr[i] << '\n';
}

 

설명

  • algorithm 라이브러리에 있는 sort() 함수를 사용
  • 배열 동적할당
  • sort 함수의 첫 번째 매개변수는 배열의 포인터, 두 번째 매개변수는 배열의 포인터 + 배열의 크기를 넣어주면 내림차순으로 정렬이 된다.
  • sort(arr, arr + N)
시간제한이 C / C++기준으로 2초이기 때문에 아래의 과정을 수행해야한다.

C와 C++과의 표준 스트림 동기화 해제입력과 출력 연결 해제endl 대신 \n 쓰기
cout(출력) cin(입력)과의 묶인것을 해제시키지 않고, C와 C++과의 입출력 동기화를 해제하지 않으면 이 문제는 틀리게 된다.
기본적으로 C++에서는 C의 표준 스트림이 동기화가 되어있다.
C와 C++가 동일한 버퍼를 공유한다는 말이다.
이러한 동기화는 성능을 저하시키지만 스레드로부터 안전하기 때문에 동기화 상태로 두는 것이 더 좋긴 하다.
하지만 알고리즘 문제풀이 또는 예외처리나 멀티스레드 작업을 필요로 하지 않는 작업에서는 동기화를 끊어주면 속도가 빨라진다.
(C++ 표준 스트림이 독립적으로 IO(intput, output)버퍼링을 할 수 있다는 것이다. 이렇게 되면 상당히 많은 양의 입출력이 있을 경우 동기화되어있는 상태에 비해 성능이 많이 좋아진다.)

endl은 단순히 줄 바꿈만 해주는 것이 아니라 출력 버퍼를 비우는 역할까지 한다.
매 줄 바꿈마다 endl을 쓰면 입출력을 끊어주는 효과를 볼 수 없다.

문제설명

 

소스코드

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
	int N;
	cin >> N;
	int* arr = new int[N];
	for (int i = 0; i < N; ++i) cin >> arr[i];
	sort(arr, arr+N);
	for (int i = 0; i < N; ++i)cout << arr[i] << endl;
}

 

설명

  • algorithm 라이브러리에 있는 sort() 함수를 사용
  • 배열 동적할당
  • sort 함수의 첫 번째 매개변수는 배열의 포인터, 두 번째 매개변수는 배열의 포인터 + 배열의 크기를 넣어주면 내림차순으로 정렬이 된다.
  • sort(arr, arr + N)

우리가 자바로 코드를 작성할 때, main함수를 선언하려면 매개변수로 args라는 String 타입 매개변수가 들어간다.

이 args 매개변수는 윈도우의 명령 프롬프트나 맥OS의 터미널에서 프로그램을 실행할 때 args에 원하는 값을 넣을 수 있다.

 

아래의 코드는 args의 값을 모두 출력하는 코드이다.

public class Main {
	public static void main(String[] args) {
		for(int i = 0; i < args.length; ++i)
		{
			System.out.println(args[i]);
		}
	}
}

이렇게 작성하고 저장을 한뒤, 명령 프롬프트로 컴파일을 한다.

이후 java 명령어를 이용하여 실행을 하되, 매개변수로 원하는 값을 넣어준다.

아래 예제에서는 매개변수로 10, 20, 30을 넣었다.

 

위에 패키지 익스플로러를 보면 default 패키지이다.

명령 프롬프트에서 10, 20, 30의 값을 주었더니 작성한 코드대로 출력하는 것을 볼 수 있다.


명령 프롬프트로 매개변수를 주지 않고 이클립스에서 주는 방법도 있다.

Run - Run Configurations를 클릭

 

이런 화면이 나오면 Main옆에 Arguments를 클릭한다.

 

원하는 argument를 넣고 apply를 누른다.

실행을 하면 아까 입력했던 값이 나오는 것을 볼 수 있다.

 

오버플로우(overflow)란 타입이 허용하는 최대값을 벗어나는 것을 말한다.

 

반대로 언더플로우(underflow)는 타입이 허용하는 최소값을 벗어나는 것을 말한다.

 

정수 타입 연산에서 오버플로 또는 언더플로가 발생하면 해당 정수 타입의 최소값 또는 최대값으로 되돌아간다.

 

 

예를 들어 byte타입일 경우 최대값 127에서 1을 더하면 128이 되어 오버플로가 발생하여 연산 결과는 최소값인 -128이 된다. 그리고 나서 다시 1을 더하면 -127이 된다.

byte value = 127;
++value;
System.out.println(value); //-128

마찬가지로 -128에서 1을 빼면 -129가 되어 언더플로가 발생하는데, 연산 결과는 최대값인 127이 된다. 그리고 다시 1을 빼면 128이 된다.

byte value = -128;
--value;
System.out.println(value);

short, int, long 타입은 값의 범위만 다를 뿐 오버플로 및 언더플로가 발생했을 때 마찬가지로 최소값 또는 최대값으로 되돌아간다.

리터럴

기본적으로 컴파일러는 정수 리터럴을 int타입으로 간주한다는 점이다.

따라서 int타입의 허용범위를 초과할 경우 long 타입임을 컴파일러에게 알려줘야 한다.

컴파일러에게 long타입임을 알려주는 방법은 정수 리터럴 뒤에 소문자 l이나 대문자 L을 붙이면 된다.

long a = 10000000000; //컴파일 에러
long b = 10000000000L; //정상

또한 실수 리터럴을 기본적으로 double 타입으로 해석하기 때문에, 실수 리터럴을 float타입으로 저장하고 싶다면 리터럴 뒤에 소문자 f나 대문자 F를 붙여 컴파일러가 float 타입임을 알 수 있도록 해야 함

float a = 3.14; //컴파일 에러
float b = 3.14F; // 정상

 


 

컴파일 단계 연산과 JVM 연산

자바는 컴파일 단계 연산과 JVM 연산이 있다.

컴파일 단계 연산은 리터럴끼리 연산을 하게 된다면 컴파일러가 먼저 연산을 하게 되지만, 변수가 하나라도 끼게 된다면, JVM이 연산을 하게 된다.

byte b1 = 1 + 1; //컴파일 단계에서 연산
byte b2 = 1;
byte b3 = b2 + 1; // JVM에서 연산하므로 우변이 int로 바뀌면서 오버 플로
byte b3 = (byte) (b2 + 1); //올바른 코딩

 

byte b1 = 1 + 1; //컴파일 단계에서 연산

이 코드는 컴파일 단계에서 2로 처리한 다음 JVM에게 넘겨준다.

byte b3 = b2 + 1; // JVM에서 연산하므로 우변이 int로 바뀌면서 오버 플로

이 코드는 컴파일러가 연산하지 않고 JVM에서 연산하게 되어서 오류가 발생한다.

 


 

정확한 계산은 정수 연산으로

int apple = 1;
double pieceJnit = 0.1;
int number = 7;

double result = apple - pieceJnit*number;
System.out.println(result); //0.3이 아닌 2.9999999

위 코드와 같이 부동 소수점 방식을 사용하는 실수 타입에서는 정확한 값이 나오지 않는다.

그렇기 때문에 정확한 계산이 필요하다면 정수 연산으로 변경해서 아래와 같이 계산하는 것이 좋다.

int apple = 1;
int totalPrice = apple * 10;
int number = 7;
int result = totalPrice - number;
System.out.println(result/10.0); //0.3

 


 

나눗셈 연산 후 NaN과 Infinity 처리

나눗셈 또는 나머지 연산에서 좌측 피연산자가 정수이고 우측 피연산자가 0일 경우 예외가 발생한다.

무한대의 값을 정수로 표현할 수 없기 때문이다.

int x = 5;
int y = 0;
int result = 5 / 0; //예외 발생

하지만 좌측 피연산자가 실수이거나 우측 피연산자가 0.0 또는 0.0f이면 예외가 발생하지 않고 연산의 결과는 Infinity 또는 NaN(Not a Number)이 된다.

즉, 5 / 0.0 은 Infinity가 나오고, 5 % 0.0은 NaN이 나온다.

Infinity 또는 NaN 상태에서 계속해서 연산을 수행하면 안 된다. 어떤 연산을 하더라도 결과는 계속해서 Infinity와 NaN이 되므로 데이터가 엉망이 될 수 있다.

따라서 Infinity 또는 NaN인지 먼저 확인하고 다음 연산을 수행하는 것이 좋다.

int x = 5;
double y = 0.0;
double z = x / y;
if(Double.isInfinite(z) || Double.isNaN(z)) System.out.println("잘못된 값이므로 연산을 할 수 없습니다.");
else System.out.println("다음 연산을 실행");

문제설명

https://www.acmicpc.net/problem/1018

 

1018번: 체스판 다시 칠하기

첫째 줄에 N과 M이 주어진다. N과 M은 8보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다.

www.acmicpc.net

 

소스코드

#include <iostream>
#include <algorithm>
using namespace std;
int** arr; //2차원 배열 동적할당을 위한 더블포인터 변수
int func(int x, int y)
{
	int case1 = 0; int case2 = 0;
	for (int i = x; i < x + 8; i++)
	{
		for (int j = y; j < y + 8; j++)
		{
			if ((i + j) % 2 == arr[i][j]) ++case1;
			else if ((i + j + 1) % 2 == arr[i][j]) ++case2;
		}
	}
	return min(case1, case2);
}
int main()
{
	int N, M; int min = 1250;
	char input;
	cin >> N >> M;
	arr = new int* [N]; //각 행의 수만큼 동적할당
	for (int i = 0; i <= N; ++i) arr[i] = new int[M]; //각 열의 수만큼 동적할당
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			cin >> input; //2차원 배열에 입력을 받음
			if (input == 'B') arr[i][j] = 0; //B이면 0
			else arr[i][j] = 1; //W이면 1
		}
	}
	for (int i = 0; i <= N - 8; i++) //N-8이 행에서의 최대 행동반경
	{
		for (int j = 0; j <= M - 8; j++) //M-8이 열에서의 최대 행동반경
		{
			int minCount = func(i, j);
			if (min > minCount) min = minCount; //가장 수정해야할 횟수가 적은 값을 저장
		}
	}
	cout << min;
}

 

설명

문제설명

 

소스코드

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str;
	int N;
	cin >> N;
	int i = 665; int count = 0;
	while (true)
	{
		++i;
		str = to_string(i); //i를 문자열로 바꿈
		if (str.find("666") == string::npos) continue; //문자열에 666이 들어가 있지 않다면 pass
		else //문자열에 666이 들어간다면
		{
			++count; //count 증가
			if (count == N) break;
		}
	}
	cout << str;
}