• 在本教程中,我们将介绍一些常用于Java开发项目的高级工具,其中包括:
  • 继承与抽象
  • 接口
  • 嵌套类
  • 正则表达式
  • 集合
  • 日期
  • I / O

继承与抽象

Java代码中的类存在于层次结构中。层次结构中给定类之上的类是该类的超类。那个特定的类是每一个类的子类都更高。一个子类继承自其超类。这个课程Object是每个班级层次的顶端。换句话说,每个类都是(并继承自)的子类Object

例如,假设我们有一个Adult类似如下的类:

1



2



3



4



5



6



7



8



9



10



11



12



13



14



15


public class Adult {



protected int age = 0;



protected String firstname = "firstname";



protected String lastname = "lastname";



protected String gender = "MALE";



protected int progress = 0;



 



public Adult() { }



public void move() {



System.out.println("Moved.");



}



public void talk() {



System.out.println("Spoke.");



}



}


我们的Adult类隐含地继承Object。这是任何类的假设,所以你不必键入extends Object类定义。但是,我们的类继承自其超类是什么意思?它只是意味着可以Adult访问其超类中的暴露的变量和方法。在这种情况下,这意味着Adult可以从任何超类看到并使用以下内容(我们目前只有一个):


  • public 方法和变量
  • protected 方法和变量
  • 包保护的方法和变量(即那些没有访问说明符的方法),如果超类在同一个包中Adult

构造函数是特殊的。他们不是成熟的OO成员,所以他们不是继承的。

如果一个子类覆盖了超类中的一个方法或变量 - 如果子类实现了一个具有相同名称的成员,换句话说 - 隐藏超类的成员。为了准确,覆盖一个变量隐藏它,并且覆盖一个方法只是覆盖它,但效果是一样的:覆盖的成员基本上是隐藏的。您仍然可以使用super关键字来获取超类的成员:

1


super.hiddenMemberName


在这种情况下Adult,所有它继承的是在ObjecttoString()例如)上的方法。因此,以下代码段完全可以接受:

1



2


Adult anAdult = new Adult();



anAdult.toString();


toString()方法不是明确存在的Adult,而是Adult继承它。

这里有一些“陷阱”,你应该记住。首先,在一个子类中给变量和方法与该类的超类中的变量和方法相同的名称很容易,然后在不能调用继承的方法时变得困惑。记住,当一个方法与一个已经存在于一个超类中的子类中具有相同的名称时,你已经隐藏了它。其次,构造函数不是继承的,而是被调用。在您编写的任何子类构造函数中,对超类构造函数进行隐式调用,这是子类构造函数的第一件事。你必须这样生活 没有什么可以改变它。例如,我们的Adult构造函数实际上在运行时看起来像这样,即使我们没有在body中输入任何东西:

1



2



3


public Adult() {



     super();



}


构造函数体中的这一行调用超类的无参数构造函数。在这种情况下,这是构造函数Object

定义类层次结构

假设我们有另一个类叫做Baby。看起来像这样:

1



2



3



4



5



6



7



8



9



10



11



12



13



14



15



16


public class Baby {



     protected int age = 0;



     protected String firstname = "firstname";



     protected String lastname = "lastname";



     protected String gender = "MALE";



     protected int progress = 0;



 



     public Baby() {



     }



     public void move() {



         System.out.println("Moved.");



     }



     public void talk() {



         System.out.println("Spoke.");



     }



}


我们AdultBaby班级看起来很相似。事实上,它们几乎相同。这种代码重复使维护代码比它需要更痛苦。我们可以创建一个超类,将所有常见元素移动到该类,并删除代码重复。我们的超类可以被调用Person,它可能是这样的:

1



2



3



4



5



6



7



8



9



10



11



12



13



14



15


public class Person {



     protected int age = 0;



     protected String firstname = "firstname";



     protected String lastname = "lastname";



     protected String gender = "MALE";



     protected int progress = 0;



     public Person() {



     }



     public void move() {



         System.out.println("Moved.");



     }



     public void talk() {



         System.out.println("Spoke.");



     }



}


现在我们可以拥有AdultBaby子类Person,这使得这两个类目前很简单:

1



2



3



4



5



6



7



8


public class Adult {



     public Adult() {



     }



}



public class Baby {



     public Baby() {



     }



}


我们有一个层次结构,我们可以将每个子类的实例引用为层次结构中任何一个超类的实例。例如:

1



2



3



4



5


Adult anAdult = new Adult();



System.out.println("anAdult is an Object: " + (Adult instanceof Object));



System.out.println("anAdult is a Person: " + (Adult instanceof Person));



System.out.println("anAdult is anAdult: " + (Adult instanceof Adult));



System.out.println("anAdult is a Baby: " + (Adult instanceof Baby));


这个代码将给我们三个true结果和一个false结果。您还可以将对象投射到其层次结构中的任何类型,如下所示:

1



2



3


Adult anAdult = new Adult();



Person aPerson = (Person) anAdult;



aPerson.move();


这段代码将会编译没有问题。我们可以转换一个Adult类型Person,然后调用它的Person方法。

因为我们有这个层次结构,我们的子类的代码更简单。但是你在这里看到一个问题吗?现在所有Adult的人和所有人Baby(借口不好的复数)会以同样的方式说话和移动。每个行为只有一个实现。这不是我们想要的,因为成年人不会像婴儿一样说话或移动。我们可以覆盖move()talk()子类,但是我们在我们的超类中定义了基本上无用的“标准”行为。我们真正想要的是强制我们每个子类以自己的特定方式移动和谈话的一种方式。这就是抽象类。