오늘 배운 내용
01. 배열의 복사
02. 접근 제어자와 정보은닉, 캡슐화
01. 배열의 복사
자바의 배열 변수 복사에는 데이터 복사, 주소값 복사 두 가지 형태가 있다.
1. 주소값 복사 == 얕은 복사
: "원본을 수정하면 복사본까지 영향을 준다."
(자바 배열 뿐 아니라 모든 참조형 데이터에 해당하는 복사방식)
2. 데이터 복사 == 깊은 복사
: 실제 요소로 들어있는 값에 대한 복사로,
"원본을 수정해도 복사본에는 영향을 주지 않는다."
실습096
얕은 복사 ( 주소값 복사)
/*=============[ 주소값 복사(얕은 복사) ]=============*/
int[] nums = {10, 20, 30, 40, 50}; // 배열 원본
int[] copys; // 복사본으로 만들 배열
int temp; // 데이터 임시 저장할 변수
// 값 타입 확인 후 배열과 비교
//(포인터를 따라갔을 때 주소가 있으면 참조타입, 값이 있으면 값 타입(데이터타입)
copys = nums; // 원본 데이터를 copys에 대입하겠다! (원본이 바뀌면 따라서 바뀐다.)
// 이 때 copys의 포인터는 nums의 배열을 가리키는 포인터와
// 같은 주소를 가리키고 있다.(같은 주소 공유)
temp = nums[0]; // temp = 10;
// copys 배열의 전체 요소 출력 확인
for (int i = 0; i <copys.length; i++)
System.out.printf("%4d",copys[i]);
System.out.println(); // 10 20 30 40 50
// temp 출력 확인
System.out.println("temp: " + temp); // temp: 10
/*===========[ 원본 배열 데이터 수정 후! ]=============*/
nums[0] = 1000; //----> 0번 데이터 수정
// copys 배열의 전체 요소 출력 확인
for (int i = 0; i <copys.length; i++)
System.out.printf("%4d",copys[i]);
System.out.println(); // 1000 20 30 40 50 ( 값 바뀜 )
// temp 출력 확인
System.out.println("temp: " + temp);// temp: 10 (값 유지 )
실습097
깊은 복사 ( 데이터 복사)
코드블록(097)
public static void main(String[] args)
{
int[] nums = {10, 20, 30, 40, 50}; // 배열 원본
int[] copys1 = nums; // cf. 얕은 복사 (주소값 복사)
int[] copys2 = copyArray(nums); // 깊은 복사 (데이터 복사)
int[] copys3 = (int[])nums.clone(); //---> 배열 객체에 클론 메소드는 없지만..
// 상위 객체에서 상속받아서 사용~
// clone(); : Object 타입 반환.
// clone() 메소드를 통해 깊은 복사 수행
nums[1] = 9999; // 원본 배열 수정 발생!
// 결과 확인 (비교)
for (int i = 0; i < nums.length; i++)
System.out.printf("%6d", nums[i]);
System.out.println(); //---> 10 9999 30 40 50
for (int i = 0; i <copys1.length; i++)
System.out.printf("%6d", copys1[i]);
System.out.println(); //---> 10 9999 30 40 50
for (int i = 0; i <copys2.length; i++)
System.out.printf("%6d", copys2[i]);
System.out.println(); //---> 10 20 30 40 50
for (int i = 0; i <copys3.length; i++)
System.out.printf("%6d", copys3[i]);
System.out.println(); //---> 10 20 30 40 50
}
// 매개 변수로 int배열 타입을 넘겨받아 복사하고, 값을 반환하는 메소드 정의
public static int[] copyArray(int[] target)
{
// 인자로 받은 대상 배열(target), 즉 원본 배열 길이만큼 메모리 공간 확보하여 복사 배열 생성
int[] result = new int[target.length]; //--> new 키워드를 통해 별도의 메모리 공간 생성
//result = target; //--> (X) 이 방식이 아닌, 원본 배열의 각 값을 복사 배열로 담는다!
for (int i = 0; i < target.length; i++)
{
result[i] = target[i];
}
// 복사한 배열 result 반환
return result;
}
/*
10 9999 30 40 50 배열 원본
10 9999 30 40 50 얕은 복사
10 20 30 40 50 깊은 복사(for문)
10 20 30 40 50 깊은 복사(clone() 메소드)
계속하려면 아무 키나 누르십시오 . . .
*/
참고: clone() 메서드
http://www.tcpschool.com/jquery/jq_elementManupulating_cloneDelete
02. 접근 제어자와 정보은닉, 캡슐화
접근(제어) 지시자 == 제어자 == 지정자 == 제한자
접근 가능한 대상을 지정한다.
* 디폴트라는 이름이 있는게 아니고 명시하지 않았을때 자동 적용되는 기본 값.
* 많이 헷갈리는 것: default보다 protected가 더 허용! (상속 받은 클래스!)
아무말 안했을 때 적용되는 기본값~이니까 상속 허용 안 할래!!
ex) 유산 상속! >> 자 이제 이건 니꺼야~ 말해주면?(지켜야 함)
정보 은닉과 캡슐화
- 정보은닉(Information Hiding):
ex) 커피자판기
사용자가 직접 제어할 필요가 없고, 그래서도 안 됨.
버튼으로 제어~ 편리하면서 안전하다.
은닉함으로서 도둑질, 고장냄 등 악의적인 상황을 방지한다.
- 캡슐화(Encapsulation) :
이론적인 이해는 쉽지만, 실무적으로 어려운 개념.
책이나 블로그 찾아보면 정보 은닉으로만 다루는 경우가 많다.
(틀린 건 아니지만.. 얕은 개념.)
*중요* 여러 기능을 하나로 묶는 캡슐은
객체지향 관점을 지키기 어렵다. (서로 저울질 해야 하는 개념)
ex) 종합 감기약 (캡슐) vs 기침약, 가래약, 두통약, 코막힘약......
이 밸런스를 맞추는 게 어렵다. 따라서 업무 파악이 중요.
(내가 실무에서 어떤것을 왜 만들고 뭐가 필요한지 잘 파악해야 이 균형을 맞출 수 있다.)
실습098
일반적으로 하나의 자바 파일에는 하나의 클래스만 구성하지만
실습상 CircleTest2 클래스 함께 구성 --> 컴파일하면 클래스 파일 2개 생성
import java.util.Scanner;
class CircleTest2
{
// int num; //--> (멤버/인스턴스/전역) 변수.
//주의!! 클래스 안에 있다고 해서 클래스 변수가 절대 아님!!
// 정보 은닉(Information Hiding)
private int num; // 접근 지시자 선언으로
//클래스 내부에서만 접근 및 참조가 가능하게 하겠다는 뜻.
// getter / setter 구성
/*
int getNum()
{
return num;
}
void setNum(int num)
{
this.num = num;
}
*/
void input()
{
Scanner sc = new Scanner(System.in);
System.out.print("반지름 입력: ");
num = sc.nextInt();
}
double calArea()
{
return num * num * 3.141592;
}
void write(double area)
{
System.out.println("반지름: " + num);
System.out.println("넓이: " + area );
}
}
public class Test098
{
public static void main(String[] args)
{
// CircleTest2 인스턴스 생성
CircleTest2 ob1 = new CircleTest2();
// ob1.num = 10;
//ob1.setNum(10);
// System.out.println("원의 반지름: " + ob1.num);
//System.out.println("원의 반지름: " + ob1.getNum());
ob1.input(); //--> 현재로써는 유일하게 CircleTest2의 num으로 데이터를 전달할 수 있는 방법
double result = ob1.calArea();
ob1.write(result);
}
}
/*
반지름 입력: 10
반지름: 10
넓이: 314.1592
계속하려면 아무 키나 누르십시오 . . .
*/
실습099
(내코드)(미완)
시간내에 완성 못했지만 할 수 있는 만큼 써보고 고민했던 흔적.
import java.util.Scanner;
class WeekDay
{ // 주요 변수
int year, month, day; // 연, 월, 일 변수
boolean isLeap; // 윤년 여부
String dayOfweek =" "; // 요일
void input() //입력 메소드
{
Scanner sc = new Scanner(System.in);
do
{ System.out.print("년 월 일 입력 (공백구분): ");
year = sc.nextInt();
month = sc.nextInt();
day = sc.nextInt();
}
while ((0 >= month || month >= 13)||(0 >= day|| day >= 32));
}
//윤년/평년 판별 메소드
boolean isLeap()
{
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
isLeap = true;
else
isLeap = false;
return isLeap; // 불리언 값 반환
}
void week() // 요일 연산 메소드
{
if (isLeap) // 윤년일 때
{
}
else // 평년일 때
{
}
}
void print()// 출력 메소드
{
System.out.printf("%d년 %d월 %d일 → %s\n",year, month, day, dayOfweek);
}
} //--> WeekDay Class의 끝
/*=================== 메인 메소드 =====================*/
public class Test099
{
public static void main(String[] args)
{
WeekDay wd = new WeekDay();
wd.input(); // 입력 메소드 호출
//wd.isLeap(); // 윤년 평년 판별 메소드 호출
//wd.week(); // 요일 연산 메소드 호출
wd.print(); // 출력 메소드 호출
}//--> main()의 끝
}
/* --------------------- 생각 메모 --------------------------
평년. (365일)
윤년. (366일)
연도별로 1월 1일의 시작 요일이 다름
// 월별 말일을 배열로
int[] end_day1[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; //평년
int[] end_day2[12] = {31,29,31,30,31,30,31,31,30,31,30,31}; //윤년
만약 평년이면, month값을 위 인덱스로 하여 해당 월의 말일을 얻기
(예를 들어 1월이면 31의 값을 얻는다 )
-----------------------------------------
월말이 28일인 달: 평년일때의 2월
월말이 29일인 달: 윤년일때의 2월
월말이 31인 달: 1,3,5,7,8,10,12
월말이 30인 달: 4,6,9,11
월의 말일은 4가지, (7로 나눈 나머지값)
31 % 7 == 4
30 % 7 == 2
29 % 7 == 1
28 % 7 == 0
----------------------------
// switch 문 (1년 1월 1일 기준)
switch (day)
{
case 1: case 8: case 15: case 22: case 29:
dayOfweek = "월요일";
break;
case 2: case 9: case 16: case 23: case 30:
dayOfweek = "화요일";
break;
case 3: case 10: case 17: case 24: case 31:
dayOfweek = "수요일";
break;
case 4: case 11: case 18: case 25:
dayOfweek = "목요일";
break;
case 5: case 12: case 19: case 26:
dayOfweek = "금요일";
break;
case 6: case 13: case 20: case 27:
dayOfweek = "토요일";
break;
case 7: case 14: case 21: case 28:
dayOfweek = "일요일";
break;
}
-----------------------------------------------------------*/
(강사님 코드)
import java.util.Scanner;
class WeekDay
{
private int y, m, d;
public void input() //입력 메소드
{
Scanner sc = new Scanner(System.in);
System.out.print("년 월 일 입력 (공백구분): ");
y = sc.nextInt();
m = sc.nextInt();
d = sc.nextInt();
}
public String week() // 요일 연산 메소드
{
int[] months = {31,0,31,30,31,30,31,31,30,31,30,31}; //월별 말일
String[] weekNames = {"일","월", "화", "수", "목", "금", "토"};
int nalsu;
if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0 )
{
months[1] = 29;
}
else // 말일을 28일로 설정
{
months[1] = 28;
}
nalsu = (y-1)*365 + (y-1)/4 - (y-1)/100 + (y-1)/400;
for (int i = 0; i < m-1; i++)
nalsu += months[i];
nalsu += d;
/*------------여기까지 날 수 연산 끝 !------------*/
int w = nalsu % 7;
return weekNames[w];
}
/* =========== 출력 메소드 (기능) ============ */
public void print(String day)
{
System.out.printf(">> %d년 %d년 %d일 → %s요일\n", y, m, d, day);
}
} //--> WeekDay Class의 끝
/*=============== 메인 메소드 ====================*/
public class Test099_1
{
public static void main(String[] args)
{
WeekDay wd = new WeekDay();
wd.input(); // 입력 메소드 호출
String result = wd.week(); // 요일 연산 메소드 호출하고 변수에 담기
wd.print(result); // 출력 메소드 호출하고 위의 값 인자로 넘겨주기
}
}
(강사님코드 - 주석있는 버전)
import java.util.Scanner;
class WeekDay
{
// 주요 변수 (속성)
private int y, m, d;// 연, 월, 일 입력값
// private로 제어 (내부에서만 사용할 데이터나 기능)
//입력 메소드 (기능)
public void input() //--> public! 일반적으로 외부에서 사용할 때. (유저 접근 가능)
{
Scanner sc = new Scanner(System.in);
System.out.print("년 월 일 입력 (공백구분): "); // 유효성 검사는 실습 편의상 생략. 실제로는
y = sc.nextInt(); // 연,월,일 각각 맞는 값인지 판별 후 재입력 요구.
m = sc.nextInt();
d = sc.nextInt();
}
/* =========== 요일 연산 메소드 (기능) ============ */
public String week() // 요일 연산 메소드
{
// 월별 말일을 배열로 구성
int[] months = {31,0,31,30,31,30,31,31,30,31,30,31};
// 2월 자리는 윤년 판별후 재할당 필요
// 요일 이름을 배열로 구성
String[] weekNames = {"일","월", "화", "수", "목", "금", "토"};
//한글은 한글자여도 char형에 담을 수 없다.
int nalsu; // 날 수 총합 변수
// 2월 말일 계산
if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0 )
{// y가 윤년이면 2월의 말일을 29일로 설정
months[1] = 29;
}
else // y가 평년이면 2월의 말일을 28일로 설정
{
months[1] = 28;
}
// ① 1년 1월 1일부터, (입력받은 년도(y)의 이전 년도 12월 31일까지)의 날 수 계산.
// : 1일이 월요일인 시작을 기준으로, 지나온 꽉 채운 1년들과, 올해 지나온 날짜를 따로 계산해야하기 때문.
//nalsu 변수에 지나온 날들을 담는다.(전체 일수(올해는 빼고) + 윤년 연산 처리)
nalsu = (y-1)*365 + (y-1)/4 - (y-1)/100 + (y-1)/400; // (지나온 해 * 365일) + (4년마다 하루씩 더한 날짜) - (100년마다..(4년과 겹치니까)) + (400년일때)
// ---------- ------- -------- --------
// 1년 365일 4년마다 +1 100년 +1(취소) 400년 + 1
// System.out.println("날 수: " + nalsu); // 1년 1월 1일 ~ 22년 12월 31일까지의 일 수 // 년 월 일 입력 (공백구분): 2023 2 16
// 날 수: 738520
// ② 입력받은 월(m)의 이전 월까지의 날 수 계산하고 그 값을 1번 결과에 더하기.
for (int i = 0; i < m-1; i++)
nalsu += months[i]; // i월까지의 월 수를 증가.
// System.out.println("날 수: " + nalsu); // 날 수: 738551
// ③ 입력받은 일(d) 만큼 2번 결과에 더하기.
nalsu += d;
// System.out.println("날 수: " + nalsu); // 날 수: 738567
/*--------------------------------여기까지 수행하면 날 수 연산 끝 !-----------------------------------*/
// ④ 무슨 요일인지 확인
int w = nalsu % 7; // 전체 일 수를 7로 나눈 나머지를 w에 담는다. (0인경우 일요일, 1이면 월요일 ... 6이면 토요일)
// System.out.println("w : " + w); // w : 4 ---> 목요일
return weekNames[w]; //---> 배열에서 인덱스와 대응되는 요일을 String형으로 반환한다.
}
/* =========== 출력 메소드 (기능) ============ */
public void print(String day)// 인자는 day라는 이름으로 받을래~
{
System.out.printf(">> %d년 %d년 %d일 → %s요일\n", y, m, d, day);
}
} //--> WeekDay Class의 끝
/*=================== 메인 메소드 =====================*/
public class Test099_1
{
public static void main(String[] args)
{
//Weekday 클래스 기반 인스턴스 생성
WeekDay wd = new WeekDay();
wd.input(); // 입력 메소드 호출
String result = wd.week(); // 요일 연산 메소드 호출하고 변수에 담기
wd.print(result); // 출력 메소드 호출하고 위의 값 인자로 넘겨주기
}//--> main()의 끝
}
/* ----------- 실행 결과 --------------
년 월 일 입력 (공백구분): 2023 2 16
>> 2023년 2년 16일 → 목요일
계속하려면 아무 키나 누르십시오 . . .
년 월 일 입력 (공백구분): 1996 2 6
>> 1996년 2년 6일 → 화요일
계속하려면 아무 키나 누르십시오 . . .
년 월 일 입력 (공백구분): 1999 2 15
>> 1999년 2년 15일 → 월요일
계속하려면 아무 키나 누르십시오 . . .
--------------------------------------*/
오늘 느낀점
접근 제어 지시자, 정보 은닉, 캡슐화에 대한 개념 덕분에
OOP 에 조금 더 가까워진 것 같아서 좋았다.
두루뭉술하게 개념적으로만 알고 있던 것들이 더 와닿는 느낌.
달력 관련 계산은 아직 어색하다. 자주 쓰면서 익숙해져야겠다.
'정리 노트 > 쌍용 KDT_(자바 Spring)' 카테고리의 다른 글
23.02.15(수): 2차원 배열, 난수 Random.nextInt() (0) | 2023.06.27 |
---|---|
23.02.14(화): 배열 기초, 배열의 배열(다차원 배열) (0) | 2023.06.26 |
23.02.13(월): 생성자(Constructor), 초기화 블록, 메서드 오버로딩, 배열 (0) | 2023.06.07 |
23.02.10(금): 지역변수와 전역변수, 클래스와 인스턴스, null, classpath와 package (0) | 2023.05.30 |
23.02.09(목): 반복문 (별 찍기,break, continue), return, 메소드의 재귀 호출 (0) | 2023.03.26 |