今天我们继续来讲一下上次封装的拓展部分。
单例模式
要讲单例模式前,我们简要的说一下概念。
设计模式:前人总结出的解决某类的问题的最优方案-----模板。
单例模式:需要某个类在整个应用程序中,只创建一个对象。
正如同封装的思想一样,单例模式为的是希望别人不能随意的创建对象,固定只创建一个对象。
而单例模式中有懒汉单例,如下图
以及饿汉单例,如下图。
懒汉单例正如其名,只有在调用方法是才会创建对象。饿汉单例还有一种叫法叫做急切式单例,正是因为它在调用类时就已经创建了对象。而懒汉单例会出现一些线程安全性的问题,这个我们以后讲到再说。
面向对象特征——继承
继承:继承是面向对象程序设计不可缺少的设计思想,是实现代码可重用的根基,是提高代码可扩展性的主要途径。
继承有以下4个要点:
1 继承是从已有的类中派生出新的类,新的类能吸收已有类的属性和行为,并能扩展新的能力。
2 在JAVA中使用extends关键字来表示继承关系。
3 JAVA不支持多继承,单继承使JAVA的继承关系很简单,一个类只能有一个直接父类。
4 继承之后子类可以调用父类的所有非私有属性和非私有方法.
总而言之,继承就是,子类继承父类,使用父类中已经实现好的功能,而且子类还可以扩展自己的功能。
这也正体现出了面向对象的设计宗旨:代码复用性高、代码扩展性强。
那么何时使用继承呢?
继承就如同现实世界中,比如猫和狗都算是动物类。而猫类和狗类就是子类,动物类就算是父类。而继承就是将子类共有的属性和行为放到父类中。
我们首先在代码中定义出Animal类。
public class Animal {
private String name;
private int age;
public void showInfo(){
System.out.println("狗的名字是"+name+"年龄"+age+"正在狗叫");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后我们定义Dog类并且继承父类Animal类。
public class Dog extends Animal{
}
随后在主方法中创建个Dog类对象就可以使用Animal类内的变量以及方法了。
public class Text {
public static void main(String[] args) {
Dog dog=new Dog();
dog.setAge(10);
dog.setName("小黑");
dog.showInfo();
}
}
以上便是简单的讲解了一下继承extends的使用。
继承的传递性
C类从B类继承,B类又从A类继承
那么C类就具有B类和A类的所有非私有属性和非私有方法
当一个没有继承任何一个类时,jvm会默认让类继承Object类
Object是 java为所有类提供的基类
Java中一个类,只能直接继承一个父类,不允许多继承,但是支持多级继承传递。
像这里我们在Dog类中定义一个特别的run()方法。
public class Dog extends Animal{
public void run(){
System.out.println("狗正在奔跑");
}
}
我们有定义一个新类使其继承Dog类
public class Xtq extends Dog{
}
最后主方法中我们可以看到,Xtq类既可以使用Animal类中的方法又可以使用Dog中的方法。
public class Text {
public static void main(String[] args) {
Xtq xtq=new Xtq();
xtq.setAge(10000);
xtq.setName("哮天犬");
xtq.showInfo();
xtq.run();
}
}
super关键字
super关键字用途:
使用super关键字访问父类成员
1.用super.成员变量名来引用父类成员变量
2. 用super.方法名(参数列表)访问父类的方法。
3. 用super.构造方法(参数列表)访问父类构造方法注意!不要把super误认为是父类对象.在创建子类对象时,不会创建父类对象。只会将父类中的信息加载到子类对象中存储
继承中的构造方法
子类构造方法会先调用父类构造方法。
使用super关键字调用父类任意一个构造方法,必须写在构造方法的第一行。
如果子类的构造方法中没有显式地调用基类构造方法,则系统默认调用基类无参数的构造方法。
这里我们在Animal类中创建了无参的构造方法和有参的构造方法。而继承Object类其实写不写无所谓,因为前面说过第一个类没有继承任何类时,他会默认继承Object类。
public class Animal extends Object{
private String name;
private int age;
public Animal() {
super();
System.out.println("动物类中无参的构造方法");
}
public Animal(String name,int age) {
super();
this.name = name;
this.age = age;
System.out.println("动物类中有参的构造方法");
}
}
Dog类也同上创建两种构造方法。此刻注意Dog类中独有属性color,在有参构造方法中super()没有color,因为父类Animal类中没有这个属性。而在无参构造方法中,super()其实可以省略,Java中是默认调用的。
public class Dog extends Animal{
private String color;
public Dog(){
super();
System.out.println("狗类的无参构造方法");
}
public Dog(String name,int age,String color){
super(name,age);
this.color = color;
System.out.println("狗类的有参构造方法");
}
}
public class Xtq extends Dog{
public Xtq(){
super();
System.out.println("哮天犬类的无参构造方法");
}
public Xtq(String name, int age, String color){
super(name,age,color);
System.out.println("哮天犬类的有参构造方法");
}
}
最后在主方法中调用一个无参的构造方法以及一个有参的构造方法。
public class Text {
public static void main(String[] args) {
Xtq x1=new Xtq();
Xtq x2=new Xtq("哮天犬",10000,"黑色");
}
}
以下是运行结果:
看到结果我们可以发现,在多级继承中,一级的父类的构造方法是先于所有子类被调用的。因为创建子类对象后,子类对象可以访问父类的信息,所以在初始化子类之前,必须先初始化父类
方法的重写
重写(Override)
应用场景:
当父类的方法实现不能满足子类需求时,可以对方法进行重写。
在子类中可以根据需要对从基类中继承来的方法进行重写。
方法重写规则:
1.方法名相同、参数列表相同;
2. 返回值类型相同;
3.访问权限不能小于父类权限;
注意:构造方法,静态方法不能重写,成员变量不存在重写
举个栗子:
public class Dog extends Animal{
private String color;
public void eat(){
System.out.println("狗正在吃饭");
}
这里重写了父类中eat()方法,注意要加上@Override
@Override
public void eat(){
System.out.println("神狗正在用餐");
}
抽象类
抽象方法:
抽象方法是一种特殊的方法:它只有声明,而没有具体的实现.抽象方法必须用abstract关键字进行修饰.
如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法。
用abstract修饰的类就是抽象类。如果某个类中包含有抽象方法,那么该类就必须定义成抽象类。
特点:
1.抽象类不能被实例化,但可以有构造方法,因为抽象类中含有无具体实现的方法,
所以不能用抽象类创建对象。
2. 抽象类只能用作基类,表示的是一种继承关系。继承抽象类的非抽象类必须实现其中的所有抽象方法,而已实现方法的参数、返回值要和抽象类中的方法一样。否则,该类也必须声明为抽象类。
抽象类就如同一个模糊的概念,需要其子类进行各种各样的描述。
我们在定义抽象类Animal,抽象类中可以没有抽象方法,但抽象方法一定要在抽象类中。
public abstract class Animal {
public abstract void go();
}
而在其子类Dog类中,要么将其定义成抽象类,要么就要实现父类中的抽象方法。
public class Dog extends Animal{
@Override
public void go() {
System.out.println("狗狗在运动");
}
}
在调用的时候,就要用子类去调用,不能用父类去调用。
public static void main(String[] args) {
new Dog().go();
}
抽象类,抽象方法,在软件开发过程中都是设计层面的概念。也就是说,设计人员会设计出抽象类,抽象方法,程序员都是来继承这些抽象类并覆盖抽象方法,实现具体功能。