- 새로 발생된 난수값과 이전의 난수값들을 비교하기 위해 중첩 for문 사용 - i-- 한 뒤 괜히 더 돌아가는걸 막기위해 break 해주는게 좋다 - 하나 중복을 발견하면 다음꺼부터는 중복일리가 없다, 이전에 이미 다 처리되었기 때문에 - 오름차순 적용
1. num[0] = 5, num[1] = 5 일때, 안의 for문이 돌아가면서 num[0] 과 num[1]을 비교해서 같으므로 i--시킴 2. 그럼 i=0이 되고 break 되어 나옴 이해 바깥 for문에 의해 ++ 되며 i=1 이 됨 3. 다시 num[1]값을 랜덤으로 받는것
오름차순하기 - 정렬알고리즘 통해서 (버블정렬 = 자신의 옆자리에 있는 것과 비교) - Arrays 클래스의 sort()함수이용해서 num 배열이름 넘겨주면 오름차순 정렬해줌(java.util.Arrays)
필드 = 멤버변수 - 메소드 바깥쪽에 정의됨 - 기본자료형은 초기화가 됨
파일 생성 - 메인메소드를 가진 클래스만 public class가 될 수 있다! + 생성자의 접근제어자 주로 public 이다.
클래스 실행하기 1. 클래스 안에 메인메소드 넣기(독립적실행가능) 2. 메인 메소드가 있는 클래스에서 실행시켜주기
필드로 직접 접근하기 예제
class Korean{
//필드
String nation = "대한민국";
String name;
String ssn; //전화번호
public Korean(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
}
public class KoreanEx {
public static void main(String[] args) {
Korean k1 = new Korean("박자바","011225-1234567");
System.out.println("k1.name : " + k1.name);
System.out.println("k1.ssn : " + k1.ssn);
Korean k2 = new Korean("김자바","930525-0654321");
System.out.println("k2.name : " + k2.name);
System.out.println("k2.ssn : " + k2.ssn);
}
}
- k1객체 만든 후 k1. 통해서 필드로 접근 - k2객체 만든 후 k2. 통해서 필드로 접근 - 필드 name, ssn을 private으로 바꾸게 되면 KoreanEx클래스에서 k1.name k2.ssn 해서 접근 불가(출력 불가) 다른 클래스라서, 다이렉트 접근을 막는다
필드값 - nation은 "대한민국"으로 초기화 - name과 ssn은 객체를 생성할때 생성자를 통해 전달된 값으로 각각 초기화됨
할당 시점 - new연산자로 힙메모리에 새로운 공간 생성할때 힙메모리상에 새로운 공간 할당받음(참조형은 다 마찬가지) - 자료형이 String이라서(필드값) null로 자동초기화되지만 여기선 객체생성시 생성자호출시 받는 값으로 초기화 (name,ssn) - 주소를가진 레퍼런스 변수 : k1, k2 - 필드들 nation, name, ssn 의 접근제어자가 default이므로 같은 패키지 내는 모두 접근 가능. 그래서 다른 클래스에서 k1.ssn 가능
그림
자바의 접근 제어자 정리
접근제어자 자신의클래스 같은패키지 하위클래스 다른패키지
private O X X X 생략(default) O O X X protected O O O X public O O O O
+ 같은 클래스 내 : 접근 제어자 의미 없다. 뭐든 접근 된다. - private : 오직 같은 클래스에서만 됨 , 다른 클래스에서 접근하는걸 허용하지 않음, 가장 좁은 접근허용 - public : 가장 넓은 접근 허용함, 다른 패키지(폴더) 안의 클래스까지 접근가능 - default : 중간정도 접근 범위, 같은 패키지(폴더) 내에선 다 가능 - protected : 상속관련, 상속 배우고 난후 공부
접근제어자 범위 비교 가장넓음 public > default > protected > private 가장좁음
생성자 오버로딩 - 생성자 여러개 생성 - 구분되는 점이 있어야함
생성자 오버로딩 구분 1. 매개변수 자료형 2. 매개변수 개수 3. 매개변수 순서
메소드 - 언어에 따라 함수라고도 한다 - 쓰는 이유 : 객체의 동작 + 여러개 코드를 모아두고 한번 메소드가 정의되면 호출을 통해 재사용하기 위해서
package p2022_06_29;
class Calculator {
// 메소드
void powerOn() {
System.out.println("전원을 켭니다.");
return;
}
int plus(int x, int y) { // 지역변수 : x, y, result
int result = x + y;
return result;
// System.out.println("test");
}
//return문 : plus() 메소드를 호출한 곳에 값을 돌려주는 역할
//return문은 메소드 가장 마지막 줄에 사용해야한다.
double divide(int x, int y) {
double result = x / (double) y;
return result;
}
void powerOff() {
System.out.println("전원을 끕니다.");
return;
}
}
public class CalculatorEx {
//p217 ~ 218
public static void main(String[] args) {
Calculator cal = new Calculator();
cal.powerOn();
int result1 = cal.plus(5, 6);
byte x = 10;
byte y = 4;
double result2 = cal.divide(x,y);
System.out.println("result2 : " + result2);
cal.powerOff();
}
}
- Calculator 클래스 앞에 private 쓰면 CalculatorEx 클래스에서 객체를 만들 수 없음(클래스 접근 불가) - Calculator 클래스의 메소드 powerOff 앞에 private쓰면 cal.powerOn() 을 할 수 없음(메소드 접근 불가)
패키지 = 폴더 - 같은 폴더 안 = 같은 패키지 안 - 서로 다른 폴더 안 = 서로 다른 패키지 안 = public만 접근가능
위 예제의 부분1
int plus(int x, int y) { // 지역변수 : x, y, result
int result = x + y;
return result;
//System.out.println("test"); 오류남
}
+ return문 : plus() 메소드를 호출한 곳에 값을 돌려주는 역할 + return문은 메소드 가장 마지막 줄에 사용해야한다. - 메소드가 실행되는동안 필요한 값들을 매개변수로 전달해줌(매개변수 = 지역변수) - 메소드 실행되는동안만 스택에 값 존재, return되면 자동으로 지워짐 - 그래서 메모리 관리 크게 신경안써도 된다.
위 예제의 부분2
cal.powerOn();
- 메인 메소드 안에서 객체 생성하며 생성자 호출후 메소드 호출 - 필드처럼 메소드에 접근할때도 객체.메소드() 로 호출함 (정적메소드가 아닌경우)
위 예제의 부분3
int result1 = cal.plus(5, 6);
- Call by Value로 값 전달하며 메소드 호출 - plus()는 결과를 리턴자료형 int로 돌려줌 그래서 int result1으로 받아야하는것
위 예제의 부분4 (주의)
double divide(int x, int y) {
double result = x / (double) y;
return result;
}
- 리턴자료형이 double이므로 double형 값을 돌려준다 - int형인 x와 int형인 y를 산술연산시 int형으로 처리됨 ex) x = 10 , y = 4 이면 다 잘리고 2만 나온다. (손실 발생) - 손실 발생을 막기 위해서 (double)을 붙여줘서 강제 형변환 -> 2.5 - 그걸 double로 return 시켜줌 - (double) 을 빠뜨리면 자동형변환되어서 2 -> 2.0 이 나옴(손실발생) - 10/4 는 2.5이므로 살리려면 (double)해서 강제형변환시켜서 값이 double로 처리되도록 해야함.
위 예제의 부분5(CalculatorEx의 main 메소드 내부)
byte x = 10;
byte y = 4;
double result2 = cal.divide(x,y);
System.out.println("result2 : " + result2);
+ byte는 1바이트 정수형 자료형 - cal.divide에서 매개변수로 byte를 넘김 -> 근데 devide 메소드의 매개변수는 int 형 4바이트 이다. - 4바이트짜리 변수에 1바이트 짜리 값을 할당 => 컴파일러가 자동형변환시켜줌. 큰자료형으로 작은값으로 들어가므로 - 자동형변환되어서 위 코드 가능함
+ plus와 divide 메소드의 매개변수 이름들이 같지만 그 메소드 내에서만 사용되는 지역변수이므로 충돌이 발생하지 않음
메소드의 매개변수에 참조형이 오는 예제 - 값이 아닌 주솟값을 전달해야한다. - sum1은 배열에 전달된 값들의 합을 돌려주는 함수
package p2022_06_29;
//p220 ~ 221
class Computer {
// 주솟값 전달에 의한 메소드 호출(Call by Reference 방식)
int sum1(int[] values) {
int sum = 0;
for (int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
// vargus : 전달된 값은 배열로 받음
int sum2(int... values) {
int sum = 0;
for (int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
}
public class ComputerEx {
public static void main(String[] args) {
Computer com = new Computer();
int[] values1 = { 1, 2, 3 };
int result1 = com.sum1(values1); // sum1() 메소드 호출 , 주솟값을 가진 values1을 넘김
System.out.println("result1 : " + result1);
int result2 = com.sum1(new int[] { 1, 2, 3, 4, 5 }); // sum1() 메소드 호출 , 배열을 만들어서 그 주소를 넘김
System.out.println("result2 : " + result2);
int result3 = com.sum2(1, 2, 3); // sum2() 메소드 호출 ,
System.out.println("result3 : " + result3);
int result4 = com.sum2(1, 2, 3, 4, 5); // sum2() 메소드 호출 ,
System.out.println("result4 : " + result4);
}
}
- "같은 int형 배열"의 주소값을 써서 메소드를 호출해야함 - Computer 클래스의 참조형변수인 int[] values는 아직 주솟값이 없는 상태 , new를 써서 int형 배열을 만들어 주솟값을 가진 걸 넘겨줘야함 - 그래서 main쪽에 int형 1차원배열을 만들어줌 - sum1에서 참조형이 매개변수로 왔으므로 같은 int형 배열의 주솟값을 넘겨야함 - new연산자를 아직 안썼으므로 아직 int[] values는 가리키는게 없음 - 주솟값 전달에 의한 메소드 호출임 + 값전달보다 주솟값 전달이 더 효율적이고 빠른 방식임
vargus (...) - ...은 vargus 라고 함 - 전달된 값들은 1개일수도 여러개일수도 있지만 전달되는 값들은 모두 배열로 받음 - 매개변수가 있으면 매개변수의 개수대로 메소드 호출시 써줘야하지만 ... 은 int값을 1개 전달하든 여러개 전달하든 모두 배열로 처리됨
Call by Reference - values1 int형 배열 힙메모리상에 4바이트 공간 3개 만들어지고 1,2,3이 들어가있고, 그 걸 values1이 그 스택에서 그 배열의 주소를 가지고 있음
int result1 = com.sum1(values1)
- 에서 주솟값을 담고있는 values1을 전달 = 주소를 전달 = Call by Reference - 주솟값이 없었던 Computer 클래스의 sum1의 values는 주솟값을 전달받아서 Computer 클래스의 values는 이제 이 {1,2,3} 배열을 가리키게됨 - 합을 구해서 리턴형 int로 result1에 값을 돌려줌
- 매개변수 중에서 참조형이 오는 경우 중 클래스/인터페이스가 가장 많다(배열은 많진 않다) - API 제공 클래스 + 사용자 정의 클래스 => 이것들도 자료형이다 - 참조형변수는 주솟값을 가진다
매개변수에 주소값을 전달하는 법(위 예제 부분)
int[] values1 = { 1, 2, 3 };
int result1 = com.sum1(values1); // sum1() 메소드 호출 , 주솟값을 가진 values1을 넘김
System.out.println("result1 : " + result1);
int result2 = com.sum1(new int[] { 1, 2, 3, 4, 5 }); // sum1() 메소드 호출 , 배열을 만들어서 그 주소를 넘김
System.out.println("result2 : " + result2);
1.이렇게 values1 처럼 만들어진 배열공간의 주소값을 전달하는방법이 있다. - values1이 가진 주솟값을 전달
2.(new int[] { 1, 2, 3, 4, 5 } 처럼 오른쪽의 new연산을 써서 새로운 공간을 만들어서 그것의 주솟값을 넘겨줄 수도 있음 - 기억공간이 만들어지고 그거의 주솟값이 sum1 메소드의 매개변수로 넘어감 - 값을 받는 변수가 없기 때문에 이건 재사용이 안된다. 이후 가비지 콜렉터가 이 힙메모리상 배열을 지워버린다. - 더이상 이 힙메모리에 접근할수없을때 해제된다. - 할당시점은 new를 썼을때 할당된다
그림
+ 배열 생성
int[] score = {80,90,100} // new안쓰지만 힙메모리에 데이터 개수만큼 공간 만들어짐
int[] score = new int[]{80,90,100}
// vargus : 전달된 값은 배열로 받음
int sum2(int... values) {
int sum = 0;
for (int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
- 메소드의 매개변수가 2개면 2개 전달 1개면 1개 전달 해야했었다. - but vargus쓰면 1개 전달하든 2개전달하든 3개전달하든 모두 매개변수로 받을 수 있다 - 전달된 값은 모두 배열로 처리됨 ex) 첫번째 전달된값은 0번방 두번째 전달된 값은 1번방... - 다만 자료형은 맞춰줘야함 int ... values 면 몇개이든 int형 값을 전달해야함
2. 일반메소드에서 메소드 호출시(같은 클래스 내부) - 객체.메소드() 가 아니라 객체없이 그냥 메소드() 만으로 호출 가능하다!
ex)
double result = avg(7, 10);
- avg() 메소드 호출 그냥 avg(7,10)만으로 호출했음
위 예제 흐름 - JVM이 main메소드 실행 -> mycal.execute() 호출함-> execute()이 실행됨 -> 그 메소드에서 avg메소드를 호출하면서 매개변수를 7, 10 넘겨줌 -> avg 매개변수의 x에 7, y에 10이 들어감, avg에서 다시 plus() 메소드를 7,10을 넘겨주며 호출 -> plus() 메소드에서 합을 구해서 합을 result 해서 plus()를 "호출"한곳에 값을 돌려줌 즉 avg로 돌아감 -> 그걸 avg의 sum이 받아서 %2 한뒤 돌려줌 - (주의) plus()가 돌려줄땐 int형인 17을 돌려줌, 근데 sum을 double로 받았으므로 17.0 해서 /2 해서 8.5가 result가 됨 -> 그 8.5 값을 avg를 호출한 execute()에 돌려줌, 그걸 println()메소드에 결과값을 넘겨서 -> println()이 출력시킴
+ 자바는 객체지향언어라서 호출할 메소드가 뒤에있어도 상관없다 + execute() 아래 println() 있지만 execute()에서 println() 호출가능 + 일반 메소드끼리는 메소드 이름만으로 서로를 호출한다
메소드 오버로딩(Method Overloading) - 1개의 클래스 안에 동일한 이름을 가진 메소드를 여러개 정의하는 것. - 생성자 오버로딩 처럼 메소드도 한개의 클래스 안에 동일한 이름의 클래스 여러개 정의할 수 있다
메소드 오버로딩 구분하는 법 1. 매개변수 자료형 2. 매개변수 개수 3. 매개변수들의 순서
- 한가지를 이상을 만족해서 구분이 되어야만 올바르게 메소드 오버로딩 시킨것임 * 나중에 메소드 오버라이딩과의 차이를 알아야 한다. 완전히 다른 개념 + 메소드 오버라이딩 : 두 클래스 사이에 상속이 정의되어야만 사용가능
String 클래스 (API 문서) - 필드 생성자 메소드 순 - 필드 : 메서드 바깥쪽에 정의되는 변수
- 생성자 : 클래스명과 동일, 형식상 괄호가 반드시 있다 - 매개변수가 없는 생성자는 기본생성자 - 나머지는 클래스명과 동일하게 생성자가 만들어지는데 구분이 되도록 정의됨 -> 생성자 오버로딩
- 메소드도 똑같은 이름을 가진 메소드들이 있다 ex) copyValueOf(char[] data) 와 copyValueOf(char[] data, int offset, int count) - equlas 메소드, format 메소드들은 같은이름인 메소드가 여러개다 -> 메소드 오버로딩
- 메소드가 같은 이름이다 - 매개변수가 1개인 areaRectangle 과 매개변수가 2개인 areaRectangle - 매개변수의 개수가 다르므로 올바르게 메소드 오버로딩 되었음 - 매개변수 1개 전달하면 매개변수 1개인 areaRectangle() 이 호출되고 2개 전달하면 매개변수 2개인 areaRectanble()이 호출됨 - 메소드 오버로딩은 아무 언어나 되는게 아니라 객체지향언어만됨(c++,java는 되고 c는 안됨)
앞으로 공부할 내용
1. static이 붙은 정적 필드(정적변수) 2. static이 붙은 정적 메소드
변수 지역변수 멤버변수 정적변수 중에서 정적변수를 하는 것이다
3. 상수를 만드는 법 (Constant) - final을 붙이거나 static final 을 붙인다 - 기존 변수에 final 붙이면 상수가 되는것 - final을 클래스, 필드, 메소드 에 붙일 수 있다-> 어디에 붙이냐에 따라 의미가 달라짐 4.접근제한자와 패키지 의 관계 5. getter와 setter 메소드
정적 필드 (정적 변수)
- static 이 붙은 변수
ex)
static int a; // 그냥 변수앞에 static을 붙이면 정적변수가 됨
- static : 공유를 목적으로 해서 누구나 쉽게 접근하도록 만들때만 static 붙인 정적 필드형태로 만듬 - API 보면 클래스들 정적필드, 메소드들 정적메소드들 엄청 많다
ex) Math 클래스안엔 정적필드랑 정적메소드뿐 - 정적 필드는 클래스명.필드 로 사용 - 누구나 쉽게 생성할수있도록 따로 객체생성없이 클래스명.필드로 사용함
ex)
int n = Integer.parseInt(String s);
- 에서 parseInt는 정적메소드라서 이렇게 클래스명.parseInt()로 사용함 - 세번째 변수인 정적멤버변수(정적 필드) - 정적 필드 = 정적 멤버변수 = 정적 변수 - 메모리상에 할당되는 시점과 저장되는 영역이 (지역변수,멤버변수, 정적변수) 다 다르다(잠시 후 공부)
Math 클래스(정적 필드/메소드 예시) - 내부에 정적 필드 뿐이다 - 정적필드 정적메소드 뿐 - 정적필드 E , PI 는 Math.E 로 접근가능 - 정적 메소드 , 정적 필드뿐이라 객체 생성할 필요가 없음 - math클래스는 생성자가 아무것도 없다 -> 생성자 자체를 못씀 - random() 메소드의 타입은 static double이다. 즉 정적메소드이고 리턴값이 double - 정적 메소드 이므로 Math.random()으로 사용할수있다
정적변수의 메모리 할당 시점/장소 - 할당 시점 : 정적 멤버변수는 클래스가 메모리에 로딩되면(실행될때) 할당됨 - 초기화 : 자동으로 설정되고 (정수형 0 실수형 0.0 논리형 false) - 값 유지 : 프로그램이 종료될때까지 값을 유지한다 - 저장 영역 : 메모리의 static영역 (공유영역)에 자동으로 할당됨 - 저장 영역 : 공유영역 = 메소드영역 = static 영역 <->힙,스택과 다름 - 주의 : 장 오래 메모리에 상주하므로 많이쓰면 메모리가 부족해짐, 특별한경우아니면 만들지 않음
+ 특별한 경우, 반드시 정적멤버변수 만들어야하는 경우 - 싱글톤 : 객체생성을 1번만 하는 것, 이 경우엔 정적필드를 만들어야함
자동초기화 - 정적멤버변수는 자동초기화 된다. - 배열과 마찬가지로 int는 0 , double 은 0.0, boolean은 false로 초기화 된다. - 메소드영역에 값을 저장하고 한번 저장된값은 프로그램이 완전히 종료될때까지 값을 유지
정적 멤버변수에 접근하는 방법 - 정적 변수를 가지고 있는 클래스명으로 직접 접근 할 수 있다.
class StaticTest{
satic int a;
public static void main(String[] args) {
System.out.println(StaticTest.a)
}
}
- 정적메소드 사용시 클래스명.메소드()
정적 필드 / 정적 메소드 정리 - 정적 필드 = static이 붙은 변수 - 정적 메소드 = static이 붙은 메소드 - static이 붙은 필드를 가진 클래스가 로딩이 되면 이 정적필드는 메소드 영역에 저장이되고 자료형에 따라 기본자료형 변수들은 자동초기화됨
package p2022_06_29;
public class NoneObject {
static int number = 3; // 정적 필드
public static void printNumber() { // 정적 메소드
System.out.println("number = " + number);
}
}
- 메인메소드없으므로 독립적 실행은 안됨, - 정적필드하나 정적메소드 하나 있는 NoneObject 클래스 - 이 클래스의 필드에 접근하고 메소드를 호출해보자
package p2022_06_29;
public class UseNoneObject {
public static void main( String[] args ) {
//이 방식 잘 안쓴다
NoneObject no = new NoneObject();
System.out.println("no.number = " + no.number);
no.printNumber();
//아래방식을 주로 쓴다
System.out.println("NoneObject.number = " +
NoneObject.number);
NoneObject.printNumber();
}
}
출력 no.number = 3 number = 3 NoneObject.number = 3 number = 3
- NoneObject 클래스는 정적필드와 정적메소드를 가지고 있다 - UseNoneObject 클래스에서 접근할때 객체를 생성한 후 필드와 메소드를 사용할 수도 있지만 - 정적 필드와 정적메소드이므로 이렇게 안쓴다 (가능은 하지만 안쓴다) - 필드 접근/메소드 호출하는 법 : NoneObject 클래스에서 .을 찍어서 정적필드, 정적메소드 로 접근
정적 필드 / 정적 메소드 접근 정리 - 누구나 쓸수있도록 하려고 static 붙인거임-> 객체생성해서 접근하지 않는다 - 결론 : 정적필드 , 정적메소드는 클래스명.필드, 클래스명.메소드() 해서 접근해라
정적 필드의 자동초기화 - 예제 : NoneObject.java 파일
package p2022_06_29;
public class NoneObject {
static int number = 3; // 정적 필드
public static void printNumber() { // 정적 메소드
System.out.println("number = " + number);
}
}
- 에서 static int number 에 3 초기화 안시킨다면? - 자동으로 0으로 초기화시켜주므로 UseNoneObject 에서 실행해보면 0으로 출력됨
정적 멤버변수의 메모리 할당/해제(위 예제) - NoneObject클래스가 로딩되자마자 number는 공유영역에 저장되면서 int형이니 0으로 초기화됨 - number의 값은 프로그램 종료시까지 계속 메모리에 상주함 - static 정적 필드 많이쓰면 메모리 부족
객체를 생성해서 정적필드/정적메소드에 접근(위 예제 부분)
//이 방식 잘 안쓴다
NoneObject no = new NoneObject();
System.out.println("no.number = " + no.number);
no.printNumber();
- 가능은 하다 Q. 그럼 이때 new를 쓰면 정적필드는 어디로 저장될까? (질문하기) -> 그래도 메소드 영역
메모리 정리 - 힙 메모리에 저장 : JVM 시작할때 생성, 객체와 배열 저장, 가비지 콜랙터가 참조되지 않는 즉, 사용될수 없는 객체 제거 - 메소드영역 메모리 : static이 붙은 필드를 가진 클래스가 로딩이 되면 이 정적필드는 메소드 영역에 저장이됨.
static 필드의 의미(그 안의 값)
정적 필드 값의 유지 예시1 : StaticCount.java, Shared.java / 메인메소드는 Shared 클래스에만 있다
package p2022_06_29;
public class StaticCount {
static int number = 3;
}
- 정적 필드 하나만 있다. 프로그램 종료까지 값을 계속 유지함 - 정적필드는 싱글톤을 만들때만 주로 쓴다. 객체를 1개만 생성해서 공유를 한다는 개념인 싱글톤
- 에서 StaticCount 클래스로 sc1라는 객체를 만들었음 - (정적필드, 정적메소드 가진 클래스로 객체 만드는건 자주안하지만 가능하긴 함) - sc1을 통해서 접근 된다!
값의 변화(값이 유지됨) 1. 처음 sc1.number 출력했더니 3 을 출력함 = 초기값이 3임 2. sc1.number++; 해서 sc1통해서 증가시킴 3. sc1.number 출력시 4를 출력함
- 그 후 같은 StaticCount로 sc2 라는 새로운 객체도 만듬
4. 근데 힙메모리 영역이 아니라 메소드 영역이라는 공유영역이기 떄문에 sc2.number로 sc2에 접근해서 출력해도 number는 3이 아닌 가장 마지막에 저장한 값 4를 출력시킴 5. sc2.number 로 또 접근해서 ++ 증가시킴 6. sc2.number 출력시 5를 출력
- 마지막엔 객체가 아닌 StaticCount.number 로, 즉 클래스 명으로 접근한다 6. StaticCount.number 를 ++ 7. 출력하면 6이 나옴
정적 필드 값의 유지 예시2 : StaticTest01.java
package p2022_06_29;
class StaticTest {
static int a = 10; // 정적 필드 : 메소드영역(공유영역) 에 저장
int b = 20; // 인스턴스 멤버변수 : heap 메모리에 저장
}
class StaticTest01 {
public static void main(String[] args) {
System.out.println("StaticTest.a->" + StaticTest.a);
StaticTest s1 = new StaticTest();
StaticTest s2 = new StaticTest();
System.out.println("s1.a->" + s1.a + "\t s2.a->" + s2.a);
System.out.println("s1.b->" + s1.b + "\t s2.b->" + s2.b);
s1.a = 100;
System.out.print("s1.a->" + s1.a);
System.out.println("\t s2.a->" + s2.a + " ");
System.out.println(StaticTest.a);
s1.b = 200;
System.out.print("s1.b->" + s1.b);
System.out.println("\t s2.b->" + s2.b);
}
}
- StaticTest01이 메인메소드가지고 있다 - StaticTest 클래스에는 static 정적 변수 1개, 일반적인 필드 1개 이렇게 있다 - 이런 일반적인 필드 는 instance 변수 또는 instance 필드 라고 부름 - 즉 a는 정적필드 b는 인스턴스필드
정적필드와 인스턴스 필드 비교 - StaicTest.a 는 정적필드기 때문에 클래스명. 으로 접근 가능 - 이때 초기값이 10으로 있기떄문에 10으로 출력됨
- 그 후 StaticTest 클래스로 new해서 객체 2개를 생성함 s1, s2 - new연산자로 할떄마다 힙메모리상에 새로운 공간을 할당받음 - 그래서 인스턴스 멤버변수인 b는 힙 메모리상에 할당받음 - 정적 변수인 a는 그대로 공유 영역에 있음
현재 상태 a=10 //정적 멤버변수 (공유영역)
s1 -------> b=20 s2 -------> b=20
(Stack영역) (Heap영역)
초기 상태 - b는 s1의 b, s2의 b 각각 있고, 힙메모리 상에 20으로 각각 있다. - 이 값의 주소를 s1, s2가 스택메모리에서 갖고 있음
- a는 정적멤버변수라서 공유영역(메소드영역) 에 초기값 10을 저장하고 있는 상태 - 그래서 s1.a, s2.a는 10으로 출력됨 s1.b나 s2.b는 20으로 출력됨
변화 - s1.a 에 접근해서 값을 100으로 수정함 - s1.b 에 접근해서 값을 200으로 수정함
a=100 //정적멤버변수 (공유영역)
s1 -------> b=200 s2 -------> b=20
(Stack영역) (Heap영역)
변화된 상태 - s1.a 와 s2.a 모두 출력시 100이 나옴 - 또한 클래스명으로 접근한 StaticTest.a 해도 100이 출력됨 - 즉 a는 가장 마지막으로 저장된 값이 공유됨
정적필드와 인스턴스 필드의 비교 - 정적 필드들은 마지막에 저장된 값을 계속 공유함 - 인스턴스 필드들은 클래스를 가지고 객체 생성할때마다 힙 공간 할당받으며 각각 초기값을 가지게 됨
정적 필드와 접근제어자 예제 : StaticTest02,03,04
StaticTest02.java
package p2022_06_29;
class StaticTest2 {
private static int a = 10; // 정적 필드
private int b = 20; // 인스턴스 필드
public static void setA(int new_a) { // 정적 메소드
a = new_a;
}
public static int getA() { // 정적 메소드
return a;
}
}
public class StaticTest02 {
public static void main(String[] args) {
// System.out.println(StaticTest2.a); //a가 private으로 선언되어서 컴파일 에러 발생
System.out.println(StaticTest2.getA());
StaticTest2 s1 = new StaticTest2();
StaticTest2 s2 = new StaticTest2();
s1.setA(10000);
int res1 = s1.getA();
System.out.println(res1);
System.out.println(s2.getA());
}
}
출력 10 10000 10000
- StaticTest2.a 로 접근이 안된다. - 이유 : 아무리 정적필드라도 외부클래스의 접근을 허용하지 않는 private 이 a 앞에 붙어서 다른 클래스인 StaticTest02 에서 접근시 에러 생김
private static int a = 10;
- 모순이 발생한다 - static은 공유를 하겠다는거고 private은 보호를 하는거라서 컴파일상 오류를 발생시키며 접근이 불가능하다. - 결과적으로는 클래스명으로 접근하는 것이 허용되지 않음
private인 static 필드값 가져오는 방법 - getA() 메소드를 이용해서 접근한다 - getA() 메소드는 정적 메소드이고 public 이라 클래스명으로 접근가능하다\ ex)
System.out.println(StaticTest2.getA());
- 가능하다, 10이 출력됨
private인 static 필드값 설정하는 방법 (위 예제 부분)
StaticTest2 s1 = new StaticTest2();
StaticTest2 s2 = new StaticTest2();
s1.setA(10000);
int res1 = s1.getA();
System.out.println(res1);
System.out.println(s2.getA());
- StaticTest2로 새로운 객체 2개 s1, s2 생성함 - setA 메소드는 public static 이라 호출 가능하다. 클래스명으로 접근 가능하지만 이렇게 s1.setA(10000)처럼 객체로 접근도 되긴함 - s1.set(10000)으로 a값을 10000으로 셋팅 - s1.getA() 해서 접근도 되고, s2.getA() 해서도 됨 둘다 출력시 10000 됨
정적 필드와 접근제어자 결론 - private static하면 모순이 생겨서 결국은 다른 클래스에서 접근 못하는 필드임 - 그래서 StaticTest2.a가 에러생김(다른 클래스에서 호출하였기 떄문에) -> private이 붙으면 정적 static 필드라도 다른 클래스에서 접근이 불가능함
정적 필드와 접근제어자 주의 - 정적 메소드 안에는 정적 필드만 사용가능함 (중요) - 정적 메소드 안에 일반적인 필드 쓰면 오류발생 (저장영역과 기능이 다르기 떄문에) - 정적메소드와 일반메소드는 기능이 다름
정적 필드 / 정적 메소드 주의 예제1 : StaticTest03
package p2022_06_29;
class StaticTest3 {
private static int a = 10; // 정적 필드
private int b = 20;
public static void printA() { // 정적 메서드에서는 this를 사용하지 못함
System.out.println(a);
// System.out.println(b); //정적메소드 안에는 일반적인 필드 사용불가
// System.out.println(this.a); //컴파일 에러 발생
}
public void printB() { // this는 인스턴스 메서드에서 여러 객체에 의해서
System.out.println(b); // 메서드가 호출될 때 이를 구분하기 위해서 사용된다.
}
}
public class StaticTest03 {
public static void main(String[] args) {
StaticTest3.printA();
StaticTest3 s1 = new StaticTest3();
StaticTest3 s2 = new StaticTest3();
s1.printB();
s2.printB();
}
}
정적메소드를 쓸 때 주의사항
1. 정적 메소드에서는 this 레퍼런스 변수를 사용할 수 없다. - this.는 힙메모리의 주소를 참조하는게 this.이고, this.는 개별적인 객체에 대한 주소를 가진 레퍼런스 변수라서 정적메소드 안에선 this. 를 사용할 수 없다
2. 정적 메소드에서는 일반적인 변수(인스턴스 필드)를 사용할 수 없다. - 정적 메소드에서는 정적멤버변수만 사용가능함 - 일반적인 필드 = 힙메모리 이고, 정적필드 = 메소드 메모리(영역) 이라서 영역이 다르기 때문
3. 정적 메소드는 메소드 오버라이딩되지 않는다. - static이 붙은 정적메소드는 메소드 오버라이딩이 되지 않는다.
+ 메소드 오버라이딩 : 두 클래스 사이 상속관계가 있을때, 부모로부터 상속되는 필드와 메소드 중, 메소드를 상속받을때, 이름과 형식은 똑같이 하면서 내부 내용은 다르게 쓰는 것.
정적 필드 / 정적 메소드 주의 예제2 : StaticTest04
package p2022_06_29;
class StaticTest4 {
private static int a = 10; // 정적필드
private int b = 20;
public static void printA() {
System.out.println(a);
// System.out.println(b); //컴파일 에러 발생 : 정적메소드 안 일반필드
}
public void printB() {
System.out.println(b);
}
}
public class StaticTest04 {
public static void main(String[] args) {
StaticTest4.printA();
StaticTest4 s1 = new StaticTest4();
StaticTest4 s2 = new StaticTest4();
s1.printB();
s2.printB();
}
}
- 정적메소드 안에는 정적필드만 사용가능하다 - 일반 메소드 안에서만 일반필드 사용가능 - 정적 메소드 , 정적 필드는 클래스명. 해서 사용함 ! StaticTest4.printA(); - 하지만 객체를 만들어서 객체.메소드 해서도 메소드 호출가능, 객체.필드 해서 필드 접근하는것도 가능
StaticTest4 s1 = new StaticTest4();
StaticTest4 s2 = new StaticTest4();
s1.printB();
s2.printB();
Math 클래스 (API 문서)
- 모두 정적필드 뿐 - 모두 정적메소드 뿐 - Math 클래스를 가지고 정적 필드와 정적메소드를 사용해보자
Math의 정적필드 2개 - E - PI
Math의 생성자 - 생성자 자체가 없다 - 생성자는 제공되지 않는데 쓸수없다 Math 클래스로 new해서 객체생성하면 오류발생
Math의 메소드 - 모두 static 붙은 정적 메소드 -> 굳이 객체를 생성할 필요도 없다 - 그냥 Math.E , Math.random() 으로 쓰면됨
Math 클래스의 필드 사용 예제 + Math라는 이름으로 사용자 지정 클래스 생성할 수 없다. 라이브러리에 이미 쓰고 있는 이름이라서
ex) max(int a, int b) , max(double a, double b) 등 - 2개짜리 매개변수 있는 메소드들만 있다 - 자바에는 2개짜리만 제공함 -> 3개 이상이 필요하면 내가 만들어서 써야함 (파이썬은 제한 없음) - 10과 20중 큰 값을 return 구문으로 돌려줌
우리가 쓰는 Scanner 객체 생성하고 안에 System.in 썼던 이유 - Scanner 클래스를 찾아보면 Scanner(InputStream source)라는 생성자 가 있다. - Scanner 생성자의 매개변수가 InputStream 타입이라서 - Scanner 객체 생성할때 생성자의 매개변수가 System.in 이었다 - System.in의 리턴형을 보면 InputStream 이다 + in은 static이 붙은 정적필드라서 System.in 이라고 쓴다.
지역변수 - 메소드 안에서 정의되고 안에서 사용되는 변수 - 기본자료형일때 메소드 호출시에 스택메모리에 할당되고, 메소드 실행이 종료되면 자동으로 메모리가 해제됨
멤버변수 (= 필드 = 전역변수) - 메소드 바깥쪽에 정의되는 변수 - 기본자료형 멤버변수는 heap 메모리 영역에 저장됨. - 그 클래스(생성자,메소드)안에서 모두 사용이 가능함 = 전역변수 - 자동으로 초기화 됨 - 참조형을 이용해서 new 연산자로 객체를 생성시 heap 메모리 영역에 새로운 공간을 할당받는데, 그 공간이 멤버변수를 위한 공간 - 가비지 콜렉터가 필요없는 힙메모리상의 변수들을 지워줌
클래스 형태의 자료형 ex) Animal클래스는 클래스 형태의 자료형이다. - 사용자가 만들어쓰는 자료형이다
클래스 - 필드 - 생성자 - 메소드
접근제어자를 사용하는 곳 - 클래스 (클래스를 구성하는) - 필드 - 생성자 - 메소드
접근제어자 역할 - 외부 클래스에서의 접근을 막을지 허용할지 설정
생성자의 역할 - 객체를 생성할때 호출되면서 이 필드값을 원하는 값으로 초기화 시키는 역할
생성자 종류 - 매개변수가 없는 생성자 = 기본생성자 - 매개변수가 있는 생성자
생성자와 메소드의 구분 1. 생성자 앞에는 접근제어자만 온다! 2. 생성자는 클래스명과 동일한 이름
필드의 역할 - 변수기 때문에 값을 저장하는 역할만 함
메소드 - 기능이 가장 많음 - 동적인걸 표현함
필드의 초기화 - 배열처럼 필드는 자동 초기화가 된다. (int는 0으로 초기화)
사용자정의 클래스 예시
Animal a1 = new Animal();
- Animal은 클래스 자료형 - a1은 레퍼런스 변수 - new는 연산자 - Animal() 은 생성자를 호출함
생성자 안만들면 - 컴파일러가 자동으로 기본생성자 만들고 호출
+ 생성자 만들었을땐 - 매개변수가 있는 생성자를 만들면 기본생성자는 자동으로 만들어지지않음 - 만들어진 생성자들만 사용가능
생성자 호출하는 방법 1. 객체를 생성할때 호출됨 2. this()를 통해 호출할 수도 있음
필드 접근 - 객체.필드 로 접근 ex) a1.age 처럼 .으로 접근 가능하다 ex) age만으로 접근 불가능하다 - 힙메모리의 필드에 접근하기 위해서는 반드시 주솟값을 가지고 있는 a1으로 참조해서만 접근 가능
파일과 클래스 - 한개의 소스파일안에 클래스 여러개 작성해도 나중에 바이트 코드는 따로 만들어짐 ex) 한개의 파일안에 클래스가 2개들어가는 예제 CarEx
파일명 - public 접근 제어자는 메인 메소드를 가진 클래스만 쓸 수 있고 그 클래스와 파일명이 같아야함 - 하나의 소스파일에는 public class 가 하나만 올 수 있고 public class명과 파일명이 같아야함
예제
class Car {
// 필드(field)
String company = "현대 자동차"; //주어진 초기값
String model = "그랜저";
String color = "검정";
int maxSpeed = 350;
int speed;
// 생성자(Constructor)
public Car() {// 기본 생성자
System.out.println("생성자 호출 성공");
}
}
public class CarEx {
public static void main(String[] args) {
// 객체 생성
Car mycar = new Car();
System.out.println("제작회사 : " + mycar.company);
System.out.println("모델명 : " + mycar.model);
System.out.println("색깔 : " + mycar.color);
System.out.println("최고속도 : " + mycar.maxSpeed);
System.out.println("현재속도 : " + mycar.speed);
// 필드값 변경
mycar.speed = 60;
System.out.println("수정된 속도 : " + mycar.speed); // 60
}
}
- 선언과동시에 초기화가 되지 않은 speed는 , 객체가 생성되어 힙메모리상에 speed 공간이 생성될때 0으로 초기화 된다 - Car()은 생성자가 아니라 생성자를 호출하는 거임
그림
의미있는 객체 vs 의미없는 객체
- main 메소드를 가진 CarEx 클래스로 객체를 만들면? -> 만들어지지만 의미 없는 객체 - 멤버(필드,생성자,메소드) 가 있는 클래스로 객체를 만들어야 의미있는 클래스이다.
생성자 호출하는 방법 1. 객체를 생성할때 호출됨 2. this()를 통해 호출할 수도 있음 - 99%는 클래스로 객체를 생성할때 생성자가 호출됨
ex)
// 객체 생성
Car mycar = new Car(); //객체를 생성할때 생성자가 호출된다.
CarEx car = new CarEx(); // 의미 없는 객체
필드 수정하기
// 필드값 변경
mycar.speed = 60;
- 필드는 객체를 통해서 접근해야한다.
지역변수와 필드의 생성과 소멸 - 지역변수는 메서드 호출시 생성, 메소드 종료시 삭제 - 필드는 클래스로 객체를 생성할때 new 연산자를 통해 힙공간에 공간 할당받고, 나중에 이 공간을 참조할 수 있는 주소가 더이상 없을때 가비지 콜렉터가 이걸 쓰레기로 인식하고 힙메모리상의 그 공간을 지워버림
필드 = 멤버변수 - 기본자료형들은 자동으로 초기값이 설정됨 ex) boolean 은 false로 - 클래스,배열, 인터페이스들은 참조할 주소가 없다는 의미인 null 로 초기화됨. - 필드는 힙메모리상에 저장됨
초기화 값 - 정수형은 0으로 초기화 - 실수형은 0.0으로 초기화 - char 필드는 은 초기화 안된다 - 논리형 필드는 false로 초기화 - 배열과 클래스 필드는 null로 초기화 된다 * null : 참조할 주소가 없다는 의미
Q. 참조형이 필드일때 즉 String 클래스가 필드로서 있을때 이걸 어떻게 할당하나? A. f.referenceField = "자바"
해제시점 - f처럼 그 객체 공간을 힙메모리상에 있는 그 개게 공간을 가리키고 있는 참조 변수가 있으면 괜찮다. - f처럼 그 공간을 가리키고있는 참조 변수가 없으면 가비지 콜렉터가 자동으로 그 힙메모리의 공간을 지움 - 그때가 해제되는 시점임
생성자 - 생성자 앞에는 접근제어자만 온다 - 생성자를 통해서 내가 원하는 초기값을 그 객체의 필드로 초기화 시킬 수 있다 - 매개변수가 있는 필드 만들어서 할 수 있다 - 객체가 생성될때 호출되어 필드들을 초기화 시키는 역할만 함 - 기본생성자는 객체가 생성될때 컴파일러가 자동으로 생성해준다
클래스명 주의 - 동일한 패키지 안에 같은 이름의 클래스가 있을땐 오류 발생 - 동일한 패키지 = 동일한 폴더 내 - .class 라는 바이트코드는 따로 만들어지는데 같은 이름인 파일이 이미 있으므로 - 메인메소드를 가진 클래스명으로 파일명을 일치시켜야함 = 메인메소드를 가진 클래스만 public class 일 수 있어서 - 메인메소드가 없다면 public을 가진 class 명과 일치시켜야함
클래스 예시 (MyDate Class)
package p2022_06_28;
class MyDate {
private int year; // 필드
private int month;
private int day;
public MyDate() { // 기본생성자(Default Constructor)
System.out.println("[생성자] : 객체가 생성될 때 자동 호출됩니다.");
}
public void print() {
System.out.println(year + "/" + month + "/" + day);
}
}// MyDate end
public class ConstructorTest02 {
public static void main(String[] args) {
MyDate d = new MyDate();
d.print();
// 접근제어자가 private 이기 때문에 외부 클래스에서 직접 접근할 수 없다.
// System.out.println(d.year);
// MyDate dd = new MyDate();
}
}
1. MyDate Class 필드 - MyDate Class의 필드들 접근제어자가 private이다. 2. MyDate Class 생성자 - 생성자가 1개 있다. 생성자 이름은 클래스명과 같고 앞엔 접근제어자만 있다 - 매개변수가 없는 생성자 = 기본생성자 3. MyDate Class 메소드 - static이 없다 - 대부분의 메소드는 static 안쓴다 - 나중에 싱클톤 만들땐 정적 메소드 형태로 만듬 - void가 있으므로 return;필요없음
메소드가 하는일 3가지 - 필드값을 출력하는 일 - 필드값을 수정하는 일 - 메소드앞엔 자료값이 오기때문에 호출했을때 값을 돌려주는 역할도 가능
클래스의 실행 정리 - 메인메소드가 없는 MyDate 클래스는 main메소드가 안에 없기때문에 독립적인 실행은 불가능하고 컴파일까지만 가능 - 실행하려면 메인메소드를 추가하든지 메인메소드를 가진 클래스에서 실행을 해줘야한다. - 클래스 내에서 자기 자신의 클래스로 객체를 만드는거 -> 의미없다 - 필드/생성자/메소드 가진 클래스로 객체 만들어야 의미있다.
MyDate d = new MyDate();
- new는 myDate 의 필드들을 저장할 공간을 힙메모리에 생성함 - 그리고 그 공간을 자동으로 0으로 초기화함 - 즉 year month day 의 값을 힙에 저장하기 위한 공간을 생성 - 그림
객체 생성시점 벌어지는 일 - 접근제어자와 상관없이 year month day는 초기값 0으로 설정 - myDate() 생성자를 호출하라는 코드로 인해 생성자 호출됨, 그 생성자로 가서 실행을 함(여기선 출력)
필드값 접근(출력)방법1 - d.year d.month d.day 로 접근시 여기선 오류가 발생한다 - private을 필드 앞에 붙이면 외부 클래스의 접근을 허용하지 않기때문에 d.year 처럼 필드에 접근이 불가능하다
필드값 접근(출력)방법2 - 메소드를 활용하는 방법을 사용해야함! - 메소드 앞의 접근 제어자가 public인 print() 메소드를 이용하면 필드값을 출력 가능하다 d.print(); 로 필드값을 출력한다.
접근제어자와 클래스 - 필드는 private이지만 private은 같은 클래스내에서는 접근이 된다 - 그래서 MyDate안 클래스 안의 print() 메소드는 필드들에 접근이 가능한것이다. - 가장 좁은 접근 private 가장 넓은 접근 public 이다 - public은 모두 접근 가능, 클래스나 패키지 달라도, 같은 파일이든 다른 파일이든 다 접근 가능 - private 같은 클래스 내에서만 접근 허용
public MyDate05(int new_year, int new_month, int new_day){ //매개변수가진 생성자
year=new_year;
month=new_month;
day=new_day;
}
MyDate05 d = new MyDate05();
d.print();
MyDate05 d2=new MyDate05(2017, 7, 19);
//매개변수가 있는 생성자 호출
d2.print();
- 필드값을 초기화 하고 있다 - 이렇게 필드랑 이름이 다를때는 상관없지만 필드와 이 생성자의 매개변수가 이름이 같을때는 this 연산자사용(곧 공부) - print()메소드는 필드값을 출력하는용도로 사용되고 있다.public 메소드라서 다른 메소드에서 접근 가능하다 - 2017이 new_year로, 7이 new_month로, 19가 new_day로 가서 이걸 생성자에서 year= new_year; 필드값을 원하는 값으로 초기값 설정 가능
호출과정 1. 한 파일 내 두개의 클래스는 이미 컴파일이 된 상태 2. JVM은 가장 먼저 main을 실행함 3. 이때 MyDate05 의 객체인 d 생성함. 4. 기본생성자를 호출함 5. 힙에 필드위한 기억공간/필드값 자동 초기화
+ 매개변수 있는 생성자 있는경우엔 기본생성자 만들어둬야함 + 안에 아무 내용없어도 형식이라도 만들어둬야함
그림
매개변수와 필드의 이름이 같은 경우 - 매개변수의 값이 필드를 초기화 할 수 없음 - 오류가 생기진 않는다. - 필드(멤버변수) 쪽에 this. 라고 붙여야함, this. 가 있으면 이름이 같아도 값 대입 가능
this. 예제
package p2022_06_28;
class MyDate04{
int year;
int month;
int day;
public MyDate04(){
year=2016; month=4; day=1;
}
public MyDate04(int year,int month,int day){
this.year=year;
this.month=month;
this.day=day;
}
public void print(){
System.out.println(year+ "/" +month+ "/" +day);
}
}// MyDate end
public class ConstructorTest04 {
public static void main(String[] args) {
MyDate04 d=new MyDate04(); //1생성자호출
System.out.println(d.year); //바로 접근해서 출력
System.out.println(d.month);
System.out.println(d.day);
d.print(); //메소드통해 출력
MyDate04 d2=new MyDate04(2017, 7, 19);
System.out.println(d2.year); //바로 접근해서 출력
System.out.println(d2.month);
System.out.println(d2.day);
d2.print();
}
}
d객체 - 여기 필드는 디폴트 접근제어자이므로 d.year로 바로 접근해서 출력 ㅏㄱ능 - 메소드 통해서 출력하는 방법되 있다. - '//1생성자호출'에서 기본생성자를 호출해서 year=2016,month=4,day=1 로 초기화됨
d2객체 - 또다시 heap 상에서 새로운 공간을 생성함. - 이 공간은 d2객체의 필드들을 저장하기 위한 공간이다
this. - 내부 레퍼런스 변수 - this는 그 객체 를 의미함 - 객체 생성되어 매개변수 있는 생성자 호출할 때, 이 객체 d2가 가진 주솟값을 갖는게 this다 - 즉 this == d2를 의미한다 ( d2를 받는다 ) - 사용법 : this.필드 = 매개변수 - 생성자 안에서 멤버변수와 매개변수가 이름이 동일한 경우에 사용
- d2객체를 생성했을 때, 매개변수를 3개 가진 생성자를 호출한다, 그럼 year month day 값은 2017, 7, 19 로 설정이 된다. - d2가 이 것들을 가리키고 있는데 this는 이 d2가 가리키는 것들을 똑같이 가리킨다 - 즉 d2랑 this는 같은 주소를 갖고 있다 - d2.year == this.year + this. 을 안썼다고 오류가 생기진 않지만 매개변수의 값이 필드로 들어가지 않음 (값전달이안됨)-> 출력시 다 0이 나오는걸로 알 수 있다
그림
생성자의 매개변수로 멤버변수값 초기화하는법 1. 멤버변수와 매개변수의 이름을 다르게 설정 2. 멤버변수와 매개변수의 이름이 같고, 멤버변수 앞에 this를 붙인다.
생성자의 사용법 - 객체마다 언제나 같은 값으로 필드값 다 같게 설정할땐 기본생성자를 사용하고 - 객체가 생성될때마다 초기화할 값을(필드를) 다 다르게 설정하기 위해선 매개변수가 있는 생성자를 사용하는것임!
메소드가 하는 일 - 필드값을 수정, 출력, 필드값을 돌려주기
이클립스 생성자 만들기 기능 - 이클립스에 생성자 만들어주는 기능이 있다 - 오른쪽마우스 -> Source -> Generate Constructor using Fields - Insertion point : 생성자가 들어갈 위치 지정 - Omit call to default constructor super() : 생성자 내부에 super()를 생략하기 위해서 체크(상속 공부한 뒤 공부) - 자동으로 생성된 생성자에도 this 사용한다
this. 정리 - 생성자에서도 사용가능하고 메소드에서도 사용가능! (메소드에서 this.쓰는거 곧 한다) - 생성자와 메소드 안에서 멤버변수와 매개변수 이름이 동일한 경우에 주로 사용함 - ex)
public Test(int a, int b) {
this.a = a;
this.b = b;
}
this() 정리 - 같은 클래스안에 있는 생성자를 호출할때 사용함(다른 클래스의 생성자는 안됨) - 객체를 생성할때만 생성자를 호출할 수 있는게 아니라 this()를 통해서도 생성자를 호출 할 수도 있다. - this()는 많이 쓰이진 않음
+ 상속 공부 후 super. , super() 를 배울것
메소드를 통해 필드값을 set 시키는 예제 (MyDate06/ConstructorTest06)
- 생성자를 통해서 객체를 생성할 때 필드값을 초기화 시킬 수 있다. - 하지만 "중간에" 어떤 필드값 하나를 수정하고 싶을떄 생성자는 그 역할을 할 수 없다 - 생성자는 객체 호출시에 실행되므로 -> 중간에 변경하고 싶을때 메소드를 통해서 필드값 수정할 수 있다
메소드 역할 정리 1. 필드값 출력 2. 필드값 수정/변경 (생성자는 못함) -> set 3. 리턴자료형을 통해서 필드값을 돌려주는 역할도 함 (메소드 호출한곳으로 돌려줌) -> get
package p2022_06_28;
class MyDate06 {
private int year; // 필드
private int month;
private int day;
// public MyDate(){ //default 생성자
// }
public MyDate06(int new_year, int new_month, int new_day) {
year = new_year; // 2017
month = new_month; // 7
day = new_day; // 19
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public void print() {
System.out.println(year + "/" + month + "/" + day);
}
}
public class ConstructorTest06 {
public static void main(String[] args) {
MyDate06 d = new MyDate06(2017, 7, 19);
d.print();
d.setYear(2022);
d.setMonth(6);
d.setDay(28);
d.print();
System.out.println("year : " + d.getYear());
System.out.println("month : " + d.getMonth());
System.out.println("day : " + d.getDay());
}
}
- 필드값들은 private - 디폴트생성자는 쓰는 일이없으면 안만들어도됨 - 생성자로 필드값을 초기화 하고 있다 - 연도값을 수정하고 싶을땐? -> 생성자가 할수없다 메소드가 해야함 -> 메소드로 수정 - 다른 클래스에 있는 main에서 호출 하여 필드값 수정할 것이므로 메소드 접근제어자는 public / default 여야한다.
그림
메소드에서 this. 사용 - setYear 메소드에서 필드와 매게변수가 또 같은 이름이다 - 매개변수값이 필드에 안들어감 즉 수정이안됨 -> 수정 위해서는 this.를 붙여줘야함 - 생성자도 그렇고 메소드 안에서도 this. 쓸 수 있다.
메소드를 통한 필드값 수정 정리 - 생성자통해 필드값 수정변경 불가능 - 또 다른 객체를 생성하면서 새로운 공간을 만들지 않도록 주의 - 기존값을 수정하기위해선 메소드를 이용해야한다
이클립스 필드값 수정해주는 메소드 생성 - 오른쪽 버튼 -> Source -> Generate Getters and Setters
Sort by - Fields in getter/setter pairs : getYear setYear getMonth setMonth 순서 - First getters the setters : getYear getMonth setYear setMonth 순서
getters 와 setters - set으로 시작하는 메소드를 setters 메소드 - get으로 시작하는 메소드를 getters 메소드 - getters method : getYear(), getMonth(), getDay() - setters method : setYear(), setMonth(), setDay()
getters - 이 getters 메소드 앞에 있는 자료형에 맞게 값을 돌려주는데 생성자에의해 할당되거나 수정된 필드값을 값을 호출한 곳으로 돌려줌 ex) getYear() 은 year 란 필드값을 돌려줌 return year;
this() 사용 예제 - this() : 같은 클래스 안에 있는 생성자를 호출하라는 의미로 사용됨
class MyDate10{
private int year;
private int month;
private int day;
public MyDate10(){
this(2016, 1, 1);
}
public MyDate10(int new_year){
this(new_year, 1, 1);
}
public MyDate10(int new_year, int new_month){
this(new_year, new_month, 1);
}
public MyDate10(int new_year,int new_month,int new_day){
year=new_year;
month=new_month;
day=new_day;
}
public void print(){
System.out.println(year+ "/" +month+ "/" +day);
}
}
public class ConstructorTest10 {
public static void main(String[] args) {
MyDate10 d=new MyDate10(2017, 7, 19);
d.print();
MyDate10 d2=new MyDate10(2017, 7);
d2.print();
MyDate10 d3=new MyDate10(2017);
d3.print();
MyDate10 d4=new MyDate10();
d4.print();
}
}
생성자 호출 - MyDate06 예제에서 클래스의 객체를 만들 때는 다른 클래스에서 만든다 - 즉, MyDate06 예제에서 생성자를 호출 할 때는 다른 클래스의 생성자를 호출 했다. - 일반적으로 다른 클래스의 생성자 호출도 가능하고 같은 클래스의 생성자 호출도 가능하다 ex) ConstructorTest10 클래스안의 main 메소드 안에서 MyDate10 d = new MyDate10(2017,7,19) 하는건 MyDate10 클래스 안의 생성자 즉 다른 클래스의 생성자를 호출한거임 - 하지만 this() 는 같은 클래스 안에 있는 생성자만 호출할 수 있다
위 예제 생성자 부분만 보기
public MyDate10(){
this(2016, 1, 1);
}
public MyDate10(int new_year){
this(new_year, 1, 1);
}
public MyDate10(int new_year, int new_month){
this(new_year, new_month, 1);
}
public MyDate10(int new_year,int new_month,int new_day){
year=new_year;
month=new_month;
day=new_day;
}
생성자 오버로딩 - 여기서 MyDate10 생성자가 4개다 - 생성자 이름이 같아도 구분이 된다면 같은 이름의 생성자가 여러개 있어도 됨 - 한개의 클래스 안에 여러개의 생성자를 만드는 것 = 생성자 오버로딩 (생성자의 이름은 클래스명이랑 같아야한다)
생성자 오버로딩을 위한 구분 1. 매개변수의 자료형을 다르게 하는법 2. 매개변수의 개수를 다르게 하는법 3. 매개변수가 여러개있을때 순서를 바꾸는방법! 도 된다. 자료형,개수 같아도 순서가 다르면 구분됨
+ MyDate10 클래스에선 생성자의 구분을 매개변수의 개수 로 하고 있다.
위 예제 설명
MyDate10 d2=new MyDate10(2017, 7);
d2.print();
1. 여기서 MyDate10(2017,7) 을 할 때, 매개변수가 2개인 생성자가 위에있다
public MyDate10(int new_year, int new_month){
this(new_year, new_month, 1);
}
2. 로 가서 위의 this(new_year, new_month, 1); 가
public MyDate10(int new_year,int new_month,int new_day){
year=new_year;
month=new_month;
day=new_day;
}
3. 인 매개변수가 3개인 생성자를 부르면서 (new_year, new_month, 1) 를 매개변수로 전달 + 이 매개변수가 3개인 생성자가 있어야만 this(2017,7, 1); 이게 가능한거임
+
MyDate10 d3=new MyDate10(2017);
- 시에
public MyDate10(int new_year){
this(new_year, 1, 1);
}
- 이 생성자가 호출되서 여기서 다시 this()로 매개변수가 3개인 생성자를 호출 하면서 (2017,1,1)값을 전달
+
MyDate10 d4=new MyDate10();
- 를 main에 쓰면
public MyDate10(){ //기본생성자
this(2016, 1, 1);
}
- 여기로 가서 this() 통해서 매개변수가 3개인 생성자 다시 호출하면서 매개변수로 값을 2016,1,1 넘겨줌
생성자 오버로딩 예시(String 클래스 by api 문서) String 클래스의 생성자 - 모두 전부 이름이 String 이고 매개변수 부분이 다르다. = "생성자 오버로딩" - 생성자 호출할때 구분이 가능해야 생성자 오버로딩이 잘 된거임 - 우리는 String(String original) 생성자를 사용
package p2022_06_30;
import java.util.Calendar;
public class CalendarHw {
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
int y = c.get(Calendar.YEAR);
int m = c.get(Calendar.MONTH) + 1;
int d = c.get(Calendar.DATE);
int h2 = c.get(Calendar.HOUR_OF_DAY);
int mm = c.get(Calendar.MINUTE);
int s = c.get(Calendar.SECOND);
int w = c.get(Calendar.DAY_OF_WEEK); // 일 : 1 ~ 토 : 7
// 요일을 저장
String[] week = { "일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일" };
System.out.println(y + "." + m + "." + d + " " + h2 + ":" + mm + ":" + s + " " + week[w-1]);
}
}
출력
강사님 코드
package p2022_07_01;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class DateTime02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 3.Calendar
// Calendar c2 = new Calendar();
// Calendar c1 = new GregorianCalendar();//업캐스팅
Calendar c = Calendar.getInstance();
int y = c.get(Calendar.YEAR);
int m = c.get(Calendar.MONTH) + 1;// 0~11
int d = c.get(Calendar.DATE);
int h1 = c.get(Calendar.HOUR); // 12시간
int ap = c.get(Calendar.AM_PM); // 0:오전
// 1:오후
int h2 = c.get(Calendar.HOUR_OF_DAY); // 24시간
int mm = c.get(Calendar.MINUTE);
int s = c.get(Calendar.SECOND);
System.out.println(y + "년 " + m + "월 " + d + "일");
if (ap == 0) {
System.out.println("오전");
} else {
System.out.println("오후");
}
System.out.println(h1 + ":" + mm + ":" + s);
int w = c.get(Calendar.DAY_OF_WEEK);// 1~7
System.out.println("w=" + w);
// 1:일, 2:월, 3:화, 4:수, 5:목, 6:금, 7:토
String[] week = { "일", "월", "화", "수", "목", "금", "토" };
System.out.println(week[w - 1] + "요일");
}
}
package p2022_06_28;
public class Hw1 {
public static void main(String[] args) {
/*
* 1~45사이의 숫자를 6개 추출 하는 프로그램을 작성 하세요?
* (단, 중복된 숫자는 1번만 출력 되도록 한다.)
* Math.random() 이용해서 프로그램을 작성 하세요.
*/
int[] random = new int[6];
for (int i = 0; i < random.length; i++) {
random[i] = (int) (Math.random() * 45 + 1);
for (int j = 0; j < i; j++) { // 이전에 들어간 값과 같은 값이 있는지 검사
if (random[i] == random[j]) { // 같은 값이 있다면
i--; // 다시 할당하기 위해서 i--;
}
}
}
// 출력하기
for (int i : random) {
System.out.print(i + " ");
}
}
}
출력
강사님 코드
package p2022_06_29;
import java.util.Arrays;
class Lotto1 {
public static void main(String[] args) {
int num[] = new int[6];
for (int i = 0; i < num.length; i++) {
num[i] = (int) (Math.random() * 45) + 1; // 1 ~ 45
// num[0]=5, num[1]=5,
for (int j = 0; j < i; j++) {
if (num[i] == num[j]) { // 중복
i--;
break;
} // if end
} // for end
} // for end
// 오름차순 정렬 : 버블정렬(자신의 옆자리에 있는 것과 비교)
int temp = 0;
for (int i = 0; i < num.length; i++){
for (int j = i+1; j < num.length; j++) {
if(num[i] > num[j]){
temp = num[i];
num[i] = num[j];
num[j] = temp;
}
}
}
// Arrays.sort(num); // 오름차순 정렬
for (int k : num) {
System.out.print(k + "\t");
}
}
}
/*
* 난수 발생 공식 난수 =(정수화) ((상한값-하한값+1)* Math.random()) + 하한값
*
*/