抽象类
面向对象(抽象类1)
当多个类中出现相同功能,但是功能主体不同,这是可以进行向上抽取的。这时,只抽取功能定义,而不抽取功能主体。
抽象:看不懂。
抽象类的特点:
1、 抽象方法一定在抽象类中。
2、 抽象方法和抽象类都必须被abstract关键字修饰。
3、 抽象类不可以用new创建对象。因为调用抽象方法没意义。
4、 抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后,建立子类对象调用。
如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
面向对象(抽象类2)
抽象类和一般类没有太大的不同。
该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。
这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体。
通过抽象方法来表示。
抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法。
抽象类不可以实例化。
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
1.抽象类
用abstract 修饰的类被称为抽象类。所谓抽象类就是没有具体实例对象的类。
抽象类通常包括一个或多个抽象方法(只有方法说明,没有方法体),抽象类的子类必须完成父类定义的每一个抽象方法,除非该子类也是抽象类。
抽象类的主要用途是用来描述概念性的内容,这样可以提高开发效率,更好地统一用户“接口”。
2. 定义一个抽象类
abstract class 类名称
{
成员变量;
方法(); //定义一般方法
abstract 方法(); //定义抽象方法
}
说明:抽象类中可以包含有一般方法,也可以包含有抽象方法;对于抽象方法不用完成其代码实现,而在派生的子类中实现所有抽象方法。
3. 抽象类实现的例子
如前所述你可能对抽象类的概念有点模糊,下面举一个具体实例来作说明。
假设想设计一个形状(shape)的父类CShape,由此类可派生出圆形(circle),长方形(rectangle)于三角形(triangle)等几何形状的类。则父类于子类的派生关系描绘成如下图所示。
4.需求假设
假设这些几何形状均具有“颜色”(color)这个属性,因此可以把color这个数据成员,以及赋值color的方法均设计在父类里,让它继承给各个形状的子类较为方便,如此就不用在每一个几何形状的子类里,设计相同的程序代码来处理“颜色”这个属性的问题。
另外,如果想为每一个几何形状的类设计一个area()方法,用来显示几何形状的面积,因每种几何形状的面积计算方式并不相同,所以把area()方法的处理方式设计在父类里并不恰当,但每一个由CShape父类所派生出的子类又都需要用到这一个方法,因此可以在父类里只声明area()方法,而把area()方法处理的方法留在子类里来定义,也就是说,把area()声明成抽象方法即可解决这个问题。根据上述的概念,可编写出如下的父类程序代码:
abstract class CShape //定义抽象类
{
protected String color; //数据成员
public void setColor(String str)
{
color=str; //一般的方法,用来赋值几何形状的颜色
}
abstract void area();//抽象类内的抽象方法
}
class CRectangle extends CShape
{
int width,height;
publicCRectangle (int w,int h)
{
width=w;
height=h;
}
public void area()
{
System.out.print(“color=“+color+”, “);
System.out.print(“area=“+width*height);
}
}
Public class app10-1{
public static void main (Stringargs[]){
CRetangle rect=newCRectangle(5,10);
rect.setColor(“yellow”);
rect.area();
CCircle cir=new CCircle(2.0)
cir.setColor(“Green”);
cir.area();
}
}
抽象类
抽象方法的特征:
抽象方法需要使用abstract关键字修饰
抽象方法没有方法体
抽象方法在参数列表后面没有大括号,但有一个分号。
具有一个或多个抽象方法的类必须声明为抽象类。
抽象类的子类,有两条路可以走:
把父类所有抽象方法都实现完成,这样子类就不用再抽象了;
不实现,或者不实现所有抽象方法,那么对子类来说也有未完成的方法,那么子类也需要声明为抽象类。
抽象方法不能是静态的,也不能是private的,更不能是final的。与多态冲突的东西都不行。
抽象类是否可以有构造器:
思考:构造器是用来干什么的?与new使用创建实例对象。
思考:抽象类不能创建实例对象,那么它有构造器有没有用?
子类是不是需要调用父类构造器?需要,那么也就是说抽象类可以有构造器,而构造器是给子类构造器来用的。
abstract class A
{
public A(String s) {}
public abstract void fun();
}
abstract class B extends A
{
public B()
{
super("hello");
}
public void fun()
{
System.out.println("hello");
}
}
抽象类是否可以不定义抽象方法?
抽象类可以不定义抽象方法,但很少有人这么做。
一个类如果有一到多个抽象方法,那么这个类必须声明为抽象类。
abstract关键字与什么关键字冲突?
private、static、final
如果你的方法中,存在以上任意一个修饰符,那么这个方法就不能是抽象方法。
在什么时候使用抽象类?
当你想调用一个方法,而这个方法的参数类型为抽象类类型!
public class Test
{
public static void main(String[] args)
{
A a = new B();
abc(a);
}
// 该方法的参数类型为A类型
// A类型是抽象类
// 我们无法实例化A类型的对象
// 说明我们需要写一个A类的子类,然后实例化子类的对象,
// 最后使用子类对象来调用abc方法。
public static void abc(A a)
{
}
}
abstract class A
{
publicabstract void fun();
}
class B extends A
{
public void fun()
{
}
}
当我们想使用abc()方法时,因为abc()方法的参数是A类的,而且A类是抽象类,那么我们就需要创建一个A类的子类类型对象,然后用这个对象来调用abc()方法。
1.创建A类的子类
2.创建子类对象
3.用这个对象来调用abc()方法
public class Test
{
public static void main(String[] args)
{
/*
自己来写三个Waiter的子类,用每个子类的对象来调用fun()方法
*/
}
public static void fun(A a)
{
a.service();
}
}
abstract class Waiter {
public abstract void fun1();
public abstract void fun2();
public void service()
{
fun1();
serv();
fun2();
}
public void serv()
{
System.out.println("服务...");
}
}
抽象类
可以有实例属性、静态属性
可以有构造器
可以有实例方法、抽象方法、静态方法
就是什么都可以有,就是不能有实例对象(不能创建对象)。
抽象类一般都会有抽象方法,即没有加工的地方。
抽象类、子类的变量和函数与父类的关系:
父类代码:
//父类,抽象类
abstract public class A
{
static{
System.out.println("A");
}
int i;
A(int i)
{
this.i = i;
System.out.println("A "+i);
}
abstractpublicvoid f1();
abstractpublicvoid f2();
publicvoid f3()
{
System.out.println("A f3 "+i);
}
}
父类是个抽象类,可以有自己的数据成员,也可以有非abstarct的成员方法。但是不能被实例化。
子类1代码:
//子类,
publicclass C1 extends A
{
C1(int i)
{
super(i);
i++;
System.out.println("C1 "+i);
}
publicvoid f1()
{
System.out.println("C1 f1 "+i);
}
publicvoid f2()
{
}
publicvoid f3()
{
i=5;
System.out.println("C1 f3 "+i);
System.out.println("C1 f3 super "+super.i);
super.f3();
}
}
该子类C1实现了父类所有的方法,抽象和非抽象的。
子类2代码:
// 子类
publicclassC2 extends A
{
C2(int i)
{
super(i); // 必须在第一行,且
this.i=8;
System.out.println("C2 "+this.i);
}
publicvoid f1()
{
}
publicvoid f2()
{
System.out.println("C2 f2 "+i);
}
}
子类C2实现了父类的抽象方法。子类在继承抽象类时,必须实现抽象类中的所有抽象方法。
publicstaticvoid main(String[]args )
{
System.out.println("main start");
C1 c1 = new C1(0);
C2 c2 = new C2(0);
System.out.println("new");
c1.f1();
c1.f2();
c1.f3();
System.out.println("c1 over");
c2.f1();
c2.f2();
c2.f3();
}
输出结果:
main start
A
A 0
C1 1
A 0
C2 8
new
C1 f1 0
C1 f3 5
C1 f3 super 5
A f3 5
c1 over
C2 f2 8
A f3 8
从整体上来说,我们知道从对象的内存角度来说,假设现在有一个父类Father,它里面的变量需要占用1M内存.有一个它的子类Son,它里面的变量需要占用0.5M内存。
现在通过代码来看看内存的分配情况:
Father f = new Father(); //系统将分配1M内存.
Son s = new Son(); //系统将分配1.5M内存,因为子类中有一个隐藏的引用super会指向父类实例,所以在实例化子类之前会先实例化一个父类,也就是说会先执行父类的构造函数.由于s中包含了父类的实例,所以s可以调用父类的方法。
1.C1 c1 = new C1(0);对应输出了A;A 0;C1 1;先输出A,这点同时验证了子类先初始化父类,再执行父类构造函数A 0;再子类的构造函数C1 1。
2.C2 c2 = new C2(0);对应输出了A 0;C2 8;看出父类初始化只有一次,当构造函数必须在执行一次。
3.子类c1实现了自己的f1、f2、f3,使用时就是使用的自己的实现;子类c2没有实现f3,调用父类的实现。但是只使用父类的方法,数据成员还是自己的,所有输出“A f3 8”。