indexof

특정 문자나 문자열이 앞에서부터 쳐음 발견되는 인덱스를 반환하며 만약 찾지 못했을 경우”-1”을 반환한다.

public class IndexOfTest{

    public static void main(String[] args){

        String indexOfTestOne = "Hello world";

        System.out.println( indexOfTestOne.indexOf("o") );  // 4
        System.out.println( indexOfTestOne.indexOf("x") );  // -1
        System.out.println( indexOfTestOne.indexOf("o",5) );  // 7
    }
}

.indexof(“찾을문자”,”시작할 위치”) 이런식으로 사용하면된다.
시작할위치는 생략 가능하며 생략할 경우 0번째 부터 찾기 시작한다.

lastindexof

lastindexOf()는 특정 문자나 문자열이 뒤에서부터 처음 발견되는 인덱스를 반환하며 만약 찾지 못했을 경우 “-1”을 반환합니다.

public class IndexOfTest{

    public static void main(String[] args){

        String indexOfTestOne = "Hello world";

        System.out.println( indexOfTestOne.lastIndexOf("o") );  // 7
        System.out.println( indexOfTestOne.lastIndexOf("x") );  // -1
        System.out.println( indexOfTestOne.lastIndexOf("o",5) );  // 4
    }
}

사용법은 indexof와 같으며 뒤에서부터 찾기 시작한다.
오른쪽에서 몇번째 위치하는지를 반환하는게 아니라 말그대로 인덱스
즉, 왼쪽에서 몇번쨰 위치하는지를 인덱스로 반환한다.

Comment and share

String VS StringBuffer/StringBuilder

String과 StrungBuffer/StrungBuilder 클래스의 가장 큰 차이점은 String은 불변의 속성을 갖는다는 점이다.
그렇기 때문에 변하지 않는 문자열을 자주 읽어들이는 경우 String을 사용해 주면 좋은 성능을 기대할 수 있다. 그러나 문자열 추가, 수정, 삭제 등의 연산이 빈번하게 발생하는 알고리즘에 String클래스를 사용하면 힙메모리에 많은 가비지가 생성되어 힙메모리 부족으로 어플리케이션 성능에 치명적인 영향을 끼치게 된다.

이를 해결하기 위해 가변성을 가지는 StringBuffer/StringBuilder클래스를 도입했다.

StringBuffer VS StringBuilder

이 두클래스의 큰 차이점은 동기화의 유무이다. StringBuffer는 동기화 키워드를 지원하여 멀티쓰레드 환경에서 안전하다는 점이다. 참고로 String도 불변성을 가지기 때문에 마찬가지로 멀티쓰레드 환경에서의 안정성을 가지고 있다.

반대로 StringBuilder는 동기화를 지원하지 않기 때문에 멀티쓰레드 환경에서 사용하는 것은 적합하지 않지만 동기화를 고려하지 않는 만큼 단일쓰레드에서의 성능은 StringBuffer보다 뛰어나다.

Comment and share

StringBuffer Class

StringBuffer Class는 변하는 문자열을 다룰 때 사용한다. 객체 생성시 크기를 정하지 않으면 기본적으로 16개의 문자를 저장할 수 있는 버퍼 공간을 가진다.

String클래스의 객체는 한 번생성이 되면 내용이 변하지 않는 반면에 StringBuffer클래스의 객체는 문자열의 내용을 변경할 수 있다. StringBuffer 클래스의 메소드는 문자열 처리 후의 결과를 원래의 StringBuffer 객체에 반영하고, 메소드 리턴 타입은 void 이다.

생성자

StringBuffer()
초기 문자열이 없고 16개의 문자를 저장할 수 있는 버퍼를 가진 객체를 생성한다.
StringBuffer(String str)
str의 초기 문자열을 가지고 16개의 문자를 저장할 수 있는 버퍼를 가진 객체를 생성한다.
StringBuffer(int length)
초기 문자열이 없고 length개의 문자를 저장할 수 있는 버퍼를 가진 객체를 생성한다.

메소드

append()

append()메소드는 이수로 전달된 값을 문자열로 변한한 후, 해당 문자열의 마지막에 추가한다.
이 메소드는 String클래스의 concat() 메소드와 같은 결과를 반환하지만, 내부적인 처리 속도가 훨씬 빠르다.
예제

StringBuffer str = new StringBuffer("Java");
System.out.println("원본 문자열 : " + str);

System.out.println(str.append("수업"));
System.out.println("append() 메소드 호출 후 원본 문자열 : " + str);

결과

원본 문자열 : Java
Java수업
append() 메소드 호출 후 원본 문자열 : Java수업

capacity()

capacity()메소드는 StringBuffer 인스턴스의 현재 버퍼 크기를 반환한다.
예제

StringBuffer str01 = new StringBuffer();
StringBuffer str02 = new StringBuffer("Java");

System.out.println(str01.capacity());
System.out.println(str02.capacity());

결과

16
20

위의 예제처럼 길이가 4인 문자열로 StringBuffer 인스턴스를 생성하면, 기본적으로 생성되는 여유 버퍼 크기인 16에 문자의 길이인 4를 더한 총 20개의 문자를 저장할 수 있는 버퍼가 생성된다.

delete()

delete()메소드는 전달된 인덱스에 해당하는 부분 문자열을 해당 문자열에서 제거한다. 또한, deleteCharAt() 메소드를 사용하면 특정 위치의 문자 한 개만을 제거할 수도 있다.
예제

StringBuffer str = new StringBuffer("Java Oracle");
System.out.println("원본 문자열 : " + str);

① System.out.println(str.delete(4, 8));
System.out.println(str.deleteCharAt(1));
System.out.println("deleteCharAt() 메소드 호출 후 원본 문자열 : " + str);

결과

원본 문자열 : Java Oracle
Javacle
Jvacle
deleteCharAt() 메소드 호출 후 원본 문자열 : Jvacle

위 예제의 ①번 라인에서는 delete() 메소드를 사용하여 해당 문자열에서 인덱스가 4인 위치의 문자부터 7인 위치의 문자까지를 삭제한다.

이처럼 delete() 메소드는 첫 번째 매개변수로 전달된 인덱스부터 두 번째 매개변수로 전달된 인덱스 바로 앞의 문자까지를 삭제하는 메소드이다.

insert()

insert()메소드는 인수로 전달된 값을 문자로 변환한 후, 해당 문자열의 지정된 인덱스 위치에 추가한다. 이때 전달된 인덱스가 해당 문자열의 길이와 같으면, append()메소드와 같은 결과를 반환한다.
예제

StringBuffer str = new StringBuffer("Java 만세!!");
System.out.println("원본 문자열 : " + str);

① System.out.println(str.insert(4, "Script"));
System.out.println("insert() 메소드 호출 후 원본 문자열 : " + str);

결과

원본 문자열 : Java 만세!!
JavaScript 만세!!
insert() 메소드 호출 후 원본 문자열 : JavaScript 만세!!

Comment and share

String Class

String 객체가 하나 생성되면, 그 값은 길어지거나 줄어들 수 없으며 그 문자들 중 어떤 것도 바뀔 수 없다.
그래서 String 객체는 변경불능이라고 한다.

String클래스의 자주 사용하는 함수이다.
|함수|반환형|설명
|—|—|—
|CharAt(int index)|char|index로 지정된 첨자 위치에 있는 문자를 반환한다.
|compareTo(String str)|int|이 String이 String str보다 사전적 순서에서 앞서면 음수값, 같으면 0값, 뒤에 있으면 0값이다.
|Concat(String str)|String|이 String과 str을 접합하여 구성되는 새로운 String을 반환한다.
|equals(String str)|boolean|이 String이 str과 같은 문자열이면 (대소문자 구분) True, 그렇지 않으면 False 반환
|equalslgnoreCase(String str)|boolean|이 String이 str과 같은 문자열이면(대소문자 미 구분) True, 그렇지 않으면 False 반환
|length()|int|이 String의 문자 개수를 반환한다.
|replace(char oldChar, char newChar)|String|String에 나타나는 oldChar를 모두 new Char로 변경한다.
|substring(int offset, int endIndex)|String|이 String의 offset 위치에서부터 endIndex-1까지에 걸친 문자열을 반환한다.
|toLowerCase()|String|모든 대문자를 소문자로 변환한다.
|toUpperCase()|String|모든 소문자를 대문자로 변환한다.

예제

public class StringClass {
    
    public static void main(String[] args){
        
        String text = "First String V";
        String concat, upperCase, replace, subString;
        
        System.out.println("기본 String : "+ text);
        System.out.println("기본 String 길이 : "+text.length());    // 문자열 길이 출력
        
        concat = text.concat(", Second String V");    // 문자열 연결
        
        upperCase = concat.toUpperCase();        // 문자열 대문자로 변환
        
        replace = upperCase.replace('V', 'K');    //  문자 V를 K로 변환
        
        subString = replace.substring(3, 10);    // 3~9 문자열 잘라내기
        
        
        System.out.println("Concat String : " + concat);
        System.out.println("upperCase String : " + upperCase);
        System.out.println("replace String : " + replace);
        System.out.println("subString String : " + subString);
        
        
    }

}

System.out.println(“기본 String 길이 : “+text.length()); // 문자열 길이 출력

text인 “First String V” 의 문자열 길이를 반환한다.

-> 14

concat = text.concat(“, Second String V”); // 문자열 연결

text인 “First String V” 뒤에 concat 메소드로 “, Second String V” 를 연결했다.

-> First String V, Second String V

upperCase = concat.toUpperCase(); // 문자열 대문자로 변환

concat인 “First String V, Second String V” 의 문자열을 대문자로 변환한다

-> FIRST STRING V, SECOND STRING V

replace = upperCase.replace(‘V’, ‘K’); // 문자 V를 K로 변환

upperCase인 “FIRST STRING V, SECOND STRING V” 문자열 중 ‘V’ 문자를 ‘K’ 문자로 변환 한다.

-> FIRST STRING K, SECOND STRING K

subString = replace.substring(3, 10); // 3~9 문자열 잘라내기

replace 인 “FIRST STRING K, SECOND STRING K” 문자열의 3번째 부터 9번째 문자열까지 잘라낸다 (0부터 시작)

-> ST STRI

Comment and share

JVM과 메모리

메모리는 OS가 관리하는데, 모든 프로그램들은 OS위에서 돌아간다.
프로그램이 돌아가려면 메모리가 있어야된다.
JVM도 메모리가 필요하면 OS에게 메모리 요청을 한다.
그런데 OS가 처음부터 자기가 가진 메모리를 전부 다 줘버리면, 다른 프로그램들에게 줄 메모리가 없게 된다.
때문에 각 프로그램에게 메모리의 일정부분만 빌려주는 방식으로 관리가 된다.

JVM을 실행하다가 메모리가 부족하면 OS에게 메모리를 더 달라고 요청한다. 그러면 OS가 또 JVM에게 메모리를 더 빌려준다.
JVM은 OS로 부터 받은 메모리들 중, 어디에다가 저장할지 그 주소를 할당하야 한다.
이 때, JVM은 자기가 받은 메모리 안에서, 절대주소가 아니라 거기에 대한 상대주소를 할당한다. 이걸 offset주소라고 한다.

Garbage Collector

프로그램을 실행하다보면 가비지가 발생하게 된다.
가비지는 정리되지 않은 메모리, 유효하지 않은 메모리 주소를 말한다.
가비지 컬렉터는 메모리가 부족할 때 가비지를 정리해주는 프로그램을 말한다.
때문에 JVM의 가비지 컬렉터는 메모리를 다른 용도로 사용할 수 있게 ‘메모리 해제’를 시키는 프로그램이다.
참고로 가비지 컬렉터는 JVM이 OS에게 추가로 메모리를 더 요청할 때 실행된다.
또, 서버 프로그램인 경우에는 24시간 내내 돌아가는데, 이때에는 JVM이 한가할 때 가비지 컬렉터가 실행된다.

Comment and share

메모리(RAM)

프로그램이 실행하게 되면 OS는 메모리(RAM)에 공간을 할당해 준다.
할당해주는 메모리 공간은 4가지(Code, Data, Stack, Heap)가 있다.

코드(Code) 영역

우리가 작성한 소스코드가 들어가는 부분.
즉, 실행할 프로그램의 코드가 저장되는 영역으로 텍스트영역이라고도 부른다.
코그영역은 실행 파일을 구성하는 명령어들이 올라가는 메모리 영역으로 함수, 제어문, 상수 등이 여기에 저장된다.

데이터(Data) 영역

전역변수와 Static변수가 할당되는 영역
프로그램의 시작과 동시에 할당되고, 프로그램이 종료되어야 메모리가 소멸되는 영역.

스택(Stack) 영역

프로그램이 자동으로 사용하는 임시 메모리 영역.
함수 호출 시 생성되는 지역 변수와 매개변수가 저장되는 영역이고,
함수 호출이 완료되면 사라진다.

힙(Heap) 영역

프로그래머가 할당/해체하는 메모리 공간이다.
Java에서는 가비지 컬렉터가 자동으로 해제한다.
이 공간에 메모리 할당하는 것을 동적 할당이라고도 부른다.

Heap과 Stack영역은 같은 공간을 공유한다. 그래서 Heap이 메모리 위쪽 주소부터 할당되고 Stack은 아래쪽 부터 할당된다. 각 영역이 상대 공간을 침범하는 일이 발생할 수 있는데 이를 각각 Heap Overflow, Stack Overflow라고 칭한다.

Comment and share

클래스의 상속

class Person {

    protected int age;
    
    protected String name;
    
    public Persion(int age, String name) {
        this.age = age;
        this.name = name;
    }
    
    public int getAge() {
        return this.age;
    }
    
    public String getName() {
        return this.name;
    }
}

age와 name이라는 멤버 변수를 가지고 있는 Person 클래스를 생각해보자. 이 클래스를 상속 받는 Student 클래스는 다음과 같이 정의할 수 있다.

class Student extends Person {
    
    protected String studentNumber;
    
    public Student(int age, String name, String studentNumber) {
        
        super(age, name);
        this.studentNumber = studentNumber;
    }
    
    public String getStudentNumber() {
    
        return this.studentNumber;
    }
}

‘extends’라는 키워드를 이용해서 Person 클래스를 상속받아 확장하여 Student 클래스를 정의했다. Student 클래스 정의에는 직접적으로 나와있지 않지만 부모 클래스인 Person 클래스의 메소드인 getAge()와 getName() 메소드를 호출 할 수 있다.

일반적으로 두 클래스 간의 관계가 ‘IS-A’ 관계이면 상속을 사용한다. Student is a person이므로 상속을 통해서 클래스를 정의하는게 자연스럽다.

클래스의 위임

상속과 다르게 위임은 다른 클래스의 객체를 멤버로 갖는 형태의 클래스 정의다.

class Employee {
    
    private Department department;
    
    private String name;
    
    publiv Employee(String name, Department department) {
        
        this.name = name;
        this.department = department;
    }
    
    public String getDepartmentName() {
    
        return this.department.getName();
    }
}

이 경우 특정 메소드 호출을 멤버 클래스의 메소드에 포워드(Forward)하는 식으로 구현하게 된다.

Employee 클래스와 Department 클래스 사이의 관계를 살펴보면 ‘IS-A’ 관계는 아니다. (Employee is a Department는 어색하다.) 따라서 멤버 변수로 Department 클래스를 포함하는 Delegation 형태로 구현을 했다.

상속과 위임

  • 두 클래스의 관계가 ‘IS-A’ 관계이면 상속(Inheritance)을 써야한다.
  • 기존에 존재하는 API에 넘겨줘야 하는 경우 상속(Inheritance)을 써야한다.
  • final 클래스를 확장하고 싶은 경우 위임(Delegation)을 써야한다.

Comment and share

implements와 extends의 차이

  • extends는 일반 클래스와 abstract 클래스 상속에 사용되고, implement는 interface 상속에 사용된다.
  • class가 class를 상속받을 땐 extends를 사용하고, interface가 interface를 상속 받을 땐 extends를 사용한다.
  • class가 interface를 사용할 땐 implements를 써야하고
    interface가 class를 사용할 땐 implements를 쓸수 없다.
  • extends는 클래스 한 개만 상속 받을 수 있다.
  • extends 자신 클래스는 부모 클래스의 기능을 사용한다.
  • implements는 여러개 사용 가능하다.
  • implements는 설계 목적으로 구현 가능하다.
  • implements한 클래스는 implements의 내용을 다 사용해야 한다.

Comment and share

Interface

하나의 시스템을 구성하는 2개의 구성요소(하드웨어, 소프트웨어) 또는 2개의 시스템이 상호작용할 수 있도록 접속되는 경계(boundary), 이 경계에서 상호 접속하기 위한 하드웨어, 소프트웨어, 조건, 규약 등을 포괄적으로 가리키는 말

자바에서 인터페이스는 여러가지 역할로 사용한다.

  • 개발자 사이의 코드 규약을 정한다.
  • 여러 구현체에서 공통적인 부분을 추상화한다.(다형성)

자바 인터페이스는 기본적으로 추상 메소드의 모음이다. 추상메소드는 아래와 같이 구현부가 없는 메소드를 말한다.

public interface Walkable {
void walk();
}

구현부가 없으므로 인터페이스를 만든다면 반드시 구현하는 클래스를 만들어야 하며, 인터페이스를 구현하기로 한 클래스는 반드시 인터페이스에 명시되어 있는 추상메소드들을 모두 구현해야 한다. 만약 이를 구현하지 않으면 컴파일 에러가 발생한다.

public class Dog implements Walkable {
// ...
@Override
public void walk() {
    // ...
    }
}

인터페이스는 구현과 상속을 모두 할 수 있다.

  • 인터페이스를 사용하는 구체 클래스는 해당인터페이스를 구현해야한다.
  • 인터페이스 사이에는 상속을 할 수 있다.

인터페이스를 사용하면 다중 상속이 가능하다. 인터페이스 사이에서도, 구체 클래스에서도 여러 인터페이스를 구현 및 상속할 수 있다.

public interface Walkable {
void walk();
}

public interface Flyable {
void fly();
}

public interface Moveable extends Walkable, Flyable {
}
    public class Bat implements Moveable {
@Override
public void walk() {
    // ...
}

@Override
public void fly() {
    // ...
}
}

인터페이스는 클래스와 달리 기본 접근제어자는 public이다.
인터페이스에 필드 변수를 선언하면 public static final로 선언해야 하며, 이 것도 기본 설정되어 있다.

인터페이스를 사용하는 이유

인터페이스를 사용하는 주된 이유는 다형성을 위해서이다. 다형성은 상속받은 클래스 또는 인터페이스의 메소드를 재정의하여 서로 다른 행동을 만들 수 있다. 상속을 통해 상위 클래스의 타입으로 통일한 후 하위 클래스들을 하나의 타입으로 관리할 수 있다. 이를 사용해서 변경에 유연한 코드를 만들 수 있다.

Comment and share

super

super는 자식 클래스가 부모 클래스로 부터 상속받은 멤버를 참조할 때 사용하는 참조 변수이다. 클래스 내의 멤버변수와 지역변수의 이름이 같은 경우 구분을 위해 this를 사용하듯 부모 클래스와 자식 클래스의 멤버의 이름이 같을 경우 super를 사용한다. this와 super는 인스턴스의 주소값을 저장하는데 static메소드(클래스 메소드)와는 무관하게 사용된다.

class JavaApp {
    public static void main(String[] args) {
        Child child = new Child();
        child.childMethod();
    }
}

class Parent {
    int x = 10;
}

class Child extends Parent {
    int x = 20;

    void childMethod() {
        System.out.println("x=" + x);
        System.out.println("this.x=" + this.x);
        System.out.println("super.x=" + super.x);
    }
}

결과값

x=20
this.x=20
super.x=10

super()

super()는 부모 클래스의 생성자를 호출하는 메서드이다. 상속받은 자식 클래스가 부모 클래스의 멤버를 사용할 경우가 있을 수도 있으므로 부모 클래스를 우선적으로 초기화해줘야 한다. 부모 클래스의 생성자는 자식 클래스의 생성자 첫줄에서 호출해준다. 이러한 부모 클래스에 대한 생성자 호출은 상속관계에 따라 Object 클래스까지 올라가서 마무리된다.

정리하면, Object 클래스를 제외한 모든 클래스의 생성자의 첫줄에는 반드시 자신의 클래스의 또다른 생성자, this() 또는 부모 클래스의 생성자, super()를 호출해줘야 한다. 이렇게 하지 않으면 컴파일러가 자동으로 super()를 생성자의 첫줄에 호출한다.

class JavaApp {
    public static void main(String[] args) {
        Point3D point3d = new Point3D();    // Point3D() 생성자로 초기화 및 인스턴스 생성
        System.out.println("point3d.x=" + point3d.x);
        System.out.println("point3d.y=" + point3d.y);
        System.out.println("point3d.z=" + point3d.z);
    }
}

class Point {
    int x = 10;
    int y = 20;

    Point(int x, int y) {
        // 생성자의 첫줄에 다른 생성자를 호출하지 않았기 때문에,
        // 컴파일러가 이 부분에 super()를 호출한다.
        // 부모 클래스이므로 Object 클래스의 super()가 호출된다.
        this.x = x;
        this.y = y;
    }
}

class Point3D extends Point {
    int z = 30;

    Point3D() {
        this(100, 200, 300);    // 자신의 클래스의 또다른 생성자 호출
    }

    Point3D(int x, int y, int z) {
        super(x, y);    // 부모 클래스 생성자 호출
        this.z = z;
    }  
}

결과값

point3d.x=100
point3d.y=200
point3d.z=300

Comment and share

Hyeon Soo Ahn

author.bio


author.job