2. 구현 클래스가 인터페이스의 추상 메소드에 대한 실체 메소드를 가지고 있지 않으면 추상 클래스가 된다.
3. 인터페이스는 인스턴스 필드를 가질 수 있다.
4. 구현 객체는 인터페이스 타입으로 자동 변환된다.
2. 인터페이스의 다형성과 거리가 먼 것은? (4)
1. 필드가 인터페이스 타입일 경우 다양한 구현 객체를 대입할 수 있다.
2. 매개 변수가 인터페이스 타입일 경우 다양한 구현 객체를 대입할 수 있다.
3. 배열이 인터페이스 타입일 경우 다양한 구현 객체를 저장할 수 있다.
4. 구현 객체를 인터페이스 타입으로 변환하려면 강제 타입 변환을 해야 한다.
3. 다음은 Soundable 인터페이스입니다. sound() 추상 메소드는 객체의 소리를 리턴합니다.
package sec7;
public interface Soundable {
String sound();
}
package sec7;
public class SoundableExample {
private static void printSound(Soundable soundable) {
System.out.println(soundable.sound());
}
public static void main(String[] args) {
printSound(new Cat());
printSound(new Dog());
}
}
package sec7;
public class Cat implements Soundable{
@Override
public String sound() {
return "야옹";
}
}
package sec7;
public class Dog implements Soundable{
@Override
public String sound() {
return "멍멍";
}
}
야옹
멍멍
4. DaoExample 클래스의 main() 메소드에서 dbWork() 메소드를 호출할 때 OracleDao 와 MySqlDao 객체를 매개값으로 주고 호출했습니다. dbWork() 메소드는 두 객체를 모두 매개값으로 받기 위해 DateAccessObject 타입의 매개 변수를 가지고 있습니다. 실행 결과를 보고 DataAccessObject 인터페이스와 OracleDao, MySqlDao 구현 클래스를 각각 작성해보세요.
package sec7;
public class DaoExample {
public static void dbWork(DataAccessObject dao) {
dao.select();
dao.insert();
dao.update();
dao.delete();
}
public static void main(String[] args) {
dbWork(new OracleDao());
dbWork(new MySqlDao());
}
}
package sec7;
public interface DataAccessObject {
public void select();
public void insert();
public void update();
public void delete();
}
package sec7;
public class MySqlDao implements DataAccessObject {
@Override
public void select() {
System.out.println("MySql DB에서 검색");
}
@Override
public void insert() {
System.out.println("MySql DB에서 삽입");
}
@Override
public void update() {
System.out.println("MySql DB에서 수정");
}
@Override
public void delete() {
System.out.println("MySql DB에서 삭제");
}
}
Oracle DB에서검색
Oracle DB에서삽입
Oracle DB에서수정
Oracle DB에서삭제
MySql DB에서검색
MySql DB에서삽입
MySql DB에서수정
MySql DB에서삭제
5. 다음은 Action 인터페이스입니다. work() 추상 메소드는 객체의 작업을 시작시킵니다.
ActionExample 클래스의 main() 메소드에서 Action의 익명 구현 객체를 만들어 다음과 같은 실행 결과가 나올 수 있도록 박스 안에 들어갈 코드를 작성해보세요.
package sec8;
public class ActionExample {
public static void main(String[] args) {
Action action = new Action() {
@Override
public void work() {
System.out.println("복사를 합니다.");
}
};
action.work();
}
}
package sec8;
public interface Action {
void work();
}
출처 : 이것이 자바다 상속 7.1 상속 개념 상속(Inheritance)이란? 현실 세계: • 부모가 자식에게 물려주는 행위 • 부모가 자식을 선택해서 물려줌 객체 지향 프로그램: • 자식(하위, 파생) 클래스가 부모(상위) 클래스의 멤버를 물려받는 것 • 자식이 부모를 선택해 물려받음 • 상속 대상: 부모의 필드와 메소드
상속(Inheritance) 개념의 활용 상속의 효과 • 부모 클래스 재사용해 자식 클래스 빨리 개발 가능 • 반복된 코드 중복 줄임 • 유지 보수 편리성 제공 • 객체 다형성 구현 가능 상속 대상 제한 • 부모 클래스의 private 접근 갖는 필드와 메소드 제외 • 부모 클래스가 다른 패키지에 있을 경우, default 접근 갖는 필드와 메소드도 제외 extends 키워드 자식 클래스가 상속할 부모 클래스를 지정하는 키워드
//A로 부터 물려받은 필드와 메소드 B b = new B(); b.field1 = 10; b.method1();
//B가 추가한 필드와 메소드 b.filed2 = "홍길동"; b.method2();
7.2 클래스 상속 현실에서 상속은 부모가 자식을 선택해서 물려주지만, 프로그램에서는 자식이 부모를 선택한다. class 자식 클래스 extends 부모글래스{ //필드 //생성자 //메소드 }
7.3 부모 생성자 호출 자식 객체 생성하면 부모 객체도 생성되는가? 부모 없는 자식 없음 • 자식 객체 생성할 때는 부모 객체부터 생성 후 자식 객체 생성 • 부모 생성자 호출 완료 후 자식 생성자 호출 완료
명시적인 부모 생성자 호출 부모 객체 생성할 때, 부모 생성자 선택해 호출
• super(매개값,…) 매개값과 동일한 타입, 개수, 순서 맞는 부모 생성자 호출 • 부모 생성자 없다면 컴파일 오류 발생 • 반드시 자식 생성자의 첫 줄에 위치 • 부모 클래스에 기본(매개변수 없는) 생성자가 없다면 필수 작성
[People.java] 부모 클래스
public class People {
public String name;
public String ssn;
public People(String name,String ssn) {
this.name = name;
this.ssn = ssn;
}
}
[Student.java] 자식 클래스
public class Student extends People {
public int studentNo;
public Student(String name, String ssn,int studentNo ) {
super(name, ssn);//부모 생성자 호출
this.studentNo = studentNo;
}
}
[StudentExample.java] 자식 객체 이용
public class StudentExample {
public static void main(String[] args) {
Student student = new Student("홍길동", "12345-1234567", 1);
//부모에서 물려받은 필드 출력
System.out.println("name :" + student.name);
System.out.println("ssn :" + student.ssn);
System.out.println("studentNo :" + student.studentNo);
}
}
7.4 메소드 재정의 메소드 재정의(@Override) 부모 클래스의 상속 메소드 수정해 자식 클래스에서 재정의하는 것 메소드 재정의 조건 (p.295~296) • 부모 클래스의 메소드와 동일한 시그니처 가져야 • 접근 제한을 더 강하게 오버라이딩 불가 public을 default나 private으로 수정 불가 반대로 default는 public 으로 수정 가능 • 새로운 예외(Exception) throws 불가 (예외처리는 10장 참조) 부모 클래스의 모든 메소드가 자식 클래스에게 맞게 설계되어 있다면 가장 이상적인 상속이지만, 어떤 메소드는 자식 클래스가 사용하기에 적합하지 않을 수도 있다. 이 경우 상속된 일부 메소드는 자식 클래스에서 다시 수정해서 사용해야 한다. 자바는 이런 경우를 위해 메소드 오버라이딩(Overriding) 기능을 제공한다.
7.4.1 메소드 재정의(Override) 메소드 오버라이딩은 상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우, 자식 클래스에서 동일한 메소드를 재정의하는 것을 말한다.
메소드가 오버라이딩되었다면 부모 객체의 메소드는 숨겨지기 때문에, 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.
메소드 오버라이딩 규칙
부모의 메소드와 동일한 시그니처(리턴타입,메소드 이름,매개 변수 리스트)를 가져야 한다.
접근 제한을 더 강하게 오버라이딩할 수 없다.
새로운 예외(Exception) 를 throws 할 수 없다(예외는 10장에서 학습한다). [Calculator.java] 부모 클래스
public class Calculator {
double areaCircle(double r) {
System.out.println("Calculator 객체의 areaCircle() 실행");
return 3.14159 * r * r;
}
}
[Computer.java] 자식 클래스
public class Computer extends Calculator {
@Override
double areaCircle(double r) {
System.out.println("Computer 객체의 areaCircle() 실행");
return Math.PI * r * r;
}
}
[ComputerExample.java] 메소드 오버라이딩 클래스
public class ComputerExample {
public static void main(String[] args) {
int r = 10;
Calculator calculator = new Calculator();
System.out.println("원면적 : " + calculator.areaCircle(r));
System.out.println();
Computer computer = new Computer();
System.out.println("원면적 : " + computer.areaCircle(r)); // 재정의된 메소드 호출
}
}
7.4.2 부모 클래스 호출(super) 부모 메소드 사용(super) 메소드 재정의는 부모 메소드 숨기는 효과 !! • 자식 클래스에서는 재정의된 메소드만 호출 자식 클래스에서 수정되기 전 부모 메소드 호출 - super 사용 • super는 부모 객체 참조(참고: this는 자신 객체 참조)
자식 클래스에서 부모 클래스의 메소드를 오버라이딩하게 되면,부모 클래스의 메소드는 숨겨지고 오버라이딩된 자식 메소드만 사용된다. 그러나 자식 클래스 내부에서 오버라이딩된 부모 클래스의 메소드를 호출해야 하는 상황이 발생한다면 명시적으로 super 키워드를 붙여서 부모 메소드를 호출할 수 있다. super는 부모 객체를 참조하고 있기 때문에 부모 메소드에 직접 접근할 수 있다.
[Airplane.java] super변수
public class Airplane {
public void land() {
System.out.println("착륙합니다.");
}
public void fly() {
System.out.println("일반비행합니다.");
}
public void takeOff() {
System.out.println("이륙합니다.");
}
}
[SupersonicAirplane.java] super변수
public class SupersonicAirplane extends Airplane {
public static final int NORMAL = 1;
public static final int SUPERSONIC = 2;
public int flyMode = NORMAL;
@Override
public void fly() {
if(flyMode == SUPERSONIC) {
System.out.println("초음속비행합니다.");
}else {
//Airplane 객체의 fly() 메소드 호출
super.fly();//부모의 함수 호출 할 경우는 super
}
}
}
[SupersonicAirplaneExample.java] super변수
public class SupersonicAirplaneExample {
public static void main(String[] args) {
SupersonicAirplane sa = new SupersonicAirplane();
sa.takeOff();
sa.fly();
sa.flyMode = SupersonicAirplane.SUPERSONIC;
sa.fly();
sa.flyMode = SupersonicAirplane.NORMAL;
sa.fly();
sa.land();
}
}
자주 사용되는 고정값들은 상수를 사용함으로써 가독성을 높여준다.
7.5 final클래스와 final 메소드 final 키워드의 용도 final 필드: 수정 불가 필드 final 클래스: 부모로 사용 불가한 클래스 final 메소드: 자식이 재정의할 수 없는 메소드 final 키워드는 해당 선언이 최종상태이고, 결코 수정될 수 없음을 뜻한다. final 키워드가 클래스,필드,메소드 선언에 사용될 경우 해석이 조금씩 달라지는데 클래스와 메소드에 final이 지정되면 어떤 효과가 날까? 클래스와 메소드 선언 시에 final 키워드가 지정되면 상속과 관련이 있다.
7.5.1상속할 수 없는 final 클래스 자식 클래스 만들지 못하도록 final 클래스로 생성 public final class 클래스{...}
final 대표적인 예는 String이다.
그래서 다음과 같이 자식 클래스를 만들 수 없다. public class NewString extends String{...}
[Member.java] 상속할 수 없는 final클래스 public final class Member {
}
[VeryVeryImportantPerson.java] 상속할 수 없는 final 클래스 //Member를 상속할 수 없음 public class VeryVeryImportantPerson extends Member {
}
7.5.2오버라이딩할 수 없는 final 메소드 자식 클래스가 재정의 못하도록 부모 클래스의 메소드를 final로 생성 public final 리턴타입 메소드([매개변수 , ...] ...) {...}
[Car.java] 재정의할 수 없는 final메소드
public class Car {
// 필드
public int speed;
// 메소드
public void speedUp() {
speed += 1;
}
// final 메소드
public final void stop() {
System.out.println("차를 멈춤");
speed = 0;
}
}
[SportsCar.java] 재정의할 수 없는 final메소드
public class SportsCar extends Car{
@Override
public void speedUp() {speed += 10;}
//오버라이딩을 할 수 없음
@Override
public void stop() {
System.out.println("스포츠카를 멈춤");
speed = 0;
}
}
7.6 protected 접근 제한자 protected는 public과 default 접근 제한의 중간쯤에 해당한다. 같은 패키지에서는 default와 같이 접근 제한이 없지만 다른 패키지에서는 자식 클래스만 접근을 허용 protected 접근 제한자 상속과 관련된 접근 제한자 • 같은 패키지: default와 동일 • 다른 패키지: 자식 클래스만 접근 허용
protected는 필드와 생성자 ,메소드 선언에 사용될수 있다.
[A.java] protected 접근 제한자
package textbook.chapter7.package1;
public class A {
protected String field;
protected A() {
}
protected void method() {
}
}
[B.java] protected 접근 제한자
package textbook.chapter7.package1;
public class B {
public void method() {
A a = new A();; //(O)
a.field = "value"; //(O)
a.method();
}
}
[C.java] protected 접근 제한자 테스트
package textbook.chapter7.package2;
import textbook.chapter7.package1.A;
public class C {
public void method() {
A a = new A(); // x
a.field = "value"; // x
a.method(); // x
}
}
[D.java] protected 접근 제한자
package textbook.chapter7.package2;
import textbook.chapter7.package1.A;
public class D extends A {
public D() {
super(); // o
this.field = "value"; // o
this.method(); // o
}
}
7.7 타입 변환과 다형성 다형성 (多形性, Polymorphism) 같은 타입이지만 실행 결과가 다양한 객체 대입(이용) 가능한 성질 • 부모 타입에는 모든 자식 객체가 대입 가능 자식 타입은 부모 타입으로 자동 타입 변환 • 효과: 객체 부품화 가능
public class Car{ Tire t1 = new HankookTire(); Tire t2 = new KumhoTire(); }
7.7.1 자동타입 변환(Promotion) 자동 타입 변환(Promotion) 은 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말한다
바로 위의 부모가 아니더라도 상속 계층의 상위면 자동 타입 변환 가능 • 변환 후에는 부모 클래스 멤버만 접근 가능 (p.308~310)
class A {}
class B extends A {}
class C extends A {}
class D extends B {}
class E extends C {}
public class PromotionExample {
public static void main(String[] args) {
B b = new B();
C c = new C();
D d = new D();
E e = new E();
A a1 = b;
A a2 = c;
A a3 = d;
A a4 = e;
B b1 = d;
C c1 = e;
//B b3 = e; //컴파일 에러(상속관계에 있지 않음)
//C c2 = d; //컴파일 에러(상속관계에 있지 않음)
}
}
부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능하다. 비록 변수는 자식 객체를 참조, 변수로 접근 가능한 멤버는 부모 클래스 멤버로만 한정된다. 그러나 예외가 있다 메소드가 자식 클래스에서 오버라이딩되었다면 자식 클래스의 메소드가 대신 호출된다 이것은 다형성(Polymorphism)과 관련이 있기 때문에 매우 중요한 성질
[Parent.java] 자동 타입 변환 후의 멤버 접근
public class Parent {
public void method1() {
System.out.println("Parent-method1()");
}
public void method2() {
System.out.println("Parent-method2()");
}
}
public class Child extends Parent {
@Override
public void method2() {
System.out.println("Child-method2()"); // 재정의
}
public void method3() {
System.out.println("Child-method3()");
}
}
[Child.java] 자동 타입 변환 후의 멤버 접근
public class ChildExample {
public static void main(String[] args) {
Child child = new Child();
Parent parent = child; // 자동 타입 변환
parent.method1();
parent.method2(); // 재정의된 메소드가 호출됨
// parent.method3(); (호출 불가능)
}
}
[ChildExample.java] 자동타입 변환 후의 멤버 접근 public class ChildExample { public static void main(String[] args) { Child child = new Child();
Parent parent = child; // 자동 타입 변환
parent.method1();
parent.method2(); // 재정의된 메소드가 호출됨
// parent.method3(); (호출 불가능)
}
}
7.7.2필드의 다형성 다형성을 구현하는 기술적 방법 • 부모 타입으로 자동 변환 • 재정의된 메소드(오버라이딩) 다형성이란 동일한 타입을 사용하지만 다양한 결과가 나오는 성질을 말한다.
주로 필드의 값을 다양화함으로써 실행 결과가 다르게 나오도록 구현하는데, 필드의 타입은 변함이 없지만, 실행 도중에 어떤 객체를 필드로 저장하느냐에 따라 실행 결과가 달라질 수 있다.
이것이 필드의 다형성이다. 부모 클래스를 상속하는 자식 클래스는 부모가 가지고 있는 필드와 메소드를 가지고 있으니 사용 방법이 동일할 것이고, 자식 클래스는 부모의 메소드를 오버라이딩(재정의)해서 메소드의 실행내용을 변경함으로써 더 우수한 실행 결과가 나오게 할 수 있다.
class Car{ //필드 Tire frontLeftTire = new Tire(); Tire frontRightTire = new Tire(); Tire backLeftTire = new Tire(); Tire backRightTire = new Tire(); //메소드 void run(){...} }
Car myCar = new Car();
myCar.frontLeftTire = new HankookTire();
myCar.backLeftTire = new KumboTire();
myCar.run();
package textbook.chapter7.exam01;
public class Tire {
// 필드
public int maxRotation; // 최대 회전수(타이어 수명)
public int accumulatedRotation; // 누적 회전수
public String location; // 타이어의 위치
// 생성자
public Tire(String location, int maxRotation) {
this.location = location;
this.maxRotation = maxRotation;
}
// 메소드
public boolean roll() {
++accumulatedRotation; // 누적 회전수 1증가
if (accumulatedRotation < maxRotation) {
System.out.println(location + " Tire 수명: " + // 정상 회전
(maxRotation - accumulatedRotation) + "회"); // (누적<최대)일 경우 실행
return true;
} else {
System.out.println("*** " + location + " Tire 펑크 ***"); // 펑크(누적=최대)일 경우 실행
return false;
}
}
}
[Car.java] Tire를 부품으로 가지는 클래스
package textbook.chapter7.exam01;
public class Car {
//필드
Tire frontLeftTire = new Tire("앞왼쪽",6);
Tire frontRightTire = new Tire("앞오른쪽",2);
Tire backLeftTire = new Tire("뒤왼쪽",3);
Tire backRightTire = new Tire("뒤오른쪽",4); //자동차는 4개의 타이어를 가진다.
//생성자
//메소드
int run() {
System.out.println("[자동차가 달립니다.]");
if(frontLeftTire.roll()==false) {stop(); return 1;}; //모든 타이어를 1회 회전시키기 위해 각 Tire
if(frontRightTire.roll()==false) {stop(); return 2;}; //객체의 roll()메소드를 호출한다.
if(backLeftTire.roll()==false) {stop(); return 3;}; //false를 리턴하는 roll()이 있을 경우
if(backRightTire.roll()==false) {stop(); return 4;}; //stop() 메소드를 호출하고 해당 타이어 번호를 리턴한다.
return 0;
}
void stop() {
System.out.println("[자동차가 멈춥니다.]");
}
}
package textbook.chapter7.exam01;
public class CarExample {
public static void main(String[] args) {
Car car = new Car(); // Car 객체 생성
for (int i = 1; i <= 5; i++) { // Car 객체의 run() 메소드 5번 반복 실행
int problemLocation = car.run();
switch (problemLocation) {
case 1:
System.out.println("앞왼쪽 HankookTire로 교체");
car.frontLeftTire = new HankookTire("앞왼쪽", 15);
break;
case 2:
System.out.println("앞오른쪽 KumhoTire로 교체");
car.frontLeftTire = new KumhoTire("앞오른쪽", 13);
break;
case 3:
System.out.println("뒤왼쪽 HankookTire로 교체");
car.frontLeftTire = new HankookTire("뒤왼쪽", 14);
break;
case 4:
System.out.println("뒤오른쪽 HankookTire로 교체");
car.frontLeftTire = new HankookTire("뒤오른쪽", 17);
break;
}
System.out.println("-------------------------------"); // 1회전 시 출력되는 내용을 구분
}
}
}
결과 => [자동차가 달립니다.] 앞왼쪽 Tire 수명: 5회 앞오른쪽 Tire 수명: 1회 뒤왼쪽 Tire 수명: 2회 뒤오른쪽 Tire 수명: 3회
[자동차가 달립니다.] 앞왼쪽 Tire 수명: 4회 * 앞오른쪽 Tire 펑크 * [자동차가 멈춥니다.] 앞오른쪽 KumhoTire로 교체
[자동차가 달립니다.] 앞오른쪽 KumhoTire 수명: 12회 * 앞오른쪽 Tire 펑크 * [자동차가 멈춥니다.] 앞오른쪽 KumhoTire로 교체
[자동차가 달립니다.] 앞오른쪽 KumhoTire 수명: 12회 * 앞오른쪽 Tire 펑크 * [자동차가 멈춥니다.] 앞오른쪽 KumhoTire로 교체
[자동차가 달립니다.] 앞오른쪽 KumhoTire 수명: 12회 * 앞오른쪽 Tire 펑크 * [자동차가 멈춥니다.] 앞오른쪽 KumhoTire로 교체
7.7.3 하나의 배열로 객체 관리
[Car.java] Tire를 부품으로 가지는 클래스
public class Car {
//필드
Tire[] tires = {
new Tire("앞왼쪽",6),
new Tire("앞오른쪽",2),
new Tire("뒤왼쪽",3),
new Tire("뒤오른쪽",4),
};
//메소드
int run() {
System.out.println("[자동차가 달립니다.]");
for(int i=0; i<tires.length;i++) {
if(tires[i].roll()==false) {
stop();
return (i+1);
}
}
return 0;
}
void stop() {
System.out.println("[자동차가 멈춥니다.]");
}
}
[CarExample.java] 실행 클래스
public class CarExample {
public static void main(String[] args) {
Car car = new Car();
for(int i=1;i<=5;i++) {
int problemLocation = car.run();
if(problemLocation!=0) {
System.out.println(car.tires[problemLocation-1].location + "HankookTire로 교체");
car.tires[problemLocation-1] =
new HankookTire(car.tires[problemLocation-1].location,15);
}
System.out.println("----------------------------------");
}
}
}
7.7.4 매개변수의 다형성 매개변수가 클래스 타입일 경우 • 해당 클래스의 객체 대입이 원칙이나 자식 객체 대입하는 것도 허용 자동 타입 변환 매개변수의 다형성 자동 타입 변환은 필드의 값을 대입할 때에도 발생하지만, 주로 메소드를 호출할 때 많이 발생한다. 메소드를 호출할 때에는 매개 변수의 타입과 동일한 매개값을 지정하는 것이 정석이지만, 매개값을 다양화하기 위해 매개 변수에 자식 타입 객체를 지정할 수도 있다.
Driver driver = new Driver(); Vehicle vehicle = new Vehicle(); driver.drive(vehicle);
우리는 여기서 매우 중요한 것을 하나 알게 되었다. 매개 변수의 타입이 클래스일 경우, 해당 클래스의 객채뿐만 아니라 자식 객체까지도 매개값으로 사용할 수 있다는 것이다.
[Vehicle.java] 부모 클래스 package textbook.chapter7.exam03;
public class Vehicle { public void run() { System.out.println("차량이 달립니다."); } }
[Driver.java] Vehicle을 이용하는 클래스 package textbook.chapter7.exam03;
public class Driver { public void drive(Vehicle vehicle) { vehicle.run(); } }
[Bus.java] 자식 클래스 package textbook.chapter7.exam03;
public class Bus extends Vehicle { @Override public void run() { System.out.println("버스가 달립니다."); } }
[Taxi.java] 자식 클래스
package textbook.chapter7.exam03;
public class Taxi extends Vehicle {
@Override
public void run() {
System.out.println("택시가 달립니다.");
}
}
[DriverExample.java] 실행 클래스
package textbook.chapter7.exam03;
public class DriverExample {
public static void main(String[] args) {
Driver driver = new Driver();
Bus bus = new Bus();
Taxi taxi = new Taxi();
driver.drive(bus);
driver.drive(taxi);
}
}
7.7.5 강제 타입 변환(Casting) 부모 타입을 자식 타입으로 변환하는 것
조건 • 자식 타입을 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때 강제 타입 변환 이 필요한 경우 • 자식 타입이 부모 타입으로 자동 변환 부모 타입에 선언된 필드와 메소드만 사용 가능 • 자식 타입에 선언된 필드와 메소드를 다시 사용해야 할 경우
[Parent.java]부모 클래스
package textbook.chapter7.exam03;
public class Parent {
public String field1;
public void method1() {
System.out.println("Parent-method1()");
}
public void method2() {
System.out.println("Parent-method2()");
}
}
[Child.java] 자식 클래스
package textbook.chapter7.exam03;
public class Child extends Parent {
public String field2;
public void method3() {
System.out.println("Child-method3()");
}
}
7.7.6 객체 타입 변환(instanceof) 객체 타입 확인(instanceof) (p.326~329) 부모 타입이면 모두 자식 타입으로 강제 타입 변환할 수 있는 것 아님 • ClassCastException 예외 발생 가능
먼저 자식 타입인지 확인 후 강제 타입 실행해야 함
[Parent.java]부모 클래스 package textbook.chapter7.exam04;
public class Parent {
}
[Child.java] 자식 클래스 package textbook.chapter7.exam04;
public class Child extends Parent{
}
[InstanceofExampl.java] 객체 타입 확인
package textbook.chapter7.exam04;
public class InstanceofExampl {
public static void method1(Parent parent) {
if (parent instanceof Child) {
Child child = (Child) parent;
System.out.println("method1 - Child로 변환 성공");
} else {
System.out.println("method1 - Child로 변환되지 않음");
}
}
public static void method2(Parent parent) {
Child child = (Child) parent; // ClassCastException 발생할 가능성 있음
System.out.println("method2 - Child로 변환 성공");
}
public static void main(String[] args) {
Parent parentA = new Child();
method1(parentA);
method2(parentA);
Parent parentB = new Parent();
method1(parentB);
method2(parentB); // 예외 발생
}
}
결과=> method1 - Child로 변환 성공 method2 - Child로 변환 성공 method1 - Child로 변환되지 않음 Exception in thread "main" java.lang.ClassCastException: textbook.chapter7.exam04.Parent cannot be cast to textbook.chapter7.exam04.Child at textbook.chapter7.exam04.InstanceofExampl.method2(InstanceofExampl.java:14) at textbook.chapter7.exam04.InstanceofExampl.main(InstanceofExampl.java:25)
7.8 추상 클래스 7.8.1 추상 클래스의 개념 추상(abstract) • 실체들 간에 공통되는 특성을 추출한 것 예1: 새, 곤충, 물고기 동물 (추상) 예2: 삼성, 현대, LG 회사 (추상) 추상 클래스(abstract class) • 실체 클래스들의 공통되는 필드와 메소드 정의한 클래스 • 추상 클래스는 실체 클래스의 부모 클래스 역할 (단독 객체 X)
7.8.2 추상 클래스의 용도 실체 클래스의 공통된 필드와 메소드의 이름 통일할 목적 • 실체 클래스를 설계자가 여러 사람일 경우, • 실체 클래스마다 필드와 메소드가 제각기 다른 이름을 가질 수 있음 실체 클래스를 작성할 때 시간 절약 • 실체 클래스는 추가적인 필드와 메소드만 선언 실체 클래스 설계 규격을 만들고자 할 때 • 실체 클래스가 가져야 할 필드와 메소드를 추상 클래스에 미리 정의 • 실체 클래스는 추상 클래스를 무조건 상속 받아 작성 7.8.3 추상 클래스 선언 클래스 선언에 abstract 키워드 사용 • New 연산자로 객체 생성하지 못하고 상속 통해 자식 클래스만 생성 가능
[Phone.java] 추상 클래스
public class Phone {
//필드
public String owner;
//생성자
public Phone(String owner) {
this.owner = owner;
}
//메소드
public void turnOn() {
System.out.println("폰 전원을 켭니다.");
}
public void turnOff() {
System.out.println("폰 전원을 끕니다.");
}
}
[SmartPhone.java] 실체 클래스
public class SmartPhone extends Phone{
//생성자
public SmartPhone(String owner) {
super(owner);
}
//메소드
public void internetSearch() {
System.out.println("인터넷 검색을 합니다.");
}
}
[PhoneExample.java] 추상 클래스
public class PhoneExample {
public static void main(String[] args) {
//Phone phone = new Phone();
SmartPhone smartPhone = new SmartPhone("홍길동");
smartPhone.turnOn();
smartPhone.internetSearch();
smartPhone.turnOff();
}
}
7.8.4 추상 메소드와 오버라이딩(재정의) 메소드 이름 동일하지만, 실행 내용이 실체 클래스마다 다른 메소드 예: 동물은 소리를 낸다. 하지만 실체 동물들의 소리는 제각기 다르다. 구현 방법 • 추상 클래스에는 메소드의 선언부만 작성 (추상 메소드) • 실체 클래스에서 메소드의 실행 내용 작성(오버라이딩(Overriding))
[Animal.java] 추상 메소드 선언
public abstract class Animal { //추상 클래스
public String kind;
public void breathe() {
System.out.println("숨을 쉽니다.");
}
public abstract void sound(); //추상 메소드
}
추상 메소드 오버라이딩
public class Dog extends Animal{
public Dog() {
this.kind = "포유류";
}
@Override //추상 메소드 재정의
public void sound() {
System.out.println("멍멍");
}
}
추상 메소드 오버라이딩
public class Cat extends Animal{
public Cat() {
this.kind = "포유류";
}
@Override
public void sound() {
System.out.println("야옹");
}
}
실행 클래스
public class AnimalExample {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.sound();
cat.sound();
System.out.println("-----");
//변수의 자동 타입 변환
Animal animal = null;
animal = new Dog();
animal.sound();
animal = new Cat();
animal.sound();
System.out.println("-----");
//메소드의 다형성
animalSound(new Dog());
animalSound(new Cat());
}
public static void animalSound(Animal animal) {
animal.sound(); //재정의된 메소드 호출
}
}
연습문제
1. 자바의 상속에 대한 설명 중 틀린 것은 무엇입니까? (1)
1. 자바는 다중 상속을 허용한다.
2. 부모의 메소드를 자식 클래스에서 재정의(오버라이딩)할 수 있다.
3. 부모의 private 접근 제한을 갖는 필드와 메소드는 상속의 대상이 아니다.
4. final 클래스는 상속할 수 없고, final 메소드는 오버라이딩할 수 없다.
2. 클래스 타입 변환에 대한 설명 중 틀린 것은 무엇입니까? (2)
1. 자식 객체는 부모 타입으로 자동 타입 변환된다.
2. 부모 객체는 항상 자식 타입으로 강제 타입 변환된다.
3. 자동 타입 변환을 이용해서 필드와 매개 변수의 다형성을 구현한다.
4. 강제 타입 변환 전에 instanceof 연산자로 변환 가능한지 검사하는 것이 좋다.
3. final 키워드에 대한 설명으로 틀린 것은? (1)
1.final 클래스는 부모 클래스로 사용할 수 있다.
2.final 필드는 값이 저장된 후에는 변경할 수 없다.
3.final 메소드는 재정의(오버라이딩)할 수 없다.
4.static final 필드는 상수를 말한다.
4. 오버라이딩(Overriding)에 대한 설명으로 틀린 것은? (4)
1. 부모 메소드의 시그니처 (리턴 타입, 메소드명, 매개 변수)와 동일해야 한다.
2. 부모 메소드보다 좁은 접근 제한자를 붙일 수 없다. (public(부모) -> private (자식))
3. @Override 어노테이션을 사용하면 재정의가 확실한지 컴파일러가 검증한다.
4. protected 접근 제한을 갖는 메소드는 다른 패키지의 자식 클래스에서 재정의할 수 없다.
5. Parent 클래스를 상속해서 Child 클래스를 다음과 같이 작성했는데, Child 클래스의 생성자에서 컴파일 에러가 발생했습니다. 그 이유를 설명해보세요.
package sec7;
public class Parent { public String name;
public Parent(String name) {
this.name = name;
}
} package sec7;
public class Child extends Parent{
private int studentNo;
public Child(String name, int studentNo) {
this.name = name;
this.studentNo = studentNo;
}
}
this.name = name; 부분을 super(name); (부모생성자 호출)으로 고쳐준다.
Parent 클래스를 상속받아 Child 클래스를 다음과 같이 작성했습니다. ChildExample 클래스를 실행했을 때 호출되는 각 클래스의 생성자의 순서를 생각하면서 출력 결과를 작성해보세요.
package sec7;
public class Parent { public String nation;
public Parent() {
this("대한민국");
System.out.println("Parent() call");
}
public Parent(String nation) {
this.nation = nation;
System.out.println("Parent(String nation) call");
}
}
package sec7;
public class Child extends Parent{ private String name;
public Child() {
this("홍길동");
System.out.println("Child() call");
}
public Child(String name) {
this.name = name;
System.out.println("Child(String name) call");
}
package sec7;
public class ChildExample {
public static void main(String[] args) {
Child child = new Child();
}
생성자(Constructor) : new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당 객체 초기화 : 필드를 초기화하거나 메서드를 호출해서 객체를 사용할 준비를 하는 것.
생성자의 실행 없이 객체 생성은 불가능.
new 연산자에 의해 생성자가 성공적으로 실행되면 힙(Heap)영역에 객체가 생성되고 객체의 주소가 리턴된다. 리턴된 객체의 주소는 클래스 타입 변수에 저장되어 객체에 접근할 때 이용된다. 만약 생성자가 성공적으로 실행되지 않고 예외(에러)가 발생했다면 객체는 생성되지 않는다.
6.7.1기본 생성자 (Default Constructor)
모든 클래스는 생성자가 반드시 존재하며 하나 이상 가질 수 있음
생성자 선언을 생략하면 컴파일러는 다음과 같은 기본 생성자 추가
public 없이 class로만 선언되면 기본 생성자에도 public이 붙지 않는다.
컴파일러는 기본 생성자를 추가하지 않는다.
6.7.2 생성자 선언
=>생성자 선언
디폴트 생성자 대신 개발자가 직접 선언
=>개발자 선언한 생성자 존재 시 컴파일러는 기본 생성자 추가하지 않음
new 연산자로 객체 생성시 개발자가 선언한 생성자 반드시 사용
클래스에 생성자가 명시적으로 선언되어 있을 경우에는 반드시 선언된 생성자를 호출해서 객체를 생성해야만 한다.
[ Car1.java]생성자 선언
publicclass Car1 {
// 생성자
Car1(String color, intcc) {// 검정, 3000
}
}
[ CarExample1.java]생성자를 호출해서 객체 생성
publicclass CarExample1 {
publicstaticvoid main(String[] args) {
Car1 myCar = new Car1("검정", 3000);
// Car1 myCar = new Car1(); (X) // 기본 생성자를 호출할 수 없다.
}
}
6.7.3필드 초기화
클래스로부터 객체가 생성될 때 필드는 기본 초기값으로 자동 설정됩니다.
만약 다른 값으로 초기화하고 싶다면 두가지 방법이 있습니다.
-필드를 선언할 때 초기값을 주는 방법
-생성자에서 초기값을 주는 방법
필드를 선언할 때 초기값을 주면 동일한 클래스로부터 생성되는 객체들은 모두 같은 데이터를 갖게 됩니다.
객체 생성 후 변경할 수 있지만 객체 생성 시점에는 필드의 값이 모두 같습니다.
예:
korean 클래스에 nation필드를 선언하면서 "대한민국"으로 초기값을 준 경우 ,Korean클래스로부터 k1과k2객체를 생성하면k1과k2객체의 nation필드에는 모두 "대한민국"이 저장되어 있다.
객체 생성 시점에 외부에서 제공되는 다양한 값들로 초기화되어야 한다면 생성자에서 초기화를 해야합니다.
위 코드에서 name(이름) 과 ssn(주민번호) 필드값은 클래스를 작성할 때 초기값을 줄 수 없고객체 생성시점에서 다양한 값을 가져야 합니다.
따라서 생성자의 매개값으로 이 값들을 받아 초기화 하는 것이 맞습니다.
생성자의 매개값으로 초기값 설정
[ Korean.java]생성자에서 필드 초기화
publicclass Korean {
// 필드
String nation = "대한민국";
String name;
String ssn;
// 생성자
public Korean(String n, String s) {
name = n;
ssn = s;
}
}
[ KoreanExample.java]객체 생성 후 필드값 출력
publicclass KoreanExample {
publicstaticvoid 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);
}
}
매개 변수와 필드명 같은 경우 this 사용
public Korean(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
6.7.4 생성자 오버로딩(Overloaging)
=>생성자 다양화해야 하는 이유
객체 생성할 때 외부 값으로 객체를 초기화할 필요
외부 값이 어떤 타입으로 몇 개가 제공될 지 모름 - 생성자도 다양화
=>생성자 오버로딩(Overloading)
매개변수의 타입, 개수, 순서가 다른 생성자 여러 개 선언
Car클래스에서 생성자를 오버로딩한 예:
생성자 오버로딩 시 주의할 점은 매개 변수의 타입과 개수 그리고 선언된 순서가 똑같을 경우 여러 변수 이름만 바꾸는 것은 생성자 오버로딩이라고 볼 수 없다.
생성자가 오버로딩되어 있을 경우 ,new 연산자로 생성자를 호출할 때 제공되는 매개값의 타입과수에 의해 생성자가 렬정된다.
[ Car2.java]생성자의 오버로딩
publicclass Car2 {
// 필드
String company = "현대자동차";
String model;
String color;
intmaxSpeed;
// 생성자
Car2() { // 기본생성자이지만, 다른 생성자들도 만들었으므로, 자동으로 생성X, 그래서 이 예제에서 명시적으로 만듬
생성자는 아래와 같이 public, protected, default, private 접근 제한을 가질 수 있습니다.
public class ClassName {
//public 접근 제한
public ClassName(...) { ... }
//protected 접근 제한
protected ClassName(...) { ... }
//default 접근 제한
ClassName(...) { ... }
//private 접근 제한
private ClassName(...) { ... }
}
클래스에 생성자를 선언하지 않았을 때 자동으로 선언되는 기본 생성자는 클래스와 같은 접근 제한을 가집니다.
접근 제한자
생성자
설명
public
클래스( ... )
public 접근 제한은 모든 패키지에서 아무런 제한 없이 생성자를 호출할 수 있도록 합니다. 생성자가 public 접근 제한을 가진다면 클래스도 public 접근 제한을 가지는 것이 정상적입니다. 클래스가 default 접근 제한을 가진다면 클래스 사용이 같은 패키지로 한정되므로, 비록 생성자가 public 접근 제한을 가지더라도 같은 패키지에서만 생성자를 호출할 수 있습니다.
protected
클래스( ... )
protected 접근 제한은 default 접근 제한과 마찬가지로 같은 패키지에 속하는 클래스에서 생성자를 호출할 수 있도록 합니다. 차이점은 다른 패키지에 속한 클래스가 해당 클래스의 자식(child)클래스라면 생성자를 호출할 수 있습니다.
default
클래스( ... )
생성자를 선언할 때 public 또는 private 를 생략 했다면 생성자는 default 접근 제한을 가집니다. default 접근 제한은 같은 패키지에서는 아무런 제한 없이 생성자를 호출할 수 있으나, 다른 패키지에서는 생성자를 호출할 수 없도록 합니다.
private
클래스( ... )
private 접근 제한은 동일 패키지이건 다른 패키지이건 상관없이 생성자를 호출하지 못하도록 제한합니다. 즉, 클래스 외부에서 new 연산자로 객체를 만들 수 없습니다. 오로지 클래스 내부에서만 생성자를 호출할 수 있고, 객체를 만들 수 있습니다.
//A a2 = new A(1); //default 다른 패키지이므로 default 생성자 호출 불가.
//A a3 = new A("문자열");//private 생성자이므로 접근 불가
}
가끔 전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우가 있다. 이럴 경우 여러 개의 객체를 만들지 못하도록 설계해야 하는데 이것을 싱글톤 패턴이라고 한다.
싱글톤 패턴은 생성자를 private접근제한으로 선언하고 ,자신의 유일한 객체를 리턴하는 getInstance() 정적 메소드를 선언하는 것을 말한다.
6.13.3 필드와 메소드의 접근 제한
필드와 메서드를 선언할 때 고려해야 할 사항은 클래스 내부에서만 사용할 것인지,
패키지 내에서만 사용할 것인지, 아니면 다른 패키지에서도 사용할 수 있도록 할 것인지를 결정해야 합니다.
이것은 필드와 메서드가 어떤 접근 제한을 갖느냐에 따라 결정됩니다.
필드와 메서드는 public, protected, default, private 접근 제한을 가질 수 있습니다.
//필드 선언
[ public | protected | private ] [static] 타입 필드;
//메소드 선언
[ public | protected | private ] [static] 리턴 타입 메소드( ... ) { ... }
접근 제한자
생성자
설명
public
필드
메소드 ( ... )
public 접근 제한은 모든 패키지에서 아무런 제한 없이 필드와 메소드를 사용할 수 있도록 해줍니다. 필드와 메소드가 public 접근 제한을 가질 경우 클래스도 public 접근 제한을 가져야 합니다. 클래스가 default 접근 제한을 가지게 되면 같은 패키지 안에서만 클래스가 사용되기 때문입니다.
protected
필드
메소드 ( ... )
protected 접근 제한은 default 접근 제한과 마찬가지로 같은 패키지에 속하는 클래스에서 필드와 메소드를 사용할 수 있도록 합니다. 차이점은, 다른 패키지에 속한 클래스가 해당 클래스의 잣기 클래스라면 필드와 메소드를 사용할 수 있습니다.
default
필드
메소드 ( ... )
필드와 메소드를 선언할 때 public 또는 private 를 생략했다면 default 접근제한을 가진다. default 접근 제한은 같은 패키지에서는 아무런 제한 없이 필드와 메소드를 사용할 수 있으나, 다른 패키지에서는 필드와 메소드를 사용할 수 없도록 한다.
private
필드
메소드 ( ... )
private 접근 제한은 동일 패키지이건 다른 패키지이건 상관 없이 필드와 메소드를 사용하지 못하도록 제한합니다. 오로지 클래스 내부에서만 사용할 수 있습니다.
어노테이션 자체는표식일 뿐이지만, 리플랙션을 이용해서 어노테이션의 적용 여부와 엘리먼트 값을 읽고 적절히 처리할 수 있습니다. 클래스에 적용된 어노테이션 정보를 얻으려면 java.jang.Class를 이용하면 되지만, 필드, 생성자, 메서드에 적용된 어노테이션 정보를 얻으려면 Class의 다음 메서드를 통해서 java.lang.reflect 패키지의 Field, Constructor, Method 타입의 배열을 얻어야 합니다.
[PrintAnnotation] 에노테이션 정의
@Target({ElementType.METHOD})//메소드에만 적용하도록 했고
@Retention(RetentionPolicy.RUNTIME)//런타임 시까지 어노테이션 정보를 유지하도록 했다
public@interfacePrintAnnotation {
String value() default"-";//기본 엘리먼트 value는 구분선에 사용될 문자
16. PrinterExample 클래스에서 Printer 객체를 생성하고 println() 메소드를 호출해서 매개값을 콘솔에 출력하려고 합니다. println() 메소드의 매개값으로는 int,boolean,double,String 값을 줄 수 있습니다. Printer 클래스에서 println() 메소드를 선언해보세요.
[Printer.java]
publicclass Printer {
//작성 위치
publicvoid println(inta){
System.out.println(a);
}
publicvoid println(booleana){
System.out.println(a);
}
publicvoid println(doublea){
System.out.println(a);
}
publicvoid println(Stringa){
System.out.println(a);
}
}
[PrinterExample.java]
publicclass PrinterExample {
publicstaticvoid main(String[] args) {
Printer printer = new Printer();
printer.println(10);
printer.println(true);
printer.println(5.7);
printer.println("홍길동");
}
}
17. 16번 문제에는 Printer 객체를 생성하고 println() 메소드를 생성했습니다. Printer 객체를 생성하지 않고 PrinterExample 클래스에서 다음과 같이 호출하려면 Printer 클래스를 어떻게 수정하면 될까요?
[Printer2.java]
publicclass Printer2 {
//작성 위치
publicstaticvoid println(inta){
System.out.println(a);
}
publicstaticvoid println(booleana){
System.out.println(a);
}
publicstaticvoid println(doublea){
System.out.println(a);
}
publicstaticvoid println(String a){
System.out.println(a);
}
}
[PrinterExample2.java]
publicclass PrinterExample2 {
publicstaticvoid main(String[] args) {
Printer2.println(10);
Printer2.println(true);
Printer2.println(5.7);
Printer2.println("홍길동");
}
}
18. ShopService 객체를 싱글톤으로 만들고 싶습니다. ShopServiceExample 클래스에서 ShopService의 getInstance() 메소드로 싱글톤을 얻을 수 있도록 ShopService 클래스를 작성 해보세요.
[ShopService.java]
publicclass ShopService {
privatestatic ShopService singleton = new ShopService();
private ShopService() {}
static ShopService getInstance() {
returnsingleton;
}
}
[ShopServiceExample.java]
publicclassShopServiceExample {
publicstaticvoid main(String[] args) {
ShopService obj1 = ShopService.getInstance();
ShopService obj2 = ShopService.getInstance();
if(obj1 == obj2) {
System.out.println("같은 ShopService 객체 입니다.");
}else {
System.out.println("다른 ShopService 객체 입니다.");
}
}
}
19. 은행 계좌 객체인 Account 객체는 잔고(balance) 필드를 가지고 있습니다. balance 필드는 음수값이 될 수 없고, 최대 백만 원까지만 저장할 수 있습니다. 외부에서 balance 필드를 마음대로 변경하지 못하도록 하고, 0 <= balance <= 1,000,000 범위의 값만 가질 수 있도록 Acoount 클래스를 작성해보세요.
1. Setter 와 Getter를 이용하세요.
2. 0 과 1,000,000은 MIN_BALANCE와 MAX_BALANCE 상수를 선언해서 이용하세요.
3. Setter의 매개값이 음수이거나 백만 원을 초과하면 현재 balance 값을 유지하세요.
byte, char, short, int, long, float, double, boolean을 이용해서 선언된 변수는 실제 값을 변수 안에 저장하지만,
2) 참조 타입인 객체(Object) 의 번지를 참조하는 타입으로
배열, 열거, 클래스, 인터페이스를 이용해서 선언된 변수는 메모리 번지를 값으로 갖는다.
번지를 통해 객체를 참조한다는 뜻에서 참조 타입이라고 부른다.
변수가 스택 영역에 생성되고, 객체는 힙 영역에 생성된다
메모리상에서 이 변수들이 갖는 값을 그림으로 표현하면 오른쪽 그림이다.
변수가 스택 영역에 생성되고 객체는 힙 영역에 생성되는 것이다.
int와 double 변수인 age와 price는 직접 값을 저장하고 있지만 ,String클래스 변수인 name과 hobby는 힙 영역의 String 객체 주소 값을 가지고 있다. 주소를 통해 객체를 참조한다는 뜻에서 String 클래스 변수를 참조 타입 변수라고 한다.
5.2 메모리 사용 영역
JVM이 사용하는 메모리 영역
java.exe로 JVM은 운영체제에서 할당받은 메모리 영역(Runtime Data Area)을 다음과 같이 세부 영역으로 구분해서 사용한다.
5.2.1 메소드(Method) 영역
=>메소드 영역
메소드 영역에는 코드에서 사용되는 클래스(.~class)들을 클래스 로더로 읽어 클래스별로 런타임 상수풀(runtime constant pool),필드(field) 데이터, 메소드(Method) ,메소드 코드 , 생성자(constructor)코드 등을 분류해서 저장한다.
메소드 영역은 JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역이다.
JVM 시작할 때 생성
로딩된 클래스 바이트 코드 내용을 분석 후 저장
모든 스레드가 공유
=>힙 영역
객체와 배열이 생성되는 영역이다.
JVM 시작할 때 생성
객체/배열 저장
사용되지 않는 객체는 Garbage Collector 가 자동 제거
힙 영역에도 두가지가 있다.
=>JVM 스택
스레드 별 생성
추가적으로 스레드를 생성하지 않았담녀 main 스레드만 존재하므로 JVM스택도 하나이다.
메소드 호출할 때마다 Frame을 스택에 추가(push)
메소드 종료하면 Frame 제거(pop)
예외 발생시 printStackTrace()메소드로 보여주는 Stack Trace의 각 라인은 하나의 프레임을 표현한다. printStackTrace() 메소드는 예외 처리에서 설명한다.
프레임 내부에는 로컬 변수 스택이 있는데 , 기본 타입 변수와 참조 타입 변수가 추가 (push) 되거나 제거(pop)된다.변수가 이 영역에 생성되는 시점은 초기화가 될 때, 즉 최초로 변수에 값이 저장될 떄이다. 변수는 선언된 블록 안에서만 스택이 존재하고 블록을 벗어나면 스택에서 제거된다.
스택의 생성 및 소멸
기본 타입 변수는 스택 영역에 직접 값을 가지고 있지만 , 참조 타입 변수는 값이 아니라 힙 영역이나 메소드 영역의 객체 주소를 가진다.
2. switch문에서 사용할 수 있는 변수의 타입은 int , double이 될 수 있다.
3. for문은 카운터 변수로 지정한 횟수만큼 반복시킬 때 사용할 수 있다.
4. break문은 switch문, for문, while문을 종료할 때 사용할 수 있다.
2. switch(변수) {
정수,String
}
3. for문을 이용해서 1부터 100까지의 정수 중에서 3의 배수의 총합을 구하는 코드를 작성해보세요.
package sec4_example;
publicclass Exercise03 {
publicstaticvoid main(String[] args) {
int sum =0;
int i =0;
for(i=1;i<=100;i++) {
if(i%3==0) {
sum = sum+i;
}
}
System.out.println("3의배수의합 :"+ sum);
}
}
3의배수의합 :1683
4. while문과 Math.random() 메소드를 이용해서 두 개의 주사위를 던졌을 때 나오는 눈을 (눈1,눈2) 형태로 출력하고, 눈의 합이 5가 아니면 계속 주사위를 던지고, 눈의 합이 5이면 실행을 멈추는 코드를 작성해보세요. 눈의 합이 5가 되는 조합은 (1,4) , (4,1) , (2,3) , (3,2) 입니다.
package sec4_example;
publicclass Exercise04 {
publicstaticvoid main(String[] args) {
while (true) {
int num1 = (int) (Math.random()*6) +1;
int num2 = (int) (Math.random()*6) +1;
if(num1 + num2 ==5) {
System.out.println("("+ num1 +","+ num2 +")");
break;
}
else {
System.out.println("("+ num1 +","+ num2 +")");
}
}
}
}
(4,4)
(1,3)
(3,5)
(6,2)
(1,6)
(4,6)
(4,2)
(4,6)
(5,6)
(3,6)
(4,3)
(4,4)
(5,6)
(3,1)
(2,1)
(1,2)
(1,4)
5. 중첩 for문을 이용하여 방정식 4x + 5y = 60의 모든 해를 구해서 (x,y) 형태로 출력해보세요.
단, x와 y는 10 이하의 자연수입니다.
package sec4_example;
publicclass Exercise05 {
publicstaticvoid main(String[] args) {
for(int x=0;x<=10;x++) {
for(int y=0;y<=10;y++) {
if(4*x+5*y==60) {
System.out.println("("+ x +","+ y +")");
}
}
}
}
}
(5,8)
(10,4)
6. for문을 이용해서 실행 결과와 같은 삼각형을 출력하는 코드를 작성해보세요.
package sec4_example;
publicclass Exercise06 {
publicstaticvoid main(String[] args)
{
for(int i=1;i<=5;i++)
{
for(int j=1;j<=i;j++)
{
System.out.print("*");
}
System.out.println();
}
}
}
*
**
***
****
*****
7. while문과 Scanner를 이용해서 키보드로부터 입력된 데이터로 예금, 출금, 조회, 종료 기능을 제공하는 코드를 작성해보세요. 이 프로그램을 실행시키면 다음과 같은 실행 결과가 나와야 합니다.
조건문과 제어문에서 사용되고 두가지 상태 (true/false)를 번갈아가면 변경하는 토글(toggle)기능을 구현할 때도 주로 사용한다.
논리 부정 연산자
[DenyLogicOperatorExample.java] 논리 부정 연산자
publicclass DenyLogicOperatorExample {
publicstaticvoid main(String[] args) {
booleanplay = true;
System.out.println(play);
play = !play;
System.out.println(play);
play = !play;
System.out.println(play);
}
}
3.3.4 비트 반전 연산자(~)
정수타입( byte, short,int,long)의 피연산자에만 사용되며 , 피연산자를 2진 수로 표현했을 때 비트값은 0을 1로 , 1은 0으로 반전한다. 연산후 , 부호 비트인 치상위 비트를 포함해서 모든 비트가 반전되기 때문에 ,부호가 반대인 새로운 값이 산출된다.
피연산자는 연산을 수행하기 전에 int타입으로 변환되고 , 비트 반전이 일어난다.
byte v1 =10;
byte v2 = ~v1;//컴파일 에러
=>
byte v1 =10;
int = ~v1;
비트 반전 연산자의 결과를 이용하면 부호가 반대인 정수를 구할 수 도 있다.
byte v1 = 10;
int v2 = ~v1 + 1;//-10이 v2에 저장
자바는 정수값을 총 32비트의 이진 문자열로 리턴하는 Integer.toBinaryString()메소드를 제공한다.
String타입의 문자열을 비교할 때는 대소 (<, <= , > , >=)연산자를 사용할 수 없고 , 동등(== , != ) 비교 연산자는 사용할 수 있으나 같은지 ,다른지를 비교하는 용도로는 사용되지 않는다.
String strVar1 = "신용권";
String strVar2 = "신용권";
String strVar3 = new String("신용권");
자바는 문자열 리터럴이 동일하다면 동일한 String 객체를 참조하도록 되어 있다. strVar1과 strVar2는 동일한 String객체의 번지값을 가지고 있다. 그러나 strVar2은 객체 생성 연산자인 new로 생성한 새로운 String객체의 번지값을 가지고 있다.
strVar1 == strVar2 ->true
strVar2 == strVar3 ->false
equals()메소드는 원본 문자열과 매개값으로 주어진 비교 문자열이 동일한지 비교한 후 true또는 false를 리턴한다.
boolean result = str1.equals(str2);
아래 것으로 변경=>
strVar1.equals(strVar2)
strVar2.equals(strVar3)
[ StringEqualsExample.java] 문자열 비교
publicclass StringEqualsExample {
publicstaticvoid main(String[] args) {
String strVar1 = "신민철";
String strVar2 = "신민철";
String strVar3 = new String("신민철");
System.out.println(strVar1 == strVar2);
System.out.println(strVar1 == strVar3);
System.out.println();
System.out.println(strVar1.equals(strVar2));
System.out.println(strVar1.equals(strVar3));
}
}
3.4.4 논리 연산자 (&&, || , & , |, ^, !)
논리곱(&&), 논리합(||), 배타적 논리합(^) ,논리 부정(!) 연산 수행
피연산자는 boolean 타입만 사용 가능
&& 는 앞의 피연산자가 false라면 뒤의 모든 피연사자를 평가하지 않고 바로 false라는 산출 결과를 낸다.
그러나 &는 두 피연사자 모두를 평가해서 산출 결과를 낸다.
|| 는 앞의 피연산자가 true라면 뒤의 모든 피연사자를 평가하지 않고 바로 true라는 산출 결과를 낸다.
그러나 |는 두 피연사자 모두를 평가해서 산출 결과를 낸다.
[ LogicalOperatorExample.java]논리 연산자
publicclass LogicalOperatorExample {
publicstaticvoid main(String[] args) {
intcharCode = 'A';
if ((charCode >= 65) & (charCode <= 90)) {
System.out.println("대문자이군요");
}
if ((charCode >= 97) && (charCode <= 122)) {
System.out.println("소문자이군요");
}
if (!(charCode < 48) && !(charCode > 57)) {
System.out.println("0~9 숫자이군요");
}
intvalue = 6;
if ((value % 2 == 0) | (value % 3 == 0)) {
System.out.println("2 또는 3의배수이군요");
}
if ((value % 2 == 0) || (value % 3 == 0)) {
System.out.println("2 또는 3의배수이군요");
}
}
}
3.4.5 비트 연산자(&, |, ^, ~, <<, >>, >>>)
비트(bit) 단위로 연산 하므로 0과 1이 피연산자
0과 1로 표현이 가능한 정수 타입만 비트 연산 가능
실수 타입인 float과 double은 비트 연산 불가
종류
•비트 논리 연산자(&, |, ^, ~)
•비트 이동 연산자(<<, >>, >>>)
=>비트 논리 연산자(&, |, ^, ~)
피 연산자가 boolean타입일 경우 일반 논리 연산자
피연산자가 정수 타입일 경우 비트 논리 연산자로 사용
비트 연산자는 피연산자를 int타입으로 자동 타입 변환 후 연산 수행
45와 25 관련 계산
byte num1 = 45;
byte num2 = 25;
byte result = num1 & num2 ; //컴파일 에러 => int result = num1 & num2;
==>고급언어: 컴퓨터와 대화할 수 있도록 만든 언어 중에서 사람이 쉽게 이해할 수 있는 언어
고급언어로 작성된 소스는 컴퓨터가 바로 이해할 수 없기 때문에 컴파일 과정을 통해서 컴퓨터가 이해할 수 있는 0과 1로 이루어진 기계어로 변환한 후 컴퓨터가 사용하게 됩니다.
==>저급언어: 기계어에 가까운 언어를 말하는데, 대표적으로 어셈블리어가 저급 언어에 속한다.
저급언어는 사람이 쉽게 이해할 수 없기 때문에 배우기가 매우 까다롭다.
대표적인 프로그래밍 언어 c , c++, java
이 언어들로 작성된 내용을 소스라고 부르고, 이 소스는 컴파일러라는 소프트웨어에 의해 기계어로 변환된 후 컴퓨터에서 실행할 수 있게 된다.
프로그램이란 컴퓨터에서 특정 목적을 수행하기 위해 프로그래밍 언어로 작성된 소스를 기계어로 번역한 것을 말한다.
1.2 자바란?
1.2.3 자바 소개
1995년 썬 마이크로시스템즈
자바는 1991년에 썬의 엔지니어들에 의해서 고안된 오크(Oak)라는 언어에서부터 시작되었다.
오크는 가전제품에서 사용될 목적이었다.
자바는 메모리 및 cpu를 지나치게 많이 사용학 때문에 윈도우 프로그래밍 언어로는 부적합하다는 문제점이 있다. 하지만 1999년부터 인터넷이 활성화되면서 웹 애플리케이션 구축용 언어로 자바가 급부상해다.
금융, 공공, 대기업 등의 엔터프라이즈 기업 환경에서 실행되는 서버 애플리케이션을 개발하는 중추적인 언어로 자리매김하고 있다.
1.2.2 자바의 특징
=>이식성이 높은 언어이다.
다른 실행 환경을 가진 시스템 간에 프로그램을 옮겨 실행할 수 있는 것을 말한다.
자바는 자바 실행 환경(jre:java runtime environment)이 설치되어 있는 모든 운영체제에서 실행 가능하다.
=>객체지향 언어이다.
OOP(Object Oriented Programming)
캡슐화,상속, 다형성 기능
=>함수적 스타일 코딩을 지원한다.
람다식을 자바 8부터 지원한다.
=>메모리를 자동으로 관리한다.
c++은 메모리에 생성된 객체를 제거하기 위해 개발자가 직접 코드를 작성해야 한다. 잘 하지 않을 경우 프로그램이 다운되는 현상을 겪게 된다.
java는 개발자가 직접 메모리에 접근할 수 없도록 설계되었으며 , 메모리는 자바가 직접 관리한다.
객체 생성 시 자동으로 메모리 영역을 찾아서 할당하고 ,사용이 완료되면 쓰레기 수집기(Garbage Collector)를 실행시켜 자동적으로 사용하지 않는 객체를 제거시켜준다.
=>다양한 애플리케이션을 개발할 수 있다.
Java SE(Starndard Edition) - 기본 에디션
Java SE는 자바 프로그램들이 공통적으로 사용하는 자바 가상 기계(JVM: Java Virtual Machine)를 비롯해서 자바 프로그램 개발에 필수적인 도구와 라이브러리 API를 정의한다. 클라이언트와 서버 프로그램에 상관없이 자바 프로그램을 개발하기 위해서는 반드시 Java SE 구현체인 자바 개발 키트(Java Development Kit)를 설치해야 한다.
Java EE(Emterprise Edition) - 서버용 애플리케이션 개발 에디션
분산 환경(네트워크 ,인터넷)에서
서버용 애플리케이션
분산 처리 컴포넌트인 EJB(Enterprise Java Bean )그리고 XML 웹 서비스 등
=>멀티 스레드를 쉽게 구현할 수 있다.
하나의 프로그램이 동시에 여러 가지 작업 처리
=>동적 로딩(Dynamic Loading)을 지원한다.
객체가 필요한 시점에 클래스를 동적 로딩해서 객체를 생성한다. 유지보수를 쉽게 빠르게 진행 할 수 있다.
=>막강한 오픈소스 라이브러리가 풍부하다.
1.2.3 자바 가상 기계(JVM)
자바 프로그램은 완전한 가계어가 아닌 ,중간 단계의 비이트 코드
JVM은 운영체제에 종속적이다.
1.3 자바 개발 환경 구축
1.3.1 자바 개발 도구 (JDK)설치
JDK (Java Development Kit) : 자바 개발 키트 : 프로그램 개발에 필요한 자바 가상 기계 , 라이브러리 API,컴파일러 등의 개발 도구가 포함
JRE (Java Runtime Environment) : 자바 실행 환경 : 프로그램 실행에 필요한 자바 가상 기계(JVM). 라이브러리 API만 포함되어있다.