女神镇楼
1、类的继承
继承是面向对象中的一个非常重要的概念,是的整个程序具有一定的弹性,在程序中,复用一些已经定义的完善的类,不仅可以减少软件的开发周期,还可以提高软件的可维护性和可拓展性。
继承的基本思想是基于某个父类的拓展,制造出一个新的子类,子类可继承父类的属性和方法,也可以增加原父类不具备的属性和方法,或者重写父类中的某个方法。比如平行四边形继承了四边形类,这个时候,平行四边形类将所有四边形类中的属性和方法保留下来,并拓展了新的属于自己的属性和方法。
代码示例---父类:
public class Test { public Test(){ //构造方法 //SomeSentence } protected void doSomething(){ // 成员方法 } protected Test dolt(){ // 方法返回值类型为Test类型 return new Test(); } }
代码示例---子类:
public class Test2 extends Test { public Test2(){ //构造方法 super(); // 调用父类的构造方法 super.doSomething(); //调用父类的成员方法 } public void doSomethingNeW(){ //新增方法 } public void doSomeThing(){ //重写父类方法 } protected Test2 dolt(){ //重写父类方法,返回值类型为Test2类型 return new Test2(); } }
实例说明:
实例中,Test类是Test2类的父类,Test2类是Test类的子类,在子类中连同初始化父类的构造方法来完成子类的初始化操作,即可以在子类的构造方法中使用super()语句调用父类的构造方法,也可以在子类中通过super关键字调用父类的成员方法。但是在子类中没有权限调用父类中使用private修饰的方法,只可以调用父类中修饰为public或者protected的成员方法。
继承不止可以拓展父类的功能,还可以对父类的方法进行重写,重写也可以成为覆盖,就是在子类中,将父类的成员方法的名称保留,重写成员方法实现的内容,可更改成员方法的储存权限,或者修改成员方法的返回值类型,还可以将权限修饰符修改为public。
继承中还有一种特殊的重写方式,子类与父类的成员方法的返回值、方法名称、参数类型及个数完全相同,唯一不同的是方法实现内容,这种特殊的重写方式称为重构。
当重写父类方法时,修改方法的修饰权限只能从小的范围到大的范围改变,例如,父类中的
doSomething0方法的修饰权限为protected, 继承后子类中的方法doSomething0的修饰权限只能修改
为public,不能修改为private。
在java中,一切都是一对象的形式进行处理,在继承机制中,创建一个子类对象,将包含一个父类子对象,这个对象与父类创建的对象是一样的,两者的区别就在于,后者来自于外部,而前者来自子类对象内部。当实例化子类对象时,父类对象也相应被实例化,也就是说,在实例化子类对象时,java编译器会在子类的构造方法中自动调用父类的无参构造方法。
代码示例:
public class Parent { Parent(){ System.out.println("调用父类的Parent()方法"); } }
public class Subparent extends Parent { //继承Parent Subparent(){ System.out.println("调用子类Subparent的构造方法"); } }
public class Subbroutine extends Subparent { // 继承 Subparent Subbroutine(){ System.out.println("调用子类Subbroutine的方法。"); } public static void main(String[] args) { Subbroutine s = new Subbroutine(); } }
运行结果:
运行结果说明:
子类Subroutine的主方法中只调用子类的构造方法实例化子类对象,并且在子类的构造方法中没有调用父类构造方法的任何语句,但是在实例化子类对象时,他会相应的调用父类的构造方法,调用构造方法的顺序是:顶级父类==上一级父类==子类,也就是实例化子类对象时首先实例化父类对象,再实例化子类对象。在子类的构造方法访问父类的构造方法之前,已经完成了父类的实例化操作。
说明:
在实例化子类对象时,父类无参构造方法将会被自动调用,但是有参构造方法不能被自动调用,只能借用super关键字显式的调用父类方法。
如果使用finalize()方法对对象进行清理,需要确保子类的finalize()方法最后一个动作是调用父类的finalize()方法,用来保证垃圾回收对象所占用的内存时,对象所有部分都能被正常终止。
2、Object类
是什么===Object类是比较特殊的类,是所有类的父类,是java类层中的最高层类。创建一个类时,它总是在继承,除非某个类指定他从某个类继承,否则他就是从java.lang.Object类继承来。
在Object类中主要包括clone()、finalize()、equals()、toString(等方法,其中常用的两个方法为equals()
和toString(方法。由于所有的类都是Object类的子类,所以任何类都可以重写Object类中的方法。
class Anything{ ... } //等价于 class Anything extends Object{ ... }
Object类中的getClass()、notify()、 notifyAll()、 wait()等 方法不能被重写,因为这些方法被定义为final类型。
Object类中的几个重要方法:
(1)getClass()方法
getClass()方法是Object类定义的方法,返回对象执行时的Class实例,然后用此实例调用getName()方法获取类的名称。
getClass().getname();
(2)toString()方法
toString方法功能是将一个对象返回字符串形式,返回一个String实例,在实际应用中,通常需要重写toString()方法,为对象提供一个特定的输出模式。当这个类转换为字符串或者字符串连接时,会自动重写toString()方法。
代码示例:
public class ObjectInstance { public String toString(){ return "在"+getClass().getName()+"类中重写了toString方法"; } public static void main(String[] args) { System.out.println(new ObjectInstance()); } }
(3)equal()方法 == 用来比较两个对象的实际内容,“==”是比较两个对象的引用是否相等
代码示例:
class V{} public class OverWhiteEqual { public static void main(String[] args) { String s1 = "123"; String s2 = "123"; System.out.println(s1.equals(s2)); V v1 = new V(); V v2 = new V(); System.out.println(v1.equals(v2)); } }
实例分析:
在自定义类中使用equals()方法进行比较时,返回false,因为equals()方法默认实现的是“==”比较两个对象的引用地址,而不是比较对象的内容,所以要想做到真正的比较两个对象的内容,需要在自定义类中重写equals()方法
3、对象的类型转换
(1)向上转型
代码示例:定义平行四边形类和四边形类
public class Quadrangle { // 四边形类 public static void draw(Quadrangle q){ // 四边形类中的方法 System.out.println("四边形类中输出!"); } }
public class Parallelogram extends Quadrangle { public static void main(String[] args) { Parallelogram p = new Parallelogram(); draw(p); } }
实例分析:
四边形类中有draw()方法,参数是Quadrangle(四边形类),在平行四边形类中调用的时候传的参数是Parallelogram(平行四边形类)。这里需要强调一下,平行四边形也是一种类型的四边形,所以平行四边形类的对象可以看做四边形类的对象,相当于“Quadrangle obj = new Parallelogram”,就是吧子类对象赋值给父类类型的变量,称之为向上转型。
平行四边形类继承了四边形类,常规的继承图都是将顶级类设置在页面的顶部,然后逐渐向下,所以将子类对象看作是父类对象被称为“向上转型”。
由于向上转型是从一个较具体的类到较抽象的类的转换,所以它总是安全的,如可以说平行四边形是特殊的四边形,但不能说
四边形是平行四边形。
(2)向下转型
向下转型是将较抽象类转换为较具体的类,可以说子类对象总是父类的一个实例,但父类对象不一定是子类的实例。
代码示例:
public class Quadrangle { // 四边形类 public static void draw(Quadrangle q){ // 四边形类中的方法 System.out.println("四边形类中输出!"); } }
public class Parallelogram extends Quadrangle { public static void main(String[] args) { Parallelogram p = new Parallelogram(); draw(p); //将平行四边形类对象看做四边形类对象,成为向上转型操作 Quadrangle q = new Parallelogram(); Parallelogram p1 = q; /* 将父类对象赋给子类对象,这种写法是错误的 */ // 将父类对象赋予子类对象,并强制转换为子类型。这种写法是正确的 Parallelogram p2 = (Parallelogram) q; } }
实例分析:
父类对象直接赋予子类,会发生编译器错误,因为父类不一定是子类的实例,
越是具体的对象具有的特性越多,越是抽象的对象,具有的对象越少。
在向下转型操作中,将特性范围小的对象转型为特性范围大的对象会出现问题。
这时候父类对象强制转换为某个子类对象,称为显式类型转换。在程序中使用向下转型操作时,必须使用显式类型转换。
4、使用instanceof操作符判断对象类型
向下转型操作时,先判断父类对象是否是子类的实例,instanceof 操作符,可以判断一个引用指向的对象是否是某一个类型或者其子类, 是则返回true,否则返回false。
instanceof的语法格式如下:
myobject instanceof ExampleClass
myobject: 某类的对象引用。
ExampleClass:某个类。
使用instanceof 操作符的表达式返回值为布尔值。如果返回值为true, 说明myobject 对象为
ExampleClass的实例对象;如果返回值为false,说明myobject对象不是ExampleClass的实例对象。
代码示例:
public class Quadrangle { public static void draw(Quadrangle q){ System.out.println("Quadrangle的draw方法"); } }
public class Square extends Quadrangle { // SomeSentence }
public class Anything { //SomeSentence }
public class Parallelogram extends Quadrangle { public static void main(String[] args) { Quadrangle q =new Quadrangle(); //实例化父类对象 //判断父类对象是否是Parallelogram子类的一个实例 , if(q instanceof Parallelogram){ Parallelogram p = (Parallelogram) q; // 向下转型操作 } // 判断父类对象是否是Square子类的一个实例 if(q instanceof Square){ Square s = (Square) q; //向下转型操作 } // 由于q对象不是Anything类的对象,所以这条语句是错误的 //System.out.println(q instanceof Anything); } }
public class Parallelogram extends Quadrangle { public static void main(String[] args) { Quadrangle q =new Quadrangle(); //实例化父类对象 //判断父类对象是否是Parallelogram子类的一个实例 , if(q instanceof Parallelogram){ Parallelogram p = (Parallelogram) q; // 向下转型操作 } // 判断父类对象是否是Square子类的一个实例 if(q instanceof Square){ Square s = (Square) q; //向下转型操作 } // 由于q对象不是Anything类的对象,所以这条语句是错误的 //System.out.println(q instanceof Anything); } }
5、方法的重载
构造方法的名称是由类名决定的,所以构造方法只有一个名称,如果希望不同的方式来实例化对象,就需要使用多个构造方法完成,为了让方法名相同而形参不同的构造方法同时存在, 就需要用到方法重载。
代码示例:
public class OverLoadTest { public static int add(int a, int b){ return a+b; } public static double add(double a, double b){ return a+b; } public static int add(int a){ return a; } public static int add(int a, double b){ return 1; } public static int add(double a, int b){ return 1; } public static void main(String[] args) { System.out.println("调用add(int,int)方法:"+add(1,2)); System.out.println("调用add(double,double)方法:"+add(2.1,3.3)); System.out.println("调用add(int)方法:"+add(1)); } }
构成重载的条件,可以总结出编译器是利用方法名、方法各参数类型、参数的个数、参数的顺序来判断类中的方法是否唯一。
6、多态
将父类对象应用于子类的特征就是多态,利用多态可以是程序具有良好的拓展性,并可以对所有类对象进行通用处理。
实例:一个四边形类,让它处理所有继承该类的对象,根据“向上转型”的原则,可以使每个继承这个四边形类的对象作为draw()方法的参数,然后在draw()方法中做出一些限制,就可以根据不同的图形类对象绘制不同的图像,这样就可以使用更为通用的四边形类取代具体的正方形类和平行四边形类,可以很好的解决代码冗余的问题,并且更易于维护。
代码示例:
public class Quadrangle { // 实例化保存四边形对象的数组对象 private Quadrangle[] qtest = new Quadrangle[6]; private int nextindex = 0; /*定义draw方法,参数是四边对象*/ public void draw(Quadrangle q){ if(nextindex < qtest.length){ qtest[nextindex] = q; System.out.println("输出参数nextindex数值:"+nextindex); nextindex ++; } } public static void main(String[] args) { // 实例化两个四边形对象,用来调用draw方法 Quadrangle q = new Quadrangle(); q.draw(new Square()); // 以正方形对象为参数调用draw方法 q.draw(new Parallelogram()); // 以平行四边形对象为参数调用draw方法 } }
//定义一个正方形类继承自四边形类 public class Square extends Quadrangle { // SomeSentence public Square(){ System.out.println("正方形"); } }
// 定义一个平行四边形类继承自四边形类 public class Parallelogram extends Quadrangle { public Parallelogram(){ System.out.println("平行四边形"); } }
实例分析:
以不同的类对象为参数调用draw方法,可以处理不同的图形问题。
7、抽象类
在实际解决问题时,一般将父类定义为抽象类,需要使用这个父类进行继承和多态处理。继承树中,越上方的类,越是抽象。在多态机制中,不需要将父类初始化对象,我们需要的只是子类对象。
抽象类的语法如下:
public abstract class Test { abstract void testAbstract(); // 定义抽象方法 }
其中,abstract是抽象类的关键字。
使用abstract关键字定义的类称之为抽象类,使用这个关键字定义的方法称为抽象方法,抽象方法没有具体的方法体,这个方法本身是没有意义的,除非他被重写,而承载这个抽象方法的抽象类必须被继承,实际上抽象类除了被继承之外没有任何意义。
只要类中有抽象方法,这个类必须被定义为抽象类。
抽象类被继承后需要实现其中所有的抽象方法。
8、接口
接口是抽象类的延伸,可以看作是纯粹的抽象类,接口中所有的方法都没有方法体。
接口使用interface关键字进行定义,语法如下:
public interface drawTest2 { void draw(); // 接口中的方法省略abstract关键字 }
一个类实现一个接口可以使用implements关键字,语法如下:
public class Parallelogram extends Quadrangle implements drawTest2 { public Parallelogram(){ System.out.println("平行四边形"); } @Override public void draw() { } }
接口中定义的方法必须被定义为public或者abstract形式,其他的修饰权限不被java编辑器认可。
实例代码:
//定义drawTest接口 public interface drawTest { void draw(); }
//定义四边形类 public class QuadrangleUseInterface { public void doAnything(){ } public static void main(String[] args) { drawTest[] d = { new SquareUseInterface(), new ParallelogramgleUseInterface() }; for(int i = 0 ;i<d.length ; i++){ d[i].draw(); } } }
//定义平行四边形类,继承了四边形类,并实现了drawTest接口 public class ParallelogramgleUseInterface extends QuadrangleUseInterface implements drawTest{ @Override //该类实现了接口,需要覆盖draw方法 public void draw() { System.out.println("平行四边形draw()"); } public void doAnything(){ //覆盖父类方法 } }
// 定义正方形类,继承自四边形类,实现draw接口 public class SquareUseInterface extends QuadrangleUseInterface implements drawTest{ @Override public void draw() { System.out.println("正方形的draw()方法"); } public void doAnything(){} }
实例说明:
正方形类和平行四边形类分别继承自四边形类,并实现了接口drawTest,所以需要覆盖接口中的方法,在调用draw方法时,先将子类对象向上转型为drawTest接口形式。
9、接口与继承
在java中不允许多重继承,但是使用接口就可以实现多重继承,因为一个类可以同时实现多个接口,这样可以将所有需要实现的接口放在implements关键字后面用“,”隔开。
多重继承语法如下:
class 类名 implements 接口1,接口2,。。。,接口n
// >> TODO 接口也可以继承接口。接口可以继承多个接口,接口之间的继承要用extends // >> TODO 接口不可以继承类 // >> TODO 继承的接口,可以有重复的方法,但是签名相同时,返回值必须完全一样,否则会有编译错误 public interface Intf3 extends Intf1, Intf2{ void m3(); }