Java面向对象进阶
一、抽象类
-
概念
抽象类必须使用 abstract class声明
一个抽象类中可以有没有抽象方法,抽象方法必须写在抽象类或者接口中。
声明格式:
abstract class 类名 { //抽象类 }
-
抽象方法
只声明而未实现的方法称为抽象方法(未实现指的是:没有”{}“方法体),抽象方法必须说那个abstract关键字声明。
声明格式:
abstract class 类名 { //抽象类 public abstract void 方法名(); //抽象方法,只声明未实现 }
-
不能被实例化
抽象类的使用有几个原则:
- 抽象类是不能直接进行实例化操作的,即:不能直接使用关键字new完成
- 一个抽象类必须被子类所继承,继承的子类(若不是抽象类)则必须重写抽象类的全部抽象方法
-
常见问题
-
抽象类是否使用final声明?
不能,因为final修饰的类不能有子类,而抽象类必须要有子类实现才有意义,所以不能。
-
抽象类能否有构造方法?
能有构造方法,而且类对象实例化的时候的流程与普通类的继承是一样的,都是要先调用父类的构造方法(默认是无参的),之后再调用自己的构造方法。
-
-
抽象类和普通类的区别
- 抽象类必须使用public或protected修饰(如果为private修饰,那么子类无法继承,也就无法实现抽象方法)。默认缺省为public
- 抽象类不可以使用new关键字创建对象,但是在子类创建对象时,抽象父类也会被Java实例化。
- 如果一个类继承抽象类,那么必须实现其所有抽象方法,如果有未实现的抽象方法,那么子类也必须被定义为abstract类
二、接口
-
概念
如果一个类的全部方法都是抽象方法都是抽象方法,全部属性都是全局属性(用public static final修饰的),那么此时就可以将这个类定义成一个接口。
定义格式:
interface 接口名称{ //全局常量; //抽象方法; }
-
面向接口编程思想
这种思想是接口是定义(规范,约束)与实现(名实分离的原则)的分离。
优点:
- 降低程序的耦合性(通俗的说——粘性)
- 易于程序的拓展
- 有利于程序的维护
-
全局常量和抽象方法的简写
因为接口本身是由全局常量和抽象方法组成,所以接口中的成员定义可以简写:
-
全局常量:
public static final String INFO = "内容"; //简写后 String INFO = "内容";
-
抽象方法:
public abstract void print(); //简写后 void print();
-
-
接口的实现implements
与extends只能单继承不同,接口可以多实现
格式
class 子类 implements 父接口1,父接口2...{ } //若一个类既继承一个父类又实现多个父类接口,则写法为 class 子类 extends 父类 implements 父接口1,父接口2...{ }
-
接口的继承
因为接口都是抽象的,不存在具体的实现,所以允许多继承
例如:
interface C extends A,B{ //A,B都是接口 }
-
注意
如果一个接口想要实现,则必须依赖子类,子类(如果不是抽象类)要实现接口中的所有抽象方法。
-
接口和抽象类的区别
- 抽象类需要被子类继承,接口需要被子类实现。
- 接口只能声明抽象方法,抽象类中既可以声明抽象方法,也可以写非抽象方法。
- 接口里定义的变量只能是公共的静态常量,抽象类中的变量是普通的变量。
- 抽象类使用继承来使用,无法多继承。接口使用实现来使用,可以多实现。
- 抽象类中可以包含static 方法,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法).
- 接口中能有构造方法,但是接口中没有。
三、多态
-
概念
多态:就是对象的多种表现形态(多种体现形态)
-
多态的体现
举一个简单易懂对例子:
Animal这个类,其子类Cat与Dog等就是Animal这个父类的多重形态。
-
多态的使用:对象的类型转换
类似于基本数据类型的转换
-
向上转型:将子类实例变成父类实例(子类---->父类)
格式:
父类 父类对象 = 子类实例;
-
向下转型:将父类实例变成子类实例(父类---->子类)
格式
子类 子类对象 = (子类)父类实例;
-
子类变成父类时自动转型,而父类变成子类则需要强制转型。就像是:猫和狗一定是动物,但是动物不一定是猫和狗一样。
-
四、instanceof
-
作用:
判断某个对象是否是指定类的实例化,则可以使用instanceof关键字
-
格式:
实例化对象 instanceof 类 //此操作返回boolean类型的数据
代码示例:
public class Test { public static void main(String[] args) { Student student = new Student(); Nurse nurse = new Nurse(); say(student); say(nurse); } //如果只想传入人这个类下的学生这个类,为了避免传入护士类出错则需要用到instanceof public static void say(Person person) { Person是接口 if(person instanceof Student) { //如果传入的person是Person类是Student形态,则为true Student student = (Student)person; student.say(); }else { System.out.println("必须传入学生!"); } } }
五、成员内部类、局部内部类、匿名内部类以及静态内部类
-
成员内部类
成员内部类是普通的内部类,它的定义位于一个类的内部
-
特点:
-
成员内部类可以无条件访问外部类的所有成员属性和方法(包括private修饰的成员和静态成员)。不过要注意的是,当成员内部类拥有和外部类同名的成员变量或方法时,会发生影藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,则需要用以下形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
示例代码:
public class Outter { private int a; public int getA() { return a; } public void setA(int a) { this.a = a; } class Inner { private int a = 100; private int b = 200; public void say() { System.out.println(a); //默认调自己的a System.out.println(Outter.this.a); System.out.println(b); } } }
-
-
-
局部内部类
局部内部类是定义在一个方法或者一个作用域内的类,它和成员内部类的区别在于局部内部类的访问权限仅限于方法内或者作用域内。
-
注意:局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
-
-
匿名内部类
匿名内部类由于没有名字,所以它的创建方式有点奇怪。创建方式如下:
new 父类构造器(参数列表)|实现接口(){
//匿名内部类的类体部分
}
在这里我们看到使用匿名内部类我们必须要继承或者实现一个接口,当然他也仅能继承一个父类或者实现一个接口。同时也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。让然这个引用是隐式的。
示例代码:
/**
* 匿名内部类
* @author VernHe
*
*/
public class AnonymousClass {
public static void main(String[] args) {
Person p = new Person() {
@Override
public void say() {
System.out.println("哈哈哈");
}
};
haha(p);
}
public static void haha(Person p) { // Person为接口,里面有一个say方法。
p.say();
}
}
- 注意事项:
1. 使用匿名内部类时,我们必须是继承一个类或实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2. 匿名内部类中是不能定义构造函数的。
3. 匿名内部类中不能存在任何的静态成员变量和静态方法。
4. 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5. 匿名内部类不能是抽象的,他必须要实现继承的类或者实现的接口的所有抽象方法。
6. 只能访问final修饰的局部变量。