黑马程序员-Java学习笔记之面向对象(多态 在上一篇笔记中)

来源:互联网 发布:java短信接口视频教学 编辑:程序博客网 时间:2024/06/08 07:13

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


一面向对象

理解

面向对象是基于万物皆对象这个哲学观点. 把一个对象抽象成类,具体上就是把一个对象的静态特征和动态特征抽象成属性和方法,也就是把一类事物的算法和数据结构封装在一个类之中,程序就是多个对象和互相之间的通信组成的.

面向对象是一个很抽象的概念,它相对面向过程而言。过程与对象都是一种解决问题的思想。

面向过程:强调的是功能行为,一种过程,先干啥,再干啥;面向对象:将功能封装到对象里,强调的是具备某功能的对象;

按照面向对象的思想,可以把任何的东西看做对象!

       面向对象具有封装性,继承性,多态性.封装隐蔽了对象内部不需要暴露的细节,使得内部细节的变动跟外界脱离,只依靠接口进行通信.封装性降低了编程的复杂性. 通过继承,使得新建一个类变得容易,一个类从派生类那里获得其非私有的方法和公用属性的繁琐工作交给了编译器. 而 继承和实现接口和运行时的类型绑定机制 所产生的多态,使得不同的类所产生的对象能够对相同的消息作出不同的反应,极大地提高了代码的通用性. 

       总之,面向对象的特性提高了大型程序的重用性和可维护性.

面向对象特点:

1:将复杂的事情简单化。

2:面向对象将以前的过程中的执行者,变成了指挥者。

3:面向对象这种思想是符合现在人们思考习惯的一种思想。

过程和对象在我们的程序中是如何体现的呢?过程其实就是函数;对象是将函数等一些内容进行了封装。

类的定义

类(class)是Java 语言的最小编程单位,也是设计和实现Java 程序的基础,类是一组事物共有特征和功能的描述。类是对于一组事物的总体描述,是按照面向对象技术进行设计时最小的单位,也是组成项目的最基本的模块。类的概念是抽象的,类似于建筑设计中的图纸,是对于现实需要代表的具体内容的抽象。类只包含框架结构,而不包含具体的数据。所以类代表的是总体,而不代表某个特定的个体。

类是抽象的,对象是具体的,实实在在的!

在类中定义其实都称之为成员。成员有两种:

1:成员变量:其实对应的就是事物的属性。

2:成员函数:其实对应的就是事物的行为。

Example:


//定义一个类class Person{String name;//定义属性int age;String sex;//定义方法(行为)也就是函数public void speak(){System.out.println("大家好我是一个人");}}/*这个程序就定义了一个以人为对象的类,其中人这个对象具有名字,年龄,性别的属性,还具有说话的功能。创建一个对象的格式:       Person p = new Person();这样就创建了一个人物对象,修改属性和使用功能的格式:       p.age=20;//对对象属性的修改       p.speak();//使用对象的功能*/


所以,其实定义类,就是在定义成员变量和成员函数。但是在定义前,必须先要对事物进行属性和行为的分析,才可以用代码来体现。

3 匿名对象

匿名对象是对象的简化形式。

成员变量和局部变量的区别:

1:成员变量直接定义在类中。

   局部变量定义在方法中,参数上,语句中。

2:成员变量在这个类中有效。

局部变量只在自己所属的大括号内有效,大括号结束,局部变量失去作用域。

3:成员变量存在于堆内存中,随着对象的产生而存在,消失而消失。

局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。

局部变量必须初始化。

成员变量存储在堆中的对象里面,由垃圾回收器负责回收。 

 

构造函数

1 构造函数:用于给对象进行初始化,是给与之对应的对象进行初始化,它具有针对性,函数中的一种。

2 特点:

1):该函数的名称和所在类的名称相同。

2):不需要定义返回值类型。

3):该函数没有具体的返回值。

记住:所有对象创建时,都需要初始化才可以使用。

 

注意事项:一个类在定义时,如果没有定义过构造函数,那么该类中会自动生成一个空参数的构造函数,为了方便该类创建对象,完成初始化。如果在类中自定义了构造函数,那么默认的构造函数就没有了。

 

一个类中,可以有多个构造函数,因为它们的函数名称都相同,所以只能通过参数列表来区分。所以,一个类中如果出现多个构造函数。它们的存在是以重载体现的。

 

3 构造函数和一般函数有什么区别呢?

1):两个函数定义格式不同。

2):构造函数是在对象创建时,就被调用,用于初始化,而且初始化动作只执行一次。

    一般函数,是对象创建后,需要调用才执行,可以被调用多次。

   

4 什么时候使用构造函数呢?

分析事物时,发现具体事物一出现,就具备了一些特征,那就将这些特征定义到构造函数内。

 

构造代码块和构造函数有什么区别?

构造代码块:是给所有的对象进行初始化,也就是说,所有的对象都会调用一个代码块。只要对象一建立。就会调用这个代码块。

构造函数:是给与之对应的对象进行初始化。它具有针对性。

 

Person p = new Person();

创建一个对象都在内存中做了什么事情?

1):先将硬盘上指定位置的Person.class文件加载进内存。

2):执行main方法时,在栈内存中开辟了main方法的空间(压栈-进栈),然后在main方法的栈区分配了一个变量p

3):在堆内存中开辟一个实体空间,分配了一个内存首地址值。new

4):在该实体空间中进行属性的空间分配,并进行了默认初始化。

5):对空间中的属性进行显示初始化。

6):进行实体的构造代码块初始化。

7):调用该实体对应的构造函数,进行构造函数初始化。()

8):将首地址赋值给p变量就引用了该实体。(指向了该对象)

 

 

二 面向对象特征之一封装

1封 装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

2好处:

1)将变化隔离。

2)便于使用。

3)提高重用性。

4)提高安全性。

3封装原则:将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问。

 

Example:


/定义一个类class Person{private String name;//定义属性并私有private int age;private String sex;//构造函数Person(String name,int age,String sex){this.name=name;this.age=age;this.sex=sex;}//对外提供的公共访问方式public String getName(){return name;}//对外提供的公共访问方式public int getAge(){return age;}//对外提供的公共访问方式public String getSex(){return sex;}//定义方法(行为)也就是函数public void speak(){System.out.println("大家好我是一个人");}}



三 this关键字

1 this:代表对象。就是所在函数所属对象的引用。

2 this到底代表什么呢?哪个对象调用了this所在的函数,this就代表哪个对象,就是哪个对象的引用。

开发时,什么时候使用this呢?

在定义功能时,如果该功能内部使用到了调用该功能的对象,这时就用this来表示这个对象。

 

this 还可以用于构造函数间的调用。

调用格式:this(实际参数)

this对象后面跟上 .  调用的是成员属性和成员方法(一般方法)

this对象后面跟上 () 调用的是本类中的对应参数的构造函数。

Example:


/定义一个类class Person{private String name;//定义属性并私有private int age;private String sex;//构造函数Person(String name,int age,String sex){this.name=name;this.age=age;this.sex=sex;}//定义方法(行为)也就是函数public void speak(){System.out.println(this.name+","+this.age+","+this.sex);}}class Inner{public static void main(String[] args){Person p=new Person("张三",18,"男");p.speak();}}


四 static 关键字

1 static:是一个修饰符,用于修饰成员(成员变量和成员函数)

2 特点:

1).想要实现对象中的共性数据的对象共享。可以将这个数据进行静态修饰。

2).被静态修饰的成员,可以直接被类名所调用。也就是说,静态的成员多了一种调用方式。类名.静态方式。

3).静态随着类的加载而加载。而且优先于对象存在。

  4).静态的注意:静态的生命周期很长。

 

 

3 弊端

1).有些数据是对象特有的数据,是不可以被静态修饰的。因为那样的话,特有数据会变成对象的共享数据。这样对事物的描述就出了问题。所以,在定义静态时,必须要明确,这个数据是否是被对象所共享的。

2).静态方法只能访问静态成员,不可以访问非静态成员。

因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。

3).静态方法中不能使用this,super关键字。

因为this代表对象,而静态在时,有可能没有对象,所以this无法使用。

4).主函数是静态的。

4成员变量和静态变量的区别:

1,成员变量所属于对象。所以也称为实例变量。

静态变量所属于类。所以也称为类变量。

2,成员变量存在于堆内存中。

静态变量存在于方法区中。

3,成员变量随着对象创建而存在。随着对象被回收而消失。

静态变量随着类的加载而存在。随着类的消失而消失。

4,成员变量只能被对象所调用 。

静态变量可以被对象调用,也可以被类名调用。

所以,成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。

5什么时候定义静态成员呢?

成员分两种:

1).成员变量。(数据共享时静态化)

该成员变量的数据是否是所有对象都一样:

如果是,那么该变量需要被静态修饰,因为是共享的数据。 

如果不是,那么就说这是对象的特有数据,要存储到对象中。 

2).成员函数。(方法中没有调用特有数据时就定义成静态)

如果判断成员函数是否需要被静态修饰呢?

只要参考,该函数内是否访问了对象中的特有数据:(该函数中没有访问到非静态数据)。

如果有访问特有数据,那方法不能被静态修饰。

如果没有访问过特有数据,那么这个方法需要被静态修饰。

 

五 静态代码块

静态代码块:就是一个有静态关键字标示的一个代码块区域。定义在类中。

作用:可以完成类的初始化。静态代码块随着类的加载而执行,而且只执行一次(new 多个对象就只执行一次)。如果和主函数在同一类中,优先于主函数执行。

静态代码块:

       格式:

              static

              {

                           静态代码块中的语句。

              }

静态代码块、构造代码块、构造函数同时存在时的执行顺序:静态代码块  构造代码块  构造函数;

 

 

六 主函数

主函数是一个特殊的函数。作为程序的入口,可以被JVM调用。

主函数的定义:

        public:代表着该函数访问权限是最大的。

        static:代表主函数随着类的加载就已经存在了。

        void:主函数没有具体的返回值。

        main:不是关键字,但是是一个特殊的单词,可以被JVM识别。

      (String[] arr):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。jvm调用main方法时,传递的实际参数是 new String[0]。

        args:arguments  参数  。唯一可以更改的地方。

主函数是固定格式的:被JVM识别。

jvm默认传递的是长度为0的字符串数组,我们在运行该类时,也可以指定具体的参数进行传递。可以在控制台,运行该类时,在后面加入参数。参数之间通过空格隔开。jvm会自动将这些字符串参数作为args数组中的元素,进行存储。

 

 

七 帮助文档的制作

生成Java帮助文档:命令格式:javadoc –d 文件夹名 –author –version *.java

/**     //格式

*类描述

*@author 作者名

*@version 版本号

*/

/**

*方法描述

*@param  参数描述

*@return  返回值描述

*/

八 单例设计模式

1 计模式:解决问题最行之有效的思想。是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。java中有23种设计模式。

2 单例设计模式:

解决的问题:保证一个类在内存中的对象唯一性。

比如:多程序读取一个配置文件时,建议配置文件封装成对象。会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中是唯一的。

 

Runtime()方法就是单例设计模式进行设计的。

 

如何保证对象唯一性呢?

思想:

1,不让其他程序创建该类对象。

2,在本类中创建一个本类对象。

3,对外提供方法,让其他程序获取这个对象。

 

步骤:

1,因为创建对象都需要构造函数初始化,只要将本类中的构造函数私有化,其他程序就无法再创建该类对象;

2,就在类中创建一个本类的对象;

3,定义一个方法,返回该对象,让其他程序可以通过方法就得到本类对象。(作用:可控)

 

代码体现:

1,私有化构造函数;

2,创建私有并静态的本类对象;

3,定义公有并静态的方法,返回该对象。


Example;


/*饿汉式:它的特点是先初始化对象。如:Single类一进内存,就已经创建好了对象。在实际开发中,出于安全考虑,建议使用饿汉式。*/class Single{private Single(){} //私有化构造函数。private static Single s = new Single(); //创建私有并静态的本类对象。public static Single getInstance(){ //定义公有并静态的方法,返回该对象。return s;}}


Example:


/*懒汉式:延迟加载方式,它的特点对象是方法被调用时,才初始化,这也叫对象的延时加载。如:在以下完整代码中,Single类进内存,对象还没有存在,只有调用了getInstance()方法时,才创建对象。*/class Single2{private Single2(){}private static Single2 s = null;public static Single2 getInstance(){if(s==null)s = new Single2();return s;}}


九 继承

概述

        继承是面向对象的一个重要特征。当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继那个类即可。这时,多个类可以称为子类,单独的这个类称为父类或者超类。例如:猫和豹子都是猫科动物,那么就可以说描述猫这个对象创建的类,是子类;而描述猫科动物这个对象创建的类,就是父类。

好处:

1:提高了代码的复用性。

2:让类与类之间产生了关系,提供了另一个特征多态的前提。

 

继承的应用

父类的由来:其实是由多个类不断向上抽取共性内容而来的。

java中对于继承,java只支持单继承。java虽然不直接支持多继承,但是保留了这种多继承机制,进行改良。

 

单继承:一个类只能有一个父类。

多继承:一个类可以有多个父类。

私有内容也被继承下来了但是如果没有父类提供的非私有SET GET方法是不能访问的。

为什么不支持多继承呢?

因为当一个类同时继承两个父类时,两个父类中有相同的功能,那么子类对象调用该功能时,运行哪一个呢?因为父类中的方法中存在方法体。

但是java支持多重继承。A继承B  B继承C  C继承D

多重继承的出现,就有了继承体系。体系中的顶层父类是通过不断向上抽取而来的。它里面定义的该体系最基本最共性内容的功能。

所以,一个体系要想被使用,直接查阅该系统中的父类的功能即可知道该体系的基本用法。那么想要使用一个体系时,需要建立对象。建议建立最子类对象,因为最子类不仅可以使用父类中的功能。还可以使用子类特有的一些功能。

 

简单说:对于一个继承体系的使用,查阅顶层父类中的内容,创建最底层子类的对象。 

 

子父类出现后,类中的成员特点:

1:成员变量。

 当子父类中出现一样的属性时,子类类型的对象,调用该属性,值是子类的属性值。

 如果想要调用父类中的属性值,需要使用一个关键字:super 

 This:代表是本类类型的对象引用。

 Super:代表是子类所属的父类中的内存空间引用。

(私有不能访问)。

 注意:子父类中通常是不会出现同名成员变量的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。

2:成员函数。

当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法。好像父类中的方法被覆盖掉一样。所以这种情况,是函数的另一个特性:覆盖(复写,重写)

什么时候使用覆盖呢?当一个类的功能内容需要修改时,可以通过覆盖来实现。

3:构造函数。(构造函数不能继承)

发现子类构造函数运行时,先运行了父类的构造函数。为什么呢?

原因:子类的所有构造函数中的第一行,其实都有一条隐身的语句super();

super(): 表示父类的构造函数,并会调用于参数相对应的父类中的构造函数。而super():是在调用父类中空参数的构造函数。

为什么子类对象初始化时,都需要调用父类中的函数?(为什么要在子类构造函数的第一行加入这个super()?)

因为子类继承父类,会继承到父类中的数据,所以必须要看父类是如何对自己的数据进行初始化的。所以子类在进行对象初始化时,先调用父类的构造函数,这就是子类的实例化过程。

 

注意:子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super(); 

如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数。

如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数。子类中至少会有一个构造函数会访问父类中的构造函数。

 

问题:super()this()是否可以同时出现的构造函数中。

两个语句只能有一个定义在第一行,所以只能出现其中一个。

 

super()或者this():为什么一定要定义在第一行?

因为super()或者this()都是调用构造函数,构造函数用于初始化,所以初始化的动作要先完成。

 

继承的细节:

什么时候使用继承呢?

当类与类之间存在着所属关系时,才具备了继承的前提。ab中的一种。a继承b。狼是犬科中的一种。

英文书中,所属关系:" is a "

注意:不要仅仅为了获取其他类中的已有成员进行继承。

 

所以判断所属关系,可以简单看,如果继承后,被继承的类中的功能,都可以被该子类所具备,那么继承成立。如果不是,不可以继承。

 

细节二:

在方法覆盖时,注意两点:

1:子类覆盖父类时,必须要保证,子类方法的权限必须大于等于父类方法权限可以实现继承。否则,编译失败。

2:覆盖时,要么都静态,要么都不静态。 (静态只能覆盖静态,或者被静态覆盖)

Example


//定义一个类class Person{private String name;//定义属性并私有private int age;private String sex;//构造函数Person(String name,int age,String sex){this.name=name;this.age=age;this.sex=sex;}//对外提供的公共访问方式public String getName(){return name;}//对外提供的公共访问方式public int getAge(){return age;}//对外提供的公共访问方式public String getSex(){return sex;}//定义方法(行为)也就是函数public void speak(){System.out.println(this.name+","+this.age+","+this.sex);}}//继承Person类。class Student extends Person{Student(String name,int age,String sex){super(name,age,sex);//调用父类构造函数。}//特有方法。public void study(){System.out.println("我正在学习!");}}class Inner{public static void main(String[] args){Person p=new Person("张三",18,"男");p.speak();Student s=new Student("刘亦菲",25,"女");s.speak();s.study();}}


final特点:

继承的一个弊端:打破了封装性。对于一些类,或者类中功能,是需要被继承,或者复写的。

这时如何解决问题呢?介绍一个关键字,final:最终。

1:这个关键字是一个修饰符,可以修饰类,方法,变量。

2:被final修饰的类是一个最终类,不可以被继承。

3:被final修饰的方法是一个最终方法,不可以被覆盖。

4:被final修饰的变量是一个常量,只能赋值一次。

其实这样的原因的就是给一些固定的数据起个阅读性较强的名称。

不加final修饰不是也可以使用吗?

那么这个值是一个变量,是可以更改的。加了final,程序更为严谨。常量名称定义时,有规范,所有字母都大写,如果由多个单词组成,中间用 连接。

Example:


class Inner{public static final double PI=3.14;public static void main(String[] args){System.out.println(PI);}}




0 0