본문 바로가기
Programming Language/Java

[국비] Java 내용정리 Day13

by tpleehan 2021. 11. 23.

static

  • static이란 직역을 하면 고정된 이라는 의미를 갖고 있다.
  • 자바에서는 static을 사용 제한자로 이용하며 필드, 메서드에 붙여서 정적 멤버로 만들 때 사용한다.
  • 정적 멤버란 객체에 속한 멤버가 아닌 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메서드를 말한다.

  • 인스턴스 멤버는 객체에 소속된 멤버로서 객체당 각각 다른 값을 가질 수 있지만 정적 멤버는 클래스에 소속된 멤버이기 때문에 객체간 모든 값을 공유하는 공유데이터가 된다.

  • static이 붙은 데이터는 그 static 데이터가 선언된 클래스 이름으로 바로 참조 할 수 있다.
  • static은 객체와 무관한 데이터고, 일반 멤버 변수와 혼동할 가능성이 있기 때문에 주소값으로 접근하지 않는다.

정적 초기화 블록

  • 인스턴스 필드는 생성자를 통해 초기화할 수 있지만, 정적 필드는 객체 생성 없이 사용하기 때문에 생성자에서 초기화 작업을 하는 것이 올바르지 않다.
  • 자바에서 정적 필드 초기화 작업을 위해 정적 블록을 제공하낟. 정적블록은 클래스가 로딩될 때 자동으로 실행된다.

  • 정적 메서드 블록과 정적 초기화 블록 내에서는 인스턴스 필드와 메서드를 사용할 수 없고 정적 필드와 정적 메서드만 사용할 수 있다.

package poly.quiz;

public class Product {

	public int price;
	public String name;
	
	public Product(int price, String name) {
		this.price = price;
		this.name = name;
		
	}
	
}
package poly.quiz;

public class Computer extends Product {

	public Computer() {
		super(1000, "com");
	}
	
}
package poly.quiz;

public class Radio extends Product {

	public Radio() {
		super(500, "radio");
	}

}
package poly.quiz;

public class Tv extends Product {

	public Tv() {
		super(600, "tv");
	}
}
package poly.quiz;

public class MyCart {

	private int money;
	private Product[] cart = new Product[1]; // 상품을 저장할 배열
	private int i = 0; // cart안에 있는 상품의 개수를 알려주는 변수

	public MyCart(int money) {
		this.money = money;
	}

	public void buy(Product p) {
		if (this.money < p.price) {
			System.out.println("금액 부족");
			return;
		}
		
		this.money -= p.price;
		add(p);
	}

	private void add(Product p) {
		if (i >= cart.length) { // 배열에 자리가 부족
			// 새로운 장바구니 추가
			Product[] temp = new Product[cart.length * 2];
			// 기존 배열에 들어 있는 상품을 새로운 배열에 옮기기
			for (int n = 0; n < cart.length; n++) {
				temp[n] = cart[n];
			}
			cart = temp;
			temp = null;
		}
		cart[i] = p; // 물건을 배열에 추가
		i++; // 개수 증가
		
		info();
	}

	public void info() {
		System.out.println("------------------------");
		System.out.println("*** 장바구니 정보 ***");
		
		int total = 0; // 총 상품의 합계를 담아줄 변수
		
		/*
		for (int idx = 0; idx < this.i; idx++) {
			System.out.print(cart[idx].name + " ");
			total += cart[idx].price;
		}
		 */
		
		for (Product p : cart) {
			if (p == null) {
				break;
			}
			System.out.print(p.name + " ");
			total += p.price;
		}
		
		System.out.println();
		System.out.println("구매 금액 합계: " + total + "원");
		System.out.println("남은 금액: " + this.money + "원");
	}
	
}
package poly.quiz;

public class MainClass {

	public static void main(String[] args) {
		MyCart cart = new MyCart(2100);
		
		Tv tv = new Tv();
		Radio radio = new Radio();
		Computer com = new Computer();
		
		cart.buy(tv);
		cart.buy(radio);
		cart.buy(com);
	}

}
package static_.field;

public class Count {

	public int a; // 인스턴스 변수 (멤버 변수)
	
	public static int b; // 정적 변수

}
package static_.field;

public class MainClass {

	public static void main(String[] args) {
		
		Count c1 = new Count();
		c1.a += 5;
		c1.b += 5;
		
		System.out.println("인스턴스 변수 c1.a: " + c1.a);
		System.out.println("정적 변수 c1.b: " + c1.b);
		
		System.out.println("------------------------");
		
		Count c2 = new Count();
		c2.a += 7;
		c2.b += 7;

		System.out.println("인스턴스 변수 c2.a: " + c2.a);
		System.out.println("정적 변수 c2.b: " + c2.b);
		
		System.out.println("------------------------");
		
		Count c3 = new Count();
		c3.a += 8;
		c3.b += 8;
		
		System.out.println("인스턴스 변수 c3.a: " + c3.a);
		System.out.println("정적 변수 c3.b: " + c3.b);
		System.out.println("정적 변수 c1.b: " + c1.b);
		System.out.println("정적 변수 c2.b: " + c2.b);
		
		Count.b++;
		Count.b++;
		
		System.out.println("정적 변수 Count.b: " + Count.b);
		
	}

}

인스턴스 메서드

  • 객체를 생성하여 주소값을 통해 접근 후 호출하는 메서드
  • 인스턴스 메서드 안에서는 정적(static) 변수와 인스턴스 변수를 모두 참조할 수 있다.
  • static 블록(메서드, 정적 초기화자) 내부에서는 static이 붙은 변수나 메서드만 사용할 수 있다. (this를 사용할 수 없다.)
  • static 블록 내부에서 non-static멤버를 사용하려면 반드시 객체를 생성해서 주소값을 통해 참조해야 한다.
package static_.method;

public class Count {

	public int a; // 인스턴스 변수
	public static int b; // 정적 변수
	
	public int method1() {
		return this.a + Count.b;	
	}

	public static int mothod2() {
		Count ccc = new Count();
		return ccc.a + Count.b;
	}
	
}
  • 메서드 내부에서 non-static 데이터를 참조하고 있는 메서드는해당 변수의 정확한 위치(객체의 주소값)를 알려줘야 하기 때문에 정적 메서드로 선언하기가 부적합하다.
  • 메서드 내부에서 인스턴스 변수를 사용하지 않고 범용성 있게 사용하는 메서드는 static 키워드를 붙여서 정적 메서드로 선언하는 것이 좋다.
package static_.calc;

public class Calculator {

	String color;
	static double pi = 3.14159265;

	public void paint(String color) {
		System.out.println("계산기에 " + color + "색을 칠합니다.");
		this.color = color;
	}
	
	public static double areaCircle(int r) {
		return r * r * pi;
	}

}
  • 정적 초기화자를 호출하려면 클래스를 로딩하면 된다.
  • 클래스 로딩 방법
    • 클래스의 이름을 참조하여 static 멤버를 호출.
    • 객체를 생성.
  • 정적 초기화자는 클래스 로딩 조건 만족 시 최초 1회만 호출.
public class DBManager {

	public static String addr;
	public static String uid;
	public static String upw;
	
	static {
		System.out.println("정적 초기화자가 호출됨");
		addr = "192.168.0.1:8181/XEPDB1";
		uid = "abc1234";
		upw = "aaa1111!";
	}

	public DBManager() {
		System.out.println("생성자가 호출됨");
		addr = "192.168.0.1:8181/XEPDB1";
		uid = "abc1234";
		upw = "aaa1111!";
	}
	
}
package static_.init;

public class MainClass {

	public static void main(String[] args) {
//		DBManager manager = new DBManager();		
		System.out.println("주소: " + DBManager.addr);
		System.out.println("계정명: " + DBManager.uid);
		System.out.println("비밀번호: " + DBManager.upw);

	}

}

싱글톤 디자인 패턴

  • 클래스의 객체를 단 1개로 제한하기 위한 코드 디자인 패턴
    1. 외부에서 해당 클래스의 객체를 생성하지 못하게 생성자를 단 한개만 선언한 후 private 접근 제한을 붙인다.
    2. 생성자를 호출할 수 있는 영역은 같은 클래스 내부 뿐이다. 자신의 클래스 내부에서 스스로의 객체를 단 1개만 생성한다.
    3. 외부에서 해당 클래스의 객체를 요구할 시, 2번에서 미리 만들어 놓은 단 하나의 객체 주소값을 공개된 메서드를 통해 리턴한다.
package static_.singletone;

public class Singleton {

	private Singleton() {
		System.out.println("객체가 생성됨");
	}

	private static Singleton s = new Singleton();

	public static Singleton getInstance() {
		return s;
	}
	
	///////////////////////////////////////////
	
	public void method1() {
		System.out.println("여러 군데에서 쓰이는 중요한 메서드");
	}
	
	public void method2() {
		System.out.println("꼭 사용해야 하는 가장 중요한 메서드");
	}
	
}
package static_.singletone;

public class MainClass {

	public static void main(String[] args) {
	
//		Singleton s = new Singleton(); (x)
//		s.method1();
//		s.method2();

		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		Singleton s3 = Singleton.getInstance();
		Singleton s4 = Singleton.getInstance();
		Singleton s5 = Singleton.getInstance();
		Singleton s6 = Singleton.getInstance();
		System.out.println("주소값: " + s1);
		System.out.println("주소값: " + s2);
		System.out.println("주소값: " + s3);
		System.out.println("주소값: " + s4);
		System.out.println("주소값: " + s5);
		System.out.println("주소값: " + s6);
		
//		s.method1();
//		s.method2();

	}
	
}

final class

  • final은 마지막의 라는 뜻을 가진 단어로 자바에서는 클래스, 필드, 메서드에 붙여서 사용할 수 있다.
  • 클래스에 final이 붙으면 마지막 클래스라는 의미가 되며, 이것을 상속해서 확장시키지 마라! 라는 의미로 사용된다. final 클래스는 상속이 불가능해진다.

final method

  • 메서드에 final이 붙으면 이것은 최종 메서드다. 라는 의미가 된다. 
  • final 메서드는 상속 시 자식클래스가 재정의(오버라이딩)할 수 없게 된다.

final field

  • 필드에 final이 붙으면 최종의 필드라는 뜻이 되므로, 한번 값이 저장되면 그것이 최종본이 된다는 의미가 된다.
  • 한번 값을 저장하면 이후로는 절대로 변경이 불가능해진다.

상수(static final)

  • 상수는 변수와 달리 불변의 값을 의미한다.
  • 불변의 값이란 원주율 파이나 지구의 반지름과 같은 것을 말한다.
  • final 필드는 상수라고 볼 수 없으며, 불변의 값이란 값이 변하지 않는 것과 동시에 유일해야 한다.
    • final 필드의 경우 값 변경만 되지 않을 뿐, 객체 별로 다른 값을 가질 수 있기 때문에 상수 조건에 어긋난다.
  • 자바에서 객체별 유일한 값(static), 값 변경불가(final)을 만족시키기 위해 필드에 static과 final을 함께 붙여 상수로 취급한다.
  • final 변수는 반드시 초기화가 필요하며, 초기화는 직접 지정하는 방법과 생성자를 이용하는 방법이 있다.
package final_.field;

public class Person {
	final String nation = "대한민국";
	final String name;
	int age;
	
	public Person(String name) {
		this.name = name;
	}
	
}
package final_.field;

public class MainClass {

	public static void main(String[] args) {

		Person kim = new Person("김철수");
//		kim.nation = "미국"; (x)
//		kim.name = "김마이클"; (x)
		
		Person park = new Person("박영희");
//		park.nation = "영국"; (x)
//		park.name = "박영국"; (x)

	}

}
package final_.method;

public class Parent {

	void method1() {}
	void method2() {}
	final void method3() {}
	
}
package final_.method;

public class Child extends Parent {

	@Override
	void method1() {
		super.method1();
	}

	@Override
	void method2() {
		super.method2();
	}
	
	/*
	void method3() {
		final메서드는 오버라이딩을 막는다.
	}
	 */

}
package final_.constant;

public class Earth {

	static final double RADIUS = 6400;
	
	static final double SURFACE_AREA;
	
	static {
		SURFACE_AREA = RADIUS * RADIUS * Math.PI * 4;
	}
	
}

abstract

  • 사전적 의미로 추상(abstract)는 어떤 사물이 실체가 없이 일정한 형태나 성질이 없고 막연하고 일반적인 것을 뜻한다.
  • 예를 들면 차량, 동물, 회사 등은 구체적인 대상이 아닌 많은 카테고리들을 모아둔 추상적인 집합적 개념이라는 것이다.
  • 호랑이, 고양이, 개와 같은 구체적인 대상들을 하나로 모아서 동물이라고 부르는 것과 같이 동물이라는 것은 추상적 개념이 된다.
  • 클래스에도 추상적 개념이 들어간 추상 클래스가 존재한다. 추상적인 것은 실체가 없기 때문에 추상클래스는 객체를 생성할 수 없다.
  • 단지 구체적인 실체클래스에게 속성과 기능을 전달하는 매개체가 된다.
  • 객체지향 프로그래밍에서는 객체간 공통된 속성과 기능을 상속이라는 개념을 통해 처리한다.
  • 상속을 통해 생성된 부모클래스를 추상 클래스로 만들어서 사용하는 이유
    • 실체 클래스들의 공통된 필드와 메서드의 이름을 통일을 하기 위함
      • 개발 프로젝트에서 설계자와 개발자는 일반적으로 다른사람이고 개발자는 여러명일 가능성이 크다.
      • A라는 개발자는 객체의 움직임을 처리하는 기능의 이름을 move()라고 선언하고, B라는 개발자는 walk()라고 선언하면 같은 기능임에도 불구하고 객체마다 사용방법이 달라진다.
      • 이 방법을 해결하기 위해 설계자는 추상클래스로 추상메서드 move()를 선언하고 개발자들에게 이 추상클래스를 상속받게함으로써 문제를 해결한다.
  • 추상 클래스는 실체 클래스가 공통적으로 가져야 할 필드와 메서드들을 정의해 놓은 추상적인 클래스로 실체 클래스의 멤버를 통일화 하는데 목적이 있다.
  • 실체 객체들이 모두 동일한 실행 내용을 가지고 있다면 추상클래스에서 해당 내용을 정의하여 상속해주면 되지만, 이름은 같아도 실체 객체들의 실행내용이 다를 경우에는 추상 클래스에서 추상 메서드를 만들어서 오버라이딩을 강제할 수 있다.
  • 추상메서드는 일반 메서드와 달리 메서드 블록을 열어 실행내용을 작성하지 않고, 세미콜론으로 닫아 선언만 하여 추상적인 규격만 만들어 둔다.
  • 추상클래스의 메서드에 abstract를 붙이면 해당 메서드는 실체가 없는 추상 메서드가 되며, 해당 메서드는 상속을 통해자식 클래스에서 무조건 오버라이딩을 하도록 강요된다.
    • 프로그래머의 오버라이딩 실수를 줄일 수 있고, 부모 클래스에서 메서드를 선언할 때 메서드 내부에 어떤 내용을 써야 할지 막연할 때도 구현하기가 편하다.
    • 다형성을 적용시켰을 경우에도 안전하게 사용이 가능하다.
    • 상속관계에 있는 자식 메서드들의 이름을 통일하기가 편하다.
  • 추상메서드는 실체가 존재하지 않는 틀 역할을 하기 때문에 메서드의 바디 ({})부분이 없고, 세미콜론으로 메서드 선언을 마감한다.
  • 일반 클래스에서는 추상 메서드를 선언할 수 없다. 추상 메서드가 하나라도 존재한다면 반드시 해당 클래스를 추상 클래스로 선언해야 한다.
  • 추상 클래스에 추상 메서드만 선언할 필요는 없다. 일반 멤버변수 및 메서드도 얼마든지 선언이 가능하다.

추상 메서드 선언시 주의사항

  • 추상 메서드는 반드시 추상 클래스에서만 선언이 가능하다.
  • 추상 메서드를 실체 클래스가 오버라이딩 하지 않을 경우 컴파일 에러가 발생하낟.
  • 추상 클래스에는 추상 메서드 이외에 일반적인 인스턴스 메서드, 정적 메서드도 선언이 가능하다.

bad Example

package abs.bad;

//쥬스 프랜차이즈 본사
public class HeadStore {

	// 상속을 받는 클래스들에게 오버라이딩을 권유 (강제 x)
	public void orderApple() {
		System.out.println("0원입니다. 가게에서 직접 가격을 정해주세요.");
	}
	
	public void orderBanana() {
		System.out.println("0원입니다. 가게에서 직접 가격을 정해주세요.");
	}
	
	public void orderGrape() {
		System.out.println("0원입니다. 가게에서 직접 가격을 정해주세요.");
		
	}

}
package abs.bad;

public class SeoulStore extends HeadStore {

	public void orderApple() {
		System.out.println("사과 쥬스의 가격은 3000원 입니다.");
	}
	
	public void orderBANANA() {
		System.out.println("바나나 쥬스의 가격은 2800원 입니다.");
	}
	
}
package abs.bad;

public class MainClass {

	public static void main(String[] args) {
		HeadStore store = new SeoulStore();
		store.orderApple();
		store.orderBanana();
		store.orderGrape();		
	}

}

Good Example

package abs.good;

public abstract class HeadStore {

	public int i;

	public HeadStore() {
		System.out.println("HeadStore의 생성자");
	}
	
	public abstract void orderApple();
	public abstract void orderBanana();
	public abstract void orderGrape();
	
	public void orderWaterMelon() {
		System.out.println("수박 쥬스의 가격은 4000원 입니다.");
	}
	
}
package abs.good;

public class SeoulStore extends HeadStore {

	public SeoulStore() {
		super();
	}

	@Override
	public void orderApple() {
		System.out.println("사과 쥬스의 가격은 3000원 입니다.");
		
	}

	@Override
	public void orderBanana() {
		System.out.println("바나나 쥬스의 가격은 2800원 입니다.");
		
	}

	@Override
	public void orderGrape() {
		System.out.println("포도 쥬스의 가격은 3200원 입니다.");
		
	}

}
package abs.good;

public class MainClass {

	public static void main(String[] args) {

		// 추상클래스는 스스로의 객체를 생성할 수 없다.
//		HeadStore h = new HeadStore(); (x)
		
		HeadStore s = new SeoulStore();
		
		s.orderApple();
		s.orderBanana();
		s.orderGrape();
		s.orderWaterMelon();
		
	}

}

Abstract Quiz

- Shape를 상속받는 Circle과 Rect 클래스를 생성
- Circle 객체를 생성할 때, 반지름도 받아서 초기화 할 수 있는 생성자를 선언
  Rect 객체를 생성할 때, 이름과 변의 길이를 받도록 처리
- 오버라이딩이 강제되는 메서드에는 각 도형의 넓이를 구하는 로직을 알아서 작성
- MainClass에서 길이가 5인 정사각형, 반지름이 4인 원의 모든 내용을 호출

abstract class Shape 요구사항
1. 도형의 이름을 저장할 수 있는 변수(name)을 private으로 선언.
2. 생성자를 선언 -> 매개값으로 도형의 이름을 받아서 저장할 수 있도록.
3. 자식 클래스에게 오버라이딩을 강제할 수 있는 메서드 getArea() 를 선언 (return type -> double)
4. 도형 이름을 출력할 수 있는 메서드 getName()을 선언. (강제 x)
package abs.quiz;

public abstract class Shape {
	
	private String name;
	
	public Shape(String name) {
		this.name = name;
	}
	
	public abstract double getArea();
	
	public String getName() {
		return "도형 이름: " + name;
	}

}​
package abs.quiz;

public class Circle extends Shape {

	private int radius;
	
	public Circle(String name, int radius) {
		super(name);
		this.radius = radius;
	}
	
	@Override
	public double getArea() {
		return radius * radius * Math.PI;
	}

}
package abs.quiz;

public class Rect extends Shape {

	private int side;
	private int width;
	private int height;
	
	public Rect(String name, int side) {
		super(name);
		this.side = side;
		
	}
	
	public Rect(String name, int width, int height) {
		super(name);
		this.width = width;
		this.height = height;
		
	}
	
	@Override
	public double getArea() {
		if (side != 0) {
			return side * side;
			
		} else {
			return width * height;
		}
	}

}
package abs.quiz;

public class MainClass {

	public static void main(String[] args) {

		Shape r = new Rect("정사각형", 5);
		System.out.println(r.getName());
		System.out.println("넓이: " + r.getArea());
		
		System.out.println("---------------------------------");
		
		Shape c = new Circle("원", 4);
		System.out.println(c.getName());
		System.out.printf("넓이: %.2f\n", c.getArea());
				
	}

}
 

'Programming Language > Java' 카테고리의 다른 글

[국비] Java 내용정리 Day15  (0) 2021.11.25
[국비] Java 내용정리 Day14  (0) 2021.11.24
[국비] Java 내용정리 Day12  (0) 2021.11.21
[국비] Java 내용정리 Day11  (0) 2021.11.20
[국비] Java 내용정리 Day10  (0) 2021.11.10

댓글