【黑马程序员】 java笔记——抽象类

11 篇文章 0 订阅
1 篇文章 0 订阅
---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

抽象类

一、面向对象(抽象类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关键字与什么关键字冲突?

privatestaticfinal

如果你的方法中,存在以上任意一个修饰符,那么这个方法就不能是抽象方法。

在什么时候使用抽象类?

当你想调用一个方法,而这个方法的参数类型为抽象类类型!

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);对应输出了AA 0C1 1;先输出A,这点同时验证了子类先初始化父类,再执行父类构造函数A 0;再子类的构造函数C1 1

2.C2 c2 = new C2(0);对应输出了A 0C2 8;看出父类初始化只有一次,当构造函数必须在执行一次。

3.子类c1实现了自己的f1f2f3,使用时就是使用的自己的实现;子类c2没有实现f3,调用父类的实现。但是只使用父类的方法,数据成员还是自己的,所有输出“A f3 8”。


---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------详细请查看: http://edu.csdn.net
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值