1. static
먼저 다음 프로그램을 살펴보세요.
public class Hello{ public static void main(String[] args){ //(1) System.out.println("Hello,world!"); //(2) } }
이 프로그램을 본 적이 있으면 Java를 공부한 대부분의 사람들에게 친숙할 것입니다. Java를 배우지 않았지만 C 등 다른 고급 언어를 배웠더라도 이 코드의 의미를 이해할 수 있어야 합니다. 단순히 "Hello, world"를 출력하고 다른 용도는 없습니다. 그러나 정적 키워드의 주요 사용법을 보여줍니다.
1번 지점에서는 main이라는 정적 메서드를 정의합니다. 이는 이 유형의 개체를 생성하지 않고도 이 메서드를 사용할 수 있음을 Java 컴파일러에 알리는 것을 의미합니다. 아직도 이 프로그램을 어떻게 실행하는지 알고 있나요? 일반적으로 명령줄에 다음 명령을 입력합니다(밑줄은 수동 입력을 의미함).
javac Hello.java
java Hello
Hello, world!
첫 번째 줄에서는 Hello를 컴파일해 보겠습니다. 실행 후 현재 파일을 살펴보면 첫 번째 줄에서 생성된 Java 바이너리 바이트코드인 Hello.class 파일이 추가로 존재하는 것을 확인할 수 있습니다. 두 번째 줄은 Java 프로그램을 실행하는 가장 일반적인 방법입니다. 실행 결과는 예상한 대로입니다. 2에서는 왜 이렇게 출력해야 하는지 궁금할 것입니다. 좋아요, 이 진술을 분석해 보겠습니다. (Java 문서가 설치되어 있지 않은 경우 Sun 공식 웹사이트에 가서 J2SE API를 찾아보세요.) 먼저 System은 java.lang 패키지에 위치한 핵심 클래스입니다. 정의를 보면 다음과 같습니다. line: public static final PrintStream out; 그런 다음 더 나아가서 PrintStream 하이퍼링크를 클릭하세요. METHOD 페이지에서 println을 찾으면 다음과 같은 줄이 있을 것입니다. println(문자열 x).
자, 이제 왜 그렇게 부르는지 이해하셔야 합니다. out은 System의 정적 변수이므로 직접 사용할 수 있고 out이 속한 클래스에는 println 메서드가 있습니다.
정적 메서드일반적으로 메서드는 클래스에서 정적으로 정의됩니다. 즉, 이 클래스의 개체 없이 이 메서드를 호출할 수 있다는 의미입니다. 아래와 같이:
class Simple{
static void go(){ System.out.println("Go...");
}
}
public class Cal{
public static void main(String[] args ){
Simple.go();
}
}
정적 메서드를 호출하는 것은 "클래스 이름. 메서드 이름"입니다. 정적 메서드의 사용은 위와 같이 매우 간단합니다. 일반적으로 정적 메소드는 애플리케이션의 다른 클래스에 대한 일부 유틸리티를 제공하는 경우가 많습니다. Java 클래스 라이브러리의 많은 정적 메소드는 이러한 목적으로 정의됩니다.
정적 변수는 정적 메서드와 유사합니다. 이러한 모든 인스턴스는 이 정적 변수를 공유하므로 클래스가 로드될 때 저장 공간 블록만 할당됩니다. 물론 최종은 또 다른 문제입니다. 아래 코드를 보세요:
class Value{ static int c=0; static void inc(){ c++; } } class Count{ public static void prt(String s){ System.out.println(s); } public static void main(String[] args){ Value v1,v2; v1=new Value(); v2=new Value(); prt("v1.c="+v1.c+" v2.c="+v2.c); v1.inc(); prt("v1.c="+v1.c+" v2.c="+v2.c); } }
결과는 다음과 같습니다.
v1.c=0 v2.c=0
v1.c=1 v2.c=1
이것은 저장 영역을 공유한다는 것을 증명할 수 있습니다. 정적 변수는 C의 전역 변수 개념과 다소 유사합니다. 정적 변수의 초기화에 대해 논의할 가치가 있습니다. 위 프로그램을 수정합니다.
class Value{ static int c=0; Value(){ c=15; } Value(int i){ c=i; } static void inc(){ c++; } }
class Count{ public static void prt(String s){ System.out.println(s); } Value v=new Value(10); static Value v1,v2; static{ prt("v1.c="+v1.c+" v2.c="+v2.c); v1=new Value(27); prt("v1.c="+v1.c+" v2.c="+v2.c); v2=new Value(15); prt("v1.c="+v1.c+" v2.c="+v2.c); } public static void main(String[] args){ Count ct=new Count(); prt("ct.c="+ct.v.c); prt("v1.c="+v1.c+" v2.c="+v2.c); v1.inc(); prt("v1.c="+v1.c+" v2.c="+v2.c); prt("ct.c="+ct.v.c); }}
실행 결과는 다음과 같습니다.
v1.c=0 v2.c=0 v1.c=27 v2.c=27 v1.c=15 v2.c=15 ct.c=10 v1.c=10 v2.c=10 v1.c=11 v2.c=11 ct.c=11
이 프로그램은 정적 초기화의 다양한 기능을 보여줍니다. Java를 처음 사용하는 경우 결과가 놀라울 수 있습니다. 정적 이후에 중괄호로 인해 혼란 스러울 수 있습니다. 가장 먼저 말씀드리고 싶은 것은 정적으로 정의된 변수는 나타나는 순서에 관계없이 다른 비정적 변수보다 우선한다는 것입니다. 프로그램에서 보듯이 v가 v1과 v2 앞에 나타나더라도 결과적으로 v1과 v2가 v보다 먼저 초기화됩니다. 다음 static{은 정적 변수를 명시적으로 초기화하는 데 사용되는 코드 조각입니다. 이 코드는 클래스가 처음 로드될 때 한 번만 초기화됩니다. 이 코드를 읽고 이해할 수 있다면 static 키워드를 이해하는 데 도움이 될 것입니다. 상속의 경우 상위 클래스의 정적 변수가 먼저 초기화되고 그 다음 하위 클래스의 정적 변수 등이 초기화됩니다. 비정적 변수는 이 기사의 주제가 아니며 여기에서는 Think in Java의 설명을 참조하십시오.
정적 클래스일반적으로 일반 클래스는 정적으로 선언할 수 없으며 내부 클래스만 선언할 수 있습니다. 이때, static으로 선언된 내부 클래스는 외부 클래스를 인스턴스화하지 않고 바로 일반 클래스로 사용할 수 있다. 다음 코드와 같이
public class StaticCls{ public static void main(String[] args){ OuterCls.InnerCls oi=new OuterCls.InnerCls(); } } class OuterCls{ public static class InnerCls{ InnerCls(){ System.out.println("InnerCls"); } } }
출력 결과는 예상한 대로입니다.
InnerCls
일반 클래스와 동일합니다. 내부 클래스의 다른 용도에 대해서는 Think in Java의 관련 장을 참조하세요. 여기서는 자세히 설명하지 않습니다.
2.이것&슈퍼
在上一篇拙作中,我们讨论了static的种种用法,通过用static来定义方法或成员,为我们编程提供了某种便利,从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是,并不是说有了这种便利,你便可以随处使用,如果那样的话,你便需要认真考虑一下自己是否在用面向对象的思想编程,自己的程序是否是面向对象的。好了,现在开始讨论this&super这两个关键字的意义和用法。
在Java中,this通常指当前对象,super则指父类的。当你想要引用当前对象的某种东西,比如当前对象的某个方法,或当前对象的某个成员,你便可以利用this来实现这个目的,当然,this的另一个用途是调用当前对象的另一个构造函数,这些马上就要讨论。如果你想引用父类的某种东西,则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系,所以我们在这一块儿来讨论,希望能帮助你区分和掌握它们两个。
在一般方法中
最普遍的情况就是,在你的方法中的某个形参名与当前对象的某个成员有相同的名字,这时为了不至于混淆,你便需要明确使用this关键字来指明你要使用某个成员,使用方法是“this.成员名”,而不带this的那个便是形参。另外,还可以用“this.方法名”来引用当前对象的某个方法,但这时this就不是必须的了,你可以直接用方法名来访问那个方法,编译器会知道你要调用的是那一个。下面的代码演示了上面的用法:
public class DemoThis{ private String name; private int age; DemoThis(String name,int age){ setName(name); //你可以加上this来调用方法,像这样:this.setName(name);但这并不是必须的 setAge(age); this.print(); } public void setName(String name){ this.name=name;//此处必须指明你要引用成员变量 } public void setAge(int age){ this.age=age; } public void print(){ System.out.println("Name="+name+" Age="+age);//在此行中并不需要用this,因为没有会导致混淆的东西 } public static void main(String[] args){ DemoThis dt=new DemoThis("Kevin","22"); } }
这段代码很简单,不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它,两者效果一样。下面我们修改这个程序,来演示super的用法。
class Person{ public int c; private String name; private int age; protected void setName(String name){ this.name=name; } protected void setAge(int age){ this.age=age; } protected void print(){ System.out.println("Name="+name+" Age="+age); } } public class DemoSuper extends Person{ public void print(){ System.out.println("DemoSuper:"); super.print(); } public static void main(String[] args){ DemoSuper ds=new DemoSuper(); ds.setName("kevin"); ds.setAge(22); ds.print(); } }
在DemoSuper中,重新定义的print方法覆写了父类的print方法,它首先做一些自己的事情,然后调用父类的那个被覆写了的方法。输出结果说明了这一点:
DemoSuper:
Name=kevin Age=22
这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问,那你可以像使用this一样使用它,用“super.父类中的成员名”的方式,但常常你并不是这样来访问父类中的成员名的。
在构造函数中
构造函数是一种特殊的方法,在对象初始化的时候自动调用。在构造函数中,this和super也有上面说的种种使用方式,并且它还有特殊的地方,请看下面的例子:
class Person{ public static void prt(String s){ System.out.println(s); } Person(){ prt("A Person."); } Person(String name){ prt("A person name is:"+name); } } public class Chinese extends Person{ Chinese(){ super(); //调用父类构造函数(1) prt("A chinese.");//(4) } Chinese(String name){ super(name);//调用父类具有相同形参的构造函数(2) prt("his name is:"+name); } Chinese(String name,int age){ this(name);//调用当前具有相同形参的构造函数(3) prt("his age is:"+age); } public static void main(String[] args){ Chinese cn=new Chinese(); cn=new Chinese("kevin"); cn=new Chinese("kevin",22); } }
在这段程序中,this和super不再是像以前那样用“.”连接一个方法或成员,而是直接在其后跟上适当的参数,因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的构造函数,如1和2处。this后加参数则调用的是当前具有相同参数的构造函数,如3处。当然,在Chinese的各个重载构造函数中,this和super在一般方法中的各种用法也仍可使用,比如4处,你可以将它替换为“this.prt”(因为它继承了父类中的那个方法)或者是“super.prt”(因为它是父类中的方法且可被子类访问),它照样可以正确运行。但这样似乎就有点画蛇添足的味道了。
最后,写了这么多,如果你能对“this通常指代当前对象,super通常指代父类”这句话牢记在心,那么本篇便达到了目的,其它的你自会在以后的编程实践当中慢慢体会、掌握。另外关于本篇中提到的继承,请参阅相关Java教程。
三、final
final在Java中并不常用,然而它却为我们提供了诸如在C语言中定义常量的功能,不仅如此,final还可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一。
final成员
当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。其初始化可以在两个地方,一是其定义处,也就是说在final变量定义时直接给其赋值,二是在构造函数中。这两个地方只能选其一,要么在定义时给值,要么在构造函数中给值,不能同时既在定义时给了值,又在构造函数中给另外的值。下面这段代码演示了这一点:
import java.util.List; import java.util.ArrayList; import java.util.LinkedList; public class Bat{ final PI=3.14; //在定义时便给址值 final int i; //因为要在构造函数中进行初始化,所以此处便不可再给值 final List list; //此变量也与上面的一样 Bat(){ i=100; list=new LinkedList(); } Bat(int ii,List l){ i=ii; list=l; } public static void main(String[] args){ Bat b=new Bat(); b.list.add(new Bat()); //b.i=25; //b.list=new ArrayList(); System.out.println("I="+b.i+" List Type:"+b.list.getClass()); b=new Bat(23,new ArrayList()); b.list.add(new Bat()); System.out.println("I="+b.i+" List Type:"+b.list.getClass()); } }
此程序很简单的演示了final的常规用法。在这里使用在构造函数中进行初始化的方法,这使你有了一点灵活性。如Bat的两个重载构造函数所示,第一个缺省构造函数会为你提供默认的值,重载的那个构造函数会根据你所提供的值或类型为final变量初始化。然而有时你并不需要这种灵活性,你只需要在定义时便给定其值并永不变化,这时就不要再用这种方法。在main方法中有两行语句注释掉了,如果你去掉注释,程序便无法通过编译,这便是说,不论是i的值或是list的类型,一旦初始化,确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型,输出结果中显示了这一点:
I=100 List Type:class java.util.LinkedList
I=23 List Type:class java.util.ArrayList
还有一种用法是定义方法中的参数为final,对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。
另外方法中的内部类在用到方法中的参变量时,此参变也必须声明为final才可使用,如下代码所示:
public class INClass{ void innerClass(final String str){ class IClass{ IClass(){ System.out.println(str); } } IClass ic=new IClass(); } public static void main(String[] args){ INClass inc=new INClass(); inc.innerClass("Hello"); } }
final方法
将方法声明为final,那就说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。另外有一种被称为inline的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。
final类
当你将final用于类身上时,你就需要仔细考虑,因为一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的成员,你可以定义其为final,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final型的。你也可以明确的给final类中的方法加上一个final,但这显然没有意义。
下面的程序演示了final方法和final类的用法:
final class final{ final String str="final Data"; public String str1="non final data"; final public void print(){ System.out.println("final method."); } public void what(){ System.out.println(str+"n"+str1); } } public class FinalDemo { //extends final 无法继承 public static void main(String[] args){ final f=new final(); f.what(); f.print(); } }
从程序中可以看出,final类与普通类的使用几乎没有差别,只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出,只是记住慎用。
위 내용은 Java에서 static, this, super 및 final을 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!