时间:2018-07-26
作者:魏文应
一、抽象类
什么是抽象类?
什么叫抽象类?我们先看下面例子:
- 抽象类
如果你要创建一个 教师 这个 类的实例,我们肯定不会用 生物 这个类去创建 教师 这个类的实例。同样的,你要是创建学生,工人这些类的实体,也不会用 生物 这个类去创建。最终导致的一个现象是,生物这个类没有实例化的必要,我们压根没有实例化它的需求。这种类就是 抽象类。抽象类,就是抽象出某类对象集合基本特性。
抽象类的形式
抽象类使用 abstract
关键字修饰:
abstract class Person{
}
这个类 不能直接实例化,比如下面是 错误的:
public class TestAbstract {
public static void main(String[] args){
Person p = new Person();
p.eat();
}
}
abstract class Person{
}
上面代码中,new Person
实例化一个抽象类,会报错 。抽象类有以下特点:
- 不可以被实例化。
- 抽象类有构造器(凡是类都有构造器)。
抽象方法
可以使用 abstract
修饰方法,表示抽象方法:
abstract class Person{
public abstract void eat();
public abstract void walk();
}
上面的 eat()
方法就是 抽象方法。方法内没有执行方法,需要子类去重写:
class Student extends Person {
public void eat(){
System.out.println("学生吃饭");
}
public void walk(){
System.out.println("学生吃饭");
}
}
上面代码中,抽象方法在子类中,必须重写,而是 所有抽象方法都要得到重写。比如,下面是 错误的,walk() 方法得不到重写:
class Student extends Person {
public void eat(){
System.out.println("学生吃饭");
}
}
抽象方法 必须在抽象类内,比如下面是 错误的:
class Person{
public abstract void eat();
}
上面的Person类 不是抽象类,那么在其内部使用抽象方法,会 导致报错 。总之,在 可以实例化 的类中,必须确保每一个方法 都有方法体。比如下面是 没有方法执行体的:
public abstract void eat();
而下面这样是 有方法执行体的,只是执行方法的内容为空:
public void eat(){
}
下面是抽象类和抽象方法的 使用:
public class TestAbstract {
public static void main(String[] args){
Person p = new Student();
p.eat();
}
}
不能使用 abstract 修饰的情况
abstract 不能用来修饰属性、构造器、private、final、static。下面的用法 都是错误的:
abstract class Test{
// 不能用abstract 修饰属性
abstract int name;
// 不能用abstract 修饰构造器
public abstract Test(){
}
// 不能用abstract 修饰private私有方法
private abstract void method1();
// 不能用abstract 修饰final方法
public abstract final void method2();
// 不能用abstract 修饰static静态方法
public abstract static void method3();
}
不能修饰的原因很简单,abstract 修饰的,就意味着子类可以去重写,如果不能重写,就不能用abstract。而使用absract 修饰 有static 修饰的方法时,当通过 类名.方法
的形式调用方法,会导致没有方法执行体,比如上面的 Test.method3()
,使用static修饰的方法也不能使用abstract修饰。
二、抽象类设计理念
抽象类体现的的就是一种 模板模式的设计。抽象类作为多个类的 通用模板。子类在抽象类的基础上进行扩展、改造,但子类从总体上 保留抽象类的行为方式。
其解决了以下问题:
- 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把 不确定的部分 暴露出去,让子类去实现。
- 编写一个抽象父类,父类提供了多个子类的 通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
三、接口
接口定义
为什么需要接口?有时必须从 几个类 中派生出 一个子类,继承它们 所有的 属性和方法。但是,Java不支持多重继承。有了接口,就可以 得到多重继承的效果。
接口,是一种 特殊的抽象类。这种抽象类中,只包含常量和方法的定义,而没有变量和方法的实现。下面是 定义接口的形式:
interface Test{
}
上面定义了 一个名为Test的接口,我们在这个接口内部,定义 常量 和 抽象方法:
interface Test{
public static final int I =12;
public static final boolean FLAG = false;
public abstract void method1();
}
事实上,在接口内部,public static final
默认 被加在常量前面,public abstract
默认 被加在方法前面。下面的写法,和上面的写法是一样的:
interface Test{
int I =12; // 默认在其前面加上 public static final
boolean FLAG = false; // 默认在其前面加上 public static final
void method1(); // 默认在其前面加上 public abstract
}
接口内部,只有 常量 和 抽象方法,没有 构造器、方法、属性。接口与接口之间,可以有 继承关系,和类只能单继承不一样,接口可以 多继承:
interface Test3 extends Test2,Test1{
}
上面代码中,Test3 继承了 Test2 和 Test1 。
接口的使用
那么接口怎么使用呢?接口定义的只是功能,要使用这些功能,需要通过类去重写方法,然后去实例化。比如下面代码:
interface Test{
int AGES = 12;
void method();
}
class Person implements Test{
public void method(){
System.out.println(AGES);
}
}
public class TestInterface {
public static void main(String[] args) {
System.out.println(Person.AGES);
}
}
上面 Person 类,是实现 Test 接口的类。Person中,重写了 method() 方法。里面的常量 AGES,在Person 中可以直接使用,在外部通过 Person.AGES
形式使用,或者通过 Test.AGES
形式使用。接口的方法必须在类中 全部得到重写:
interface Test1{
void method1();
}
interface Test2{
void method2();
}
interface Test3 extends Test2{
void method3();
}
class Person implements Test1,Test3{
public void method1(){
System.out.println(AGES);
}
public void method2(){
System.out.println(AGES);
}
public void method3(){
System.out.println(AGES);
}
}
上面代码中,接口的实现类 Person ,需要重写Test1、Test3的所有方法。因为Test3继承了Test2,所以 Person 类还必须要 重写Test2内的所有方法。也就是说,Person 类要重写 Test1、Test2、Test3 接口的所有方法。
从上面可以看出,接口的实现依赖类,让类去实现接口中的抽象定义。
接口的多态
接口的多态,和类的多态有些类似:
interface Runner{
public abstract void run();
}
interface Swimmer{
void swim();
}
class Duck implements Runner,Swimmer{
public void swim() {
System.out.println("鸭子游泳。");
}
public void run() {
System.out.println("鸭子走路。");
}
}
public class TestInterface {
public static void main(String[] args) {
Duck d = new Duck();
TestInterface.test1(d);
TestInterface.test2(d);
}
public static void test1(Runner r){
r.run(); // 虚拟方法调用
}
public static void test2(Swimmer s){
s.swim();
}
}
上面代码中,Duck 类是 Runner 和 Swimmer 两个接口的实现,那么可以通过 虚拟方法调用,实现类似于上面的多态传参。
四、接口设计模式:工厂方法
什么是工厂方法?定义一个 用于创建对象的接口,让 子类决定实例化 哪一个类。比如,下面代码:
interface Work{
void doWork();
}
interface IWorkFactory{
Work getWork();
}
class StudentWork implements Work{
public void doWork() {
System.out.println("学生写作业");
}
}
class TeacherWork implements Work{
public void doWork() {
System.out.println("老是批改作业");
}
}
class StudentWorkFactory implements IWorkFactory{
public Work getWork() {
return new StudentWork();
}
}
class TeacherWorkFactory implements IWorkFactory{
public Work getWork() {
return new TeacherWork();
}
}
public class TestFactoryMethod {
public static void main(String[] args){
IWorkFactory i = new StudentWorkFactory();
i.getWork().doWork();
IWorkFactory i1 = new TeacherWorkFactory();
i1.getWork().doWork();
}
}
上面代码中,子类 StudentWorkFactory 决定实例化 StudentWork 类,子类 TeacherWorkFactory 决定实例化* TeacherWork 类。这样,就达到了接口只是定义了创建一个什么样的类,具体的执行内容根据需求在子类中去实现,并且由子类决定何时去实例化它。
五、内部类
类的成员之五:内部类。内部类就是在类的内部,去定义新的类。
class Person{
class Bird{
// 成员内部类
}
public void method1(){
class A{
// 局部内部类
}
}
}
在外部类Person的内部,直接定义的类Bird,Bird 叫做 成员内部类;在类内方法method1内部,定义类A,类A叫做 局部内部类。那么,任何创建成员内部类的对象?
创建成员内部类对象
看下面例子:
public class TestInnerClass {
public static void main(String[] args){
// 创建静态的成员内部类
Person.Dog d = new Person.Dog();
// 创建非静态的成员内部类
Person p = new Person();
Person.Bird b = p.new Bird();
}
}
class Person{
class Bird{
}
static class Dog{
}
}
区分调用内部类、外部类的变量
看下面例子:
public class TestInnerClass {
public static void main(String[] args){
Person p = new Person();
Person.Bird b = p.new Bird();
b.setName("setName");
}
}
class Person{
String name = "Person";
class Bird{
String name = "Bird";
void setName(String name){
System.out.println(name); // 这个 name 是 setName 的参数
System.out.println(this.name); // 这个 name 是 Bird 的属性
System.out.println(Person.this.name); // 这个 name 是 Person 的属性
}
}
}
局部内部类的使用
常常使用一个方法,使其 返回值 为某个类或接口的 对象。而这个类或接口在方法内部创建:
interface Test{
void show();
}
class OuterClass{
public Test getTest(){
class MyClass implements Test{
public void show(){
System.out.println("show print!");
}
}
return new MyClass();
}
}
public class TestInnerClass {
public static void main(String[] args) {
OuterClass c = new OuterClass();
Test t = c.getTest();
t.show();
}
}
首先,MyClass 类在方法 getTest() 内部,创建这个类以后,使用这个类创建类的对象,我们可以获得这个对象的引用。达到的效果就是,这个类只在方法的内部使用。 当然,我们还可以使用 匿名的方式 创建内部类:
class OuterClass{
public Test getTest(){
return new Test(){
public void show(){
System.out.println("这是匿名类");
}
};
}
}
匿名的方式,和上面有名的方式的效果是一样的。