과제 풀이
https://laker99.tistory.com/23?category=1065834

 

		System.out.printf("%.2f", l);

- 소숫점 둘째자리까지(%.2) 출력하고 실수형태(%f) 로 출력

 

		DecimalFormat df = new DecimalFormat("0.00"); //내 코드
		DecimalFormat df = new DecimalFormat("###.00"); //강사님 코드

- 0과 #은 의미가 다르다. 
- ###을 넣으면 두자리여도 두자리만 표시하고 세자리면 세자리로 표시한다.
- 000을 넣으면 0은 무조건 자리를 채워넣는 형식, 31.42인 값이 031.42 로 표시된다.


상속을 쓰는 이유
- 중복코드를 줄이기 위해서
- 통일성있는 클래스를 설계하기 위해서
- 그래픽프로그램에서 상속을 많이 사용한다
- 나중에 JSP model2 = MVC 패턴개발 를 할때 클래스가 많을때 사용한다

자바에서 그래픽 프로그램
- CUI(Console User Interface)
- GUI(Graphic User Interface)

그래픽 프로그램 관련 패키지
- java.awt.*
- javax.swing.*
- javafx.*
- 여기 그래픽 프로그램 만들기 위한 클래스들이 모여있다
- 그래픽 프로그램에서 상속을 많이 사용함
- 캔버스, 체크박스, 프레임, 스크롤바, 메뉴, 텍스트필드 등 만들 수 있다

상속관련 용어 정리
- 부모클래스 = 상위클래스 = 슈퍼클래스
- 자식클래스 = 하위클래스 = 서브클래스 = 파생클래스

단일상속
- 자바는 단일상속만 가능
- 두개이상의 클래스로부터 상속받을수없다(C++, Python은 가능)

+ 부모클래스로 객체를 만드는게 의미없다(상속)
+ 자식클래스로 객체를 만드는게 의미있다(부모클래스의 필드와 메소드도 받았으므로)

패키지와 파일
- 한개 파일에 클래스 여러개 들어가도된다
- 나중에 컴파일될때 바이트코드는 클래스별로 따로따로 만들어짐
- 패키지는 폴더를 의미한다

 

상속 예제1
- SuperSub01.java 
- 자식클래스명 옆에 extends 옆에 부모클래스명을 쓰자

package p2022_07_05;

class Parent {
	public void parentPrn() {	//부모 클래스
		System.out.println("슈퍼 클래스 메서드는 상속된다.");
	}
}

//Parent를 슈퍼 클래스로 하는 서브 클래스 Child 정의 
class Child extends Parent {	//자식 클래스
	public void childPrn() {
		System.out.println("서브 클래스 메서드는 슈퍼가 사용 못한다.");
	}
}

//Parent를 슈퍼 클래스로 하는 서브 클래스 Child 정의
//Parent클래스의 parentPrn() 메소드는 자식 클래스에 상속된다.
class SuperSub01 {
	public static void main(String[] args) {
		Child c = new Child(); // 서브 클래스로 객체를 생성
		c.parentPrn(); // 슈퍼 클래스에서 상속 받은 메서드 호출
		c.childPrn(); // 서브 클래스 자기 자신의 메서드 호출
		System.out.println("-------------------------------------->> ");
		Parent p = new Parent(); // 슈퍼 클래스로 객체 생성
		p.parentPrn(); // 슈퍼 클래스 자기 자신의 메서드 호출
		// p.childPrn( ); //서브 클래스 메서드는 가져다 사용 못함
	}
}

- 상속은 부모가 자식에게 일방적으로 주는것만 가능하다
- 부모클래스는 자식클래스의 멤버(필드나 메소드) 사용불가!

 

상속 예제2
- SuperSub04.java

package p2022_07_05;

class Point2D { // 부모 클래스
	protected int x = 10; // private int x=10;
	protected int y = 20; // private int y=20;
}

// 부모 클래스의 필드의 접근제어자가 private이면 자식이 접근할 수 없다.(상속 불가), 여기선 오류 발생
class Point3D extends Point2D { // 자식 클래스
	protected int z = 30;

	public void print() {
		System.out.println(x + ", " + y + ", " + z);
	}
}

class SuperSub04 {
	public static void main(String[] args) {
		Point3D pt = new Point3D();
		pt.print();
	}
}

- Point2D클래스가 부모클래스, 안의 필드 x, y 의 접근제한자가 protected
- Point3D클래스가 Point2D 클래스를 상속받음
- Point3D의 필드 protected int z, 메소드 print();
- 메인메소드가 있는 클래스 SuperSub04
- 메인메소드에서 Point3D 클래스로 객체를 생성하고 pt.print()로 메소드 호출
- 그럼 이 print()에서 x,y는 자기 클래스의 멤버는 아니지만 부모 클래스의 멤버이므로
자기거인것처럼 출력하는 것임

 

- 이때 접근 제어자, Point2D의 접근제어자를 private으로 두면? 오류발생, 자식 클래스에 상속 안된다!
- 부모클래스의 필드/메소드가 private이면 자식클래스라도 외부클래스이므로 접근불가, 상속을 못함
- private만 아니면 다 됨, default, public, protected는 다 상속 된다

 

접근제어자 protected
- 상속에선 나중에 접근제어자를 protected를 많이 씀
- 심지어 패키지가 달라져도 상속관계에 있을때는 자식이 접근 가능하다

패키지가 달라져도 접근가능한 경우
1. public인 경우
2. protected이고 상속관계인 경우 자식이 부모클래스 필드/메소드 접근

* 자바의 접근 제어자


1. 상속 관계가 있는 경우에 2개의 클래스(부모,자식 클래스)가 같은 패키지 안에 들어 있을때는 부모의 접근제어자가 default, protected, public 접근제어자인 경우에 자식클래스에서 접근 할수 있다. (단, private접근 제어자만 자식 클래스에서 접근 할 수 없음)

2. 상속 관계가 있는 경우에 2개의 클래스(부모,자식 클래스)가 다른 패키지 안에 들어 있을때는부모의 접근제어자가 protected, public 접근제어자인 경우에 자식클래스에서 접근 할수 있다. 

3. 상속 관계가 없는 경우에 2개의 클래스가 서로 다른 패키지 않에 들어 있을때는 public 접근제어자로 되어 있어야만 다른 클래스에서 접근 할 수 있다.

+ 메소드 오버라이딩
- 상속에서 가장 중요한 개념
- 반드시 상속이 전제되어야만 쓸 수 있다.
- 메소드는 상속이 되지만, 부모로 부터 상속받은 메소드를 이름과 형식은 같게 하지만 안의 내용을 다르게 하는것
- 나중에 자세히 공부할것

 

상속 예제 3-1 (필드)
- SuperTest02.java

package p2022_07_05;

//부모 클래스 안에 있는 필드는 자식 클래스에게 상속된다.
class Point2D2 {	//부모 클래스
	protected int x = 10;
	protected int y = 20;
}

class Point3D2 extends Point2D2 {	//자식 클래스
	protected int z = 30;

	public void print() {
		System.out.println(x + ", " + y + ", " + z); // x와 y는 상속 받아 사용하는 멤버변수
	}
}

class SuperTest02 {
	public static void main(String[] args) {
		Point3D2 pt = new Point3D2();
		pt.print();
	}
}

+ protected이므로 상속 가능
- 이 예제는 필드 중복 그거 아니고 다음 예제이다
- 위 예제는 그냥 부모 클래스 안에 있는 필드는 자식 클래스에게 상속된다는걸 보여주는 예제

- 그럼 부모클래스에 있는 필드 x, y 를 상속받은 자식 클래스에서 똑같은 이름 x, y으로 필드를 정의해보자
- 자식클래스로 객체 만들어서 필드값출력할떄 어느값을 출력하는가?
-> 새로정의한 x, y값만 출력됨.
- 다음 예제에서 설명

 

상속 예제 3-2 (필드의 상속)
- SuperTest03.java

package p2022_07_05;
//부모 클래스에 있는 필드를 자식 클래스에서 재정의(동일한 이름의 변수)하면,
//자식 클래스에서 재 정의한 필드만 사용 가능하다.
class Point2D3 {	//부모 클래스
	protected int x = 10; // Point3D에서 다시 한번 정의되므로 은닉 변수가 됨
	protected int y = 20; // 은닉 변수는 쉐도우 변수라고도 함
}

class Point3D3 extends Point2D3 {	//자식 클래스
	protected int x = 40; // 슈퍼 클래스에 존재하는 멤버변수를
	protected int y = 50; // 서브 클래스에 다시 한 번 정의함

	protected int z = 30;

	public void print() {
		System.out.println(x + ", " + y + ", " + z); // x와 y는 재 정의된 Point3D 클래스 소속
	}
}

class SuperTest03 {
	public static void main(String[] args) {
		Point3D3 pt = new Point3D3();
		pt.print();
	}
}
40, 50, 30

- x, y가 중첩되고 있다
- 결론은 새로정의한 x, y값만 출력됨, 즉 10, 20 대신 40, 50 이 출력됨
- 기존에 상속해준 부모 클래스의 x, y 필드는 더이상 사용이 되지않음, 은닉이된다
-> 이 변수를 은닉 변수 = 쉐도우 변수 라고 함
- 이 은닉 변수들에 굳이 접근하고 싶으면 super. 을 사용해서 접근한다!

필드의 상속 정리
- 부모 클래스에 있는 필드를 자식 클래스에서 재정의(동일한 이름의 변수)하면, 자식 클래스에서 재정의한 필드만 사용 가능하다.

은닉 변수에 접근
- super.x, super.y 하면 은닉변수에 접근가능하다
- 쓸 일이 많지 않다

은닉 변수에 접근 예시
- 은닉변수에 굳이 접근하려면 super. 해서 접근
- 부모 클래스에 접근해서 값을 구해오라는 의미
- SuperTest04.java

package p2022_07_05;

//super : 부모 클래스를 의미함
//super.x 는 부모 클래스의 은닉된 필드를 호출할 때 사용된다.
//super.x 는 "자식클래스"의 "메소드 안"에서만 사용할 수 있다. (주의)
//그 외의 장소에서 System.out.println(super.x); //오류 발생

class Point2D4 { // 부모 클래스
	protected int x = 10; // 은닉 변수
	protected int y = 20; // 혹은 쉐도우 변수
}

class Point3D4 extends Point2D4 { // 자식 클래스
	protected int x = 40; // 슈퍼 클래스에 존재하는 멤버변수를
	protected int y = 50; // 서브 클래스에 다시 한 번 정의함

	protected int z = 30;
	// System.out.println(super.x); 메소드 내부가 아니므로 오류

	public void print() {
		System.out.println(x + ", " + y + ", " + z); // x와 y는 재 정의된 Point3D 클래스 소속
	}

	public void print02() {
		System.out.println(super.x + ", " + super.y + ", " + z); // Point2D 클래스 소속 멤버변수로 접근
	}
}

class SuperTest04 {
	public static void main(String[] args) {
		Point3D4 pt = new Point3D4();
		pt.print(); // 40, 50, 30 // Point3D의 x, y
		pt.print02(); // 10, 20, 30 // Point2D의 x, y

		System.out.println(pt.x); // 40
	}
}
40, 50, 30
10, 20, 30
40

 

은닉 변수에 접근 정리
- super : 부모 클래스를 의미함
- super.x 는 부모 클래스의 은닉된 필드를 호출할 때 사용된다.
- super.x 는 "자식클래스"의 "메소드 안"에서만 사용할 수 있다. (주의)
- 그 외의 장소에서 System.out.println(super.x); //오류 발생

super 와 this
- super : 부모 클래스를 의미하는 일종의 레퍼런스 변수
- this : 자기 클래스를 의미하는 일종의 레퍼런스 변수

* this와 super
this. : 생성자와 메소드에서 매개변수와 멤버변수 이름이 동일한 경우에 사용

this() : 같은 클래스내에 있는 다른 생성자를 호출(다른 클래스 내의 생성자는 이걸로 호출하지않음)
super. : 부모 클래스에 있는 은닉된 멤버변수와  메소드를 호출  할때 사용, 반드시 자식 클래스의 메소드 내에 써야만 사용가능하다. 자식 클래스의 메소드 밖이나 다른 클래스 내에서 사용불가
super() : 부모 클래스의 매개변수를 가진 생성자를 호출 할 때 사용

메소드로의 적용 (메소드 오버라이딩)
- 필드에서 재정의된 필드가 사용되는 것 처럼 메소드도 마찬가지이다.
- 메소드는 메소드 오버라이딩이라고 하지만 필드는 이런거 따로 부르는 이름이 없다
- 결론적으론 메소드도 새로 정의된 메소드 오버라이딩된 메소드만 호출됨

- 부모 클래스의 메소드는 은닉 메소드가 됨, 굳이 호출하고 싶으면 super.메소드() 로 호출하면된다
- 필드때처럼 부모의 메소드와 똑같은 이름 + 똑같은 형식 으로 메소드를 만들어야함
- 메소드 오버라이딩(Method Overriding), 동일이름 동일형식 다른내용 상속전제 필수

메소드 오버라이딩 예제

- SuperSub05.java 

package p2022_07_05;

// 메소드 오버라이딩(Method Overriding)
// : 부모 클래스로부터 상속받은 메소드를 자식 클래스에서 재정의 해서 사용하는 것.
// 1. 부모 클래스로부터 상속받은 메소드를 자식 클래스에서 메소드 오버라이딩을 하면
//  메소드 오버라이딩 된 메소드만 호출되고, 부모 클래스의 메소드는 은닉이 되어서
//  자식클래스에 상속되지 않는다.
// 2. 부모 클래스에 은닉된 메소드를 호출할 때는 자식클래스의 메소드 안에서
//  super.parentPrn() 형식으로 호출해야 된다.

class Parent05 {	//부모 클래스
	public void parentPrn() {	//은닉 메소드가 된다
		System.out.println("슈퍼 클래스 : ParentPrn 메서드");
	}
}

//Parent05를 슈퍼 클래스로 하는 서브 클래스 Child05 정의 
class Child05 extends Parent05 {	//자식 클래스
//	super.parentPrn(); //오류 발생, 자식클래스의 메소드 내부여야함
	
	// 슈퍼 클래스에 있는 ParentPrn 메서드를 오버라이딩하면
	// Child05로 선언된 객체는 슈퍼 클래스의 메서드가 은닉되어 상속 받지 못하게 된다.
	public void parentPrn() {	// 메소드 오버라이딩(Method Overriding), 동일이름 동일형식 다른내용 상속전제 필수
		System.out.println("서브 클래스 : ParentPrn 메서드");
	}

	public void childPrn() {	// 자식 클래스만 있는 메소드
		super.parentPrn();	//부모 클래스의 은닉 메소드 호출
		System.out.println("서브 클래스 : ChildPrn 메서드");
	}
}

class SuperSub05 {
	public static void main(String[] args) {
		Child05 c = new Child05(); // 서브 클래스로 객체를 생성
		c.parentPrn(); // 오버라이딩된 서브 클래스의 메서드 호출(새로 정의된, 오버라이딩된 메소드만 호출됨)
		c.childPrn(); // 서브 클래스 자기 자신의 메서드 호출
		System.out.println("-------------------------------------------->> ");
		Parent05 p = new Parent05(); // 슈퍼 클래스로 객체를 생성
		p.parentPrn(); // 슈퍼 클래스(자기 자신)의 메서드 호출
	}
}
서브 클래스 : ParentPrn 메서드
슈퍼 클래스 : ParentPrn 메서드
서브 클래스 : ChildPrn 메서드
-------------------------------------------->>
슈퍼 클래스 : ParentPrn 메서드

- 은닉된 메소드도 호출할때는 super.메소드() 로 호출하면 호출된다!
- super.메소드() 를 쓰는 위치는 필드랑 동일하게 자식 클래스의 메소드 안에서 사용해야함 (주의)
ex) super.parentPrn();을 자식 클래스의 메소드 밖에서 쓰면 오류 발생함

 

- 메소드 오버라이딩을 하면 오버라이딩된 메소드만 호출된다.
- 부모클래스로부터 상속된 메소드는 사용이 더이상되지않고 은닉메소드가 된다
- 은닉메소드를 굳이 호출될때는 자식 클래스의 메소드 내에서 super.메소드() 로 호출

 

메소드 오버라이딩(Method Overriding) 정리
- 부모 클래스로부터 상속받은 메소드를 자식 클래스에서 재정의 해서 사용하는 것.
- 부모 클래스로부터 상속받은 메소드를 자식 클래스에서 메소드 오버라이딩을 하면 메소드 오버라이딩 된 메소드만 호출되고, 부모 클래스의 메소드는 은닉이 되어서 자식클래스에 상속되지 않는다.
- 부모 클래스에 은닉된 메소드를 호출할 때는 자식클래스의 메소드 안에서 super.parentPrn() 형식으로 호출해야 된다.

* 메소드 오버라이딩(Method Overriding)
- 부모 클래스로 부터 상속받은 메소드를 자식 클래스에서 재정의 해서 사용하는것

- Spring , JSP 할 때도 계속 따라가는 개념이므로 잘 익혀둬야 한다.

 

은닉 정리
- 결과적으로 자식 클래스에서 재정의 시 부모클래스안의 상속이 되는 필드, 메소드는 상속이 되지 않는다
- 은닉이 됨. 사용하려면 자식클래스의 메소드 안에서 super.필드명, super.메소드명() 으로 호출가능하다

+ 메소드 오버라이딩을 해도 되고 안해도 되지만 곧 배울 추상클래스, 인터페이스의 경우에는 반드시 메소드 오버라이딩을 해야하는 경우가 있다, 하지 않으면 오류 발생
+ 상속에서는 메소드와, 메소드 오버라이딩이 제일 중요하다

생성자와 상속
- 기본적으로 생성자는 상속되지 않는다, 어떻게 쓸까?
- 자식 클래스의 객체를 생성할때, 자식 클래스의 생성자가 호출되면 부모 클래스의 기본 생성자를 연쇄적으로 호출해줌
- 자식 클래스의 생성자는 기본생성자 여도 되고 매개변수를 가진 생성자여도 상관없음
- 이때 부모 클래스의 생성자는 "기본 생성자"만 호출되어 실행이됨

 

생성자와 상속 예제
- SuperTest05.java

package p2022_07_05;

class Point2D05 {	//부모 클래스
	protected int x = 10;	//필드
	protected int y = 20;

	public Point2D05() {	//부모클래스의 기본 생성자
		System.out.println("슈퍼 클래스인 Point2D 생성자 호출");
	}
}

class Point3D05 extends Point2D05 {	//자식 클래스
	protected int z = 30;

	public void print() {
		System.out.println(x + ", " + y + ", " + z);
	}

	public Point3D05() {	//자식클래스의 기본 생성자
		System.out.println("서브 클래스인 Point3D 생성자 호출");
	}
}

class SuperTest05 {
	public static void main(String[] args) {
		Point3D05 pt = new Point3D05();	//자식클래스의 기본생성자 호출
		pt.print();
슈퍼 클래스인 Point2D 생성자 호출
서브 클래스인 Point3D 생성자 호출
10, 20, 30

- 즉, main메소드에서 자식클래스의 생성자가 먼저 호출되었지만 실행 결과는 부모클래스의 기본 생성자의 출력문이 먼저 출력됨
- Point3D05 클래스의 기본 생성자가 호출되면 부모 클래스인 Point2D05 클래스의 기본 생성자를 호출함
- 그러므로 "슈퍼 클래스인 Point2D 생성자 호출" 출력 다음 "서브 클래스인 Point3D 생성자 호출" 메세지를 출력한다
- 부모 클래스의 기본생성자 만들어놓지 않으면 컴파일러가 자동으로 만듬
- 지금처럼 기본생성자 만들어뒀을때는 객체 생성할때 부모 클래스의 기본 생성자를 호출함

생성자와 상속 정리
* 상속에서의 생성자
1. 생성자는 기본적으로 상속이 되지 않는다
2. 자식클래스를 이용해서 객체를 생성할때 자식클래스의 생성자(기본생성자,매개변수 있는 생성자 모두 가능)가 호출되면, 부모클래스의 기본생성자가 자동으로 호출된다.
3. 매개변수가 있는 생성자가 있는 경우에는 더이상 컴파일러가 기본 생성자를 자동으로 생성해 주지 않는다.
4. 부모 클래스의 매개변수가 있는 생성자를 자식 클래스에서 호출 할때는 super()를 이용해서 호출할 수 있다. 단, super()는 자식 클래스의 생성자 안에서만 사용 가능함.

부모클래스의 기본생성자 호출
- 심지어 부모 클래스 쪽에 기본생성자가 없어도 된다.
- 자식생성자가 부모 클래스의 기본생성자를 만들어서 호출해줌
- 또한 자식 클래스 쪽에 기본생성자가 없어도 된다, 컴파일러가 자동으로 기본클래스를 생성 후 부모 클래스의 기본 생성자 호출
- 만약 부모 클래스의 기본생성자가 아닌 매개변수가 있는 생성자를 호출하고 싶다면, super()로 명시적으로 호출해줘야지만 부모클래스의 매개변수가 있는 생성자를 호출 할 수 있다

 

- 자식클래스는 매개변수 있는 상속자여도 된다

	public Point3D05(int a) {	//자식클래스의 기본 생성자
		System.out.println("서브 클래스인 Point3D 생성자 호출");
	}
// main
	Point3D05 pt = new Point3D05(30);	//자식클래스의 기본생성자 호출

- 이어도 문제 없고 부모클래스의 기본생성자를 호출함 또한

	public Point3D05() {	//자식클래스의 기본 생성자
		System.out.println("서브 클래스인 Point3D 생성자 호출");
	}

- 이 코드(자식클래스의 기본생성자) 를 주석으로 막아도 컴파일러가 자동으로 기본클래스를 생성
- 또한 연쇄적으로 부모클래스의 기본 생성자도 호출됨

 

부모클래스의 기본생성자 호출 정리
- 자식클래스는 기본생성자여도 되고 매개변수가 있는 생성자여도 됨
- 부모클래스는 기본생성자만 연쇄적으로 자동적으로 호출해주게 되고, 매개변수가 있는 생성자는 우리가 super()로 명시해서 직접 호출해 줘야함

 

부모클래스에 기본생성자가 없다면
- 부모클래스에서 이미 매개변수가 있는 생성자가 있는경우 기본생성자가 자동으로 만들어지지 않는다
- 그래서 기본생성자를 내가 명시해서 만들어주지 않으면 자식클래스의 생성자에도 오류가 생김

부모클래스에 기본생성자가 없다면 예제/super() 통해서 호출하는 예제
- SuperTest06.java
- 이 예제에선 부모클래스에서 이미 매개변수가 있는 생성자가 있으므로 기본생성자가 만들어지지 않는다
-> 그래서 기본생성자를 내가 명시해서 만들어주지 않으면 자식클래스의 생성자에도 오류가 생김

	public Point2D06( ){ System.out.println("슈퍼 클래스인 Point2D 생성자 호출"); }

- 부모 클래스의 기본 생성자 주석을 풀어야만 자식클래스에서 오류가 생기지 않음
- 부모의 기본생성자를 자식 객체 생성될때 연쇄호출됨
- 부모 클래스에서 매개변수가 있는 생성자는 자식 클래스의 생성자에서 호출해주지 않는다, super() 로 호출해야함
- 그리고 부모 클래스에서 매개변수가 있는 생성자가 있고 기본 생성자가 적혀져있지 않으면 기본생성자는 자동으로 만들어지지 않는다
- 이런 경우에는 기본생성자를 명시해주지 않으면 자식 생성자쪽에서 오류가 남.

+ 매개변수가 있는 생성자가 있을때는 예외적으로 컴파일러가 기본생성자를 생성해주지 않는다.

- 물론 자식 클래스에서 호출하지 않을때는 안만들어도 되지만 자식 클래스 쪽에서 호출을 할떄는 부모클래스의 기본생성자가 필요하고, 부모 클래스의 기본생성자 없으면 오류남
- 지금은 자식 클래스의 생성자가 호출되므로 부모 클래스의 기본 생성자도 호출되기때문에 부모클래스의 기본 생성자가 필요하다

 

부모클래스의 매개변수가 있는 생성자 호출하는 방법
- 자식클래스에서 super() 형태로 호출해야함
- super()는 자식클래스의 생성자 내부 첫번째 라인에 써야함! 위치가 정해져있다

package p2022_07_05;

//super()
//1. super()는 부모 클래스의 매개변수를 가진 생성자를 호출할 때 사용한다.
//2. super()는 자식 클래스의 생성자 안에서 첫번째 라인에 사용해야 한다.
//3. super()를 이용해서 부모 클래스의 매개변수를 가진 생성자를 호출하면,
//더이상 부모클래스의 기본생성자를 호출해주지 않는다.


class Point2D06 { // 부모 클래스
	protected int x = 10; // 필드
	protected int y = 20;
	
	// 매개변수가 있는 생성자가 있을때는 예외적으로 컴파일러가 기본생성자를 생성해주지 않는다.
	public Point2D06() { //기본 생성자
		System.out.println("슈퍼 클래스인 Point2D 생성자 호출"); 
	}

	public Point2D06(int xx, int yy) {
		x = xx;
		y = yy;
	}
}

class Point3D06 extends Point2D06 {	//자식 클래스
	protected int z = 30;

	public void print() {
		System.out.println(x + ", " + y + ", " + z);	//상속받은 필드 x, y
	}

	public Point3D06() {	//자식 클래스의 기본 생성자
		super(50,60);	// 부모 클래스의 매개변수가 있는 생성자 호출
		System.out.println("서브 클래스인 Point3D 생성자 호출");
	}
}

class SuperTest06 {
	public static void main(String[] args) {
		Point3D06 pt = new Point3D06();	//자식 클래스의 생성자 호출
		pt.print();
	}
}
서브 클래스인 Point3D 생성자 호출
50, 60, 30

- 부모클래스의 매개변수 2개인 생성자가 호출되면서 x값이 50, y값이 60이 되는 것이다.

 

위 예제 처리과정
1. 메인메소드에서 먼저 Point3D06이란 자식클래스의 생성자를 호출한다
2. 자식클래스의 생성자가 호출됨, 내부 코드를 실행함
3. 원래는 자식클래스의 생성자가 호출되면 연쇄적으로 부모클래스의 기본 생성자를 호출함
4. 근데 여기선 super()를 썼으므로 super()가 우선이 됨 
- super()를 통해 호출된 부모클래스의 매개변수있는 생성자가 호출되면 부모클래스의 기본생성자는 호출되지 않음
5. 그래서 "슈퍼 클래스인 Point2D 생성자 호출" 은 출력되지 않았고 값은 50, 60으로 변경됨
- 즉 매개변수를 가진생성자만 호출되었다는 의미


Point2D06 (부모)클래스의 매개변수 2개인 생성자를 호출하고 싶다면? (위 예제 부분1)

	public Point2D06(int xx, int yy) {
		x = xx;
		y = yy;
	}

- 이걸 호출하고 싶으면 자식클래스의 생성자 내부

	public Point3D06() {	//자식 클래스의 기본 생성자
		super(50,60);
		System.out.println("서브 클래스인 Point3D 생성자 호출");
	}

- 에서 super()를 통해서 부모 클래스의 매개변수가 2개인 생성자를 호출한다
- 부모 클래스의 기본생성자 주석으로 막아도 오류 안생김

 

super() 유의사항
- super()는 자식클래스의 생성자 안에서만 사용가능하다
- 생성자 내부의 첫번째 라인만 가능하다! 다른 장소에 들어가면 오류 발생한다
- 자식 생성자가 호출되면 부모의 기본생성자가 호출되지만 자식 생성자 내에서 super()를 써서 매개변수 있는 생성자 호출할떄는
부모의 기본생성자는 호출되지 않는다.
- super()가 우선순위 높다, super()쓰면 기본생성자 호출안해줌 
- 동시에 2개(기본 생성자, 매개변수가 있는 생성자) 호출해주지 않는다
- super()는 메소드 안에 못쓴다.
- super()가 우선순위 높다, super()쓰면 기본생성자 호출안해줌 

super() 정리
1. super()는 부모 클래스의 매개변수를 가진 생성자를 호출할 때 사용한다.
2. super()는 자식 클래스의 생성자 안에서 첫번째 라인에 사용해야 한다.
3. super()를 이용해서 부모 클래스의 매개변수를 가진 생성자를 호출하면, 더이상 부모클래스의 기본생성자를 호출해주지 않는다.

super. 와 super()의 차이
- super. : 메소드 오버라이딩 되었을때 부모클래스 안의 필드, 메소드를 호출할 때 사용, 잘 안 쓴다
- super() : 자식클래스에서 부모 클래스의 매개변수를 가진 생성자를 호출할 때 사용, 잘 쓴다

+ super() 는 그래픽 프로그램 예제 다룰 때 썼었다
- FrameTestEx extends Frame 클래스의 생성자  첫번째 라인에서 super("Frame Test"); 라고 썼다. 
- 자기 부모의 매개변수가 있는 생성자를 호출하라는 의미로서 부모인 Frame클래스의 매개변수가 String인 생성자를 호출하고 있다

 

this. 예제
- DMBCellPhone.java

package p2022_07_05;
// p290 ~ 292

class CellPhone{							// 부모 클래스
	String model;							// 필드
	String color;
	
	void powerOn() {						// 메소드
		System.out.println("전원을 켭니다.");
	}
	void powerOff() {
		System.out.println("전원을 끕니다.");
	}
	void bell() {
		System.out.println("벨이 울립니다.");
	}
	void sendVoice(String message) {
		System.out.println("자기:"+message);
	}
	void receiveVoice(String message) {
		System.out.println("상대방:"+message);
	}
	void hangUp() {
		System.out.println("전화를 끊습니다.");
	}	
}

// 부모 클래스의 필드와 메소드는 상속된다.
class DMBCellPhone extends CellPhone{	// 자식 클래스
	int channel;

	public DMBCellPhone(String model, String color, int channel) {
		this.model = model;				// 자바폰
		this.color = color;				// 검정
		this.channel = channel;			// 10
	}
	
	void turnOnDmb() {
		System.out.println("채널:"+channel+"전 DMB방송 수신을 시작 합니다.");
	}
	void changeChannelDmb(int channel) {
		this.channel = channel;
		System.out.println("채널"+channel+"번으로 바꿉니다.");
	}
	void turnOffDmb() {
		System.out.println("DMB방송 수신을 멈춥니다.");
	}	
}

public class DMBCellPhoneEx {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		DMBCellPhone dmb = new DMBCellPhone("자바폰", "검정", 10);
		
		// model, color 필드는 부모 클래스로 부터 상속 받아서 사용
		System.out.println("모델:"+dmb.model);
		System.out.println("색상:"+dmb.color);
		System.out.println("채널:"+dmb.channel);

		// 부모 클래스로 부터 상속 받아서 사용되는 메소드
		dmb.powerOn();
		dmb.bell();
		dmb.sendVoice("여보세요");		
		dmb.receiveVoice("안녕 하세요! 저는 홍길동 입니다.");		
		dmb.sendVoice("아~ 예 반갑습니다.");		

		dmb.turnOnDmb();
		dmb.changeChannelDmb(12);
		dmb.turnOffDmb();		
	}

}

 

위 예제 부분 1

		DMBCellPhone dmb = new DMBCellPhone("자바폰", "검정", 10);

위 예제 부분 2

	public DMBCellPhone(String model, String color, int channel) {
		this.model = model;				// 자바폰
		this.color = color;				// 검정
		this.channel = channel;			// 10
	}

- DmbCellPhone 클래스의 생성자에서 초기화할때 상속받은 필드들도 다 초기화하고 있다
- this. 써서 가능 model과 color는 상속을 받아 쓰는것

 

위 예제 부분 3

	void changeChannelDmb(int channel) {
		this.channel = channel;
		System.out.println("채널"+channel+"번으로 바꿉니다.");
	}

- DmbCellPhone 클래스의 메소드인 changeChannelDmb()에서 매개변수와 필드가 이름이 같기때문에 여기서도 this. 를 쓴다

 

위 예제 부분 4

		// 부모 클래스로 부터 상속 받아서 사용되는 메소드
		dmb.powerOn();
		dmb.bell();
		dmb.sendVoice("여보세요");		
		dmb.receiveVoice("안녕 하세요! 저는 홍길동 입니다.");		
		dmb.sendVoice("아~ 예 반갑습니다.");

- 상속받은 메소드를 자기것처럼 씀

+ Ctrl키 누르고 메소드명 클릭하면 그게 정의되어있는 곳으로 감! 파일 달라도 거기로 찾아감

 

메소드 오버라라이딩 예제
- ComputerEx.java

package p2022_07_05;
// p296 ~ 297
// 메소드 오버라이딩(Method Overriding)
// : 부모 클래스로 상속받은 메소드를 자식 클래스에서 재정의 해서 사용하는 것

class Calculator{						// 부모 클래스
	double areaCircle(double r) {
		System.out.println("Calculator 객체의 areaCircle() 실행");
		return 3.14159 * r * r;
	}
}

class Computer extends Calculator{		// 자식 클래스	

	@Override
	double areaCircle(double r) {		// 메소드 오버라이딩
//		super.areaCircle(r);
		System.out.println("Computer 객체의 areaCircle() 실행");
		// TODO Auto-generated method stub
		return Math.PI * r * r;
	}		
	
}

public class ComputerEx {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		int r = 10;
		Calculator cal = new Calculator();
		System.out.println("원의 면적:"+ cal.areaCircle(r));
		
		// 자식클래스에서 메소드 오버라이딩 된 메소드만 호출된다.
		Computer comp = new Computer();
		System.out.println("원의 면적:"+ comp.areaCircle(r));
	}

}

- 부모클래스로부터 상속받은 Computer 클래스는 메소드 오버라이딩을 수행한다.
- 이때, 부모클래스의 이름과 형식 (매개변수, 리턴형) 그대로 해야한다
- 만약 부모클래스의 메소드를 호출하고 싶다면 return super.areCircle(r)로 super. 를 쓰면 된다.

- 클래스를 상속받을땐 메소드 오버라이딩을 해도 되고 안해도 됨.
- 상속을 하더라도 메소드 오버라이딩 안해도 됨
- 이게 끝나면 추상클래스라는걸공부하는데 추상클래스가 extends뒤에 오게되면 반드시 메소드 오버라이딩을 해야함 

+ 인터페이스는 클래스처럼 extends로 상속받는게 아니라 implements 키워드로 상속을 받음
+ 직접 내가 클래스를 만들때 상속을 하려면 ex까지 쓰고 ctrl+ space하면 자동으로 입력됨

 

이클립스 메소드 오버라이딩 생성
- 이때 이클립스를 이용하면 메소드 오버라이딩 자동으로 만들어주는 기능 있다
- 주석으로 막고 이클립스로 메소드 오버라이딩 해보자

오른쪽 마우스 -> Source -> Override/Implement Methods
- 부모클래스, 조상클래스(Object등)가 보이고 그 안의 오버라이딩 할 메소드 선택 가능
- 원하는 메소드만 클릭해서 오버라이딩 하면 된다

- 그럼 이게 생성된다

	@Override
	double areaCircle(double r) {
		// TODO Auto-generated method stub
		return super.areaCircle(r);
	}

 

오버라이드 어노테이션

@Override

- 컴파일러가 이 어노테이션 만나면 아래쪽에 메서드가 오버라이딩 잘 되었는지 체크한다
- 이런역할을 어노테이션이 해줌
- 없어도 실행에 지장은 없다.

 

메소드 오버라이딩 정리
- 오버라이딩 하면 부모클래스의 메소드가 아닌 자식클래스에서 메소드 오버라이딩 된 메소드만 호출된다.
- 은닉된 메소드를 굳이 호출할떄는 자식 클래스의 메소드에서 super. 으로 호출한다
- 이 내용이 그 다음의 추상클래스, 인터페이스와 연결됨 거기선 반드시 메소드 오버라이딩을 해야함 

final 키워드
- 상속을 배우기 전 간단히 설명 했었다
- final 필드 : 수정 불가 필드 = 상수
- final 메소드 : 자식 클래스에서의 이 메소드 오버라이딩을 허용하지 않음 (마지막 메소드)
- final 클래스 : 상속을 허용하지 않는 클래스 (마지막 클래스)
- 여기서 final 메소드와 final 클래스를 볼 것

final 메소드
- 상속이 되더라도 메소드 오버라이딩 불가능 메소드 오버라이딩 하려하면 오류 발생시킴

final 클래스
- 해당 클래스를 상속 하려고 하면 오류 발생시킴

상속에서의 접근제어자
* 자바의 접근 제어자


1. 상속 관계가 있는 경우
1-1. 2개의 클래스(부모,자식 클래스)가 같은 패키지 안에 들어 있을때
- 부모의 접근제어자가 default, protected, public 접근제어자인 경우에 자식클래스에서 접근 할수 있다. 
- (단, private 접근제어자만 자식 클래스에서 접근 할 수 없음)

1-2. 2개의 클래스(부모,자식 클래스)가 다른 패키지 안에 들어 있을때
- 부모의 접근제어자가  protected, public 접근제어자인 경우에 자식클래스에서 접근 할수 있다. 

2. 상속 관계가 없는 경우
- 2개의 클래스가 서로 다른 패키지 안에 들어 있을때는 public 접근제어자로 되어 있어야만 다른 클래스에서 접근 할 수 있다.

import 기준
- 패키지가 달라질때 -> 반드시 import

패키지가 다를때 접근하는법
1. 접근제어자가 public 이면 다 됨
2. 두 클래스 상속관계 && 접근제어자 protected

+ 그래서 이 protected 접근제어자는 상속에서 많이 씀

상속과 접근제어자
1. 같은 패키지 안의 클래스 2개가 부모-자식 상속관계 -> private 빼고 다 접근가능하다
- public은 다 되고, default 는 원래부터 같은 패키지면 접근가능하고
- protected는 원래부터 같은 패키지면 접근 가능했고 또 상속관계면 다 접근 가능하기떄문에 접근 가능

2. 다른 패키지 안의 부모-자식 상속관계 -> protected, public 둘 중 하나여야만 함
- default 는 다른패키지는 접근 불가
- protected는 패키지가 달라져도 상속관계면 접근 가능함
- private은 자식 클래스라도 외부클래스이므로 접근 불가
+ private은 딱 자기 클래스만 접근 가능
- 다른 패키지더라도 상속관계이고 protected면 접근 가능하다

상속과 접근제어자 예제
- 예제 중에서 부모클래스는 src - packTest - packOne -  AccessTest.java(부모클래스)
- 자식클래스 패키지 없기때문에 복사를 해서 바로 scr에 집어넣기, SubOne.java
- 제3의 클래스 SuperSubA.java

- 부모클래스 AccessTest.java는 scr의 packTest.packOne 패키지를 만들어서 넣기
- 자식 클래스는 default package안에 들어가게됨
- 부모클래스에는 4개의 변수가 있는데 접근제어자가 다 다르다
- 원래는 public 일떄만 다른 패키지 접근 가능하지만
- 상속관계에 있고 protected 면 접근 가능하다
- 즉 부모와 자식이 서로 다른 패키지 안에 들어가있을때 4가지 접근제어자 중 어떤 접근제어자가 접근가능하게하는지 알아보는 예제임

정리
- 부모클래스 (AccessTest) -> 여기 필드값에 다양한 접근제어자가 있다
- 자식 클래스(SubOne)는 다른 패키지 안에 있을떄 protected, public이 가능함
- 제3의 클래스(SuperSubA)는 public 클래스만 접근 가능

 

// 부모 클래스
package packTest.packOne;

public class  AccessTest {  //다른 패키지에서 가져다 사용할 것임으로 pubic으로 
  private int    a=10;   //[1] private           
  int            b=20;  //[2] 기본 접근 지정자
  protected  int c=30;  //[3] protected        
  public     int d=40;  //[4] public       

  public void print( ){
    System.out.println("AccessTest의 print");
    System.out.println(a);
    System.out.println(b);
    System.out.println(c);
    System.out.println(d);
  }
}
// 자식 클래스
import  packTest.packOne.AccessTest;

//AccessTest의 서브 클래스로 SubOne을 설계
class SubOne extends AccessTest {
  void subPrn( ){
    System.out.println(a); //[1. Sub] private -X 에러
    System.out.println(b); //[2. Sub] 기본 접근 지정자-X 에러
    System.out.println(c); //[3. Sub] protected -O
    System.out.println(d); //[4. Sub] public -0
  }
}
// 제 3의 클래스
//AccessTest랑 상속관계가 없는 클래스 
class SuperSubA{
  public static void main(String[] args){
    AccessTest at=new AccessTest( );
    at.print( );
    System.out.println("main");
    System.out.println(at.a); //[1. main] private -X 에러
    System.out.println(at.b); //[2. main] 기본 접근 지정자-X 에러
    System.out.println(at.c); //[3. main] protected -X 에러
    System.out.println(at.d); //[4. main] public -O
  }
}

 

1. 상속 관계 있는 자식클래스 SubOne 에선 부모 클래스와 자식 클래스가 서로 다른 패키지에 있을때
- 이때 private 접근제어자로 되어있는 a는 자식이더라도 상속이 안됨(접근이 안됨)
- 이때 default 접근제어자로 되어있는 b는 자식이더라도 상속이 안됨(접근이 안됨)
- 이때 protected 접근제어자로 되어있는 c는 자식이라서 상속이 됨(접근 가능)
- 이때 public 접근제어자로 되어있는 d는 public이니까 상속이 됨(접근 가능)

2. 상속 관계 없는 제3의 클래스 SuperSubA 에선 제3의 클래스인데 서로 다른 패키지에 있을때
- 기존 내용처럼 public 접근제어자로 되어있는 필드만 접근 가능\

강사님 정리
src - packTest - packOne -  AccessTest.java(부모클래스)

SubOne.java(자식클래스) : protected, public만 접근가능
SuperSubA.java(제3의 클래스) : public만 접근가능    

- 필드의 접근제어자를 주로 protected를 많이 씀
- 두 패키지가 상속관계 && 서로다른 패키지 -> public, protected 접근 가능
- public > protected > default > private

레퍼런스 형변환
- 추상클래스, 인터페이스까지 공부 후 공부

자바 3가지 자료형변환
1. 기본자료형 변환
2. Wrapper클래스로 주로 String <-> int 형 변환
3. 레퍼런스 형변환(반드시 두 클래스 사이 상속관계 있어야만 가능함)

추상클래스 개념 1 (개념과 특징)
- 이름만 있고 내용이 없는 클래스
- 자체적으로 객체를 생성할 수 없는 클래스
- 여태껏 한개의 클래스로 무한개 객체만들어냈지만 추상클래스는 객체 생성 불가

추상클래스 개념 2 (형식과 구성)
- 추상클래스는 일반 클래스와 구분하기 위해서 클래스 앞에 abstract를 붙임
- 추상메소드 앞에도 반드시 abstract 붙이고 맨뒤 세미콜론 ex) abstract void Method01();
- 추상클래스에 들어가는 멤버 : 일반 멤버변수, 추상 메소드, 일반 메소드 등 있다. 
- 일반 클래스와 가장 큰 차이는 추상메소드를 가질 수 있다는 점
- 추상메소드를 가질 수 있다는게 가장 큰 특징
+ 추상메소드는 이름만있고 내용이 없는 메소드

추상클래스 개념 3 (생성과 사용)
- 추상클래스 상속 받을때는 클래스기떄문에 자식들은 일반 클래스 상속처럼 extends로 상속
- 그리고 부모 추상 클래스 나머지 안에 있는 추상메소드들을 반드시 메소드 오버라이딩 해야함
- 자체적으로 new연산자로 객체 생성 불가능한게 추상클래스 특징
- 부모 추상 메소드 : 공통적인 내용들을 넣어야함
ex) 새 클래스, 곤충 클래스, 물고기 클래스 -> 공통특징 추출해서 부모클래스 만듬 -> 부모클래스는 동물클래스
+ 나중에는 부모클래스 만들때 일반클래스보다 추상클래스를 부모클래스로 많이 만듬

추상클래스 개념 4 (상속)
- 자체적으로 객체 생성 안되므로 추상 클래스를 일반 클래스가 상속받아서 쓰는거다.
- 추상클래스를 상속받은 일반 클래스는 추상클래스 안에 들어있는 추상 클래스를 반드시 Method Overriding을해야한다
- 부모클래스의 이름, 형식 그대로 따라서 내용을 다르게 써내려가면 된다
- 메소드 오버라이딩 안하면 오류발생
- 추상클래스도 단일상속만 가능하다,2개이상의 추상클래스 상속 불가
- 자바는 일반클래스든 추상클래스든 다중상속 허용안하기 때문임

인터페이스
+ 추상클래스의 기능 있음 (추상메소드 가질수있음)
+ 인터페이스는 다중상속까지 허용함

+ 나중엔 추상클래스는 거의 안쓰고 인터페이스를 쓴다
+ 인터페이스 쓰기위해서 추상클래스 이해하는것임
+ 보통 추상클래스를 부모로 만드는게 아니라 인터페이스를 부모로 만듬

복습 / 과제 설명
https://laker99.tistory.com/20?category=1065834
- Calendar 클래스 로 날짜 시간 요일 까지 출력
- 새로 만드는게 아니라 시스템으로부터 구해오는 클래스라서 Calendar 클래스는 new 연산자로 객체 못만듬

Calendar 객체 생성법
1. 

Calendar c1 = new GregorianCalendar


- 이미 형성된 상속관계, 자식 클래스인 GregorianCalendar 로 객체를 만들어서 왼쪽의 부모에게 전달 = 레퍼런스 형변환
- 자식 객체가 부모에게 전달 = 업캐스팅
- 세번째 형변환인 레퍼런스 형변환이며, 두 클래스 사이에 상속관계가 전제되어야만 가능함

2. 

Calendar c = Calendar.getInstance();


- Calendar 클래스 안의 정적메소드 getInstance() 메소드 사용해서 객체를 구해옴, Calnedar 객체를 받음 
* 객체 = 인스턴스

- 년월일시분초 -> Calendar의 정적필드를 이용해서 구해야함(API 문서)
- 클래스명.필드명 으로 필드 사용
- 연도 구해올땐 Calendar.YEAR 필드로 get함수써서 구함

 

int y = c.get(Calendar.YEAR);
int m = c.get(Calendar.MONTH) + 1; // 0~11 이므로 +1 해주기
int d = c.get(Calnedar.DATE);

int h1 = c.get(Calendar.HOUR); // 12시간
int h2 = c.get(Calendar.HOUR_OF_DAY); // 24시간

int ap = c.get(Calendar.AM_PM); // 오전 0 오후 1
int w = c.get(Calendar.DAY_OF_WEEK); //1~7 을 돌려줌

- 1,2,3,4,5,6,7 로 일정히 증가하므로 숫자를 요일로 변환시켜주기 위해서는 String[] 클래스 배열을 사용한다 

 

		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]+"요일");

- 이렇게 배열인덱스와 맞춰주면 됨 
- "금" 은 6이 나오는데 String배열에는 인덱스 5번이므로 -1 해줌

 

문자열 처리 클래스 종류
- String, StringBuffer, StringTokenizer
1. String : 문자열을 저장함, 가장 빈도 높은 사용
2. StringTokenizer : 여러 문자들을 구분기호로 파싱할때 사용
3. StringBuffer - 가장 빈도 낮지만 알아야함

문자열 처리 클래스 차이
- 3개의 클래스가 기능이 조금씩 다르다.
- 메모리 상에서 데이터 처리하는데 차이가 있다
- String 클래스의 메소드 아주 많다

String 클래스와 메소드
- 자바에서 가장 사용빈도 높은 클래스
- java.lang 패키지 안, import필요 없다
- 지원되는 메소드 100개 넘음

 

String 클래스 예제1

- StringTest00.java

package p2022_07_01;

class StringTest00 {
	public static void main(String[] args) {
		String str1 = new String("Java Programming");
		String str2 = new String("Java Programming");

		if (str1 == str2) { // 주소 비교
			System.out.println("같다");
		} else {
			System.out.println("다르다"); // 다르다
		}
		String str3 = "Java Programming";
		String str4 = "Java Programming";

		if (str3 == str4) {
			System.out.println("같다"); // 같다
		} else {
			System.out.println("다르다");
		}

		if (str1.equals(str3)) { // 값 비교
			System.out.println("같은 값");
		} else {
			System.out.println("다른 값");
		}
	}
}
다르다
같다
같은 값

- String 클래스만 new를 써도 되고 그냥 String name = "mimi" 라고 할 수도 있다
- new연산자 쓸때마다 매번 힙메모리상에 새로운 공간을 생성하라는 표시
- 힙메모리상에 "Java Programming"을 저장하고 있다

 

- 여기서 str3와 str2의 주솟값이 같을까? 즉 같은 "Java Programming"일까?
-> 아니다 위에서든 밑에서든 new를 쓰면 그냥 단독으로 새로운 공간 가지는거임

- str3에서 힙에 Java Programming 만들어졌으므로
- String str4 = "Java Programming"; 하면 그냥 str4는 이미 str3때 만들어진 값의 주솟값을 리턴받음
-> str3 == str4 같다 (같은 주소다)

 

그림

 

참조형의 비교대상 2개
1. 비교연산자로 주솟값 비교
2. equals로 값을 비교

 

String 클래스 예제2
- ConnectString.java

package p2022_07_01;

public class ConnectString {
	
    public static void main( String[] args ) {
	// String 객체 선언
	String gemini = "gemini";
	String johnharu = "johnharu";
		
	// 두 String 객체를 "+" 연산 수행
	String tempString1 = gemini + johnharu;
	System.out.println( tempString1 );
	System.out.println( "gemini" + "johnharu" );

	// String + 정수형
	String tempString2 = tempString1 + 100;
	System.out.println( tempString2 );
    }
}
geminijohnharu
geminijohnharu
geminijohnharu100

 

위 예제 부분1

	String tempString1 = gemini + johnharu;
	System.out.println( tempString1 );

 

- 객체 + 객체 를 쓰면 주솟값이 아닌 가리키는 값들끼리를 연결해준다

 

위 예제 부분2

	System.out.println( "gemini" + "johnharu" );

- 이렇게해도 연결되어 출력된다

 

String형 자료형과 기본자료형 변수끼리 결합(위 예제 부분3)

	// String + 정수형
	String tempString2 = tempString1 + 100;
	System.out.println( tempString2 );

- String형 자료형과 기본자료형 변수끼리 산술적인 연산 +가 안된다
- 붙어서 geminijohnharu100이 출력되고 결과는 전체가 String형으로 바뀜

 

String 클래스 예제3(String과 메모리)

- StringTest01.java

package p2022_07_01;

// String 객체를 생성한 후에 메소드에 의해서 값의 변화가 일어나면
// 변경된 값을 힙메모리 영역에 다시 저장 한다.

// Garbage Collection 기능 (쓰레기 수집 기능)
// : 재사용할 수 없는 힙메모리 영역의 데이터를 모아서 지워주는 기능

class StringTest01 {

  public static void main(String[] args) {
    String str1 = "Java Programming";
    
	str1.toUpperCase();              // 대문자로 변환
    System.out.println(str1);      //메서드 호출 후에도  str1의 내용은 수정되지 않는다.(원래값출력)
	System.out.println(str1.toUpperCase()); //대문자 변환된 내용 출력

    String str2=str1.toUpperCase();  //메소드의 처리 결과를 str2에 저장
    System.out.println(str2);          //대문자 변환된 내용 출력
  }    
}
Java Programming
JAVA PROGRAMMING
JAVA PROGRAMMING

 

위 예제의 부분1

	str1.toUpperCase();              // 대문자로 변환
   	System.out.println(str1);      //메서드 호출 후에도  str1의 내용은 수정되지 않는다.(원래값출력)

- 왜 위의 예제에서 str1.toUpperCase() 하면 str1의 값을 출력했을때 대문자로 변환되어 출력되지 않을까?
- String class는 값의 변화가 일어나면 변경된 값을 힙메모리상에 저장시킴
- 하지만 이 변경된 값을 재사용하지 못할때는 가비지 콜렉터가 변경이 되더라도 지워버림
- 즉 대문자로 변경이 되었지만 가비지콜렉터 기능때문에 재사용을 못하기 때문에 그 값을 지워버리고 처음 원래의 값(소문자)을 출력시킴

 

- Java Pro 에서 str1.toUpperCase() 를 쓰면 바뀐 내용(대문자) 를 힙메모리상에 새로 저장해줌
(그림 있음 2번번호)
- 하지만 접근하는방법이 없고, 재사용이 안되기 때문에 가비지 콜렉터가 지워버림
- 그래서 str1을 출력하면 원래값을 출력함
- 힙메모리상에서 값의 변화가 일어나면 매번 그걸 힙메모리상에 따로 새로 저장하기 때문이다

 

위 예제의 부분2

System.out.println(str1.toUpperCase());

 

- 한번 대문자로 출력한 후 참조하는 방법이 없으므로 없어짐(그림 3번 표시)
- 이 이후 str1을 출력시 다시 Java Programming으로 나옴

 

변경된 값 재사용
- str2처럼 이 바뀐 결과를 저장하는 새로 힙에 생긴 공간의 주솟값을 저장을 해야함(그림 4번 표시)

위 예제의 부분3

    String str2=str1.toUpperCase();  //메소드의 처리 결과를 str2에 저장
    System.out.println(str2);

- 이 str2가 새로바뀐 결과가 저장된 힙메모리의 공간을 받아서 주솟값을 저장해야만 재사용가능하고 str2를 통해 그 바뀐 결과를 출력 할 수 있다
- 즉 가리켜주는 객체 없으면 값이 사라짐 재사용되지 않는 힙메모리 값은 가비지콜렉터가 지움

 

그림

 

값이 변할때 메모리 처리
- String 객체를 생성한 후에 메소드에 의해서 값의 변화가 일어나면 변경된 값을 힙메모리 영역에 다시 저장 한다.

 

Garbage Collection 기능 (쓰레기 수집 기능)
- 재사용할 수 없는 힙메모리 영역의 데이터를 모아서 지워주는 기능
- 자동 메모리 관리 기능임
- 개발자가 메모리 관리 신경 크게 안써도 됨

+ StringBuffer 클래스
- 메모리상에 문자열 저장하는건 공통적이지만 String과 달리 새로운 공간에 저장하진 않는다

 

변경된 값을 재사용하기

String original = "Java Programming"
String lowerCase = original.toLowerCase(); //java programming
String upperCase = original.toUpperCase(); //JAVA PROGRAMMING

- String은 이렇게 변경된 값의 공간의 주소를 저장할 변수가 있어야 재사용 가능하다.

 

String 클래스 메소드

 

String클래스의 toUpperCase() 메소드
- 문자열을 대문자로 바꿔주는 메소드

String클래스의 toLowerCase() 메소드
- 문자열을 소문자로 바꿔주는 메소드

String클래스의 length() 메소드
- 한글이든 영문이든 몇글자인지를 구해준다.
- 한칸 비어있는 공백도 1글자로 인식함
- 문자열의 길이를 구해준다. 글자수를 구해줌

String클래스의 length() 메소드 예제

- FindBlankIndex.java

package p2022_07_01;

public class FindBlankIndex {
    public static void main( String[] args ) {
		
	String message = "Java program creates many objects.";
	
	// message의 길이를 구함.
	// length() : 문자열의 길이를 구해준다. 글자수를 구해줌
	int len = message.length();

	System.out.println(len);  // len = 34;

	// message 중에서 ' '을 찾음
	for( int i=0 ; i<len ; i++ ) {
	    char c = message.charAt( i );
		if( c == ' ' ) { //공백의 인덱스 번호를 구해서 출력
		    System.out.println( "index = " + i );
		}
	}//for end
    }
}

- 공백이나 . 까지도 한글자로 포함
- 반환형 int, 문자열의 길이를 리턴
-  for문으로 루프 돌려서 0부터 len보다 작을때까지 즉 len 만큼 for문을 돌림

 

length vs length() vs size()
- 배열의 크기를 구해주는 length 속성 -> 속성이므로 ()가 없다
- String 자료가 몇글자인지 구해주는 length() 메소드 -> 메소드이므로 ()
- 자료구조에 저장된 원소의 개수를 구해주는 size() 메소드 -> 메소드이므로 ()

String클래스의 중 charAt() 메소드
- 매개변수 int 이고 안에 인덱스 번호가 들어감
- 리턴형은 char임 즉 인덱스를 받아서 거기에 해당하는 문자를 가져옴.
- 인덱스는 0번부터 시작

그림

String클래스의 중 charAt() 메소드 예제

package p2022_07_01;

public class FindBlankIndex {
	public static void main(String[] args) {

		String message = "Java program creates many objects.";

		// message의 길이를 구함.
		// length() : 문자열의 길이를 구해준다. 글자수를 구해줌
		int len = message.length();

		System.out.println(len); // len = 34;

		// message 중에서 ' '을 찾음
		for (int i = 0; i < len; i++) {
			char c = message.charAt(i);
			if (c == ' ') { // 공백의 인덱스 번호를 구해서 출력
				System.out.println("index = " + i);
			}
		} // for end
	}
}
34
index = 4
index = 12
index = 20
index = 25

- ' ' 즉 공백이 인덱스 몇변이 있는지 출력해줌 (공백을 찾아주는 코드)

 

String클래스의 indexOf() 메소드
- charAt과 반대기능의 메소드
- 특정 문자의 인덱스번호를 구해주는 메소드
- 찾고자하는 문자가 없을때는 -1을 리턴함
- API 문서에서 indexOf() 메소드 찾아보면 메소드 오버로딩 되어있다

String클래스의 indexOf() 메소드 예제1

	String message = "Java program creates many objects.";
	int index1 = message.indexOf( 'a' );

- 출력 : 1
- a란 문자의 index 번호를 구해와라
- a가 여러개 있을땐, 가장 먼저 나오는 a의 인덱스 번호를 구해라

 

String클래스의 indexOf() 메소드 예제2

	String message = "Java program creates many objects.";
	int index2 = message.indexOf( 97 );

- 출력 : 1
- 97은 10진수 아스키 코드값이므로 거기에 해당하는 영문자를 찾아줌
- 이 영문자를 찾아서 인덱스를 가져와줌
- 10진수 아스키코드(97) 에 해당되는 문자('a')의 인덱스 번호를 구함.
- 이 97을 65로 바꾸면? -> 'A'를 찾는데 찾고자하는 문자가 없으므로 -1을 리턴함

 

String클래스의 indexOf() 메소드 예제3(매개변수가 2개)

	String message = "Java program creates many objects.";

	//index번호 13번째 이후에서 'a'의 인덱스 번호를 구함
	int index3 = message.indexOf( 'a', 13 ); 
	System.out.println( index3 );

- index번호가 13번 이후에 가장 먼저나오는 'a' 의 인덱스 번호를 찾아라
- 즉 c(13) 부터 찾아라 그 'a' 는 16이 나옴
- 이렇게 위치가 지정될땐 그 이후의 문자를 찾고 위치 지정이 안되었을떈 아까의 예시처럼 가장 먼저나오는 문자의 인덱스값을 찾음

 

String클래스의 indexOf() 메소드 예제4(매개변수가 String)

	String message = "Java program creates many objects.";

	// 가장 먼저 나오는 'av'의 인덱스 번호를 구함
	int index4 = message.indexOf( "av" );
	System.out.println( index4 );

- 출력 : 1
- Java의 av 이므로 결과는 1
- 문자 한글자가 아니라 문자열이 들어가는 경우임
- 가장 먼저 나오는 av의 인덱스 번호를 구해라

 

String클래스의 indexOf() 메소드 예제5(매개변수가 2개)

	String message = "Java program creates many objects.";

	// index번호 12번 이후에 나오는 'man'의 인덱스 번호를 구함
	int index5 = message.indexOf( "man", 12 );
	System.out.println( index5 );

- 이렇게 위치가 지정될땐 그 이후의 문자열의 인덱스번호를 주고 위치 지정이 안되었을떈 아까의 예시처럼 가장 먼저나오는 문자의 인덱스값을 찾음

 

String클래스의 indexOf() 메소드 예제6(결과가 없는 경우)

	String message = "Java program creates many objects.";
	// 찾는 문자가 없는 경우에는 -1을 리턴한다.
	int index6 = message.indexOf( "java" );
	System.out.println( index6 );

- 출력 : -1
- 소문자 java가 없다 찾고자하는 결과없으면 -1 리턴한다
- 이 indexOf 메소드는 대소문자를 구분한다

 

String클래스의 trim() 메소드
- 문좌열 좌,우의 공백을 없애주는 역할

String클래스의 equals() 메소드
- boolean equals()
- 값을 비교하는 역할
- 가리키는 값들끼리 비교함

 

String클래스의 trim() 메소드 / equals() 메소드 예제

package p2022_07_01;

public class TrimTest {

    public static void main( String[] args ) {
    	
//    trim() : 문좌열 좌.우의 공백을 없애주는 역할
//    boolean equals() : 값을 비교하는 역할
	String str1 = new String( "gemini   " );
	String str2 = new String( "   gemini " );

	System.out.println( str1.equals( str2 ));
	System.out.println( str1.trim().equals( str2.trim()));
    }
}

- str1 가 가리키는 gemini 문자열 오른쪽에 공백이 있다
- str2 가 가리키는 gemini 문자열 왼쪽에 공백이있다
- 공백도 메모리상에 공간을 차지함

 

위 예제 부분1

	System.out.println( str1.equals( str2 )); //false

- 공백도 문자로 인식하므로 서로 다른 값으로 인식함.

 

위 예제 부분2

	System.out.println( str1.trim().equals( str2.trim()));

- 서로 같은값임. 
- str1.trim()은 str1이 가리키는 문자열 오른쪽 공백 없애줌(좌,우 모두)
- str2.trim()은 str2이 가리키는 문자열 왼 공백 없애줌(좌,우 모두)
- 둘다 gemini만 남게되고 값이 같으므로 true리턴

 

String클래스의 replace() 메소드
- 문자열에서 첫번째 매개변수 값을 두번째 매개변수 값으로 변환시켜줌
ex) replace("자바","JAVA") : "자바"를 "JAVA"로 치환

 

String클래스의 replace() 메소드 예제1
- ReplaceEx.java

package p2022_07_01;

public class ReplaceEx {

	public static void main(String[] args) {

	//p506
	//replace("자바","JAVA") : "자바"를 "JAVA"로 치환
		
		String oldStr = "자바는 객체지향 언어 입니다.";
		
		String newStr = oldStr.replace("자바", "JAVA");
		System.out.println(oldStr);
		System.out.println(newStr);
	}

}
자바는 객체지향 언어 입니다.
JAVA는 객체지향 언어 입니다.

 

String클래스의 replace() 메소드 예제2

 

		String oldStr = "자바는 객체지향 언어 입니다. 자바";
		
		String newStr = oldStr.replace("자바", "JAVA");
		System.out.println(oldStr);
		System.out.println(newStr);
자바는 객체지향 언어 입니다.
자바 JAVA는 객체지향 언어 입니다.JAVA

- "자바" 가 여러개 있어도 다 JAVA로 바꿈

 

+ String클래스는 메소드를 통해 값의 변화가 일어나면 매번 힙메모리상의 새로운공간에 바뀐값을저장함

 

String클래스의 substring() 메소드
- 일부 문자열만 추출
- 메소드 오버로딩되어있다
- 매개변수가 2개인 경우, 매개변수가 1개인 경우 추출하는 범위가 달라진다.

1. 매개변수 2개인경우
- 시작과 끝 인덱스를 지정한다
- 하지만 실제 우리가 추출하는건 하나 적게 추출함 즉 begInindex ~ endIndex-1까지 추출

2. 매개변수 1개인경우
- 시작 인덱스만 지정한다
- 시작 인덱스로부터 끝까지 추출하라는 의미

String클래스의 substring() 메소드 정리
- substring() : 전체 문자열에서 특정 범위의 문자를 추출하는 역할
- substring(n) : index번호 n번 부터 끝까지 추출
- substring(n1, n2) : index번호 n1번 부터 (n2-1)번 까지 추출

 

String클래스의 substring() 메소드 예제1(매개변수 1개)

	String message = "Java program creates many objects.";

	// 인덱스번호 13번 부터 끝까지 추출		
	String str1 = message.substring( 13 );
	System.out.println( str1 );
creates many objects.

- c가 13번 위치였다. 즉 13부터 모두 추출
- 13은 인덱스 번호 13번부터 끝까지 추출해서 결과를 str1 변수에 저장하고 출력시켜라는 의미

 

String클래스의 substring() 메소드 예제2(매개변수2개)

	String message = "Java program creates many objects.";

	// 인덱스번호 13번 부터 15번까지 추출
	String str2 = message.substring( 13, 16 );
	System.out.println( str2 );
cre

- c는 13, r은 14, e는 15 (15까지 추출)
- beginIndexNum이 13번 endIndexNum이 16인데 실제로는 13부터 15번까지 추출함

 

- 여기서 str1과 str2는 변경된 문자열을 가리킴(저장)
- 자바는 substring 메소드만 제공함

 

String클래스의 substring() 메소드 예제3
- SubstringEx.java
- substring() 이용해서 한글자 추출하기
- substring(그 글자 인덱스, 그 다음글자 인덱스)
+ string값 비교에 == 쓰지말고 equals쓰기

package p2022_07_01;

public class SubstringEx {

	public static void main(String[] args) {

//아래와 같이 주민번호가 있을때, 남자인지 여자인지 판별하는 프로그램을 작성하세요?

		String jumin = "900101-2233456"; // 2000년 이후 남자는 3번, 여자는 4번
		String gender = jumin.substring(7, 8);

		if (gender.equals("1") || gender.equals("3")) {
			System.out.println("남자");
		} else if (gender.equals("2") || gender.equals("4")) {
			System.out.println("여자");
		}
	}

}

- 문자끼리 비교하는거니 gender.equals("1") 라고 "1"이라고 문자를 넣어야지 숫자 1을 넣으면 안됨!
-  if-else로 해도 되지만 주민번호 첫자리 수 5(귀화한분들) 이런 경우도 있으므로 안전상 if-elseif로 해야한다

 

위 예제 부분1

		String gender = jumin.substring(7, 8);

- 이렇게 해야 7번 인덱스의 문자열을 가져옴

 

String클래스의 substring() 메소드 예제4

- 키보드로 주민번호를 입력받는다, 주민번호 문자로 받아야함(문자열 클래스로 이것저것 할 것 이므로)
- 앞자리 6자리가아니면 메세지 뿌림
- 뒷자리 7자리가아니면 메세지 뿌림 
- 이런걸 유효성 검사라고 한다
- 그리고 이 모든게 완벽한 값이면 입력한 주민번호가 남자인지 여자인지도 판별

package p2022_07_01;

import java.util.Scanner;

public class JuminCheck {

	public static void main(String[] args) {
// 키보드로 주민번호를 입력 받아서 남자인지 여자인지를 판별하는 프로그램을 작성하세요?
// 단, 주민번호 앞자리는 6자리, 뒷자리는 7자리 인지를 유효성 검사를 한다.

		// 키보드로 주민번호 입력받기
		Scanner sc = new Scanner(System.in);
		System.out.println("주민번호 앞자리를 입력 하세요?");
		String jumin1 = sc.nextLine();

		System.out.println("주민번호 뒷자리를 입력 하세요?");
		String jumin2 = sc.nextLine();

		// 남녀
		String g = jumin2.substring(0, 1); // 0번 인덱스 추출한다는 의미

		// 유효성 검사 & gender
		if (jumin1.length() != 6) {
			System.out.println("주민번호 앞자리 6자리를 입력하세요.");
		} else if (jumin2.length() != 7) {
			System.out.println("주민번호 뒷자리 7자리를 입력하세요.");
		} else if (g.equals("1") || g.equals("3")) {
			System.out.println("남자");
		} else if (g.equals("2") || g.equals("4")) {
			System.out.println("여자");
		} else {
			System.out.println("똑바로 입력 하세요.");
		}

	}
}

- if-elseif-else문 처리기때문에 처음 주민번호 앞자리 6자리가 아니면 더이상 아래쪽을 실행안함.
- 주민번호 앞자리 6자리인데 뒷자리 7자리 아니면 두번째에 걸림 아래 실행 안함
- 둘다 개수가 맞으면 이제야 남/녀 검사를함
- 주민번호는 6,7 다 맞는데 뒷자리 첫번째가 1234가 아니면 마지막인 else 구문에서 걸린다

 

+ String클래스의 split() 메소드
- 이후 뒤에서 자세히 설명함(StringTokenizer 클래스 공부 이후)
- 전체 문자에서 구분기호를 가지고 문자를 추출할때 사용함
- @ 나 / 등의 정규 표현식을 구분자로 해서 문자를 자름 이걸 파싱이라고함
- 결과를 돌려주는 자료형은 String형 1차원 배열로 돌려줌

 

+ Strping클래스의 split() 메소드는 은 StringTokenizer 와 비슷한 역할을 하지만 더 쓰기 편하다

+ String클래스의 split() 메소드 오버라이딩
- split(String regex)
- split(String regex, int limit)

 

String 클래스와 메모리 정리
- String 클래스는 메소드에서 값의 변화가 일어나면 변경된 값을 힙 메모리에 매번 새로운공간을 만들어서 사용함
- 재사용은 값을 받아주는 변수가 있을때만 사용가능하고 변경된정보에 접근이 불가능하면 가비지 콜렉터가 지움

사용빈도
String >>> StringTokenizer >>>>> StringBuffer 

 

String, StringTokenzier, StringBuffer

- 공통적 특징 : 힙메모리상에 문자를 저장함
- 하지만 메모리상에 데이터를 처리하는부분이 다름 (메모리상에 문자를 저장하는 방식이 다름)
- String : 메소드에서 값의 변화가 일어나면 변경된 값을 힙 메모리에 매번 새로운공간을 만들어서 사용함
- StringBuffer : 버퍼라는 기억장소를 쓰기때문에 값의 변화가 일어나더라도 새로운 공간에 값을 저장하지 않음. 시작 주소값이 바뀌지 않음. 값을 계속 누적하는 경우에 이 클래스 쓰는 경우가 있다

 

StringBuffer
- 사용빈도 낮음
- StringBuilder 와 비슷한 기능(이건 안함)
- StringBuffer도 java.lang에 있어서 import 안해도 됨
- Buffer라는 임시 기억공간에 문자열을 저장함, 기능적으로 String과 다르다

 

StringBuffer 생성자
- 매개변수가 int capacity로 된 생상자가 있다, 버퍼라는 임시기억장소의 크기를 생성하는 거임
- 크기를 지정하면 지정된크기로 만들어줌, 크기 설정 따로 안해도 버퍼라는 임시기억소 만듬
- 설정안하면 기본적으로 16characther임 16글자 저장하기위한 힙메모리상의 공간을 만들어줌

 

StringBuffer 예제
- StringBufferTest.java

package p2022_07_01;

public class StringBufferTest {
	public static void main(String[] args) {

		StringBuffer sb1 = new StringBuffer("gemini");
		System.out.println("sb1.length() : " + sb1.length());	//6
		System.out.println("sb1.capacity() : " + sb1.capacity());	//22

		sb1.append("A string buffer implements" + "a mutable sequence of characters");
		System.out.println("sb1.length() : " + sb1.length());	//64
		System.out.println("sb1.capacity() : " + sb1.capacity());	//64

		StringBuffer sb2 = new StringBuffer();
		System.out.println("sb2.length() : " + sb2.length());	//0
		System.out.println("sb2.capacity() : " + sb2.capacity());	//16
	}
}

 

위 예제 부분1(매개변수가 있는 생성자)

		StringBuffer sb1 = new StringBuffer("gemini");

- 매개변수가 있는 생성자로 객체를 생성하고 있다
- "gemini"가 힙메모리 상에 저장되고, sb1이 이 주소를 갖고 있다.

 

- length()메소드 지원 : 문자열길이를 구해줌
- capacity() 메소드 : 힙메모리상의 할당된 바이트 크기 알려줌 //22바이트
- append() 메소드 : 문자열을 추가함

 

- 동적으로 공간을 늘려주는 기능도 있다 자동으로 늘려줌
- 만약 22보다 큰 크기의 문자열을 넣으면 동적으로 공간을 늘려줌
- 다시 length()를 구했을때 64가 나옴
- 처음에 22바이트였던 공간을 64만큼 늘려줌

 

위 예제 부분2(기본생성자)

		StringBuffer sb2 = new StringBuffer();

- 기본생성자로 객체를 생성하고 있다
- 길이 length 는 0임
- 자동으로 16바이트(16글자공간 만들어줌) 로 만들어줌
- 아무것도 안가리키기 때문에 length()는 0

 

StringBuffer와 값의 변화
- StringBuffer는 값의 변화가 일어나더라도 시작주소값이 바뀌지 않는다
- String과 다른 특징

 

StringBuffer와 값의 변화 예제
- AppendStringBuffer.java

package p2022_07_01;

public class AppendStringBuffer {
    public static void main( String[] args ) {
		
	// StringBuffer 객체 생성
	StringBuffer sb1 = new StringBuffer( "gemini" );
	System.out.println( "sb1 = " + sb1 );

	// StringBuffer sb1에 문자열을 추가해 새로운 객체 생성
	StringBuffer sb2 = sb1.append( " is beautiful" );
	System.out.println( "sb2 = " + sb2 );
	System.out.println( "sb1 = " + sb1 );

	if(sb1 == sb2) {
		System.out.println("같은 주소"); //같은 주소
	}else {
		System.out.println("다른 주소");
	}

	// 정수형 데이타 형을 추가
	System.out.println( sb1.append( 1004 ));
	System.out.println( "sb1 = " + sb1 );
	System.out.println( "sb2 = " + sb2 );

	String str = new String(sb1); // StringBuffer를 String으로 변환
	System.out.println(str.toUpperCase());
    }
}
sb1 = gemini
sb2 = gemini is beautiful
sb1 = gemini is beautiful
같은 주소
gemini is beautiful1004
sb1 = gemini is beautiful1004
sb2 = gemini is beautiful1004
GEMINI IS BEAUTIFUL1004

 

위 예제 부분1

	// StringBuffer 객체 생성
	StringBuffer sb1 = new StringBuffer( "gemini" );
	System.out.println( "sb1 = " + sb1 );

- 힙메모리상에 "gemini"라는 값이 저장되고 그걸 sb1이 시작주소를 가지고 있다

 

위 예제 부분2

	// StringBuffer sb1에 문자열을 추가해 새로운 객체 생성
	StringBuffer sb2 = sb1.append( " is beautiful" );
	System.out.println( "sb2 = " + sb2 );
	System.out.println( "sb1 = " + sb1 );

- append() 함수 : 기존 문자열에 새로운 문자열을 이어 붙여줌 (String클래스엔 이 함수 지원안됨)
- 그리고 이걸 sb2가 주소를 가지게 함

 

위 예제 부분3

	if(sb1 == sb2) {
		System.out.println("같은 주소"); //같은 주소
	}else {
		System.out.println("다른 주소");
	}

- 출력 : 같은 주소
- 문자열이 변경되었지만 새로운 힙공간에 저장하지 않음 그러니 sb1과 sb2가 가리키는건 같음!
- 둘다 같은 문자열이 출력됨
- 즉 같은 주소, 같은 값 같은걸 가리키고 있음

그림

 

String vs StringBuffer 차이 정리
- String : 값의 변화 일어나면 새로운 공간에 그 결과 저장. 원래 값의 주솟값 != 바뀐 값의 주솟값
- StringBuffer : 값의 변화 일어나도 기존 공간에 적용, 변경된 값의 시작주솟값이 바뀌지 않음

 

위 예제 부분4

	// 정수형 데이타 형을 추가
	System.out.println( sb1.append( 1004 ));
	System.out.println( "sb1 = " + sb1 );
	System.out.println( "sb2 = " + sb2 );

	String str = new String(sb1); // StringBuffer를 String으로 변환
	System.out.println(str.toUpperCase());

- sb1에 append(1004) 함 
- 그 후에 sb1출력하나 sb2 출력하나 같은 gemini is beautiful1004 가 출력됨
- 같은주소를 리턴받았었기때문에 그렇다

 

StringBuffer로 저장된 문자열을 대문자로 바꾸기
- StringBuffer 클래스는 toUperCase() 메소드가 없어서 못쓴다 (langth()는 있음)
- String과 StringBuffer 사이는 상속관계 없으므로 String 클래스 안에 있는걸 StringBuffer가 상속받아 쓸수도 없음

 

-> String 클래스의 생성자를 활용해서 대문자로 바꿔주면 된다.
-> String 클래스 생성자를 찾아보면 매개변수가 StringBuffer인 생성자가 있다
- String(StringBuffer buffer) 생성자
- 이 생성자를 통해서 StringBuffer -> String 으로 변환 가능
- String 객체로 받아서 toUpperCase 해서 대문자 변환하기

 

StringBuffer로 저장된 문자열을 대문자로 바꾸기 예제
- InsertStringBuffer.java
- StringBuffer 클래스의 append() 메소드 : 기존 문자열에 끝 추가
- StringBuffer 클래스의 insert() 메소드 : 지정된 특정 위치에 문자열을 삽입 시킴

package p2022_07_01;

public class InsertStringBuffer {
	public static void main(String[] args) {

		StringBuffer sb1 = new StringBuffer("gemini is beautiful");
		System.out.println(sb1);

		// 인덱스 10번 위치에 very 문자를 삽입
		sb1.insert(10, "very");
		System.out.println(sb1);

		// 인덱스 0번 위치에 1004 를 삽입하라(숫자로 써도 문자가됨)
		sb1.insert(0, 1004);
		System.out.println(sb1);
	}
}
gemini is beautiful
gemini is verybeautiful
1004gemini is verybeautiful

- 처음엔 인덱스 10번위치에 "very"란 문자열을 삽입시켜라는 의미
- is beautiful 사이의 공백이 인덱스 10이다. 그 10번 뒤에 삽입 됨-> is verybeautiful
- 이후 인덱스 0번위치에 1004를 삽입하라는 의미

 

StringBuffer 클래스의 사용
- 문자열 처리 보통은 String 클래스로 다 처리가능함
- 값을 계속 누적시키거나 하는 특별한 경우들에만 StringBuffer를 사용함

 

+ 문자열 분리 방법 2가지
- 구분기호를 가지고 문자를 추출하는, 잘라서 토큰으로 추출하는 역할
1. java.util.StringTokenizer이용
2. String의 split() 메소드 활용

 

StringTokenizer 클래스
- 앞의 두 클래스와 기능이 좀 다름
- 특정 문자를 구분기호를 가지고 토큰을 구해오는 역할을 함
- 문자를 파싱해서 처리해주는 역할을 한다
- java.util 안에 있다

 

StringTokenizer 클래스 생성자
- 매개변수 1,2,3개인 3개의 생성자 중 StringTokenizer(String str, String delim) 을 주로 사용한다
- 첫번째 매개변수에는 전체 문자열 또는 그 문자열을 담은 변수명이 온다
- 두번째 매개변수에는 구분기호가들어간다, 구분기호는 @ # / 등이 될 수 있다.

 

StringTokenizer 클래스 예제1
- StringTest04.java

package p2022_07_01;

import java.util.*;

class StringTest04 {

	public static void main(String[] args) {
		StringTokenizer str = new StringTokenizer("이순신#을지문덕#강감찬#광개토대왕", "#");

//		System.out.println(str.nextToken());
//		System.out.println(str.nextToken());
//		System.out.println(str.nextToken());
//		System.out.println(str.nextToken());
//		System.out.println(str.nextToken()); // 예외발생

		// 파싱된 문자열이 모두 몇 개인지 되는지 알려줌
		int cnt = str.countTokens();
		System.out.println("파싱할 문자열의 총갯수-> " + cnt);

//		System.out.println(str.nextToken());
		while (str.hasMoreTokens()) { // 토큰이 있으면
			System.out.print(str.nextToken() + "\t");// 차례대로 파싱된 문자열을 얻어온다.
		}

	} // main() end

}

 

StringTokenizer 클래스 사용예시(위 예제 부분1)

StringTokenizer str = new StringTokenizer("이순신#을지문덕#강감찬#광개토대왕", "#");

 

- 이렇게 쓰고 두번째 자리에는 구분기호가 들어감
- 이 #으로 전체문자열을 파싱 = 자른다
- 파싱을 하고 리턴되는 "이순신" "을지문덕" 등을 토큰이라고함
- nextTokens() 메소드 : 이 토큰들을 구해오는 메소드

		System.out.println(str.nextToken());

- 하면 첫번쨰 토큰인 이순신 이 출력됨

		System.out.println(str.nextToken());
		System.out.println(str.nextToken());

- 이렇게 더 하면 이순신, 을지문덕  출력됨
- nextToken()메소드는 String형으로 리턴한다

		StringTokenizer str = new StringTokenizer("이순신#을지문덕#강감찬#광개토대왕", "#");

		System.out.println(str.nextToken());
		System.out.println(str.nextToken());
		System.out.println(str.nextToken());
		System.out.println(str.nextToken());
이순신
을지문덕
강감찬
광개토대왕

- 가져올토큰 없는데 한번 더 nextToken() 메소드를 쓰면? 
- 예외가 발생해서 프로그램이 비정상적으로 멈추게됨

Exception in thread "main" java.util.NoSuchElementException
                 at java.util.StringTokenizer.nextToken(Unknown Source)
          at p2022_07_01.StringTest04.main(StringTest04.java:14)

- NoSuchElementException 예외 발생
* 예외 : 예상하지못했던 프로그램상 오류
-> 가져올 토큰이 있는지 없는지 판별을 해야함

 

가져올 토큰이 있는지 판별(위 예제 부분2)
- 가져올 토큰이있는경우에만 출력하기

		// 파싱된 문자열이 모두 몇 개인지 되는지 알려줌
		int cnt = str.countTokens();
		System.out.println("파싱할 문자열의 총갯수-> " + cnt); //4출력

- 토큰들 : 잘려진 부분들
- countTokens() 메소드 : 토큰의 개수를 구해주는 메소드
- 토큰이있을때만 가져오기위해서 처리하기 아래에

		while (str.hasMoreTokens()) { // 토큰이 있으면
			System.out.print(str.nextToken() + "\t");// 차례대로 파싱된 문자열을 얻어온다.
		}
이순신 을지문덕 강감찬 광개토대왕

- hasMoreTokens() 메소드 : 가져올 토큰이 있는지 없는지 판별하는 역할을 함
- 결과를 돌려주는 리턴자료형은 boolean, 가져올 토큰 있으면 true,없으면 false 리턴해서 while문 제어
-> 이러면 예외가 발생할 가능성이 없다

 

nextTokens()의 특징(위 예제 부분3)

		System.out.println(str.nextToken());
		while (str.hasMoreTokens()) { // 토큰이 있으면
			System.out.print(str.nextToken() + "\t");// 차례대로 파싱된 문자열을 얻어온다.
		}

- 이순신을 위에서 부르고 while문 에서는 을지문덕 감감찬 광개토대왕 만 나옴
- 한번 nextToken()쓰면 그건 끝난것, 그 뒤부터 출력됨

 

+ String 클래스의 split() 메소드가 이와 비슷한 역할을 한다.

 

StringTokenizer 클래스 예제2
- StringTokenizerTest1.java

package p2022_07_01;

import java.util.StringTokenizer;

public class StringTokenizerTest1 {

	public static void main(String[] args) {
	
		String source1="111-111|강원도|춘천시|퇴계동";
		StringTokenizer st1=new StringTokenizer(source1,"|");
		
		String zip=st1.nextToken();
		String dou=st1.nextToken();
		String si=st1.nextToken();
		String dong=st1.nextToken();

		System.out.println("우편번호:" +zip);
		System.out.println("도:" +dou);
		System.out.println("시:" +si);
		System.out.println("동:" +dong);
	}

}
우편번호:111-111
도:강원도
시:춘천시
동:퇴계동

- String 객체 source1에 문자열들이 있다
- - StringTokenzier 는 구분기호를 1개밖에 쓰지 못한다!
- "-" 같은거까지 더 구분 못함
- split()은 여러개 구분기호로 파싱 가능
- nextToken() 의 리턴형은 String
- nextToken() 으로 토큰 구해와서 String 객체에 저장했다.

 

StringTokenizer 클래스 예제3(매개변수가 3개)
- StringTokenizerTest2.java

package p2022_07_01;

import java.util.StringTokenizer;

public class StringTokenizerTest2 {

	public static void main(String[] args) {

		String source1 = "한국 미국 태국 중국 이란";
		StringTokenizer st1 = new StringTokenizer(source1, " ");
		while (st1.hasMoreTokens()) {
			System.out.println("st1.token:" + st1.nextToken());
		}

		System.out.println();
		System.out.println();

		String source2 = "푸들,삽살개,풍산개,진돗개";
		StringTokenizer st2 = new StringTokenizer(source2, ",");
		while (st2.hasMoreTokens()) {
			System.out.println("st2.token:" + st2.nextToken());
		}

		System.out.println();
		System.out.println();

		// true : 구분기후(,)를 토큰으로 처리해준다.
		StringTokenizer st3 = new StringTokenizer(source2, ",", true);
		while (st3.hasMoreTokens()) {
			System.out.println("st3.token:" + st3.nextToken());
		}

	}

}

 

위 예제 부분1

		String source1="한국 미국 태국 중국 이란";
		StringTokenizer st1=new StringTokenizer(source1," ");

- 국가명 사이에 공백이 들어가있으니 공백으로 파싱하자
- 첫번째 매개변수에는 문자열 또는 문자열이 저장된 변수가 들어간다
- 두번쨰 매개변수에는 구분기호가 들어가는데 여기선 " " 공백으로 파싱

 

위 예제 부분2

		while(st1.hasMoreTokens()){
			System.out.println("st1.token:"+st1.nextToken());
		}

- hasMoreTokens() : 가져올 토큰이 있는지 판별
- while문 안에 가져올 토큰이있을때만 true를 리턴해서 while문 돌려서 모두 출력함
- 가져올 토큰이 없으면 false리턴

 

위 예제 부분3

		String source2="푸들,삽살개,풍산개,진돗개";
		StringTokenizer st2=new StringTokenizer(source2,",");
		while(st2.hasMoreTokens()){
			System.out.println("st2.token:"+st2.nextToken());
		}

 

매개변수 3개인 StringTokenizer(위 예제 부분4)

		//true : 구분기후(,)를 토큰으로 처리해준다.
		StringTokenizer st3=new StringTokenizer(source2,",",true);
		while(st3.hasMoreTokens()){
			System.out.println("st3.token:"+st3.nextToken());
		}
st3.token:푸들
st3.token:,
st3.token:삽살개
st3.token:,
st3.token:풍산개
st3.token:,
st3.token:진돗개

- 첫번째, 두번쨰 매개변수에 넣어야하는건 위와 같다 
- 하지만 3번째 매개변수가 boolean형이다 즉 true나 false값을 써야함
- 세번째 매개변수에 true가 오면, 그 구분기호도 파싱될 토큰으로 처리하라는 의미
- 그래서 여기는 , 도 토큰기호로 인식해서 nextToken()하면 푸들 다음의 토큰으로 , 라는 토큰이옴
- false넣으면 다른 위의 예제들과 같게 구분기호는 토큰으로 인식 안함. 

 

String클래스의 split() 메소드
- StringTokenzier과 비슷한 역할하는 "메소드"
- 기능적으로 보면 split()이 쓰기가 더 편하고 여러개의 구분기호 가지고 있어도 파싱 가능함

- String[] Split(String regex)
- 이 매개변수에 구분기호가 온다
- 이 파싱된 결과를 왼쪽의 String 1차열 배열로 리턴
- 첫번째 토큰은 0번방, 두번째 토큰은 1번방, 세번째 토큰은 2번방...

 

String클래스의 split() 메소드 예시1

- 주민번호앞자리-주민번호뒷자리 를 - 로 파싱하자

		String jumin = "900101-1234567";
		String[] j = jumin.split("-");

- 구분기호를가지고 잘라서 결과를 왼쪽의 j (String형 배열) 로 전달
- "900101"인 첫번째 토큰이 j[0], "1234567" 인 두번째 토큰은 j[1]로 들어감

 

String클래스의 split() 메소드 예제1
- SplitEx.java

package p2022_07_01;

public class SplitEx {

	public static void main(String[] args) {

// String[] split(String regex) 메소드
		
		String jumin = "900101-1234567";
		String[] j = jumin.split("-");
		
		System.out.println("주민번호 앞자리 : " + j[0]);
		System.out.println("주민번호 앞자리 : " + j[1]);
		
		for (int i = 0; i < j.length; i++) { //향상된for문
			System.out.println("주민번호 " + j[i]);
		}

	}

}
주민번호 앞자리 900101
주민번호 뒷자리 1234567
주민번호 900101
주민번호 1234567
900101
1234567

+ 나중에 배열의 크기를 잘 모르는 경우에 length속성을 꼭 써야한다

 

String클래스의 split() 메소드 예제2

		String tel = "010-1234-5678";
		String[] t = tel.split("-");
		
		for (int i = 0; i < t.length; i++) {
			System.out.println(t[i]);
		}
010
1234
5678

 

String클래스의 split() 메소드 예제3

		// @를 구분기호로 아이디명과 도메인명 파싱하기
		String email = "totoro@naver.com"; //정해진 형식이 있다.
		String[] e = email.split("@");
		
		System.out.println("아이디 : " + e[0]);
		System.out.println("도메인 : " + e[1]);
아이디 : totoro
도메인 : naver.com

 

구분기호 여러개로 파싱하기(split() 메소드 예제4)

- 구분기호 여러개 섞여있어도 파싱하는게 가능하다

		String[] names = text.split("$|,|-");

- 구분기호를 연결해서서 | (파이퍼) 로 연결하면 또는 이런 구분기호들을 이용해 파싱하겠다는 의미
- 이런건 StringTokenizer 가 지원하지 못하는 기능임

 

- SplitEx1.java

package p2022_07_01;

public class SplitEx1 {

	public static void main(String[] args) {
// p512
//		String[] split(String regex); 메소드
		
		String text = "홍길동&이수홍,박연수,김자바-최명호";
		
		String[] names = text.split("&|,|-");
		
//		for (int i = 0; i < names.length; i++) {
//			System.out.println(names[i]);
//		}
//		System.out.println();
		
		for(String name : names) {
			System.out.println(name);
		} //향상된for문
		
	}

}
홍길동
이수홍
박연수
김자바
최명호

+ | 는 구분기호로 쑬 수 없다

 

문자열 파싱하는 방법 정리
1. StringTokenizer 클래스
2. String의 split() 메소드

등장 배경

전통적 프로세스 :

- 같은 OS, CPU 등의 환경이여아 프로그램이 돌아간다, 사실상 동일기계여야 돌아감
- 마이크로 소프트웨어 PC 세데에선 OS, 환경들이 동일했으므로 같은 환경이어서 동작했었다

 

But 웹의 등장 이후 :

- 모든 소프트웨어는 웹에서 서비스 하게 되었다.

- VM 은 같은 OS여도 보조 앱들이나 라이브러리 디펜던시가 다를 수도 있다.

- VM은 비교적 무거움

 

Image vs Container

- Image : 조리법(레시피), Container는 만들어진 요리, 실제 사용되는 프로세스
- Stopped Container : 만들어서 냉장고에 보관한 요리

 

세상의 모든 SW는 Containerized 되어 제공된다.

- Container 배포 = SW 배포
- Container 테스팅 = SW 테스팅
- Containerized된 어플리케이션 앱 = 프로세스

- 그 앱이 실행되는데 필요한 환경, OS, 커널, 비표준 라이브러리 등의 환경(Helper Process)까지 장착하여 한 패키지로 되어 어디서나 실행할 수 있도록 만들어짐

- Container Engine : 컨테이너를 동작시키기 위한 OS 수준에서 Virtualize 를 해준다.

 

Docker Hub

- Container 이미지들을 모아둔 Github 같은 저장소이다. 하지만 최근 대부분 Github 사용

 

VM vs Container

- VM : OS가 다같이 들어있으므로 과도한 메모리 점유, 느린 부팅시간, 여러 VM 실행시 성능 저하, Portability 제한, VM 사이 데이터 공유 불가능, 낮은 효율

- Container : 낮은 메모리 점유, (각 app은 미니멀하게 dependancy만 가지고 있다), 빠른 부팅 시간, 훨씬 많은 수의 container 실행 가능, platform에 좌우되지 않음, 여러 Container끼리 데이터 공유 가능, 높은 효율

 

Container

- App 과 App 실행에 필요한 모든 Dependency 를 묶은 Package

- 지정된 OS 위에서는 Container가 실행될 수 있도록 container engine을 기계에 장착 = 이건 OS level에서의 virtualization 을 app에 제공하는 것.

 

Container의 일생

- Docker Hub 에서 pull/push 로 Image를 가져옴, 이건 환경을 만드는데 필요한 템플릿(코드,시스템,OS 다 포함됨) 이다.

- 또한 Docker File 이라는 텍스트 파일에 이미지 생성 명령과 규정을 적어두고 BUILD(코드+환경까지 같이 컴파일) 해서 Image를 만들수도 있다.

- 이 Image를 RUN 시켜서 Container 를 만듬, 이건 이미지의 instance 라고 볼 수 있음

- Container는 STOP과 DELETE 하면 사라짐

 

Docker 설치 및 기본 명령

sudo apt-get install docker.io
docker run hello-world

- hello-world 라는 dockerhub의 기본 이미지를 가져와서 run하는것. 실제로는 pull 후 run 해야하지만 이건 맛보기

sudo usermod -aG docker $USER

- docker란 그룹에 나를(ubuntu) add 해란 뜻-> sudo 쓸 필요 없다.

cat /ect/group

- add 됐는지 확인 가능

 

Docker 기본 명령 (ubuntu 사용 예시)

docker pull ubuntu // ubuntu 커널 가져오기
docker images //현재 가져온 이미지 확인하기, 이미지 사이즈 확인 가능

 

#RUN(이미지로부터 컨테이너 만듬)

docker run -it -d ubuntu // 여기선 우분투를 RUN

= interactive 하게 우분투 커널을 detached 모드로 RUN 실행만 시키겠다, OS를 demo로 만들어서 동작시켜라

-> terminal 하게 bash shell에서 쓸수있도록, 즉 현재 ubuntu 기계에서 ubuntu OS 를 쓰도록 해준다.

docker ps

= 현재 process 들의 상태를 알려준다.

 

#EXEC(그 컨테이너 실행)

docker exec -it <container id> <prorm in the container>

-  <ps로 확인한 컨테이너이름> bash 로 작성했었다.

-> </ps로 확인한 컨테이너이름>이후 우분투 OS root로, 즉 컨테이너 안으로 들어옴

exit

- 그 우분투 OS 에서 나가기

 

#STOP

docker stop <container id>
docker kill <container id>
docker ps -a //모두 확인

#REMOVE

docker rm-f <container id> //컨테이너 프로세스를 제거
docker rmi-f <image id> //이미지를 제거

 

Containerized Web Service 실습
docker exec -it <container id> bash 이후, 즉 EXEC 이후

1. apache2 설치

apt-get update
apt-get install -y apache2

2. vim 설치 후 /var/www 에서 html 문서 작성하기

cd /var/www
apt get install -y vim
vim 1.html // 작성함

3. 우분투 root에서 빠져나옴

exit

4. 은 laker99/firstweb 처럼 내가 웹 서비스 컨테이너를 이미지로 만든것임

docker commit <container id> <new image name>

5. 이미지 및 프로세스 확인

docker images // image size 확인가능
docker ps // 실행중인 프로세스 확인 가능

6. #WEBSERVICE CONTAINER 실행

6-1. 이 이미지를 81번 포트로 포트포워딩 함. (디폴트는 TCP80)

6-2. EC2의 security inbound rule 에서 TCP 81 열기 브라우저는 IP:81

docker run -it -p 81:80 -d laker99/firstweb
docker ps
docker exec -it <container id> bash //docker ps 에서 확인한 container id 를 적기

7. #CONTAINER 속에서

Service apache2 status //상태확인
Service apache2 start //시작
Exit

 

Dockerfile

= Docker image를 만들기 위해 내리는 순차적인 command들을 모아둔 text 파일

- docker command 들을 입력하여 container 를 수동으로 생성하는 대신에 Dockerfile 안의 Docker build command를 사용해서 container 생성

 

기존 vs Dockerfile

기존: image -> RUN -> container -> EXEC -> inside the container -> do jobs -> exit -> COMMIT -> save a new image

Dockerfile 이용 : Dockerfile -> BUILD -> Docker Image -> RUN -> Docker container

 

Dockerfile 기본 구문

FROM : base image 지정, 기본적으론 OS ex)ubuntu
RUN : base image 위에 추가 SW 계층 설치, image 생성 시점에 사용할 명령어 지정
ADD : ADD <source> <컨테이너내 목적지> 로 파일들을 복사함. COPY와 다른점은 source로 url 을 사용가능
CMD : container 안에서 실행하려 하는 command, container 실행 시 실행되는 명령어를 지정한다.
ENTRYPOINT : CMD 와 유사
ENV : container에서 사용할 Environment 환경 변수를 지정

 

예시

docker run -it ubuntu bash // docker 구문

- entrypoint는 /bin/sh -c (Docker 는 /bin/sh-c를 디폴트 entrypoint )

- image는 ubuntu

- command는 bash

Dockerfile 예시 

- 반드시 vim Dockerfile 라는 이름으로 해야함

FROM ubuntu ##이 커널을 쓸테니 dockerhub에서 ubuntu 이미지를 가져와라

ENV DEBIAN_FRONTEND noninteractive
ENV DEBCONF_NONINTERACTIVE_SEEN true
## 이걸 쓰면 이미지 만들때 Timezone설정하라는 등 뭐 안나옴. interactive 모드 끄는것

RUN apt-get update ## docker image만드는데 필요한 명령어니까 RUN 작동
RUN apt-get install -y apache2

ADD . /var/www/htm
## 현재 디렉토리, 즉 커널 디렉토리의 모든 파일을 모두 다 특정 디렉토리 (html)로 카피(add)하란 명령어임

ENTRYPOINT apache2ctl -D FOREGROUND
## FOREGROUND 의미 : 이게 없으면 Entrypoint에 지정되어있는 apache2ctl을 호출하고 그냥 끝난 후 container process stop되어 아무일도 안벌어짐
## ENTRYPOINT는 반드시 한줄만 있으며 container를 run 시킨 후 해당 container에 entry 하는것 == 위에 bash해서 그 안에서 Service apache2 start 와 같다

ENV env_var_name <변수 이름>

- 이렇게 dockerfile 작성후 이 dockerfile 로 빌드하자

 

BUILD

docker build . -t <image name>
ex) docker build . -t laker99/test

1.

- docker file로부터 이미지 만들기 ex) laker99/test

- .는 현재 디렉토리의 (dockerfile)의미, t는 태그를 의미, image name은 만들 이미지 이름

docker run -it -p <host port #:container port#> -d <image>
ex) docker run -it -p 81:80 -d laker99/test
docker run -it -p <port #:port #> -v <local dir : mounting point dir in container> -d <image>
ex) docker run -it -p 81:80 -v ~/dockerfile:/var/www/html -d laker99/test

= 호스트기계의 디렉토리(~/dockerfile)를 컨테이너의 디렉토리(/var/www/html)에 마운트 시킨것이다.

= volume 쓰면 실행 이후 즉 kill 하지 않은 돌고 있는 상태에서 html 파일을 고칠수있다. 파일내용 업데이트 시 그대로 반영

 

* Docker에서 “마운트"는 컨테이너 외부에있는 데이터를 컨테이너에서 사용할 수 있는 상태가 될 것을 의미한다.

 

Docker-compose

- 다수의 docker container들을 규정하고 실행하고 관리하는 automation 도구 (서로 연동법등 규정)

- 여태까지 했던 것들은 단일 container를 만드는 것이었다.
- but 요즘 웹서비스는 한덩어리 프로그램이 아님. 여러개가 돌아감.

 

Monolithic App vs Microservices

Monolithic App : 비현실, 비효율, 낮은 생산성, 하나의 앱 vs
Microservices : 각 단일 서비스가 서로 느슨하게 결합, 그 사이에서는 HTTP 통신 프로토콜을 이용해서 데이터 교환(REST-API JSON)

 

* 여러개의 프로세스들 = 개별적인 container들 이 모여서 하나의 서비스

- 그래서 다수의 컨테이너를 만들어야함 -> docker compose 사용

 

Docker-compose 를 우분투에 깔아보자

mkdir test;
cd test;
vim docker-compose.yml

- docker compose 사용위해선 이 .yml 포멧의 텍스트파일 사용해아함. 이게 2개이상이 한 디렉토리에 있어선 안된다.

 

version: '3.9' //compose file version
service:
    hello-world: // 컨테이너 하나
        image: hello-world:latest // 가장 최신걸로 이미지인 hello-world를 가져와라

- 키는 version, ' ' 으로 value 와 key 구분
- service의 value는 또다른 key-vaule 짝
- hello-world 의 value는 또다른 key-value 짝
- image의 value는 hello-world:latest

 

.yml 파일 작성 후

docker-compose up -d // .yml을 실행해라, 지금은 연습이니 container 하나만 docker ps

.yml 실행하기

 

예시. wordpress 를 만들어보자

- 여기선 1개 host기계에서 2개 컨테이너를 실행할 것이지만 실제로는 2개 host에서 함

version: "3.9"
service:
    db: //라는 컨테이너임
        image: mysql:5.7
        volumes: //DB 서비스가 사용하는 volume을 create 함.
          - db_data:/var/lib/mysql // name volume 이다. db-data는 volume이름을 작명한 것이고 docker가 이걸 알아서 volume을 만듬
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: somewordpress
          MYSQL_DATABASE: wordpress
          MYSQL_USER: wordpress
          MYSQL_PASSWORD: wordpress // MYSQL에 필요한 것들을 environment 변수로 작성해줌
    wordpress: //wordpress 컨테이너
        depends_on: -db // db란 이름의 서비스에 depend한다는 의미, 즉 위에 있는 db의 결과를 사용한다. db:가 wordpress: 보다 위에 써져있어야만 함
        image: wordpress:latest // 가져와
        volumes:
          - wordpress_data:/var/www/html //name volume이다. wordpress_data를 여기 /var/www/html에 마운트 시키란 의미
        ports: - "8000:80" //8000번으로 포트포워딩 시키기
        restart: always
        environment:
          WORDPRESS_DB_HOST: db
          WORDPRESS_DB_USER: wordpress
          WORDPRESS_DB_PASSWORD: wordpress
          WORDPRESS_DB_NAME: wordpress
volumes: //named volumes
  data_base: {}
  wordpress_data: {}

이 .yml 파일 작성 후 그 디렉토리에서 docker-compose up -d 하면 됨

 

Docker Volume 

 

메모리 구조

- container의 가상파일시스템(var/lib/mysql) 이 있고 그 바깥에 host machine의 host의 file system 이 있다.

 

Docker Volume 배경

Container에는 그 안 자체의 메모리앱이 있다., 모두 in 메모리 os리소스이다.

Container를 만든 의도는 메모리상에서만 실행되면 되는 app들을 위해 만들어진것이다.

하지만 persistant한 storage에 OS를 write/read file 서비스 하는 container를 만들어야 할때를 위해 docker volume을 만든 것.

-> 호스트 기계의 on-disk persistent filesystem을 컨테이너의 in-memory virtual filesystem에 mount 하여, 컨테이너가 종료되어도 데이터가 사라지지 않고 호스트 기계의 파일 시스템에 저장되어 다시 동일 컨테이너가 시작되어 과거 데이터를 사용할 수 있도록 제공된 기능.

- DB를 사용하는 컨테이너의 경우에는 필수적

 

Docker Volume의 세가지 타입

1. Host volume

docker run -v /home/mount/data:/var/lib/mysql/data 

- : 의 앞에서 : 뒤로 마운트, 호스트 기계의 파일 시스템 디렉토리가 앞,

- 이게 호스트 볼륩 방식으로 마운트 시키기인데 실제론 사용안함

- 이유: 내 마음대로 원하는 호스트기계 파일 디렉토리 만드는거라 다른 사람들은 경로를 모른다.

 

2. Anonymous volume

docker run -v /var/lib/mysql/data

- 호스트 기계의 볼륨을 지정안했음

- docker가 호스트 기계의 정해진 경로에 폴더를 생성하는데 random hash하여 경로 이름을 달리함.

- yml 설치 이후 이 디렉토리에 호스트 볼륨 자동으로 만들어줌

- 이름 필요없을때 사용

 

3. named volume

docker run -v name:/var/lib/mysql/data

- docker가 생성하게 하되 생성된 볼륨에 이름을 붙여서 여러 컨테이너가 이름을 통해 공유할수있도록함.

 

 

컨테이너 간 통신

- 독립된 OS 가진 다른 컨테이너끼리는 같은 기기더라도 네트워크 통해 통신해야하는데 이때 내부 private network에서 rest-api (json)형식으로 통신한다.

 

+ docker swarm 또는 전통적인 분산환경에서의 동일한 종류의 서비스 제공은 서로 다른 호스트에 같은 서비스를 제공하는것, 외부로부터의 서비스 요구를 여러 호스트에 나누어 할당하는 reverse proxy 이용

 

*curl : 컴과 컴 사이에서 데이터를 주고받는 어플리케이션, 그 사이에서 프로토콜의 지정없이 귀찮은 작업을 다 해줌 확인가능

'CS지식 > 개발 상식' 카테고리의 다른 글

프론트엔드의 상태 관리  (1) 2023.05.17
DOM 과 Virtual DOM, 그리고 React (리액트)  (0) 2023.05.17

자바의 보조 제어문 break

1. switch-case문 빠져나가기 
2. 반복문 빠져나갈때 (while, for, do~while문)

- 주로 무한 루프를 빠져나올 때 break문을 이용함
- 어떤 무한루프에서 조건식을 쓰고 그 조건을 만족하면 빠져나오는게 일반적

 

for 무한루프 돌리는 방법 2가지

1. 초기, 증감, 조건 식 안적기

		for(;;)
		{
			System.out.println("무한출력");
		}

2. 조건 식 안적기

		for (int i = 1;; i++) {
			System.out.println(i + "무한출력");
			if (i == 100)
				break;
		}

break문 주의1

- break 뒤에 다른 내용 있으면 안된다.

while(true)
{
	break;
	System.out.println("출력"); // 오류생김
}

 

break문 주의2

- break문 만나는 순간 그 밑은 아예 안보고 바로 빠져나옴
- break문의 위치를 잘 설정해야함

- 비교해보기(아래 예제2개)

		while(true) {
			
			System.out.println(i+ "무한루프");
			if(i==100) break;
			i++;
			
		}

- 이러면 100번 출력

		while(true) {
			
			System.out.println(i+ "무한루프");
			i++;
			if(i==100) break;
			
		}

- 이러면 99번 출력됨 
* "if"나 "메소드"를 빠져나올땐 break가 아닌 return문을 써야함!!

 

break문 예시

- 1부터 45사이의 난수를 발생 시킬때, 6이 나올때까지 몇번 루프가 돌아가는지 구해보자

package p2022_06_24;

public class Break03 {

	public static void main(String[] args) {

		
		int i = 1;
		while (true) {

			i++;
			int num = (int) (Math.random() * 45) + 1; // 난수발생 : 1~45

			if (num == 6)
				break; // 무한루프를 빠져나옴
		}
		System.out.println("루프횟수 : " + (i - 1)); 
		System.out.println("프로그램 종료");

	}

}

- 루프횟수에서 초기값이 0이 아니라 1이니까 -1 해줌.(한번 루프 돌아도 i값이 2가 되기때문)

 

continue

- 반복문 안"에서만" 사용가능!! (while, do~while, for)
- 다시 반복문으로 돌아가라는 의미
- break문처럼 반복문 가장 위에있으면 오류가 생김
- break문처럼 if문과 함께 쓰임.

		for (int i = 1; i <= 10; i++) {

			if (i == 5)
				continue;
			System.out.println("출력 : " + i);

		}

- i==5일때 출력5는 실행되지 않음
- i==5일때 아래를 실행안하고 i++쪽으로 돌아감, 6이되고 다시 조건식 참이니 출력 : 6이 출력됨

 

메소드 호출

- 여태껏 main 메소드 안에 모든 내용을 다 집어넣었다 이젠 사용자 정의 메소드 만들고 호출해볼것
- 나중에 클래스로 객체 쓸때도 메소드는 계속 쓰므로 중요함.

 

메소드

- 여러가지 코드를 묶어둔 집합체
- 한번 정의된 메소드를 호출 통해서 재사용하기 위해 사용
- 어떤 언어에선 함수 라고 부른다.
- 각 언어마다 메소드 만드는 방법이 꽤 다르다.

 

메소드 호출 방법

- Call by Value
- Call by Reference

 

메소드 의미

접근제어자     공유      자료형     메소드명(매개변수)
public           static         void         check(){

 return   값;
}

 

접근 제어자 종류

-  public
- 생략(default접근제어자)
- private
- protected

 

접근 제어자

- 접근 허용할지 막을지 설정
- 클래스 앞에 도 붙일수 있고, 클래스를 구성하는 3가지 필드(변수), 생성자, 메소드 앞에도 붙일 수 있다
- default : 아무 표시 안하면 default 접근제어자
- public : 모든 접근을 허용한다는 의미로 사용됨, 가장 많이 사용됨

 

static 자리

- 공유를 할경우, 누구나 쉽게 접근할 수 있도록 공유원할때 붙임
- static이 붙은 메소드를 정적메소드라고 불린다.
- 어제 배운 Math 클래스는 정적메소드 밖에 없었다.
+ 아직 클래스-객체 개념 안배웠기떄문에 모두 정적메소드로 해서 공부/실습할것

 

void 자리 = 리턴자료형 자리

- void 대신 기본자료형도 들어갈 수 있고, 참조형인 클래스, 배열, 인터페이스도 여기에 올 수 있다
- void는 값을 돌려주지 않아도 된다는 의미이므로 void 가 왔을땐 마지막의 return이란 구문을 생략 할 수있다
- 자료형이 오면  return 자료형 ; 해서 자료형에 맞는값을 반드시 돌려줘야함
ex) void대신 int란 자료형이 왔을땐 int형 값을 return으로 돌려줘야함.

 

메소드 명 

ex) check()
- 예약어로 설정된 Class If Switch 같은건 메소드 명으로 쓰면안됨
- 메소드 쓸땐 괄호를 반드시 가져야함 . 괄호가 없는 메소드는 거의 없다.
- 괄호만 써도 되고, 메소드가 실행되는 동안 필요한 값이 전달되어야할 땐 변수를 쓴다. 기본자료형, 참조형 다 올 수 있다.

 

메소드 호출/반환

- 기본자료형의 경우 값을 전달한다.
ex) int형이 매개변수로 있다면 int형 값을 전달해서 호출을 해야함
- 클래스가 오면 객체를 전달해야하고, 배열이 오면 배열주소값을 전달해야한다
- void 자리에 클래스, 인터페이스, 배열등의 참조형이 올땐 주소값을 리턴해줘야 한다.

 

main 메소드

-main 메소드는 이름 바꾸지 못함
- public static void check(){}
- 개발자가 호출 할 수 없다 JVM이 해줌

 

사용자 정의 메소드 작성

- 자바는 메소드 안 메소드가 들어가는 중첩메소드 허용안함
- 즉 메인 메소드 안에 메소드가 들어갈 수 없다. (파이썬은 중첩함수 허용)
- 그러므로 메인 메소드 바깥 (위,아래) 에 메소드 작성하기

 

Main 메소드 호출 vs 사용자 정의 메소드 호출

- Main 메소드는 MethodEx01.class란 바이트 코드에서 JVM이 호출해줌. 
- 사용자 정의 메소드는 프로그래머가 직접 호출해줘야함

 

static이 붙은 정적메소드 호출 vs static이 없는 메소드 호출

- static이 붙은 메소드는 그 클래스 . 해서 바로 호출 ex) Math.random()
- static이 없는 메소드는 그 클래스로부터 객체를 생성해서 . 을 해서 메소드를 호출

 

접근 제어자

- 같은 클래스 내에선 접근 제어자가 아무 의미 없다
- protected이든 private이든 public이든 default든 같은 클래스 내에선 다 접근 가능
- 이 클래스,메소드,변수,생성자에 접근을 허용할건지 말건지를 정해둔 것임.
- 아무것도 없어도 default 접근제어자이다

 

return 자료형 적는 자리

- void 가 오든지 자료형이 오든지 둘 중 하난 반드시 와야함
- void면 return구문 생략 가능 또는 return;

 

메소드 호출 방법

- 매개변수가 없는 check() 는 그냥 check(); 라고 호출해주면 됨.

 

메소드 작성 순서
- 자바는 객체지향언어라서 메소드가 적힌 순서가 상관없다
ex) check()가 main() 보다 밑에 있어도 main()에서 check() 호출 가능
- 메소드가 적혀있는 순서가 중요한 언어가 있다.
ex) 파이썬, C

static 메소드 호출
- Math는 정적메소드만 있다. 그래서 Math.random()처럼 사용함
- 원래 static 메소드 호출 시, 클래스명.메소드명() 이 원칙이지만 같은 클래스내에 있는 정적메소드를 호출시엔 클래스명을 생략 가능
ex) MethodEx01.check(); 이지만 같은 클래스 내에선 check(); 만 작성해도 됨.

+ Math메소드의 호출법
- static이 붙은 필드 = 정적 필드 만 있다
- static이 붙은 메소드 = 정적 메소드 만 있다.
- random() 메소드는 static double random() 이다.
- 그러므로 객체 생성하지 않고 Math.random()으로 호출함.

* 클래스 : 필드, 생성자, 메소드가 있다.(api문서)

 

static이 없다면?

MethodEx01 mx = new MethodEx01();
mx.check()

- 이런식으로 객체를 생성하여 호출해야 한다.

 

매개변수가 없을때 vs 매개변수가 없을때 호출

- 매개변수가 없을때 : check(); 으로 호출
- 매개변수가 있을때 : check(ck) ; 로 호출, ck는 int값이어야

	static void check(int a) { //매개변수(parameter) : int a
		System.out.println("전달된 값 : " + a);
	}

- 즉 정수값을 전달해야 함수가 실행될때 매개변수는 스택에 자리가 잡혔다가 메소드 끝나면 지워짐
- check(30); 시 매소드 실행될때 a에게 30을 주게 되고 이 매개변수(파라미터)를 출력
* 매개변수는 지역변수

 

Call by Value

- 이렇게 매개변수가 기본자료형일떈 값을 전달해서 호출하는 것.
- 값 전달에 의한 메소드 호출 방식

Call by Reference
- 매개변수가 참조형 일땐 주소값을 가짐
- 주소값을 전달해서 호출

메소드 "오버로딩"
- 매소드 정의 할때 이름이 똑같음 check(), check(int a)
- 한개 클래스내 동일 이름 클래스 정의할 수 있는 것.
- 하지만 매개변수를 다르게 함으로서 구분이 가능하도록 해야한다. 

지역변수 정리
- 메소드 안에서 사용되는 지역변수 = 매개변수 = 스택에 저장됨 = 메소드 종료시 삭제됨
- 블럭문 안에서 사용되었던 i 같은것도 지역변수

 

메소드 예시

	static void check(int a, double d) {
		double result = a + d;
		System.out.println("전달된 값의 합 : " + result);
	}

- check(10, 10.5); 로 호출 (Call by Value)값을 전달해서 호출함
- 순서에 맞게 자료형에 맞게 전달해야함 int, double 순으로

 

매서드의 매개변수에 참조형이 오는 경우

ex) String 클래스

	static void check(String s) { 

		System.out.println("전달된 값 : " + s);
	}

- main 에서 

check("자바");
check(new String("파이썬"));

- 호출 시 둘 다 잘 출력됨.

- 바로 check("자바") 해서 객체 만들 수도 있고 new 연산자를 써서 String객체를 만들 수도 있다.

 

+ String클래스만 두가지 방법 모두 가능.
1. String s = "자바"
2. String s = new String("파이썬");

static void check(String s) { //String s = "자바"

		System.out.println("전달된 값 : " + s); 
		//String s = new String("파이썬");
}

- 바로 check("자바") 해서 객체 만들 수도 있고 new 연산자를 써서 String객체를 만들 수도 있다.

 

Call by Reference (위 예시 참고)

- Call by Reference = 주솟값 전달에 의한 매서드 호출 방식
- 우리가 볼땐 '자바' '파이썬'을 직접 전달하는거같지만 사실 주솟값을 전달하는것
- 그 데이터가 저장된 영역의 주솟값을 메소드로 전달하는것임
- "자바' "파이썬"데이터는 모두 힙이란 영역에 저장
- 매개변수 s는 스택영역에서 "자바", "파이썬" 의 주솟값을 가지게 됨

 

그림

 

Math 클래스의 리턴자료형

- Math클래스 random() 메소드의 리턴값은 double
- 그 우리는 그것을 int 강제형변환 해서 사용했었다.

 

메소드 예시1

// return문 : 메소드를 호출한곳에 값을 돌려주는 역할을 한다.
static int check01()
{
	System.out.println("출럭");
	return 50; 
	System.out.println("출럭"); //오류발생

}

- 리턴이 void가 아니므로 필수, int형 값만을 돌려줘야한다. 50.5를 리턴할순X

* 리턴구문은 메소드 가장 마지막 줄에 써야한다. 아니면 오류 발생
* 저걸 check()라는 이름을 쓸 수 없다 .  맨 위에서 check()를 썼기때문에 오류 발생
check01로 만들었다.
- Main에서 check01() 로 호출하면 출력은 되지만 리턴으로 돌려받은 값을 받지 않았으므로 그 값을 쓸수 없게됨

 

int result = check01();

- 리턴형이 int이기 떄문에 그 값을 받는 변수도 int여야함

 

값 돌려받는 2가지 방법

1. System.out.println("돌려 받은 값1 : " +  result);
2. System.out.println("돌려 받은 값1 : " +  check01());
- check01()에서 리턴값을 돌려받자마자 바로 출력해도 된다. 변수로 안받아서 출력해도됨

 

메소드 예시2

- 더블형이 리턴일땐

static double check02(int a, double d)
{
	double result = a + d;
	return result;
}

- 이 result도 이 메소드 안의 지역변수다 그래서 이 메소드 말고 다른 메소드에선 사용가능
- main 메소드에서

double result2 = check02(50,3.14);
System.out.println("돌려 받은 값3 : " + result2);
System.out.println("돌려 받은 값4 : " + check02(50,3.14));

 

return

- 메소드를 호출 한 곳으로 값을 돌려줌
- static int check01 에서 int자리에 참조형이 올땐 값이 아니라 주솟값을 돌려줌!

 

메소드 주의

- 클래스 배열 인터페이스도 매개변수 자리, 리턴 자료형자리 들어 갈 수 있다.
- 값을 돌려받을 때는 그 메소드의 리턴 자료형과 일치하는 자료형으로 값을 받아야함.
ex) int i = sc.nextInt();
- public static void main 에서처럼 이 순서 바뀌면 안됨 접근제어자 다음 static 다음 리턴자료형 다음 함수명
- 지역변수는 다른 메서드일땐 이름같아도 충돌 발생하지 않는다

System.out.println(max(n1,n2));
System.out.println(min(n1,n2));

- 가능함
- 원래는 MethodEx03.max(n1,n2)지만 같은 클래스라 생략(max는 static 함수이므로)

 

메모리
- 기본 자료형들은 값을 참조형은 주소를 저장
- 기본 자료형이라 하더라도 항상 스택에만 저장되는게 아님
- 기본 자료형 중 지역변수 만 스택이란 영역에 값이 저장됨
- 멤버,정적 변수는 기본자료형이라도 스택에 저장되지 않음

메모리 예시1
- 참조형 중 클래스 중 String 클래스 예시

String name = "신용권"


- 값인 "신용권"은 힙이란 메모리에 저장되고
- 이 저장된 값의 시작 주소값을 스택 영역에서 가지고 있다


메모리 예시2

String hobby = "독서"


- "독서" 는 힙에 저장 , 그거의 시작 주소값은 참조형 변수가 가지고 있는데 그건 스택에 있다
- (name, 2312415) 이런식으로 (주솟값 : 2312415) 스택에 있는거

 

배열

- 동일한 타입의 데이터를 연속된 공간에 저장하는 정적인 자료구조
- 변수를 확장시킨 개념이 배열 (변수는 1개의 데이터만 저장)
- 변수가 여러개 필요할때 간결하게 쓰기 위해 배열을 쓴다.
- 10개과목 점수 입력받으려면 변수가 10개나 필요함 - > 배열을 쓰면 코드가 늘어나지 않음
- 1가지 자료형 데이터만 저장 가능
- 거의 대분의 언어는 배열을 지원함
- 배열 정의 할때도 반드시 자료형을 적어야함.
- 1차원 배열은 [] 1개 2차원은[][] 2개... 

int[][] score = new int[5][3];


- 배열이름 (변수)쓰고 new를 씀 = 명시적으로 힙메모리상에 새로 공간을 만드는것
- 왼쪽이 int면 오른쪽도 int형으로 해야

 

배열 의미

int score[] = new int[3];

- 힙메모리에 int인 4바이트 연속된 공간을 3개를 생성하란 의미

 

그림

 

배열 기타
+ 우리가 여태껏 봤던 main 메소드의 매개변수는 String 형 1차원 배열
- main(String[] args)
- []는 자료형 뒤에 써도 되고 변수명 뒤에 써도 된다
- String[] args == String ar[] 도 가능하다

new
- 연산자 오퍼레이터
- 힙 메모리상에 배열을 위한 새로운 공간을 만들라는 의미
+ 클래스로 객체 생성할때 쓰는 new도 같은 원리로 힙 메모리 상에 새 공간 만들라는 의미임
- String 클래스만 new를 써도 되고 안써도 되지만 다른 클래스 99%는 new를 꼭 써야만 객체 생성 가능

배열의 초기화
- 배열을 정의 시 힙메모리 상의 연속된 공간은 자동 초기화 된다.
- 자료형에 따라 초기화 되는 값이 다르다
- int 형 배열을 정의하면 힙메모리상의 공간(배열)이 자동으로 0으로 초기화 된다.
- boolean 배열 은 false 로 double 배열 은 0.0 으로 int는 0 으로 초기화 됨
- 또는 원하는 할당값이 있을 때는

score[0] = 80;
score[1] = 90;
score[2] = 100;

 

* 지역변수는 초기화되지 않지만 참조형은 자료형에 따라  초기화 되는 값들이 있다

 

배열을 만드는 2가지 방법
1.

int score[] = new int[3]


2-1.

int[] score = {80,90,100};


2-2. int[] score = new int[]{80,90,100}

int[] score = new int[]{80,90,100}; // 도 가능함

 

 

1. int score[] = new int[3] 방법

- 배열에 어떤 값이 들어가야할지 아직 안정해지거나 모를때
- new가 반드시 들어가야함

2. int[] score = {80,90,100}; 방법
- 값이 정해져있을때 쓰는 방식
- 배열 선언과 동시에 초기화를 할 때 주로 사용

 

int[] s1 = {80,90,100};
int[] s2 = new int[]{80,90,100};
//두가지 형식으로 선언 가능하다

 

- 다른 자료형도 마찬가지

double[] d = {3.14,10.5,42.195}

 

- 같은 자료형만 가능

int[] score = {80,90,100,3.14} // 오류 발생

 

변수->배열->리스트

- 변수를 확장시킨게 배열이고 배열을 확장시킨게 리스트(List)
- 배열은 한가지 자료형만 가능하고 리스트 라는 자료구조를 쓰면 자료형 혼용가능, 동적으로 자료크기 늘릴수도있다

 

char형 배열 / String형 배열

 

char[] c = {'j','a','v','a','안'};
String[] str = {"java", "jsp","oracle"}
String[] str = new String[]{"java", "jsp","oracle"}

- 모두 가능

 

메모리 정리
- 모든 참조형으로 변수 만들땐 거의 다 new를 필수적으로 쓴다 (클래스,배열,인터페이스)
- 힙메모리상에 저장된 값은 초기화 된다!
- 스택 메모리상에 저장된 값은 초기화 되지 않음

 

선언 이후 값 할당

 

score[0] = 80; //0번 방에 80점을 할당
score[1] = 90; //1번 방에 90점을 할당
score[2] = 100; //2번 방에 90점을 할당

 

배열 자동초기화

- int형 배열은 자동초기화 0으로 됨
- double형 배열은 자동 초기화 0.0으로 됨
- char형 배열은 자동으로 초기값 설정이 안됨!
- char 배열은 자동으로 초기화가 되지 않는다.
- boolean 형 배열은 자동초기화 false 로 됨!

 

참조형 배열 예제 : String 배열

- String형 배열은 자동 초기화 null 로 됨 (초기값이 없다는 의미)
* null : 값이 없다 는 의미

배열과 반복문
- 배열을 의미있게 쓰려면 반복문을 써야(ex) 합, 곱 등 구하기

배열의 크기 구하기 : length 속성
- length란 속성 이용해서 배열의 크기 구할 수 있다.
- 메소드가 아니라서 괄호가 없다! 속성이다. 
- 그 배열이름.length 하면 된다

 

배열의 인덱스 주의
+ 배열은 인덱스 번호가 0번 부터 시작함 
- 출력시 for문 쓸때 주로 i=0 해야하는점 주의, 
- <=말고 < 써야하는거도 함께 주의 

 

배열에서의 형변환
- double형 배열 예시

	double[] dd = { 3.14, 10.5, 42.195, 50 }; 

	for (int i = 0; i < dd.length; i++) {
	 	System.out.println(dd[i] + "\t");
	}

- int형 값인 50을 넣으면? 내부적으로  double 로 바뀜(자동형변환)
- 이 반대의 경우는 안됨 double 이 int보다 크므로

- 반대로 int 형 배열에 double 형 자료를 넣어선 안됨

 

크기 구하는 방법 3가지의 비교(중요)

1. 배열크기 구할땐  length 속성
2. 문자열의 길이 구할땐 length() "메소드"
3. list같은 자료구조는 size() "메소드"

 

+ 형변환 항상 주의

double avg = (double)sum / 3; // 또는  sum/3.0
System.out.println("평균 : " + avg);

- sum은 int 형 3도 int형이라 (double)을 안쓰면 소수점 전부 짤려서 나옴.

- 살리려면 (double)을 붙여야함 또는 3.0으로 연산

 

+ 평균을 소수점 2째자리까지 출력하려면

System.out.printf("평균 : %.2f",avg); // 평균 : 86.67

- 실수값일땐 뒤에 f를 써줌, %.2 는 둘째자리까지 출력하란 뜻이고 셋째자리에서 반올림된다.

 

출력형태 정리

 %b : boolean 형식으로 출력 
 %d : 정수 형식으로 출력
 %o : 8진수 정수의 형식으로 출력
 %x / %X :16진수 정수의 형식으로 출력
 %f : 소수점 형식으로 출력
 %c : 문자형식으로 출력 
 %s : 문자열 형식으로 출력
 %n : 줄바꿈 기능
 %e / %E : 지수 표현식의 형식으로 출력

 

+ Recent posts