`
wtt2312
  • 浏览: 14041 次
  • 性别: Icon_minigender_2
最近访客 更多访客>>
社区版块
存档分类
最新评论

黑马程序员-------面向对象---概述、封装、继承、抽象类

 
阅读更多
---------------http://www.itheima.comjava培训、android培训期待与您交流!---------------



    相信大家都听说过这个脑筋急转弯:把大象放冰箱里,需要怎么做(需要哪三步)?相信大家都会不假思索地说出来。是的,第一步,打开冰箱门;第二步,把大象放进冰箱;第三步,关上冰箱门。这也是毕老师对面向对象的一个讲解的最形象的、最易于理解的一个例子(只有把学到的东西用生活的语言讲出来才是最好的一种理解方式)。
       一切来源于生活。是的,正是有了对生活的更好理解,才会研发出那么多的技术更好的为生活服务。其实,这些技术是从生活中总结出来的。呃,,不好意思,有点扯远了。步入正题(嘻嘻)。。。
      首先,先研究研究那个把大象放进冰箱的脑筋急转弯:
从一个角度说,把大象放进冰箱,就那三步(脑筋急转弯嘛),打开冰箱,把大象存进冰箱,关上冰箱。在这里,我们强调的是我们对冰箱做的这几个动作:打开,存进,关闭。这种强调动作的的行为(强调功能行为),我们称之为 面向过程 ,这在C语言中十分常见。简单地说,我们把大象放进冰箱,我们是一个执行者,是我们在操作冰箱,我们在指挥着冰箱在做某些事(开,存,关)。
而显然,冰箱自己是具有这些功能(开,存,关)的,我们看不到冰箱的具体操作动作,如冰箱是如何开的,因为冰箱门有一些能够滑动的零件,冰箱可以实现开,关的这些动作。这是冰箱自带的。只是我们人为地将冰箱自带的这些功能当做是我们施加给冰箱的外力。那么,我们    换一个角度 思考  ,将冰箱看做是一个事物,或者是一个对象。因为冰箱自己是具有哪些功能的(开,存,关),所以,我们就想象着我们指挥 冰箱打开,冰箱存储,冰箱关闭。这时,我们就是在面对着一个事物,让这个事物将自身所具有的一些功能展现出来,那么,我们就不必去强调那些动作(这些动作可能是多个,甚至更多),只要我们只操作这个对象(事物),那么,我们需要这个事物的哪些功能就操作哪些功能,这样,问题就变得简单了。这种强调对象(事物)的行为称之为 面向对象
      简单地说,面向对象就是将一个事物的自身所具有的功能隐藏(封装,后面会将到的)起来,当我们需要某些功能的时候只要把这个事物(对象)拿来,对事物(对象)进行特定的操作(调用)就能满足我们的需要(需要哪些功能)。面向对象其实是一种思考问题的另一种简化方式,我们不需要了解过程是怎样的,我们只需要结果。所以,他的好处就在于把复杂的问题简单化。
简单地举几个例子吧(毕老师的经典例子就不重复了,讲个我自己理解的吧):拿我们经常用的笔记本来说吧(因为我现在正在用它,嘻嘻):我现在正面向着一个对象,这个对象就是笔记本。因为我想要使用笔记本的一些功能,比如,我现在正在打字,我就在使用(调用)笔记本的打字功能。我不需要知道笔记本到底是怎么实现打字功能的,我只要敲笔记本上的键盘就可以在文本文件中打字来满足自己的需求。当然,笔记本还有很多功能:开机,关机,散热,上网等等。
      引用毕老师的经典语录吧:“万物皆对象”。当然,这还要自己慢慢理解、体会。
      面向对象的三个特征:封装、继承、多态。在开发过程中,就是在找对象,建立对象,使用对象和维护对象关系的过程。

一、类与对象的关系

1、类:对具有相同属性和行为的事物的描述。
     对象(对象在堆中):用关键字 new  创建的一个实体,也就是事物的一个实例(个体)。
     在描述事物(类)时,一般描述的是事物的属性和行为
     一般属性是类中的变量,行为是类中函数或方法,统称为类中成员变量、成员方法
     比如:描述人,人的属性(姓名、性别、年龄等)和行为(行走,吃饭,哭等)
   
 class Person {
              //描述人的属性
              String name;
              int age;
             //描述人的行为
              void walk(){}
     }
      class PersonDemo{
          public static void main(String[] args)
             {
                 //创建人的实体(人的实例化)
                 Person p = new Person();  //P 是类类型变量,指向子类对象
                //调用对象成员     格式:对象.对象成员
               p.name = "wangting";
               p.age = 22;
               p.walk();
           }  }

分析一下在内存中的存储情况:人只是笼统的一类事物,而每个人则是这一类人的具体(看的见摸的着)的一个个体。在创建人的实体时,在栈内存中就创建了P这个具体对象,P对应着有一个地址值,这个地址值就指向了堆内存中在堆内存中就存储了P这个对象的具体的一些属性(name = "wangting"; age = 22)。

2、成员变量/局部变量
     其作用域不同:
        成员变量作用于整个类中,存储在堆内存中,且有默认值(0)。
        局部变量作用于函数或语句中,存储在栈内存中,需要初始化。如:for循环中的变量,只要循环一结束,其中的变量就会被释放。

3、匿名对象的应用
     比如,在创建对象并调用方法时,上面的例子可以写成匿名对象
Person  p  =  new Person();
p.name = "wangting"; 
p.walk();
------可写成:
new Person().name = "wangting"; //这是无意义的
new Person().walk();

需要注意的是:匿名对象调用方法有意义,而调用属性无意义
使用方式一:对对对象的方法只能调用一次
使用方式二:可将匿名对象作为实际参数进行传递。show(new Person());

二、封装

1、封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。 
     就我的理解来说,我认为,封装就是将对象的一些属性或者功能(比如说冰箱具有开、关的功能),我们可以不知道它实现开、关的细节(零件),但应当对外提供一些方式(外力),当用的时候通过调用这个对象的一些方法来实现(开关的动作)。
     好处:将变化隔离、便于使用、提高重用性、提高安全性(例子:台式机的机箱,当机箱中的部件坏了时,只需要换掉(隔离)就可以再次使用(重用性)。我们不需要知道里面的构造(封装),只要使用就可以。)
     封装原则:将不需要对外提供的内容都隐藏起来、把属性都隐藏,提供公共方法对其访问

2、private:修饰符,关键字。
                 权限修饰符。用于修饰类中的成员(成员变量、成员方法(函数))。
                 私有只在本类中有效。
当有些功能不需要对外提供的时候,我们可以用private修饰。

3、构造函数、构造代码块
     构造函数:
       写法规范:
              (1)函数名和类名一致
              (2)不用定义返回值,与一般方法不同(一般方法在没有返回值时写void)
              (3)不可写return语句
       构造函数的作用:
             用于给对象进行初始化动作(初始化就是事物特有的属性和行为,如:name,age)
       什么时候使用构造函数?
             (1)当描述(分析)事物时,事物本来就具有某种特性和行为时
             (2)在判断有无未知参数时,这时用于传参
       当一个类中未定义构造函数时,系统会默认添加一个空参的构造函数。
       对象一建立,就立即调用对应的构造函数(空参,一个参数,几个参数),默认值为null。

构造代码块(笔试常见):
          作用:给所有对象进行初始化。表现的是对象们所具有的共性。
         优先于构造函数执行。

4、this关键字
          应用情况:当成员变量和局部变量相同时。哪个对象调用,this就代表哪个对象。

5、static 
          static是一个修饰符,可用于修饰成员变量或者函数。可被对象调用,被类直接调用。
          格式为:类名.静态成员
          对象初始化前就已经存在(优先于对象存在)。  且存在于方法区中。
          类变量存在于方法区中,且随着类的消失而消失。
          实例变量存在于堆区中,随着对象的消失而消失。
注意:(1)静态方法只能访问静态成员,非静态方法可访问非静态,也可访问静态(静态成员优先存在)。(2)静态中不能有this、super。    
        静态应用:用于修饰静态成员。当修饰变量时,该变量是共享数据,并非属性。
                     当修饰函数时:当功能中未访问到对象的特有数据时。
静态代码块:优先于主函数执行

7、单例设计模式  详见博客  黑马程序员---单例设计模式

三、继承

1、概述:简单滴说,继承是事物间的一种关系(子类不断向上抽取形成的父类)。
      举个例子:当我们在描述学生类的时候,他的属性和行为分别是name,age和study。而描述工人的时候,他的属性和行为分别是 name,age和work。通过分析,我们知道,学生类和工人类都具有姓名和年龄这两个属性,而行为不同,那么,我们就可以将姓名和年龄属性定义到另一个类Person中。也就是说,学生类和工人类中的共有特性都向上抽取形成一个单独的类Person,这时学生类、工人类和Person类就有了某种关系,这个关系就是继承。表示类与类之间的关系,并用关键字extends来标识这种关系,这种关系是所属关系。只要子类继承了父类(超类、基类),那么子类中就自然的有了父类中的内容。

继承的特点:
     提高了代码重用性,当然,不能单纯的为了简化代码或者使用一些共性属性而使用继承。
     类与类产生了关系,有了关系才有了多态。
要注意的是:
      java中只支持单继承(只能继承一个类),不支持多继承,但支持多实现。因为多继承会出现安全隐患,比如,父类中定义了功能,但功能内容不同,那么子类对象就不确定要运行哪一个。
      java支持多层继承,也就是说C继承了B,B继承了A,那么,C也可以继承A。
这时就形成了继承体系,在查阅继承体系时,要先查阅父类中定义的功能,创建子类对象使用功能。

2、类与类之间的其他关系
    类与类之间的以下关系其实是类与类之间紧密联系的程度。
    聚集关系:has a
    聚合关系:比如球队和球员的关系。球队可以没有其中的某个球员,因为可以再找。
    组合关系:人类器官和人的关系,人没有了某个器官是不行的。紧密关系最高。

3、子父类中类成员的特点

     (1)变量
     当子类与父类的非私有变量相同时:
           子类访问本类(子类)中的变量,用this(代表本类对象的引用)。
           若子类访问父类中的变量,则使用super(父类对象引用)。

    (2)函数
     当子类中函数名与父类中函数的方法名一样时,子类会覆盖父类的方法,也就是重写。重写的原理就是子类沿袭(保留)父类功能,并改变父类功能的具体内容,有利于功能扩展。
     需要注意的是:
子类覆盖父类是,必须保证子类的权限大于等于父类的权限,否则编译失败。
     重写与重载的区别: 
     重载:只看同名函数的参数列表
     重写:子父类方法要一模一样,包括返回类型。

    (3)构造函数---子类实例化过程
    在对子类对象进行初始化时,父类构造函数也会运行。
    子类实例化过程:
    子类中所有的构造函数,默认都会访问父类中空参数的构造函数。因为子类么一个构造函数内的第一行都有一句隐式super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句来指定要访问父类中的构造函数。
当然,子类的构造函数第一行也可以手动制动this语句来放哪寡恩本类中的构造函数,子类中至少会有一个构造函数会访问父类中的构造函数。

6、final 关键字
     (1)可修饰类、变量、函数
     (2)被final修饰的类不能被继承
     (3)被final修饰的方法不可被复写
     (4)被final修饰的变量是一个常量,只能赋值一次。 
         即可修饰成员变量,也可修饰局部变量。常量都大写
     (5)内部类定义在类中的局部变量位置上时只能访问该局部都被final修饰的局部变量。

7、抽象类---不可实例化
     抽象类,从字面上理解就是一些笼统的、不明确(看不懂)的类。也就是说,在描述事物的功能时,不同的事物的功能相同,但功能主体不同。比如:建筑专业的学生和软件专业的学生,他们都具有学习的功能,但他们学习的内容不同,这时就应该把学生定义成抽象类,学生中的学习方法定义成抽象方法,然后让其具体子类复写其学习(study)抽象方法,建立子类对象调用即可,当然,无论是建筑专业的学生还是软件专业的学生,他们都有吃饭这个功能,那么这个共性方法就可以定义在父类(Student)中.

具体代码如下:
//定义一个抽象类Student(这个类是建筑和软件学生类的相同功能向上抽取得来的)
abstract class Student
{
	//定义抽象方法study();(建筑专业学生类和软件专业学生类共同的学习功能)  
        //没有方法体
	 abstract void study();
       //也可定义一般方法:建筑专业学生和软件专业学生共有的功能   吃饭
	void eat()
	{
		System.out.println("I can eat!!!");
        }
}
//定义Student的子类ConsStudent 
class ConsStudent extends Student
{
	//复写抽象方法,用于实现自己特有的学习内容(建筑课程)
	void study()
	{
		System.out.println("我学习建筑课程");
	}
}
//定义Student的子类SoftStudent 
class SoftStudent extends Student
{
        ////复写抽象方法,用于实现自己特有的学习内容(软件课程)
	void study()
	{
		System.out.println("我学习软件课程");
	}
}

class  AbstractDemo
{
	public static void main(String[] args) 
	{
		//建立子类对象,并调用子类复写的方法,实现自己学习的不同内容
		//new ConsStudent().study();
		ConsStudent cs = new ConsStudent();
		cs.study();
                cs.eat();
		
			new SoftStudent().study();
                        new SoftStudent().eat();
		//SoftStudent ss = new SoftStudent();	
		//ss.study();

	}
}


抽象类一些注意的地方:
     (1) 抽象方法一定在抽象类中
     (2)抽象方法和抽象类都必须用abstract关键字修饰
     (3)抽象类不能用new创建对象,调用抽象方法无意义
     (4)抽象类中的抽象方法要被使用,必须由子类复写所有的抽象方法后,建立子类对象调用。
     (5)若子类只覆盖了父类的部分抽象方法,那么该子类还是抽象类
     (6)抽象类其实和一般的类一样,只不过抽象类中有一些不明确的方法。所以,抽象类中可定义抽象方法,也可定义一般方法。

8、模板方法设计模式
需求:获取一段程序的运行时间=程序运行的开始时间 - 结束时间
获取时间:System.currentTimeMillis();

模版方法:在定义功能时,功能的一部分是确定的,但有一部分是不确定的,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去。

模版方法设计模式提高了程序的扩展性,代码重用性。
注意:模版方法不一定都是抽象的,只要子类需要实现某些功能,就需要用此模式,若不需要子类复写的方法用final修饰。

具体代码如下:
//定义一个抽象类,因为我们不需要知道机器是怎么获取的
abstract class GetTime
{
	//子类不能复写getTime方法,我们
	public final void getTime()
	{
			long start = System.currentTimeMillis();

			runcode();//调用方法
			
			long end = System.currentTimeMillis();
			System.out.println("毫秒:"+(end-start));		
	}

	//把运行语句封装起来,方便调用
	//因为运行的内容不一样,所以将方法设置为抽象
	public abstract void runcode();		
}
//要显示另一程序的运行时间就要继承GetTime
class SubTime extends GetTime
	{
			//复写runcode方法,定义自己的运行代码
			public void runcode()
		{
				for (int x=0;x<1000;x++ )
			{
				System.out.print(x);
			}
		}
	}
class Template 
{
	public static void main(String[] args) 
	{
                //建立子类对象并调用复写的方法
		SubTime sb=new SubTime();
		sb.getTime();
		
	}
}



   

---------------http://www.itheima.comjava培训、android培训期待与您交流!---------------

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics