fd1.a[i]++;//Objectisn'tconstant!
//!
fd1.v2=newValue();//Error:
Can't,v2是final的
//!
fd1.v3=newValue();//changehandle,v3是final的
//!
fd1.a=newint[3];//a是final的
fd1.print("fd1");
System.out.println("CreatingnewFinalData");
FinalDatafd2=newFinalData();
fd1.print("fd1");
fd2.print("fd2");
}
}
publicclassfinalequals{
publicstaticvoidmain(String[]args){
Stringa="hello2";
finalStringb="hello";
Stringd="hello";
Stringc=b+2;
System.out.println("c=b+2"+c);
Stringe=d+2;
System.out.println("e=d+2"+e);
System.out.println((a==c));
System.out.println((a==e));
System.out.println(a.equals(e));
}
}///:
~
c=b+2hello2
e=d+2hello2
true
false
true
大家可以先想一下这道题的输出结果。
为什么第一个比较结果为true,而第二个比较结果为fasle。
这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。
也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。
这种和C语言中的宏替换有点像。
因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b替换为它的值。
而对于变量d的访问却需要在运行时通过链接来进行。
想必其中的区别大家应该明白了,不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化。
(2)static关键字(修饰属性(一定是类属性而不是局部变量)和方法,但是可以修饰内部类)
类变量:
(存储在堆里)
1.类变量:
staticintsum;
2.类变量被类内所有实例共享
3.类变量又叫static变量,classvariablesstaticvariables
类方法:
1.类方法:
方法前static
2.类方法可以被调用即使类实例不存在,即可以使用类名调用类方法;
doubleroot=Math.sqrt(453.0);
3.不能从一个static方法内部发出对于一个非static方法的调用,但是反过来可以。
说明:
有些属性希望被所有对象共享,则必须将其声明为static属性,被static声明的
属性成为全局属性。
声明为static的方法有以下几条限制:
· 它们仅能调用其他的static方法。
· 它们只能访问static数据。
· 它们不能以任何方式引用this 或super。
构造函数前面不能有访问修饰符,默认的是public,但可以写为private以及
protected。
构造函数不允许加static。
Eg:
test_static
packagestatic_java;
publicclasstest_static{
publicintx=0;
protecteddoubley=1;
classtestone{
testone(){
System.out.println("woshi");
}
}
staticclasstesttwo{
testtwo(){
System.out.print("\n");
//newtextone();只能引用static方法或者内部类,不能引用外部类因为外部类非static
}
//publicinti=x;static类里面只能引用static属性
publicintx;
//this.x=1;static类里面不能用this以及super
}
}
先加载父类,再加载子类:
classPerson{
static{
System.out.println("personstatic");
}
publicPerson(Stringstr){
System.out.println("person"+str);
}
}
classMyClassextendspersonStatic{//先执行父类成员属性初始化
Personperson=newPerson("MyClass");//(5)
static{
System.out.println("myclassstatic");//
(2)
}
publicMyClass(){
System.out.println("myclassconstructor");//(6)
}
}
publicclasspersonStatic{
Personperson=newPerson("Test");//(3)
static{
System.out.println("teststatic");//
(1)
}
publicpersonStatic(){
System.out.println("testconstructor");//(4)
}
publicstaticvoidmain(String[]args){
newMyClass();//有main方法时,要先于成员初始化执行
}
}///:
~
teststatic
myclassstatic
personstatic
personTest
testconstructor
personMyClass
myclassconstructor
类似地,我们还是来想一下这段代码的具体执行过程。
首先加载Test类,因此会执行Test类中的static块。
接着执行newMyClass(),而MyClass类还没有被加载,因此需要加载MyClass类。
在加载MyClass类的时候,发现MyClass类继承自Test类,但是由于Test类已经被加载了,所以只需要加载MyClass类,那么就会执行MyClass类的中的static块。
在加载完之后,就通过构造器来生成对象。
而在生成对象的时候,必须先初始化父类的成员变量,因此会执行Test中的Personperson=newPerson(),而Person类还没有被加载过,因此会先加载Person类并执行Person类中的static块,接着执行父类的构造器,完成了父类的初始化,然后就来初始化自身了,因此会接着执行MyClass中的Personperson=newPerson(),最后执行MyClass的构造器。
(3)super
classPerson{
publicstaticvoidprt(Strings){
System.out.println(s);
}
Person(){
prt("APerson.");
}
Person(Stringname){
prt("Apersonnameis:
"+name);
}
}
publicclassChineseextendsPerson{
Chinese(){
super();//调用父类构造函数
(1)
prt("Achinese.");//
(2)
}
Chinese(Stringname){
super(name);//调用父类具有相同形参的构造函数(3)
prt("hisnameis:
"+name);//(4)
}
Chinese(Stringname,intage){
this(name);//调用当前具有相同形参的构造函数(5)
prt("hisageis:
"+age);//(6)
}
publicstaticvoidmain(String[]args){
Chinesecn=newChinese();
cn=newChinese("kevin");
cn=newChinese("kevin",22);
}
}//结果:
APerson.
Achinese.
Apersonnameis:
kevin
hisnameis:
kevin
Apersonnameis:
kevin
hisnameis:
kevin
hisageis:
22
在这段程序中,this和super不再是像以前那样用“.”连接一个方法或成员,而是直接在其后跟 上适当的参数,因此它的意义也就有了变化。
super后加参数的是用来调用父类中具有相同形式的 构造函数,如1和2处。
this后加参数则调用的是当前具有相同参数的构造函数,如3处。
当然,在 Chinese的各个重载构造函数中,this和super在一般方法中的各种用法也仍可使用,比如4处,你 可以将它替换为“this.prt”(因为它继承了父类中的那个方法)或者是“super.prt”(因为它 是父类中的方法且可被子类访问),它照样可以正确运行。
但这样似乎就有点画蛇添足的味道 了。
classPerson{
publicintc;
privateStringname;
privateintage;
protectedvoidsetName(Stringname){
this.name=name;
}
protectedvoidsetAge(intage){
this.age=age;
}
protectedvoidprint(){
System.out.println("Name="+name+"Age="+age);
}
}
publicclassDemoSuperextendsPerson{
publicvoidprint(){
System.out.println("DemoSuper:
");
super.print();
}
publicstaticvoidmain(String[]args){
DemoSuperds=newDemoSuper();
ds.setName("kevin");
ds.setAge(22);
ds.print();
}
} //DemoSuper:
Name=kevinAge=22
子类protected
子类private
在DemoSuper中,重新定义的print方法覆写了父类的print方法,它首先做一些自己的事情,然后调用父类的那个被覆写了的方法。
输出结果说明了这一点:
DemoSuper:
Name=kevinAge=22
这样的使用方法是比较常用的。
另外如果父类的成员可以被子类访问,那你可以像使用this一样使用它,用“super.父类中的成员名”的方式,但常常你并不是这样来访问父类中的成员名的。
在构造函数中构造函数是一种特殊的方法,在对象初始化的时候自动调用。
在构造函数中,this和super也有上面说的种种使用方式,并且它还有特殊的地方。
注意(访问权限):
父类protected的属性和方法,子类可以使用;父类private的属性和方法,子类无法使用;protected以及private的属性和方法在子类父类以外无法使用
1、在派生类的构造函数中没有指明基类构造函数时,java会自动在第一行加入对基类构造函数的默认调用Super()语句。
2、当基类的构造函数需要参数时,则一定要在派生类的构造函数第一条语句明确调用基类代参数的构造函数super(args)。
(4)继承(Inheritance):
派生derivation
继承作用:
实现代码复用,减少开发时间,减少维护时间
Subclass:
子类;baseclass:
父类
父类private无法继承,protected可以继承,java不支持多根继承
基本知识:
1.子类有不同于父类的属性和方法
2.父类接收到的消息可以被子类接收到
3.子类和父类有相同的类型
执行顺序:
有extends父类的情况下,先执行父类static属性定义,然后进行属性声明,然后是是构造函数,然后是子类执行
基类子对象:
创建一个派生类对象时,它在其中包含了基类的一个“子对象”。
基类子对象必须初始化,有且只有一种方法。
在派生类的构造函数中,第一条语句调用基类的构造函数来初始化基类子对象。
如何实现基类子对象初始化:
1.在派生类的构造函数中没有指明基类构造函数时,java会自动在第一行加入对基类构造函数的默认调用Super()语句。
2.当基类的构造函数需要参数时,则一定要在派生类的构造函数第一条语句明确调用基类代参数的构造函数super(args)。
3.当基类的构造函数不需要参数时,则在派生类的构造函数中可以不明确的调用super()。
Whatkeyworddoesthesubclassusetodeclareitssuperclass?
Super.
Sandwich.java
classMeal{
Meal(){System.out.println("Meal()");}
}
classBread{
Bread(){System.out.println("Bread()");}
}
classCheese{
Cheese(){System.out.println("Cheese()");}
}
classLettuce{
Lettuce(){System.out.println("Lettuce()");}
}
classLunchextendsMeal{
Lunch(){System.out.println("Lunch()");}
}
classPortableLunchextendsLunch{
PortableLunch(){
System.out.println("PortableLunch()");
}
}
publicclassSandwich{
Breadb=newBread();
Cheesec=newCheese();
Lettucel=newLettuce();
Sandwich(){
System.out.println("Sandwich()");
}
publicstaticvoidmain(String[]args){
newSandwich();
}//构造方法按顺序声明
}//
Bread()
Cheese()
Lettuce()
Sandwich()
(5)抽象类(abstractclass)
1.抽象类不能被初始化
2.有抽象方法必须要有抽象类
3.抽象类中不一定要有抽象方法PS:
抽象类中可以有普通方法,但是必须要有方法体
4.普通子类继承抽象类必须把所有的抽象方法实现
5.抽象方法:
方法前加abstract,不实现
6.抽象类不可以实例化(声明对象)
7.抽象子类继承抽象父类,不可以实现父类抽象方法
8.使用限制:
abstract不能与final并列修饰同一个类。
abstract不能与private、static、final或native并列修饰同一个方法。
9.注意,抽象类和普通类的主要有三点区别:
1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继
承,子类便无法实现该方法),缺省情况下默认为public。
2)抽象类不能用来创建对象;
3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。
如果子类没有实
现父类的抽象方法,则必须将子类也定义为为abstract类。
抽象方法是一种特殊的方法:
它只有声明,而没有具体的实现。
抽象方法的声明格式为:
Abstractvoidfun();
如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键
字修饰。
因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。
publicclassabstractclass{
abstractclassab{
abstractinttext();//此处仅为抽象方法,没有方法的实现
}
}
对于一个父类,如果它的某个方法在父类中实现出来没有任何意义,必须根据子类的实
际需求来进行不同的实现,那么就可以将这个方法声明为abstract方法,此时这个类也
成为abstract类了。
publicclassabstractclass{
abstractclassab{
abstractinttext();
}
publicstaticvoidmain(String[]args){
abab=newab();//此处报错
}
}
(6)接口(interface):
必须放在extends之后classCextendsBimplementsA
创建