面向对象程序设计(二)
六、抽象:abstract
抽象基本只要注意四点:
(1):抽象类就是为了派生子类,而所有的接口都具有抽象属性。
(2):抽象类可以声明实例,但不能创建实例。
(3):只有抽象类、抽象函数,没有抽象成员。
(4):抽象类不一定需要有抽象函数,但仍不能实例化。有抽象函数的类一定是抽象类。抽象类的子类要重写所有父类抽象方法,否则子类仍是抽象类。
父类例:
abstract public shape { int A=0; abstract public int getArea(); }
子类例:
class triangle extends shape2 { int a; int b; int c; int A; public triangle(int a,int b,int c) { System.out.println("son construct"); //不要忘了会在此句前执行super();语句 this.a=a; this.b=b; this.c=c; this.A=a+10; super.A=a+15; } public int getarea() { System.out.println("son"+A); return a+b+c; } }
不能创建抽象类对象:shape ab=new shape(); //报错Cannot instantiate the type shape
但可以声明抽象类对象:
public static void main(String []args) { shape ab=new triangle(10,5,12); System.out.println(String.valueOf(ab.getarea())+'+'+String.valueOf(ab.A)); }
输出结果:
son construction //调用子类构造方法,输出此句然后设置a=10,b=5,c=12,子类A=20,父类A=25;
son20 //上转型的抽象方法ab.getArea()会调用子类方法,会输出son20,然后返回27
27+25 //ab.A属于同名成员域,所以上转型调用父类A25,输出27+25
七、静态:static
类一般不具有静态属性(除内部类),一般是静态域和静态方法。
每个实例成员都有一套独立的不具有静态属性的成员域,而静态成员一般属于类,但访问静态成员方法或域可以使用任一个实例对象表达式或者类名。
例:Shape s1=new Shape(); Shape s2=new Shape(); 则s1.number==s2.number==Shape.number,其中number是Shape类中一个静态成员域,静态成员方法亦是如此。
非静态成员方法访问本类或父类非静态成员域或成员方法,直接调用名字或者用this修饰(正常情况,注当子类没有与父类同名的成员域时,this,super均可调用父类成员名)。
非静态成员方法访问本类或父类静态成员方法或成员域时,直接使用名字或者用类名.成员域(或方法,不可使用this)。
静态成员方法访问本类或者父类的静态成员域或成员方法时,直接使用名字或使用类名.成员域(或方法)。
一般来说,静态成员方法只能访问静态成员域和静态成员方法,但要想访问某类的非静态成员方法可以通过创建该类实例对象来访问(例如main访问其他类成员方法)。
static块:即在类中有形如static{// }的程序块,会在类加载时运行且只运行一次(因为一个类只会加载一次),可以将其看作是一个没有名称、没有返回值、没有参数、不需要调用的三无静态方法,它具有普通静态方法的特征:1 不能调用非静态变量或非静态成员方法。 2 无法使用this和super关键字,因为这是属于类对象的。 3 无法在其中声明静态变量,其实普通成员方法也不能声明静态变量,因为静态变量是属于类的,只能在类体中声明。 4 无法使用public、private、protected等关键字,只能使用默认,同样的其他方法也不能声明具有访问属性的变量,只能声明在类体中。
关于类加载的时机:(1)调用Class.forName()方法。(2)实例化对象或者是子类对象。(3) 调用静态成员域。(4)调用静态成员方法。而不加载类也有一些需要注意,若是调用的静态变量是final类型,那么不需要加载类,也就不会执行static语句块。若只创建引用不需要加载类。若子类调用父类静态方法时,子类有没有覆盖父类方法则不加载子类,只加载父类,否则都加载。
多个static块的执行语序,包括与静态成员域都是按照顺序来执行,且优先于其他调用语句包括构造函数。
八、最终:final
用于修饰非抽象类、类的非抽象成员方法、接口的成员域、类的成员域,不可修饰所有的接口成员方法、类的抽象成员方法、构造方法、抽象类、接口。
若用于修饰非抽象类则该类不能被继承。
若修饰类非抽象成员方法则该类不能被覆盖重写,若在子类中声明一个与父类名称相同的成员方法,则会编译出错。
若修饰接口或者类的成员域,且同时还有静态属性那么只能在定义时赋值,之后不能再修改,成为常量,若只有final类型而无static类型,那它能在定义时或者构造函数时赋值。
例如:
class A { final int a; public A(int m) { this.a=m; //可以 } } public class finaltest { public static void main(String []args) { A fin=new A(8); System.out.println(fin.a); } }
会输出8,证明确实构造函数修改了a的值。
或者将类A修改为:
class A { final int a=5; //可以 public A(int m) { } }
会输出5,赋值也可以。
但是:
class A { final int a=5; public A(int m) { this.a=m; //不可以,The final field A.a cannot be assigned } }
class A
{
final int a; //不可以,The blank final field b may not have been initialized
}
就会报错。这也就说明只能直接声明时赋值或者构造函数赋值二选一,且只能赋值一次,但不能不赋值,它不会自动变为0。
静态最终变量就只能声明时初始化赋值像:final static int b=9;并且不能用构造函数赋值,且也是不能不赋值。
class A { final static int a=5; //可以 } class A { final static int a; //不可以 } class A { final static int a; public A(int m) { this.a=m; //不可以 } } class A { final static int a=5; public A(int m) { this.a=m; //不可以 } }
2018.4.28补:注意到final修饰值无法修改,例如:
public class finaltest { public static void main(String []args) { final int x=0; x=5; //报错,提示final变量无法修改,The final local variable x cannot be assigned. It must be blank and not using a compound assignment } }
但如果final修饰的是引用呢?
final String a=new String("final"); a=a+" test";
会报同样的错误,让我们来换一个对象
final StringBuilder a=new StringBuilder("final"); a.append(" test"); System.out.println(a.toString());
输出 final test说明final修饰引用的值是可以改变的,但其指向的对象不会改变。
九、接口:interface
(1)Java不允许继承多个父类,但可以实现多个接口,变相的实现了多继承。而形式如下:
class d implements getA,getB //getA和getB是两个接口。
(2) inteface可以有public和默认模式,若是public模式,则文件名与此接口名相同(规则同类),可以被所有软件包使用(不同包用import导入,同类)。若是默认模式则只能本包使用。而接口一般不具有private和protected属性,这点也与类相同。此外接口一般都具有抽象属性因此加abstract修饰基本没用,因此接口也能声明对象但不能创建对象——与抽象类相同。
(3)接口可以继承其他接口,同样的成为父接口、子接口。不同的是子接口可以继承多个父接口,而子类只能继承一个父类,类也能实现多个接口。例如:
class A extends B implements C,D,E{} //类实现多个接口 interface F extends G,H,I{} //接口继承多个子接口
若类A不是抽象方法则必须要实现C、D、E中全部的抽象方法,但若其中有同名同参函数,只需实现一个即可。若实现F这种接口需要实现其所有父接口G、H、I的抽象方。总结来说也就是说一个非抽象类要实现所有接口及其父接口具有的所有的不同名抽象方法。但若是一个抽象类,可以只实现其中一部分,但子类要实现剩余所有方法,否则仍必须声明为抽象类。
若类实现的接口中有父子关系那么等价于实现最底层的子接口。例如,(2018.05.20添加代码)
public interface getB { int A=52; double getarea(int c); } //子接口 public interface getA extends getB{ int B=63; double get(int c); } //父接口 class d implements getA{ @Override public double get(int c) { // TODO Auto-generated method stub System.out.println(c*5); return 0; } @Override public double getarea(int c) { // TODO Auto-generated method stub System.out.println(c); return 0; } } //类不管是implements getA还是implements getA,getB都是一样的,因为getA继承于getB
//但要是implements getB只需要实现父接口getB的方法即可
若父类(可以是抽象类)实现了的接口,子类再实现此接口或其父接口没有意义,但不会报错,子类和父类合力把接口及其父接口全部实现即可。
(4)接口所有的成员域都具有public、static、final属性,而接口所有的成员方法都具有public和abstract属性,不论你是否添加上述修饰词。因为static属性,所以访问成员域时可以用类名、接口名以及实例对象名——类名是指所有实现此接口的类名或其子类名,接口名指定义成员域的接口名称及其子接口名,实例对象名则是上述接口和类的实例对象名(即使是抽象类和接口也能声明)。
因为具有static和final属性,所以成员域必须初始化赋值且不能改变。成员方法由于不是静态方法只能使用实例对象名调用。