Untitled

[관계형 데이터베이스] - ER 다이어그램 (Entity-Relationship Diagram) 본문

데이터베이스

[관계형 데이터베이스] - ER 다이어그램 (Entity-Relationship Diagram)

Bool 2016.12.10 01:42
1. 개요

ER 다이어그램은 ER 모델 (Entity-Relationship Model)을 기반으로 데이터베이스의 논리적 구조를 표현하기 위한 도구이다. ER 다이어그램에는 ER 모델과 같이 개체 (entity), 속성 (property), 관계 (relationship)가 존재한다.


2. 구성 요소

ER 다이어그램을 구성하는 각 구성 요소들의 기호는 아래의 [그림 1]과 같다.


[그림 1] ER 다이어그램의 구성 요소 및 기호


위의 [그림 1]에 나타낸 기호 이외에도 total participation은 double line으로, subtype과 supertype 관계는 supertype에서 subtype으로 향하는 화살표로 나타낸다.


3. ER 다이어그램의 변환

ER 다이어그램은 데이터베이스를 설계하기 위한 도구이기 때문에 ER 다이어그램으로 작성된 것을 실제 데이터베이스로 변환하는 작업이 필요하다. 다음은 ER 다이어그램을 구성하는 요소들을 실제 데이터베이스로 어떻게 변환하는지 보여준다.


  • 개체 (entity): 개체가 regular entity인 경우에는 base table (base relation)로 변환한다. Table의 key는 ER 다이어그램에서 정의된 key attribute를 이용한다.
  • many-to-many relationship: 이러한 관계는 base table로 변환한다. 두 개체를 각각 base table로 정의할 수도 있고, 두 개체가 연관된 relationship 자체를 base table로 정의할 수도 있다.
  • one-to-many relationship: 이러한 관계는 따로 table로 변환할 필요가 없다. 단순히 many 쪽에 해당하는 개체에 foreign key를 추가하기만 하면 된다.
  • weak entity: 개체를 정의하기 위해 외부에서 가져온 속성을 foreign key로 설정한다. 그 다음, weak entity의 key와 foreign key를 조합하여 primary key를 생성한다.
  • 속성 (attribute): 속성에 해당하는 요소들은 테이블의 필드로 변환된다.
  • subtype과 supertype: subtype과 supertype에 해당하는 개체는 동일한 primary key를 갖는다. 또한, subtype에서 primary key는 foreign key의 역할도 한다.


안드로이드/Android intent(인텐트)를 사용해 보자. (intent filter)


안녕하세요. 오늘은 암시적 인텐트에 대해서 알아볼텐데요.! 인텐트는 안드로이드 시스템에서 커뮤니케이션을 담당하는 역할로 컴포넌트간(Activity, ContentProvider, BroadcastReceiver, Service) 의 호출과 메시지 전달에 이용한다. 쉽게 말해서 값을 주고 받는 전달자 정도로 생각 하시면 됩니다.


안드로이드에서는 시스템을 구성하는 4가지 컴포넌트가 있는데요. Activity(화면구성), BroadCastReceiver(방송수신), Service(백단작업), ContendProvider(Data공유) 들이 서로 유기적으로 시스템을 구성하고 있습니다. 그런데 이 4가지를 사용하면서, 서로 호환이 안된다면, 프로그램을 구성하는데 있어서 엄청난 제약을 받게 됩니다.


그렇기 때문에 intent를 이용해서 4가지 컴포넌트들이 서로 호출, 메시지, Data등을 주고받는 역할을 한다고 생각 하시면 됩니다. 


intent를 사용하는 방법에는 2가지 방법이 존재하는데요, 명시적 intent와 암시적 intent가 존재 합니다. 명시적 인텐트는 말 그대로 intent를 받는 대상을 명시해주는 방법이구요. 암시적 intent는 intent를 받는 대상을 명시해 놓지 않고, 호출 하는 방법 입니다.


보통 우리가 흔히 쓰는 방법은 명시적 intent인데요. 다음과 같이 사용 하시면 됩니다.

 
 Intent i = new Intent(FirstrActivity.this, SecondActivity.class);

 startActivity(i);


보시는 바와 같이, intent가 "SecondActivity라는 Class를 호출해라" 라고 명시 해놓고 호출 하고 있습니다. 이렇게 사용하는 방법이 명시적 intent 입니다. 이렇게 명시를 해준다면, intent 역시 정해진 곳 만 호출을 하게 됩니다.


다음은 암시적 intent를 사용하는 방법 입니다.

 
 Intent i = new Intent("arabiannight.tistory.com.intentfilter.secondview");

 startActivity(i);


암시적 intent를 사용하는 방법은 명시적 intent 처럼 지정된 곳을 호출 하는게 아니라 intent에 (action)을 추가해서 보내고 싶은 명령을 보내는 것 입니다. 물론 보내고 싶은 명령을 받아야 할 곳에는 intent에 추가해서 보낸 (action) 값을 추가해 주어야 합니다. 



이미지 출처 : 커니의 안드로이드(http://androidhuman.tistory.com/262)



위의 그림과 같이 무수히 많은 intent들이 다른 컴포넌트들에게 메시지를 보내지만, 호출을 받는 컴포넌트의 intent filter에 호출한 intent의 action 값이 정의 되어 있어야 통과 할 수 있습니다. 그래야 해당 intent를 컴포넌트에 전달하게 되고, 해당 컴포넌트는 intent에 실려온 메시지를 받아 작업을 수행 할 수 있게 되는 것 입니다.


 자 다음은 암시적 intent와 intent filter를 사용한 예제를 만들어 보겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package arabiannight.tistory.com.intentfilter;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
 
public class FirstrActivity extends Activity {
     
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.first);
         
        Button button = (Button) findViewById(R.id.btn_call);
        button.setOnClickListener(new View.OnClickListener() {
             
            @Override
            public void onClick(View v) {
                Intent i = new Intent("arabiannight.tistory.com.intentfilter.secondview");
                startActivity(i);
                 
            }
        });
    }   
}


위의 예제에서 보는 것처럼 new Intent(String action) 값을 사용자 임의로 추가해서 호출 할 수 있습니다. 또한 시스템에서 제공하는 "Intent.ACTION_VIEW" 같은 action 값도 사용 할 수 있습니다. 

1
2
Intent i = new Intent("arabiannight.tistory.com.intentfilter.secondview");
startActivity(i);



"arabiannight.tistory.com.intentfilter.secondview" action 을 추가하게 되면, 다른 Activity(컴포넌트)에서 intent filter에 action값으로 "arabiannight.tistory.com.intentfilter.secondview" 를 등록하게 되면, 해당 intent를 호출 할 때, intent filter를 지정한 Activity에서는 메시지를 전달 받을 수 있게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--?xml version="1.0" encoding="utf-8"?-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="arabiannight.tistory.com.intentfilter" android:versioncode="1" android:versionname="1.0">
 
    <uses-sdk android:minsdkversion="7">
 
    <application android:icon="@drawable/ic_launcher" android:label="@string/app_name">
        <activity android:label="@string/app_name" android:name=".FirstrActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN">
 
                <category android:name="android.intent.category.LAUNCHER">
            </category></action></intent-filter>
        </activity>
         
        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="arabiannight.tistory.com.intentfilter.secondview">
                <category android:name="android.intent.category.DEFAULT">
            </category></action></intent-filter>
        </activity>
         
    </application>
 
</uses-sdk></manifest>



여기서 주의해야할 사항이 있는데요, 위의 AndroidManifest.xml을 보게되면, intent filter에 <action> 말고도 <category>가 추가된 것을 볼 수 있을텐데요, 왜냐하면 intent로 Activity를 호출할 경우에는 DEFAULT category 를 반드시 추가 해야 하기 때문입니다. 그래서 intent filter에 DEFAULT category를 추가한 모습을 볼 수 있습니다.


반대로 intent를 보낼때에는 intent action만 추가한다면, 기본적으로 "android.intent.category.DEFAULT" category가 추가되서 보내 지기 때문에, action값만 설정해 주시면, 자동으로 DEFAULT category가 추가되서 보내 지게 됩니다.


아래는 암시적 intent로 호출한 화면 입니다.


이렇게 호출 하게 되면, intent filter에 action을 추가한 Activity가 호출 되게 됩니다. 다음은 다른 APP의 Activity에 intent filter에 추가한 경우 입니다.


다른 APP의 Activity의 경우에도 intent filter에 해당 action을 추가 하게 되면, intent를 호출시 실행 하게 됩니다. 위의 예제코드처럼 AndroidManifetst.xml에 파일에 intent filter를 추가해 주시면 됩니다.

 

다른 APP에 경우에도 해당 intent filter를 추가 하고, intent가 호출 됬을 경우 Activity가 실행 되게 됩니다. APP이 실행 중 이 아니더라도 단말에 설치가 되어있는 경우 에도 해당 intent를 받을 수 있습니다. 그렇기 때문에 해당 intent filter를 선언한 모든 Activity들이 호출 되게 됩니다.


다음은 TestIntentFliterReceiver APP의 AndroidManifest.xml 파일의 모습입니다.

 
 <activity

       android:name=".ForthActivity" >
        <intent-filter >
          <action android:name="arabiannight.tistory.com.intentfilter.secondview" />
          <category android:name="android.intent.category.DEFAULT" />
       </intent-filter>

 </activity>



자 이렇게 명시적 intent 와 암시적 intet 에 대해서 알아 보았습니다. 어떠신가요? ㅎ 참 intent란 재밌는 녀석인거 같다는 생각이 드시나요? ㅎㅎ 재 생각도 그렇습니다.


아무튼 intent에 대한 설명은 여기서 마치도록 하겠습니다. ^^





파일첨부 : 

 TestIntentFilter.zip



출처: http://arabiannight.tistory.com/entry/안드로이드Android-인텐트를-사용해-보자-intent-filter [아라비안나이트]

클래스 상속의 종류 중 public 상속과 private 상속에 대해 알아보자우선 일반적인 클래스 상속이라 하면

부모 클래스의 기능 중 필요한 일부를 하위 클래스가 계승한다. (물려 받는다.) 가 되겠다.

하나의 예를 들어보자상속 모델 개념 설명에 흔하게 사용되는 조류를 예로 들어보겠다.

다음과 같이 Bird라는 클래스가 있다.

  1. class Bird  
  2. {       
  3. public:  
  4.         virtual void fly()=0;  
  5.         virtual void walk()=0;  
  6. protected:  
  7. };  

 

대부분 새는 날 수도 걸을 수도 있다물론 아닌 새도 있다하지만 이럴 때에 대해선 우선 넘어가도록 하자.

다음으로 독수리라는 클래스를 만들려고 한다독수리는 새의 일종이므로 Bird 클래스를 상속받으면 될 것 같다.

  1. class Eagle : public Bird  
  2. {  
  3. public:  
  4.         void fly() {};  
  5.         void work() {};  
  6. };  

 

자 이제 독수리 클래스는 자신만의 나는 방법(fly)과 걷는 방법(work)을 구현할 것이고 객체는 Bird의 일종으로 취급될 것이다.

이처럼 public 상속은 is-a 관계가 만족 돼야 한다 Eagle is Bird 관계가 성립된다코드 상으로 표현하면

  1. Eagle *pEagle = new Eagle();  
  2. Bird *pBird = pEagle;      // 독수리는 새의 일종이므로 가능함.  

 

 

하지만 모든 새는 독수리가 아니므로 반대는 성립되지 않는다. (Bird is not Eagle)

  1. Eagle eagle;  
  2. Bird *pBird = &eagle;     // 독수리는 새의 일종이므로 가능하다.  
  3. Eagle *pEagle = pBird;    // 모든 새는 독수리가 아니므로 불가능하다.  

 

 

이처럼 public 상속은 반드시 is-a 관계가 성립 돼야 한다단순히 부모 클래스의 기능 중 일부가 필요해(is-a가 아니지만) public 상속을 하려 한다면 다른 방법(?)을 생각해 봐야겠다여기서 다시 public 상속을 마무리하기 전에 왜 public 상속이 필요할까?의 좀 더 기초적인 문제로 들어가 보자 Eagle과 위에는 표시되지 않았지만, Duck이라는 클래스도 있을 수 있고 기타 등등의 여러 조류종류가 있겠다이들 각각은 왜 Bird에서 public 상속을 받아야만 할까그냥 각자의 클래스로 구현 돼도 상관없을 텐데… 라는 생각이 든다면 코드의 재활용과 객체 지향적 구현이라고 대답할 수 있겠다위에서 보았듯 부모 클래스의 포인터 즉 Bird *pBird는 모든 종류의 자신의 자식 클래스의 포인터를 받을 수 있다.

  1. pBird = &eagle;  
  2. pBird = &duck;  

 

 

이게 뭐 어떻다는 건가? 라는 의문은 다음의 함수로 없앨 수 있을 것 같다.

(이 함수는 클래스 멤버 함수가 아니며 Bird형의 클래스 객체들을 사용하는 외부에 선언된 함수다.)

  1. // 인자로 입력된 새 객체를 걸은 후 날게 한다.  
  2. void flyAndWolk(Bird *pBird)  
  3. {  
  4.            pBird->walk();    // 걸은 후  
  5.            pBird->fly();       // 난다(물론 duck은 날지 못하지만 여기선 pass)  
  6. }  

 

위 함수는 모든 Bird로부터 public 상속된 객체에 대해 사용 가능하다.

만약 상속되지 않은 Eagle Duck을 위해 flyAndWolk(…)를 구현 해야 한다면?

public 상속의 아주 추상적이고 단편적인 사용 예를 들었지만 실제로는 상속을 받음으로써 구현이 편리해지고 각종 디자인 패턴 등을 적용할 수 있다하지만 위의 예로 public 상속이 어떤 경우에 사용 돼야 하는지 감이 올 것으로 생각한다.

다음으로 private 상속에 대해 알아보자.

 

위에서 public상속을 설명하면서 중간에 다른 방법(?)을 생각해 봐야겠다.”라고 말한 게 있다.

그 경우에 다른 방법을 생각해 봐야 하는 게 바로 private 상속이 되겠다.

우선 private 상속의 개념을 간단히 정리해 보자면 부모(?) 클래스의 public protected 영역이

자식 클래스의 private 멤버가 된다정도로 정리할 수 있을 것 같다.

이게 무슨 의미 일까곰곰히 생각해 보자

  1. class B : private A  
  2. {  
  3. };  
  4. void main()  
  5. {  
  6.     B b;  
  7.     A *pA = &b; // 불가능 하다.  
  8. }  

 

이처럼 public 상속에서 가능하던 게 되지 않으므로 확실히 is-a 관계는 아닌 것으로 보인다.

그럼 무슨 관계냐. Is-implemented-in-terms-of 의 관계가 되겠다즉 부모 클래스의 일부로 자식 클래스를 구현 한다의 개념이 아닌 자식 클래스 내에서 부모 클래스의 기능중 일부를 사용 하겠다 가 되겠다.

그럼 여기서 예를 들어보겠다이번에는 TV와 리모컨의 관계를 사용해 보려 한다.

  1. class Tv  
  2. {  
  3. public:  
  4.         void upChannel(){…};  
  5.         void downChannel(){…};  
  6.   
  7. protected:  
  8.         int m_curChennel;  
  9. };  

 

이번에는 완전한 동작을 하는 Tv부모 클래스다. (public 모델 설명에서 사용된 Bird 같이 virtual 클래스가 아니다이 부분도 일단 pass)

요즘 출시되는 Tv는 모르겠지만, 일반적으로 Tv 본체로 채널을 돌리기 위해선 up/down으로 조절을 해야 한다 1번에서 10번 채널로 바로 갈 수 없다너무 불편하므로 리모컨을 만들어야겠다.

  1. class RemoteCon : private Tv  
  2. {  
  3. public:  
  4.         void changeChennel(int nChennel);  
  5. };  

 

Private 상속을 받았다이제 private 상속의 정의에 나왔듯 Tv클래스의 두 멤버함수 upChannel, downChannel과 멤버변수 m_curChennel는 내 것이 된다.

Public 상속을 받아도 똑같지 않느냐? 라는 의문이 드는 사람은 is-a 관계를 다시 한번 떠올려 보자.

그럼 이제 답답하게 채널을 한 칸 위, 한 칸 아래로 조절하는 게 아닌 한번에 원하는 채널로 이동할 수 있는 리모컨의 changeChennel() 함수를 구현 해보자.

  1. void RemoteCon::changeChennel(int nChennel)  
  2. {  
  3.         while(m_curChennel != nChennel)  
  4.         {  
  5.                if(m_curChennel < nChennel)  
  6.                        upChannel();  
  7.                else  
  8.                        downChannel();  
  9.         }  
  10. }  

 

대충이런 모양이 될 듯(?)하다지금은 구현 부가 중요한 건 아니니 그냥 넘어가겠다m_curChennel에 바로 nChennel를 대입하지 않은 이유는 내부적으로 채널 변경에 핵심 동작이 upChannel() 과 downChannel() 내에 구현되 있다는 가정하에 구현됐기 때문이다.

이처럼 Tv 클래스를 이용해 리모컨 객체를 구현하게 됐다.

하지만 대부분 private 상속은 심사숙고를 해본 후 사용하는 게 좋다.

이유는 아래와 같은 몇 가지 단점이 존재하기 때문이다.

 

1. 하나의 객체가 다수 객체를 포함하는 개념을 표현해야 할 경우

위 TV와 리모컨의 예에서 리모컨이 두 이상의 TV를 지원 한다고 가정할 동일 클래스를 두 상속받을 수 없으므로 표현할 수 가 없다.

만약 이를 위해 TV2라는 클래스를 만들어 TV와 TV2를 다중 private상속 하도록 한다면 마찬가지로 TV2의 부모가 TV이므로 인터페이스가 충돌하여 이런 문제를 피할 수 없다.

 

2. 하위 객채를 더 확장시킬 때

마찬가지로 위 TV와 리모컨의 예에서 RemoteCon객체의 기능 확장을 위해 이를 부모로 RemoteCon2를 생성했다고 해보자.

이 경우 RemoteCon2는 TV클래스의 protected멤버와 심지어 public멤어 또한 사용할 수 없게 된다. private 상속의 특성상 해당 멤버들은 이미 RemoteCon클래스가 상속하는 과정에서 RemoteCon클래스의 private멤버로 가려졌기 때문이다.

 

하지만 Is-implemented-in-terms-of라는 다소 복잡해 보이는 관계가 대부분 has-a라는 방법으로 구현할 수 있다.

또한 이 방법을 사용할 경위 위의 문제점 들을 간단하게 해결 할 수 있다.

 

 Remocon 클래스에서 멤버 변수로 Tv객체 하나를 갖게 한다.(상속이 아니다.)

내부에서 changeChennel()함수를 구현하기 위해서는 Tv클래스의 두 public 함수인

  1. void upChannel();  
  2. void downChannel();  

 

 

를 이용한면 된다.

구현은 다음과 같이 바뀔 것이다.

  1. class RemoteCon  
  2. {  
  3. public:  
  4.         void changeChennel(int nChennel);  
  5.   
  6. private:  
  7.   
  8.         m_Tv;   // tv 객체  
  9. };  
  10.   
  11. void RemoteCon::changeChennel(int nChennel)  
  12. {  
  13.         while(m_Tv.getCurChennel() != nChennel)  
  14.         {  
  15.                if(m_Tv.getCurChennel()  < nChennel)  
  16.                        m_Tv.upChannel();  
  17.                else  
  18.                        m_Tv.downChannel();  
  19.         }  
  20. }  

 

위와 이 상속 개념이 아닌 소유의 개념이므로 객체를 통해서 함수가 호출되도록 변경됐다.

또한,  비 상속 관계에서는 protecte또는 private 영역에 접근이 불가능하므로 현재 채널 정보를 가진 m_curChennel 변수를 대신할 int getCurChennel() 함수가 Tv클래스 쪽에서 구현이 돼야 한다.

 

Private 상속과 has-a 관계 외에도 비슷한 목적을 위한 기법이 몇 개 있지만 private상속에 대한 설명은 여기까지 충분한 것으로 보인다이처럼 상속구조보다는 has-a관계로 객체 형식으로 사용하는 게 내부에서 구현을 할 때에도 이 함수는 어떤 어느 객체의 함수인지가 명확해지므로 코드 가독성이 높아지는 효과도 있다.

 

Serializable 과 transient


(1) Serializable


데이터를 파일에 쓰거나, 네트워크를 타고 다른 곳에 전송할 때는 데이터를 바이트 단위로 분해하여 순차적으로 보내야 한다. 이것을 직렬화(Serialization)라고 한다.


기본 자료형(boolean, char, byte, short, int ,long, float, double)은 정해진 바이트의 변수이기 때문에 바이트 단위로 분해하여 전송한 후 다시 조립하는데 문제가 없다.


하지만 객체의 크기는 가변적이며, 객체를 구성하는 자료형들의 종류와 수에 따라 객체의 크기는 다양하게 바뀔 수 있다. 이런 객체를 직렬화 하기 위해서 Serializable 인터페이스를 구현하게 된다.


[JAVA] 객체 직렬화 ObjectInputStream / ObjectOutputStream


* 직렬화가 가능한 객체의 조건


① 기본형 타입(boolean, char, byte, short, int, long, float, double)은 직렬화가 가능

② Serializable 인터페이스를 구현한 객체여야 한다. (Vector 클래스는 Serializable 인터페이스구현)

③ 해당 객체의 멤버들 중에 Serializable 인터페이스가 구현되지 않은게 존재하면 안된다.

④ transient 가 사용된 멤버는 전송되지 않는다. (보안 변수 : null 전송)


객체 직렬화는 객체에 implements Serializable 만 선언해 주면 된다.


(2) transient


하지만, 객체의 데이터 중 일부의 데이터는(패스워드와 같은 보안) 여러가지 이유로 전송을 하고 싶지 않을 수 있다. 이러한 변수는 직렬화에서 제외해야 되며, 이를 위해서 변수에 transient를 선언한다.


또한, 직렬화 조건 중 객체의 멤버들 중에 Serializable 인터페이스 구현되지 않은 객체가 있으면, 직렬화 할 수 없다.(NonSerializableException) 직렬화 해야 되는 객체 안의 객체 중 Serializable 인터페이스가 구현되지 않으면서 전송하지 않아도 되는 객체 앞에는 transient 를 선언해준다. 그러면 직렬화 대상에서 제외되므로 해당 객체는 직렬화가 가능해진다.


* Serializable 과 transient 사용 예제


UserClass.java

 


import java.io.Serializable;

// 직렬화 한다.
public class UserClass implements Serializable{
	
	private static final long serialVersionUID = 4220461820168818967L;
	
	String name;
	
	// age 비 전송
	transient int age;
	
	// NonSerializable 클래스
	NonSerializableClass nonSerializable;
	
	
	public UserClass() {
		
	}
	
	public UserClass(String name, int age){
		
		this.name = name;
		this.age = age;
		
		this.nonSerializable = new NonSerializableClass(false);
		
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	

	public NonSerializableClass getNonSerializable() {
		return nonSerializable;
	}

	public void setNonSerializable(NonSerializableClass nonSerializable) {
		this.nonSerializable = nonSerializable;
	}

	@Override
	public String toString() {
		return "UserClass [name=" + name + ", age=" + age
				+ ", nonSerializable=" + nonSerializable + "]";
	}
	
}


NonSerializableClass 보기


ObjectStream.java 보기



- UserClass.java 의 변수를 보면 transient int age; 로 age 변수는 직렬화에서 제외했다.

- NonSerializableClass 객체는 Serializable 인터페이스를 구현하지 않은 클래스이다.

- 따라서 UserClass.java 로 직렬화를 시도하면, 위와 같이 NonSerializableClass Exception이 발생한다.


- 위의 문제를 해결하기 위해서는 NonSerializableClass.java 에 Serializable 인터페이스를 구현하여 직렬화를 할 수 있게 하는 방법과

- NonSerializableClass 를 전송하지 않아도 되면, 또는 않아야 한다면 transient 를 앞에 붙여주는 것이다.

- 그러면 NonSerializableClass 객체는 직렬화 대상에서 제외되면서 UserClass 가 정상적으로 직렬화되어 처리될 것이다.


* NonSerializableClass 객체 선언 앞에 transient 선언 결과


 

import java.io.Serializable;

// 직렬화 한다.
public class UserClass implements Serializable{
	
	private static final long serialVersionUID = 4220461820168818967L;
	
	String name;
	
	// age 비 전송
	transient int age;
	
	// NonSerializable 클래스
	transient NonSerializableClass nonSerializable;
	
	
	public UserClass() {
		
	}
	
	public UserClass(String name, int age){
		
		this.name = name;
		this.age = age;
		
		this.nonSerializable = new NonSerializableClass(false);
		
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	

	public NonSerializableClass getNonSerializable() {
		return nonSerializable;
	}

	public void setNonSerializable(NonSerializableClass nonSerializable) {
		this.nonSerializable = nonSerializable;
	}

	@Override
	public String toString() {
		return "UserClass [name=" + name + ", age=" + age
				+ ", nonSerializable=" + nonSerializable + "]";
	}
	
}



- 객체가 정상적으로 직렬화되어 전송되고, 가져와 출력되는 것을 볼 수 있다.

- 당연히 transient가 붙은 age 변수와 nonSerializable 은 직렬화 되지 않기에 데이터가 없다.



상속받은 CheckingAccount 클래스의 슈퍼클래스인 Account 클래스에 생성자가 있었다면 어떻게 될까?

예)


public class Account {
 String accountNo;
 String ownerName;
 int balance;
 
 Account(String accountNo, String ownerName, int balance)
 {
  this.accountNo = accountNo;
  this.ownerName = ownerName;
  this.balance = balance;
 }
 
 void deposit(int amount){
  balance += amount;
 }
 
 int withdraw(int amount) throws Exception{
  if (balance < amount)
   throw new Exception("잔액이 부족합니다.");
  balance -= amount;
  return amount;
 }
}



public class CheckingAccount extends Account {
 String cardNo;
 
 CheckingAccount (String accountNo, String ownerName, int balance, String cardNo) {
  this.accountNo = accountNo;
  this.ownerName = ownerName;
  this.balance = balance;
  this.cardNo = cardNo;
 }
 
 int pay(String cardNo, int amount) throws Exception {
  if(!cardNo.equals(this.cardNo) || (balance < amount))
  {
   throw new Exception("지불이 불가능합니다");
  }
  else
   return withdraw(amount);
 }
}




컴파일 에러 발생

Implicit super constructor Account() is undefined. Must explicitly invoke another constructor 라는 에러메시지가 뜬다.

위의 에러 메시지는 Account()라는 생성자를 찾을 수 없다고 말하고 있습니다. Account 클래스 no-arg constructor(파라미터를 받지 않는 생성자)를 말하는 거죠. 그리고 이런 컴파일 에러가 CheckingAccount.java의 3행 마지막 부분, 다시 말해서 CheckingAccount 클래스의 생성자 본체가 시작되는 부분에서 발생했다고 표시 하고 있습니다.
왜 이런 컴파일 에러가 발생한 걸까?

이런 컴파일 에러가 발생한 이유는 자바 컴파일러가 컴파일을 할 때 생성자의 첫 번째 명령문이 슈퍼클래스의 생성자 호출문이 아니면 자동으로 슈퍼클래스의 no-arg constructor 호출문을 그 위치에 추가하기 때문 입니다. 그런데 Account 클래스에는 no-arg constructor가 없었기 때문에 컴파일 에러가 발생한 것입니다.

이런 에러가 발생하지 않도록 하려면 두 가지 방법이 있습니다. 첫 번째 방법은 Account 클래스에 no-arg constructor를 추가하는 것입니다. 하지만 객체지향 프로그래밍에서는 슈퍼클래스의 소스 코드를 건드리지 않고 상속받는 것을 원칙으로 하기 때문에 이런 방법은 해결책이라고 할 수 없습니다.

또 다른 방법은 CheckingAccount 클래스의 생성자 안에 슈퍼 클래스의 Account 클래스의 생성자 호출문을 명시적으로 써넣는 것입니다. 자바 컴파일러는 생성자 안에 슈퍼클래스의 생성자 호출문이 있으면 no-arg constructor 호출문을 추가하지 않기 때문입니다. 이 방법은 Account 클래스의 소스 코드를 건드리지 않고도 할 수 있는 방법이기 때문에 올바르 해결책이라고 할 수 있습니다.

그러면 이제 슈퍼클래스의 생성자 호출문 작성 방법을 배워보자.

서브클래스의 생성자 안에서 슈퍼 클래스의 생성자를 호출할 때는 슈퍼클래스의 이름 대신 super 키워드를 써야 합니다.
예를 들면..

super(accountNo, ownerName, balance);


※ 슈퍼클래스의 생성자 호출문은 반드시 생성자의 첫 번째 명령문이어야 합니다.


출처 : 뇌를 자극하는 JAVA 프로그래밍

'프로그램 > Java' 카테고리의 다른 글

public 상속과 private 상속에 대해  (0) 2017.03.20
[JAVA] Serializable 과 transient  (0) 2017.02.24
자바/Java 자바의 다형성이란?  (0) 2017.02.06

자바/Java 자바의 다형성이란?




객체지향 개념에서의 다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 의미하며, 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현 하였습니다.


이를 좀 더 구체적으로 말하자면, 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다는 것 입니다.


1) CarTest.java 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package arabiannight.tistory.com.java.test;
 
public class CarTest {
 
    public static void main(String[] args) {
 
        Car c = new FireEngine();
        Car c2 = new Ambulance();
 
        c.drive();
        c2.drive();
         
    }
}
 
class Car {
    String color;
    int door;
 
    void drive() { //운전하는 기능
        System.out.println("drive, brrrr~");
    }
 
    void stop() { // 멈추는 기능
        System.out.println("stop!!!");
    }
}
 
 
class FireEngine extends Car {  // 소방차
    void water() {              // 물 뿌리는 기능
        System.out.println("warter!!!");
    }
 
    @Override
    void drive() {
        super.drive();
        System.out.println("소방차는 급해요~!");
    }
}
 
class Ambulance extends Car {  // 앰뷸런스
    void siren() {             // 사이렌을 울리는 기능
        System.out.println("siren~~~~");
    }
}


실행결과 :

1
2
3
drive, brrrr~
소방차는 급해요~!
drive, brrrr~





<정리>

1) 조상의 참조변수로 자손의 인스턴스는 무조건 참조가 가능하다.

(조상의 참조변수의 멤버수가 자손의 인스턴스의 멤버수보다 적기 때문에)

(자손 인스턴스는 조상의 멤버를 모두 상속 받기 때문에 조상의 멤버보다 같거나 많을수 밖에 없다.)

2) 자손의 참조변수로 조상의 인스턴스는 무조건 참조가 불가능 하다.

(자손의 참조변수의 멤버수가 조상의 인스턴스의 멤버수보다 많기 때문에)






출처 : 자바의 정석

1) du -sh www/     - www 폴더의 용량보기

2) du -h --max-depth=1   -디렉토리 별로 사용량 보기

3) du -sh /*   -전체디렉토리 합계는 안나옴

4) df -h  -전체 하드용량

5) ls -alh -MB 단위로 파일 보기
설명

난수를 생성합니다. rand()는 0부터 RAND_MAX 사이의 난수를 생성합니다. 그러나 문제는 프로그램을 새로 실행할 때 마다 매번 다른 난수를 만들어 내지 않고 계속 같은 난수를 반복하게 됩니다.

#include <stdio.h>
#include <stdlib.h>

int main( void)
{ 
   int   ndx;   
        
   for ( ndx = 0; ndx < 10; ndx++)
   {
      printf( "%d %dn", ndx, rand() % 100);
   }        
   
   return 0;
}
]$ ./a.out
0 83
1 86
2 77
3 15
4 93
]$ ./a.out
0 83
1 86
2 77
3 15
4 93
]$ 

결과를 보듯이 난수는 생성하지만 실행할 때마다 똑 같은 난수를 똑 같이 생성합니다.

이 문제를 해결하기 위해서는 난수를 생성하기 전에 난수를 생성하기 위한 씨앗 즉, 난수 seed를 바꾸어 주어야 하며, seed를 바꾸기 위해서는 srand()를 사용해야 합니다. 이에 대한 자세한 설명은 강좌 게시판의 "난수 만들기"를 참고하여 주십시오.

헤더stdlib.h
형태int rand( void);
반환

int

0부터 RAND_MAX 사이의 난수
예제
#include <stdio.h>
#include <stdlib.h>
#include <time.h>    // time()
#include <unistd.h>  // getpid()
int main( void)
{
   int   ndx;

   srand( (unsigned)time(NULL)+(unsigned)getpid());
   for ( ndx = 0; ndx < 5; ndx++)
      printf( "%d %dn", ndx, rand() %100 +1);

   return 0;
}

]$ ./a.out 0 45 1 48 2 72 3 60 4 78 ]$ ./a.out 0 2 1 80 2 63 3 99 4 93



설명

파일의 읽기/쓰기 위치를 파일의 처음 위치로 초기화합니다.

파일의 위치는 기준 옵션에 따라 앞으로 또는 뒤로 읽기/쓰기 위치로 건너 띕니다.

whence설명

SEEK_SET

파일의 시작
SEEK_CUR현재 읽기/쓰기 포인터 위치
SEEK_END파일의 끝

건너 띈다는 말씀은 인수로 받은 숫자의 위치로 이동한다는 뜻이 아니라 건너 띄듯이 count 한다는 뜻입니다. 아래의 그림을 참고하여 주십시오.

헤더stdio.h
형태int fseek( FILE *stream, long offset, int whence);
인수FILE *stream대상 파일 스트림
long offset이동할 바이트 수
int whence시작 시점
반환int0을 반환, 오류가 발생하면 -1을 반환
예제
#include <stdio.h>

int main( void)
{
   FILE *fp;
   char  str[1024];
   int   ndx;

   fp = fopen( "./test.txt", "r");

   printf( "%sn", fgets( str, 1024, fp));

   fseek( fp, 10, SEEK_SET);              // 파일의 시작에서 10번을 건너 띈다.
   printf( "%sn", fgets( str, 1024, fp));

   fseek( fp,  5, SEEK_SET);              // 파일의 시작에서 5번을 건너 띈다.
   fseek( fp,  5, SEEK_CUR);              // 현재 위치에서 다시 5번을 건너 띈다.
   printf( "%sn", fgets( str, 1024, fp));

   fseek( fp,  -5, SEEK_END);             // 파일의 시작에서 5번을 건너 띈다.
   printf( "%sn", fgets( str, 1024, fp));

   fclose( fp);

   return 0;
}

test.txt 파일에 16진수 문자, 0부터 f 까지 입력되어 있을 때 실행 결과입니다.

0123456789abcdef

]$ ./a.out 0123456789abcdef abcdef abcdef bcdef ]$



매번 SQL을 만드시느라 고생하셨죠?

토드에는 sql을 자동으로 만들어주는 기능이 있습니다.


file아래 schema browser버튼을 클릭합니다.


쿼리를 생성할 테이블을 선택합니다.


테이블에서 오른쪽버튼 클릭 후

generate statement 후 select 등 필요한 쿼리를 선택합니다.



테이블에서 오른쪽버튼 클릭 후

generate statement 후 select 등 필요한 쿼리를 선택합니다.


insert, merge, select, update 등 쿼리를 자동으로 생성해줘서

편리하게 사용할 수 있습니다.



+ Recent posts