今天继续深入Java知识,巩固这门面向对象的语言。
1、this 与super
this 指代当前对象,主要应用场合:
在一些容易混淆的场合,如成员方法的参数名与数据成员同名;
成员方法的局部变量名与数据成员同名。
Public class test{
int i;
Public othermethod(){
int i=3;
this.i=i+2;
}
}
this(参数)——引用重载的构造方法
this指代当前对象。
super本文指代父类中的域变量或方法,super访问当前类的直接父类,主要应用场合:
子类的数据成员或成员方法与父类的数据成员或成员方法名字相同时,当要调用父类的同名方法或同名数据成员时则可用super来指明。即super.数据成员;super.成员方法
super(参数)//调用父类构造函数
2、构造方法的多态与继承
构造方法的多态
构造方法的多态指构造方法可以被重载和被子类覆盖。
一个类的若干个重载的构造方法之间可以相互调用。当一个构造方法需要调用另一个构造方法时,可以使用关键字 this,同时这个调用语句应该是整个构造方法的第一个可执行语句。构造方法的调用不能直接使用其名称,必须使用this进行相互调用,而且必须为构造方法中的第一句
使用关键字this来调用同类的其它构造方法,优点同样是 以最大限度地提高对已有代码的利用程度,减少程序的维护 工作量。
class AddClass
{
public int x=0,y=0,z=0;
AddClass()
{
this.x=1;
}
AddClass (int x)
{ this.x=x; }
AddClass (int x,int y)
{ this(x); this.y=y; } //调用第一个构造方法
AddClass (int x,int y,int z)
{ this(x,y);
this.z=z;
} //调用第二个构造方法
public int add()
{return x+y+z; }
}
public class RunAddClass
{
public static void main(String[] args)
{
AddClass p1=new AddClass(2,3,5);
AddClass p2=new AddClass(10,20);
AddClass p3=new AddClass(1);
AddClass p4=new AddClass();
System.out.println("x+y+z="+p1.add());
System.out.println("x+y="+p2.add());
System.out.println("x="+p3.add());
}
}
结果:
x+y+z=10
x+y=30
x=1
构造方法的继承
构造方法的继承遵循以下的原则:
① 父类构造方法,子类可以在自己的构造方法中使用super来调用,但必须是子类构造方法的第一个可执行语句。
② 若子类构造方法中没有显式调用父类构造方法,也没有用 this调用重载的其它构造方法,则在产生子类的对象时, 系统在调用子类构造方法的同时,默认调用父类无参构造方法。若子类构造方法中显式调用了父类构造方法,或使 用了this,则不会默认调用父类无参构造方法。
综上两点:子类的构造方法必定调用父类的构造方法。如果不显式用super方法,必然隐含调用super()。
例如:
class AddClasse
{
public int x=0,y=0,z=0;
AddClasse (int x)
{ this.x=x; }
AddClasse (int x,int y)
{ this(x); this.y=y; } //调用第一个构造方法
AddClasse (int x,int y,int z)
{ this(x,y); this.z=z; } //调用第二个构造方法
public int add()
{return x+y+z; }
}
public class SonAddClass extends AddClasse
{ int a=0,b=0,c=0;
SonAddClass (int x)
{ super(x); a=x+7; }
SonAddClass (int x,int y)
{ super(x,y); a=x+5; b=y+5; }
SonAddClass (int x,int y,int z)
{ super(x,y,z); a=x+4; b=y+4; c=z+4;}
public int add()
{System.out.println("super:x+y+z="+super.add());
return a+b+c;
}
public static void main(String[] args)
{ SonAddClass p1=new SonAddClass (2,3,5);
SonAddClass p2=new SonAddClass (10,20);
SonAddClass p3=new SonAddClass (1);
System.out.println("a+b+c="+p1.add());
System.out.println("a+b="+p2.add());
System.out.println("a="+p3.add());
}
}
结果:
super:x+y+z=10
a+b+c=22
super:x+y+z=30
a+b=40
super:x+y+z=1
a=8
class Pare
{int i;
Pare(){i=6;}
}
class Construct extends Pare
{ Construct(){}
Construct(int num){}
public static void main(String[] args){
Construct ct = new Construct(9);
System.out.println(ct.i);
}
}
6
3、抽象类
抽象类注意以下几个方面:
① 抽象类中可以有零个或多个抽象方法,也可以包含非抽象方法。
② 只要有一个抽象方法,类前就必须有abstract修饰。若没有抽象方法,类前也可有abstract修饰。
③ 抽象类不能创建对象,创建对象必须由具体子类来实现,但可以有声明,声明能引用所有具体子类的对象。
④ 对于抽象方法来说,在抽象类只指定其方法及其类型,而不书写其实现代码。抽象类必定要派生子类,若派生的子类是具体类,则具体子类中必须实现抽象类中定义的所有抽象方法(覆盖);若子类还是抽象类,父类中已有的abstract方法,则子类中就不能再有。
⑤ 在抽象类中,非抽象方法可以调用抽象方法。
⑥ abstract不能与final并列修饰同一个类;abstract 不能与private ,static,final或native并列修饰同一个方法;abstract 类中不能有private成员 。
4、接口
接口的两种含义:
① 可以被引用调用的方法(public方法或同包中的protected方法或默认方法);
② 另一种是同“类”概念地位相当的专有概念interface, interface是功能方法说明的集合。
接口的特性:
① 接口定义,使用关键字 interface,而不是 Class,interface前的修饰符要么是public,要么是默认。
② 接口中定义的域变量全是final static ,即静态常量。即使没有任何修饰符,其效果完全等效。
③ 接口没有构造方法,接口中所有方法自动属于 abstract,即使没有任何修饰符,其效果完全等效。
④ 接口中不能包含静态方法,因为static 修饰的方法必然被直接调用,所以不能声明为抽象方法。
⑤ 接口中只能定义静态常量,不能含有实例域。
⑥ 不能在接口中实现方法。
⑦ 接口具有继承性,可通过extends关键字声明接口的父接口。
接口和子接口都不能有自己的实例对象,但可以有自己的声明,可引用那些实现本接口或子接口的类对象。因此接口中的常量必须定义为static,以表明其没有对象这一特点。
接口实现要注意几点:
① 在类中,用implements关键字来实现接口。一个类可以实现多个接口,在implements后用逗号隔开多个接口的名字。一个接口也可被多个类来实现。
② 接口的实现者可以继承接口中定义的常量,其效果等效于在这个实现类当中定义了一个静态常量。
public interface Caculator
{ static final double PI=3.1415926;
static final double E=2.71828183 ;
double sum;//接口中的域自动设为static final
}
③ 如果实现某接口的类不是abstract的抽象类,则在类的定义部分必须实现接口的所有抽象方法,而且方法头部分应该与接口中的定义完全一致。
④ 如果实现接口的类是abstract类,则它可以不实现该接口的所有方法。但对于抽象类的任何一个非抽象的子类而言,接口中的所有抽象方法都必须实现。这些方法体可以来自抽象的父类(被子类继承),也可以来自子类自身的实现,这主要体现了非抽象类中不能存在抽象方法的原则。
⑤ 接口的抽象方法的访问限制符如果为缺省或public时,类在实现方法时,必须显式使用public修饰符,否则将被警告为缩小了接口中定义的方法的访问控制范围。即所有实现interface中的方法必须被申明为public。
抽象类 | 接口 | ||
共同点 | 二者都可具有抽象方法,都不能实例化,但都可以有自己的声明,并能引用子类或实现类对象。 | ||
不同点 | 属性变量 | 可以有变量 | 不能有,只能是静态常量。 |
成员方法 | 可以有具体方法(而且具体方法可以调用抽象方法)。 | 如果有方法,则全部是抽象方法。 | |
实现策略 | 必须有子类继承 | 必须有实现类实现 | |
扩展性 | 弱 | 强 |
5、引用
抽象类声明进行引用:
引用的形成:先声明、后赋予对象、成为引用
抽象类声明进行引用的特点:抽象类声明可以引用所具体子类对象
父类声明和子类声明都引用同一个子类对象时的区别:父类声明是从父类的角度去引用对象,而子类声明是从子类角度去引用对象。父类声明所引用的子类对象可以经过显式的转化赋值给予子类声明。但子类声明所引用的子类对象赋值给父类的声明则不需要显示的转化。
接口声明进行引用:
接口声明进行引用的特点:只能引用实现类对象的接口方法。
如:
A a=new A(); //a引用A的对象
a=new A1();//a又引用A1的对象,放弃引用A对象
A1 a1=(A1) a ;//通过 a,a1也应用了A1对象,但是需要显式转化。
a=new A2(); //a放弃引用A1对象,而引用了A2对象
A aa=a1 ;//将A1对象通过a1赋值给父类声明aa,此时aa也引用了A1对象。
非法:A1 a1=new A2(); A1 a1=new A();
6、内部类
局部内部类:
定义在方法中的内部类称为局部类
定义形式:
void f(){
class Inner{…..}
}
与方法的局部变量类似,局部类不能用public或private修饰
局部类的作用域被限定在声明这个局部类的块中
局部类可以对外部世界完全隐藏,除了f()方法外,其他代码方法都不能访问局部类
局部类可以访问外部类的所有属性和方法,包括private属性
局部类只能引用方法体内的常量,不能访问方法体内的局部变量
成员内部类
定义在外部类的中,作为外部类的一个成员存在,与外部类的属性、方法并列
定义形式:
class Outer{
class Inner{….}
}
成员类可以引用外部类的所有成员,包括被private修饰的
class Outer{
private int a=10;
void f(){…}
class Inner{int d;
public Inner(){ d=a; f();}
}
}
外部类与成员类定义的成员重名时,利用关键字this引用成员
class Outer{
int a;
class Inner{
int a=20;
void f(){
int a=10;
System.out.println(a);
System.out.println(this.a);
System.out.println(Outer.this.a); }
}
}
结果:
10
20
0
成员类的不能被static修饰,但允许静态常量存在成员
class Outer{
class Inner{
int a; //可以
void f(){…} //可以
static final int b=10; //可以
static int c; //错误
static void g(){….} //错误
}
}
只有创建了外部类对象之后,才能创建内部类对象,才能使用内部类的成员
Outer out=new Outer();
Outer.Inner in=out.new Inner();
匿名内部类
没有取名的内部类就是匿名类
定义形式:
new 类A() {
方法体
}
此时产生的对象是类A的子类对象
new 接口A() {
方法体
}
此时产生的对象是接口A的实现类的对象
匿名内部类的使用原则:
匿名内部类不能定义构造方法
不能定义任何静态成员、方法
不能被public、protected、private、static修饰
只能创建一个实例对象
只用到类的一个实例
类在定义后马上用到
类非常小
静态内部类
被static修饰的成员类,称为静态内部类
定义形式:
class Outer{
static class Inner{
}
}
静态类只能引用外部类的static成员,不能引用普通成员
class Outer{
static int a=10;
void f(){…}
static class Inner{
int d=a;//static变量
void g(){f(); //不能引用普通方法}
}
}
静态类作为外部类的静态成员,可以在不创建外部类对象的条件下,直接创建Inner类对象
Outer.Inner in=new Outer.Inner();