자바 코드 에 자주 발생 하 는 오류 분석 (2)

5. 흔 한 오류 5\#: 잘못된 데 이 터 를 복사 합 니 다.
때때로 프로그래머 는 반드시 복사 본 을 되 돌려 야 한 다 는 것 을 알 지만, 부주의 로 잘못된 데 이 터 를 복사 했다.일부 데이터 복사 작업 만 했 기 때문에 아래 코드 는 프로그래머 의 의도 와 차이 가 있 습 니 다.
 

  
  
  
  
  1. import java.awt.Dimension;
  2. /*** Example class. The height and width values should never * be
  3. negative. */
  4. public class Example{
  5. static final public int TOTAL_VALUES = 10;
  6. private Dimension[] d = new Dimension[TOTAL_VALUES];
  7. public Example (){ }
  8. /*** Set height and width. Both height and width must be nonnegative * or
  9. an exception will be thrown. */
  10. public synchronized void setValues (int index, int height, int width)
  11. throws IllegalArgumentException{
  12. if (height < 0 || width < 0)
  13. throw new IllegalArgumentException();
  14. if (d[index] == null)
  15. d[index] = new Dimension();
  16. d[index].height = height;
  17. d[index].width = width;
  18. }
  19. public synchronized Dimension[] getValues()
  20. throws CloneNotSupportedException{
  21. return (Dimension[])d.clone();
  22. }
  23. }

 
문 제 는 getValues () 방법 이 배열 만 복 제 했 을 뿐, 배열 에 포 함 된 Dimension 대상 은 복제 되 지 않 았 다 는 점 입 니 다. 따라서 호출 자 는 내부 의 배열 을 바 꾸 어 서로 다른 Dimension 대상 을 가리 키 게 할 수 없 지만, 호출 자 는 내부 의 배열 요소 (즉 Dimension 대상) 의 내용 을 바 꿀 수 있 습 니 다.방법 getValues () 의 더 좋 은 버 전 은:
 

  
  
  
  
  1. public synchronized Dimension[] getValues() throws CloneNotSupportedException{
  2. Dimension[] copy = (Dimension[])d.clone();
  3. for (int i = 0; i < copy.length; ++i){
  4. // NOTE: Dimension isn’t cloneable.
  5. if (d != null)
  6. copy[i] = new Dimension (d[i].height, d[i].width);
  7. }
  8. return copy;
  9. }

 
원자 형식 데이터 의 다 차원 배열 을 복제 할 때 도 비슷 한 오 류 를 범 할 수 있다.원자 유형 은 int, float 등 을 포함한다.간단 한 클론 int 형의 1 차원 배열 이 정확 합 니 다. 다음 과 같 습 니 다.
 

  
  
  
  
  1. public void store (int[] data) throws CloneNotSupportedException{
  2. this.data = (int[])data.clone();
  3. // OK
  4. }

 
int 형의 2 차원 배열 을 복사 하 는 것 이 더 복잡 하 다.자바 에는 int 형의 2 차원 배열 이 없 기 때문에 int 형의 2 차원 배열 은 실제 적 으로 이러한 1 차원 배열 이다. 그의 유형 은 int [] 이다.간단 한 클론 int [] [] 형의 배열 은 위의 예 에서 getValues () 방법 1 버 전과 같은 오 류 를 범 할 수 있 으 므 로 피해 야 합 니 다.다음 예 는 int 형 2 차원 배열 을 복제 할 때 잘못된 방법 과 정확 한 방법 을 보 여 줍 니 다.
 

  
  
  
  
  1. public void wrongStore (int[][] data) throws CloneNotSupportedException{
  2.   this.data = (int[][])data.clone(); // Not OK!
  3. }
  4. public void rightStore (int[][] data){
  5.   // OK!
  6.   this.data = (int[][])data.clone();
  7.   for (int i = 0; i < data.length; ++i){
  8. if (data != null)
  9.   this.data[i] = (int[])data[i].clone();
  10.   }
  11. }

 
6. 흔 한 오류 6\#: new 작업 의 결과 가 null 인지 확인 합 니 다.
자바 프로 그래 밍 초보 자 는 가끔 new 작업 의 결과 가 null 인지 확인 합 니 다.가능 한 검사 코드 는:
 

  
  
  
  
  1. Integer i = new Integer (400);
  2. if (i == null)
  3. throw new NullPointerException();

 
검 사 는 물론 오류 가 없 지만 필요 하지 않 습 니 다. if 와 throw 라 는 두 줄 의 코드 는 완전히 낭비 입 니 다. 그들의 유일한 기능 은 프로그램 전 체 를 비대 하 게 하고 운행 을 더 느리게 하 는 것 입 니 다.
C/C++ 프로그래머 는 자바 프로그램 을 시작 할 때 자주 이렇게 합 니 다. 이것 은 C 에서 malloc () 의 반환 결 과 를 검사 하 는 것 이 필요 하기 때 문 입 니 다. 이렇게 하지 않 으 면 오류 가 발생 할 수 있 습 니 다.C++ 에서 new 작업 의 결 과 를 검사 하 는 것 은 좋 은 프로 그래 밍 행위 일 수 있 습 니 다. 이것 은 이상 이 가능 한 지 여부 에 의존 합 니 다. (많은 컴 파일 러 들 이 이상 이 금지 되 어 있 는 상황 에서 new 작업 이 실패 하면 null 로 돌아 갑 니 다.)자바 에서 new 작업 은 null 로 돌아 갈 수 없습니다. 만약 null 로 돌아 가면 가상 컴퓨터 가 붕 괴 될 수 있 습 니 다. 이 럴 때 결 과 를 검사 하 더 라 도 소 용이 없습니다.
7. 흔 한 오류 7\#: 사용 = = 대체. equals
자바 에 서 는 두 데이터 가 같은 지 확인 하 는 두 가지 방법 이 있 습 니 다. = 연산 자 를 사용 하거나 모든 대상 이 이 루어 진 equals 방법 을 사용 합 니 다.원자 유형 (int, flosat, char 등) 은 대상 이 아니 기 때문에 그들 은 = = 연산 자 만 사용 할 수 있 습 니 다. 다음 과 같 습 니 다.
 

  
  
  
  
  1. int x = 4;
  2. int y = 5;
  3. if (x == y)
  4. System.out.println ("Hi");
  5. // This ’if’ test won’t compile.
  6. if (x.equals (y))
  7. System.out.println ("Hi");

 
대상 이 좀 더 복잡 하 다.
더욱 혼 란 스 러 운 것 은 자바. lang. Object 가 제공 하 는 결 성 된 equals 방법의 실현 사용 = = 비교 되 는 두 대상 이 같은 것 인지 아 닌 지 를 간단하게 판단 하 는 것 이다.
많은 클래스 가 부족 한 equals 방법 을 덮어 쓰 고 있 습 니 다. 예 를 들 어 String 류, equals 방법 은 두 개의 String 대상 이 같은 문자열 을 포함 하고 있 는 지, Integer 의 equals 방법 은 포 함 된 int 값 이 같은 지 확인 합 니 다.
대부분의 경우 두 대상 이 같은 지 확인 할 때 equals 방법 을 사용 해 야 하 며, 원자 형식의 데이터 에 대해 서 는 = 조작 자 를 사용 해 야 합 니 다.
8. 흔히 볼 수 있 는 오류 8\#: 원자 조작 과 비 원자 조작 을 혼동 합 니 다.
자바 는 읽 기와 쓰기 32 자리 또는 더 작은 값 을 원자 조작 으로 보장 합 니 다. 즉, 한 단계 에 완성 할 수 있 기 때문에 끊 길 수 없 기 때문에 이러한 읽 기와 쓰 기 는 동기 화 할 필요 가 없습니다.다음 코드 는 스 레 드 보안 (thread safe) 입 니 다.
 

  
  
  
  
  1. public class Example{
  2.   private int value; // More code here...
  3.   public void set (int x){
  4. // NOTE: No synchronized keyword
  5. this.value = x;
  6.   }
  7. }

 
그러나 이 보증 은 읽 기와 쓰기 에 만 국한 되 고 아래 의 코드 는 스 레 드 가 안전 하지 않 습 니 다.
 

  
  
  
  
  1. public void increment (){
  2.   // This is effectively two or three instructions:
  3.   // 1) Read current setting of ’value’.
  4.   // 2) Increment that setting.
  5.   // 3) Write the new setting back.
  6.   ++this.value;
  7. }

 
테스트 를 할 때, 너 는 이 오 류 를 포착 하지 못 할 것 이다.우선 스 레 드 와 관련 된 오 류 를 테스트 하 는 것 은 어렵 고 시간 이 많이 걸린다.그 다음 에 일부 기계 에서 이 코드 들 은 하나의 명령 으로 번 역 될 수 있 기 때문에 정상적으로 작 동 하고 다른 가상 컴퓨터 에서 테스트 할 때 만 이 오류 가 나타 날 수 있다.따라서 시작 할 때 코드 를 정확하게 동기 화 하 는 것 이 좋 습 니 다.
 

  
  
  
  
  1. public synchronized void increment (){
  2.   ++this.value;
  3. }

 
9. 자주 발생 하 는 오류 9\#: catch 블록 에서 제거 작업
catch 블록 에서 제거 작업 을 하 는 코드 는 다음 과 같 습 니 다.
 

  
  
  
  
  1. OutputStream os = null;
  2. try{
  3.   os = new OutputStream ();
  4.   // Do something with os here.
  5.   os.close();
  6. }catch (Exception e){
  7.   if (os != null)
  8.   os.close();
  9. }

 
비록 이 코드 는 몇 가지 측면 에서 문제 가 있 지만 테스트 에서 이 오 류 를 빠 뜨리 기 쉽다.다음은 이 코드 에 존재 하 는 세 가지 문 제 를 보 여 줍 니 다.
1. 구문 os. close () 가 두 곳 에 나타 나 는 것 은 쓸데없는 짓 이 고 유지 에 도 번 거 로 움 을 가 져 올 수 있 습 니 다.
2. 위의 코드 는 Exception 만 처 리 했 을 뿐 Error 와 관련 이 없습니다.그러나 try 블록 이 실 행 될 때 오류 가 발생 하면 흐름 도 닫 혀 야 합 니 다.
3. close () 는 이상 을 던 질 수 있 습 니 다.
위 코드 의 더 좋 은 버 전 은:
 

  
  
  
  
  1. OutputStream os = null;
  2. try{
  3.   os = new OutputStream ();
  4.   // Do something with os here.
  5. }finally{
  6.   if (os != null)
  7. os.close();
  8. }

 
이 버 전 은 위 에서 언급 한 두 가지 문 제 를 제거 했다. 코드 가 중복 되 지 않 고 Error 도 정확하게 처리 할 수 있다.그러나 세 번 째 문 제 를 처리 하 는 좋 은 방법 이 없다. 아마도 가장 좋 은 방법 은 close () 문 구 를 try/catch 블록 에 따로 두 는 것 일 것 이다.
10. 흔 한 오류 10\#: 불필요 한 catch 블록 추가
일부 개발 자 들 은 try/catch 블록 이라는 이름 을 듣 고 모든 try 블록 에 일치 하 는 catch 블록 이 있어 야 한다 고 생각 합 니 다.
C++ 프로그래머 는 특히 C++ 에 finally 블록 이라는 개념 이 존재 하지 않 고 try 블록 이 존재 하 는 유일한 이 유 는 catch 블록 과 어 울 리 기 위 한 것 이 라 고 생각 합 니 다.
불필요 한 catch 블록 을 추가 하 는 코드 는 아래 와 같 습 니 다. 캡 처 된 이상 은 즉시 던 져 집 니 다.
 

  
  
  
  
  1. try{
  2.   // Nifty code here
  3. }catch(Exception e){
  4.   throw e;
  5. }finally{
  6.   // Cleanup code here
  7. }

 
불필요 한 catch 블록 이 삭제 되면 위의 코드 는 다음 과 같이 단 축 됩 니 다.
 

  
  
  
  
  1. try{
  2.   // Nifty code here
  3. }finally{
  4.   // Cleanup code here
  5. }

 
일반적인 오류 11\#;equals, hashCode, clone 등 을 정확하게 실현 하 는 방법 이 없습니다.
방법 equals, hashCode, clone 은 자바. lang. Object 가 제공 하 는 결 성 실현 이 정확 합 니 다.불 행 히 도 이러한 부족 한 성 은 대부분 쓸모 가 없 기 때문에 그 중 몇 가지 방법 을 덮어 더욱 유용 한 기능 을 제공한다.그러나 문제 가 또 생 겼 다. 여러 가지 방법 을 덮 은 부 류 를 계승 할 때 자 류 는 보통 이런 방법 을 덮어 야 한다.코드 심 사 를 할 때 부모 클래스 가 equals, hashCode, clone 등 방법 을 실현 했다 면 하위 클래스 도 정확 해 야 합 니 다.equals, hashCode, clone 을 정확하게 실현 하려 면 기술 이 필요 합 니 다.
51CTO 개발 채널

좋은 웹페이지 즐겨찾기