이런 클래스는 데이터 필드를 직접 조작할 수 있어서 캡슐화의 이점을 누릴수가 없다. 불변식도 강제할 수 없고, 필드를 사용하는 순간에 어떤 동작이 실행되도록 만들수도 없다. 이러한 클래스는 private필드와 public 접근자 메서드(getter)로 바꿔야 한다.

 선언된 패키지 밖에서도 사용 가능한 클래스에는 접근자 메서드를 제공하라. 그래야 클래스 내부 표현을 자유로이 수정할 수 있게 된다. 

하지만 package-private 클래스나 private 중첩 클래스는 데이터 필드를 공개하더라도 잘못이라 말할 수 없다. 클래스의 내용을 제대로 기술하기만 한다면, 접근자 메서드보다는 시각적으로 깔끔하다. private 중첩 클래스의 경우는, 그 클래스의 바깥 클래스 외부의 코드는 아무 영향도 받지 않을 것이기 떄문이다.


요약

public 클래스는 변경 가능 필드를 외부로 공개하면 안된다. 변경 불가능 필드인 경우에는 외부로 공개하더라도 많이 위험하진 않겠지만, 그럴 필요가 없다고 생각된다. 하지만 package-private나 private로 선언된 중첩 클래스의 필드는 그 변경 가능 여부와는 상관없이 외부로 공개하는 것이 바람직할 때도 있다.


잘 설계된 모듈과 그렇지 못한 모듈을 구별 짓는 가장 중요한 속성 하나는 모듈 내부의 데이터를 비롯한 구현 세부사항을 다른 모듈에 잘 감추었느냐는 것이다. 정보은닉과 캡슐화가 잘 되어진 모듈은 모듈사이의 의존성을 낮춰서, 각자 개별적으로 개발 및 변경이 가능하다. 또한 다른 모듈에 영향을 끼칠 걱정 없이 디버깅을 진행할 수 있기 때문에 좋은 성능을 낸다고 할 순 없지만, 효과적인 성능튜닝을 할 수 있는 방법이다.


접근제어는 클래스와 인터페이스, 그리고 그 멤버들의 접근 권한을 규정한다. 접근 제어의 적절한 사용은 정보 은닉 원칙을 실현하는 핵심적인 부분이다.

각 클래스와 멤버는 가능한 한 접근 불가능하도록 만들어라

다시말해서 개발 중인 소프트웨어의 정상적인 동작을 보장하는 한도 내에서 가장 낮은 접근 권한을 설정하라는 원칙이다. 최상위 레벨 클래스나 인터페이스는 가능한 package-private(default)로 선언해야 한다. package-private로 선언하면 API의 일부가 아니라 구현 세부사항에 속하게 되므로, 다음번 릴리즈에 클라이언트 코드를 깨뜨릴 걱정없이 자유로이 변경,삭제,대체가 가능하다.

객체 필드는 절대로 PUBLIC으로 선언하면 안된다.

그 필드에 관계된 불변식을 강제할 수 없다. 필드가 변경될 때 특정한 동작이 실행되도록 할 수도없으므로, 변경 가능 PUBLIC 필드를 가진 클래스는 다중 스레드에 안전하지 않다. 그러나 상수들이 핵심적 부분을 구성한다고 판단되는 경우, 해당 상수들을 public static final 필드들로 선언하여 공개할 수 있다. 이런 필드들은 반드시 기본 자료형 값들을 갖거나, 변경 불가능 객체를 참조해야 한다.

public static final 배열 필드를 두거나, 배열 필드를 반환하는 접근자를 정의해서는 안된다.

해결법1. public으로 선언되었던 배열은 private로 바꾸고 변경이 불가능한 public 리스트를 하나 만드는 것.

해결법2.배열을 private로 선언하고, 해당 배열을 복사해서 반환하는 public 메서드를 하나 추가하는 것.


요약

접근 권한은 최대한 낮추고 최소한의 public API를 설계한 다음, 다른 모든 클래스,인터페이스, 멤버는 api에서 제외하라. public static final 필드를 제외한 어떤 필드도 public 필드로 선언하지 마라. 그리고 public static final필드가 참조하는 객체는 변경 불가능 객체로 만들어라.



compareTo 메소드는 Comparable 인터페이스를 구현하는 유일한 메소드이다. Comparable인터페이스를 구현하는 클래스의 객체들은 자연적 순서를 갖게 된다.Comparable을 구현한 객체들의 배열을 정렬하는 것은 아래처럼 아주 간단하다.


 마찬가지로 Comparable 인터페이스를 구현한 객체들은 검색하거나 최대/최소치 계산하기도 간단하다. 그러므로

알파벳 순서나 값의 크기, 또는 시간적 선후관계처럼 명확한 자연적 순서를 따르는 값 클래스를 구현할 때는 Comparable 인터페이스를 구현할 것을 고려해봐야 한다.

compareTo 메소드는 이 객체의 값이 인자로 주어진 객체보다 작으면 음수를, 같으면 0을, 크면 양수를 반환한다.

compareTo 규약

-객체 참조를 비교하는 방향을 뒤집어도 객체 간 대소 관계는 그대로 유지되어야 한다. 즉 만일 첫 번째 객체가 두번째 객체봐 작다면, 두 번째 객체는 첫 번째 객체보다 큰 객체여야 한다. ( a.compareTo(b) 가 1이면 b.compareTo(a) 는 -1 이어야 한다.)


-첫 번째 객체가 두 번째 객체보다 크고 두 번째 객체가 세 번째 ㄱ개체보다 클 경우 첫 번째 객체는 반드시 세 번째 객체보다 커야 한다.

(a.compareTo(b) 가 1이고 , b.compareTo(c)가 1이면 , a.compareTo(c) 는 1이다.)


-비교결과 같다고 판정된 모든 객체 각각을 다른 어떤 객체와 비교할 경우, 그 비교 결과는 전부 통일해야 한다.


즉, 반사성, 대칭성, 추이성을 만족해야 한다는 것인데 이는 equals의 규약과 같다.




내용이 어렵다..나중에 다시 정리하도록 하자..


cloneable은 어떤 객체가 복제를 허용한다는 사실을 알리는데 쓰려고 고안된 인터페이스다. cloneable 인터페이스에는 어떠한 메소드도 없는데 그렇다면 대체 Cloneable이 하는 일은 무엇일까? protected로 선언 된 Object의 clone메서드가 어떻게 돚악할지 정하는 역할을 한다. Cloneable 인터페이스를 구현하면 해당 객체를 필드 단위로 복사한 객체를 반환한다. 구현하지 하지 않은 클래스라면 CloneNotSupportedException이 발생한다.

사실 인터페이스라는 것은 클라이언트에게 해당 클래스가 무슨 일을 할 수 있는지 알리는 것인데, Cloneable은 상위 클래스의 protected 메서드의 동작을 규정하는 역할을 하고 있으므로 좀 이상하다..

비 fianl 클래스에 clone을 재정의할 때는 반드시 super.clone을 호출해 얻은 객체를 반환해야 한다.
why? 만약 하위클래스에서 super.clone()을 호출하면 프로그래머는 하위 클래스 객체가 반환될 것이라고 생각할 수 있다. 하지만 이러한 기능을 제공하려면 상위 클래스에서도 다시 super.clone을 호출해 만든 객체를 적절히 변환해서 반환할 수 밖에 없다. 모든 상위클래스가 이런식으로 동작을 하게 된다면 결국 Object의 clone메서드를 호출하게 될 것이다.
>> 사실 why에 대한 답이 아닌 것같다...본인조차도 이해가 잘 안되는..추후에 다시 공부하였을 떄 정리할 수 있으면 하도록 해야지..

clone을 정의해야할 때 주의해야 할 점은 복재할 객체가 변경 가능 객체에 대한 참조필드를 가지고 있다면 일반적인 방법으로는 오동작하게 될 확률이 매우매우매우 높다. 

 위의 두 클래스가 있다고 했을 때, 단순히 super.clone을 해버리면 원본 객체와 복사 된 객체가 obj에 대한 같은 참조값을 가지게 되므로 문제가 생긴다. clone메소드 또 다른 형태의 생성자로 볼수 있다. 원래 객체를 손상시키는 일이 없도록 해야하고, 복사본의 불변식도 제대로 만족해야 한다. 

이러한 문제는 clone을 재귀적으로 호출하여 처리할 수 있다. 


정리

Cloneable을 구현하는 모든 클래스는 반환값 자료형이 자기 자신인 public clone 메서드를 재정의해야한다. 이 메서드는 처음에 super.clone을 호출해야 한다. 만일 클래스 내부 필드가 전부 기본형이거나 변경 불가능 객체에 대한 참조라면 필드를 수정할 필요는 없지만(일련 번호같은 필드는 제외) , 참조 필드에 대한 복사는 재귀호출을 이용하여 deepCopy를 하여야 한다. 
 또 다른 객체 복제를 지원하는 좋은 방법은, 복사 생성자나 복사 팩터리를 제공하는 것이다. 


java.lang.Object 클래스가 toString 메서드를 제공하긴 하지만, 이 메서드가 반환하는 문자열은 일반적으로는 사용자가 보려는 문자열이 아니다. 클래스 이름 다음에 @ 기호와 16진수로 표시된 해시 코드가 붙은 문자열의 형태이다. 

"toString() 메소드는 사람이 읽기 쉽도록 간략하지만 유용한 정보를 제공해야 한다"

라고 명시가 되어있다. 앞서 말한 형태는 일반적으로 해당하지 않다고 생각이 된다. 그리고 toString을 잘 만들어 놓으면 클래스를 좀 더 쾌적하게 사용할 수 있다. toString 메서드는 println이나 printf 같은 함수 등에 객체가 전달되면 자동으로 호출되므로 편리하게 사용할 수 있다.

가능하다면 toString 메서드는 객체 내의 중요 정보를 전부 담아 반환하면 좋지만, 객체가 너무 커서 문자열로 변환하기 적합하지 않다면 요약정보로 작성하여도 된다. toString 메서드를 재정의 할 때 중요하게 생각해야 할 부분이 있다

toString이 반환하는 문자열의 형식을 문서의 반환할 것인가 , 말 것 인가의 문제

 문자열을 형식에 규정해두면, 문자열로부터 객체를 만드는 정적 팩터리 메서드나 생성자 등을 이용하여 편리하게 사용할 수 있지만 ,  해당 클래스가 널리 쓰인다고 가정했을 때 규정을 바꾸지 못한다는 단점이 있다. 이미 수많은 프로그래머가 문자열을 파싱하여 사용하고 있을 터인데 , 문자열의 규정을 바꿔버리면 이 문자열을 이용했던 수많은 프로그램이 오동작하게 될 것이기 때문이다.

toString 메서드는 어떤 의도로 작성되었는지 문서에 남겨두어야 한다. 그리고 toString에 포함되어 반환되는 정보들은 전부 프로그래밍을 통해서 가져올 수 있도록 하여야 한다(메서드를 통하여..)


equals 메서드를 재정의하는 클래스는 반드시 hashCode 메서드도 재정의 해야 한다. 그렇지 않으면 hashCode의 일반 규약을 어기게 되므로 해시기반 컬렉션과 함께 사용하면 오동작하게 된다.

실행중에 hashCode를 여러 번 호출하는 경우, equals가 사용하는 정보들이 변경되지 않았다면, 언제나 동일한 정수가 반환되어야 한다.

equals 메서드가 같다고 판정한 두 객체의 hashCode 값은 같아야 한다.

equals 메스더가 다르다고 판정한 두 객체의 hashCode 값은 꼭 다를 필요는 없다.


쓸만한 hashCode 만드는 법

1. 0이 아닌 상수를 result라는 이름의 int 변수에 저장한다.
2. 객체 안에 있는 모든 중요 필드 f에 대해서 아래의 절차를 시행한다.
A).
1)필드가 boolean이면 (f ? 1 : 0)을 계산한다.
2)필드가 byte,char,short,int 중 하나면 (int)f 를 계산한다.
3)필드가 long이면 (int)(f^f>>>32)를 계산한다.
4)필드가 float이면 Float.floatToIntBits(f) 를 계산
5)필드가 double이면 Double.doubleToLongBits(f) 를 계산하고 그 결과로 얻은 long 값을 위의 절차 3) 을 따라서 해시 코드로 변환한다.
6)필드가 객체 참조이고 equals 메서드가 해당 필드의equals 메서드를 재귀적으로 호출하는 경우에는 해당 필드의hashCode 메서드를 재귀적으로 호출하여 해시 코드를 계산한다.
7)필드가 배열인 경우에는 배열의 각 원소가 별도 필드인 것처럼 계산한다.
B). 절차 A에서 계산된 해시코드 c를 result에 다름과 같이 결합한다.
result = 31 * result + c
4.구현이 끝났다면 동치 관계에 있는 객체의 해시 코드값이 똑같이 계산되는 지 비교.


hashCode를 명시적으로 42와 같이 지정하지 않는 이유는?

해당 클래스의 모든 객체가 같은 해시코드를 갖기때문에 전부 같은 버킷에 해시되므로, 해시 테이블의 성능이 엄청나게 안좋아진다.


이클립스에 SDK 설치 등등을 하는 챕터이다. 지금은 안드로이드 스튜디오를 쓰기도 하고 별다른 내용이 없어서 정리는 하지 않겠다..

안드로이드

운영체제와 미들웨어,그리고 핵심 애플리케이션을 포함한 모바일 디바이스 개발에 적합한 소프트웨어 스택. 즉 안드로이드는 모바일 디바이스용 애플리케이션을 쉽게 제작하기 위한 소프트웨어 프레임워크를 제공해준다.




안드로이드 플랫폼의 구조를 나타낸 것으로 크게 애플리케이션 플레임워크,라이브러리,안드로이드 런타임,리눅스 커널로 구성되어 있다.



안드로이드 프레임워크 

리눅스 커널

안드로이드는 리눅스 기반의 플랫폼이다. 따라서 부팅 시에 부트로더를 통해 리눅스 커널이 먼저 시작된다.

init

안드로이드 init 프로세스는 각종 디바이스를 초기화하는 작업을 비롯해서 안드로이드 프렘워크 동작에 필요한 각종 데몬, 컨텍스트 매니저, 미디어 서버,Zygote등을 실행하는 역할을 한다.
데몬 프로세스에는 USB데몬(USB연결관리) , 안드로이드 디버그 브리지 데몬(adbd - 안드로이드 디버그 브리지 연결관리) , 디버거 데몬(디버그 시스템 시작) , 무선 인터페이스 레이어 데몬(무선 통신 연결 관리)이 있다.

컨텍스트 매니저

컨텍스트 매니저는 안드로이드의 시스템 서비스를 관리하는 중요한 프로세스다. 시스템 서비스는 안드로이드 프렘워크를 구성하는 중요한 컴포넌트로서 카메라,오디오,비디오 처리에서부터 각종 애플리케이션 제작에 필요한 중요 API를 제공하는 등의 역할을 수행한다. 즉 안드로이드 내에서 동작하는 각종 시스템 서비스에 대한 정보는 컨텍스트 매니저에게 얻을 수 있다.

미디어 서버

미디어 서버 프로세스는 안드로이드에서 Audio Flinger(오디오 출력 담당)나 Camera서비스와 같이 C/C++ 기반으로 작성돼 있는 네이티브 시스템 서비스를 실행하는 역할을 함.

Zygote

Zygote는 안드로이드 애플리케이션의 로딩 시간을 단축하기 위한 프로세스로서 모든 자바 기반 안드로이드 애플리케이션은 Zygote를 통해 포크된 프로세스 상에서 동작한다.

시스템 서버

Zygote에서 최초로 포크되어 실행되는 안드로이드 애플리케이션 프로세스다. 애플리케이션 생명 주기를 제어하는 액티비티 매니저 서비스, 단말기의 위치 정보를 제공하는 로케이션 매니저 서비스와 같은 자바 시스템 서비스를 실행하는 역할을 한다.




'개발서적 > 인사이드안드로이드' 카테고리의 다른 글

02.안드로이드 개발 환경 구축  (0) 2017.04.18

+ Recent posts