黑马程序员_五 【初识面向对象】【类、构造方法、对象】【封装、继承、多态】【this、super】

时间:2021-08-18 15:39:03


--------------------- android培训java培训、java学习型技术博客、期待与您交流! -------------------


 1  面向对象初识 

面向对象的概念:

面向对象是相对面向过程而言,面向对象原来指是专指在程序设计中采用封装继承多态等设计方法。现在已经扩展到很多领域。面向过程强调的是功能行为,面向对象是将功能封装进对象,强调具备了功能的对象,和面向过程一样都是一种编程思想。

面向过程与面向对象的区别:

面向过程强调的功能行为,面向对象可以将功能封装进对象,强调具备了功能的对象。

面向对象是基于面向过程的。

面向对象的特点:

1、  是一种符合人们思考习惯的思想。

2、  可以将复杂的事情简单化。

3、  将程序员从执行者转换成了指挥者。

4、  完成需求时:    a、先去找具有所需的功能的对象来用。

b、如果该对象不存在,那么创建一个具有所需功能的对象。

c、这样可以简化开发并提高复用性。

开发的过程:

就是不断创造对象,使用对象,指挥对象的的过程。

设计的过程:

其实就是管理和维护对象之间的关系。

面向对象的特征:封装,继承,多态

面向对象理解:

简单的一个例子,笔记本电源适配器,插座提供的是220V的电压,笔记本需要的是19V电压,通过适配器就可以得到适合的电压。可以把适配器看做一个对象,该对象有转换电压的功能,实现过程很复杂,但是作为使用者的我们不需要知道它里里面是怎么实现的只需要知道它的功能直接用就可以,指挥该对象完成转换电压的功能。

 2  类、构造方法、对象 

对象是用类来描述的,但类并不都是用来描述对象的。对象的存在首先要存在一个类来描述;而一个类存在并不一定就是描述对象或者说是创建对象的。

理解:

一个汽车,有颜色和时速属性,而这些属性不是开始就有的,需要有一个设计图纸进行设计,设计通过后,才能生产出对应的汽车;汽车就是对象,,设计图纸就是类,需要用类把属性描述完了,才能产生出相应的对象。


黑马程序员_五 【初识面向对象】【类、构造方法、对象】【封装、继承、多态】【this、super】

一个抽象类或者接口或者工具类,是不能创建对象的,这都是一种设计思想,接口的存在,是为了提供更多的选择性,让实现它的类强行带有某些功能等,有的抽象类实现了某一接口,是为了让其子类能够不用实现接口中全部的方法,用到哪个方法就复写父类中的某一方法即可,如事件监听中的WindowAdapter类,就是抽象类,实现了WindowListener接口,需要某一功能继承WindowAdapter复写其功能即可,提高灵活性。工具类是不能创建对象的,存在的目的就是为了服务于其他类,如Collections(服务于集合)Arrays(服务于数组)等,类中提供各种方法操作对应的对象。

类类型类本身也是一种类型,和数组一样,属于引用型变量,那么它的值是什么呢?int 型变量的值可以是2、可以是20等,类可以描述对象,类的值就是其产生出的对象,一个类的引用星变量都可以关联到一个对应的对象实体,假定有一个Person类,描述人的姓名和乃年龄属性,有对象p,如下图:

黑马程序员_五 【初识面向对象】【类、构造方法、对象】【封装、继承、多态】【this、super】

类在堆内存创建对象实体,将地址值传给引用pp就指向了对应的Person对象实体。道理和数组是一样的。

定义了就是在定义行为和属性,定义好了属性和行为就定义了一类事物。就描述好了该类对象。

例子:

class Person
{
//定义属性
private String name;
private int age;
Person(String name,int age)
{
this.name=name;
this.age=age;
}
//定义行为
public void eat()
{
System.out.println(“吃饭”);
}
}


这样就简单的定义了一个类来描述人的属性和行为,

Person p=new Person(“张三”,23);这样就创建了一个对象,描述一个人。

构造函数

构造函数可以对对象进行初始化,其特点是:函数名必须与类名一致,不需要定义返回值类型,不需要return语句,多个构造函数是以重载的形式存在的.

构造函数的特点

2.对象一建立就会调用与之对应的构造函数。(他的作用———

3. 当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数。(方便该函数的初始化,为什么要初始化?)

4. 当在类中自定义了构造函数后,默认的构造函数就没有了。

5.就多个构造函数是以重载的形式存在的。(初始化方式不一样的时候,就构造不同的构造函数,方便初始化)

6.构造函数属于成员函数,也可以被私有化,但是私有化以后就不能创建对象了,因为对象不能进行初始化动作。

7构造函数之间只能用this语句调用

构造函数和一般函数的区别

|----方法书写不同

构造方法名和所属类名相同,普通方法名符合命名规范就可以

|----方式不同:

构造函数是在对象一建立就自动运行。给对象初始化。

而一般方法是对象调用才执行,给对象添加对象具备的功能。(对象调用才会使用一般方法)

|----可运行次数不同

一个对象建立,构造函数只运行一次。

而一般方法可以被该对象调用多次。

什么时候定义构造函数

当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。(如定义一个人,刚刚出生时有名字和年龄,这样初始化名字和年龄)

构造代码块:

作用:给对象进行初始化。

对象一建立就运行,而且优先于构造函数执行。

构造代码块和构造函数的区别:

构造代码块是给所有对象进行统一初始化,

而构造函数是给对应的对象初始化。

构造代码快中定义的是不同对象共有特性的初始化内容。

对象:

对象属于引用型变量,可作为参数传递给方法,对对象进行操作。普通对象就是用类直接创建,如new Person(“张三”,23);特殊对象,就是匿名对象。

匿名对象:匿名对象顾名思义就是没有名字的对象。是一种简化形式,简化就会有好处,同时存在着弊端。

|----有名字的对象:Person p=new Person(“张三”,23); p就是对象实体的名字,可 以用p来调用方法,再去调用属性,可以重复使用。

|----匿名对象:new Person(“张三”,23); 并没有引用指向该实体。匿名对象简化了书写,同时存在着弊端。

|----匿名对象的使用,例子:

//定义一个类,描述人,有吃饭行为

class Person
{
//定义属性
private String name;
private int age;
Person(String name,int age)
{
this.name=name;
this.age=age;
}
//定义行为
public void eat(String str)
{
System.out.println(“吃”+str);
}
}
class Demo
{
public static void main(String[] args)
{
//创建一个有名字的普通对象
Person p1=new Person(“张三”,23);
p1.eat(“馒头”);
p1.eat(“肉”);
//创建匿名对对象并调用方法
new Person(“李四”,24).eat(“馒头”);//只能使用一次
}
}


普别很明显,有名字的对象可以调用类中的方法多次,张三可以吃馒头也可以吃肉,还可以吃很多东西。匿名对象只能调用一次方法,李四也就只能吃一种了。

这就是匿名对象的弊端,不能重复使用。它的存在不可能是为了弊端而存在。

匿名对象正确使用:

匿名对象的使用非常简单,当只需要调用一次方法时,就可以使用匿名对象。

一个简单的例子,买汽车,只需要买一辆,购买一次就可以。

//汽车父类
Class Car
{
Private String name;
Car(){ }
}
//各品牌汽车类
class AuDi extends Car
{
private String name;
AuDi()
{
name=”audi”;
}
}
class BaoMa extends Car
{
private String name;
BaoMa()
{
name=”baoma”;
}
}
class FengTian extends Car
{
private String name;
FengTian()
{
name=”fengtian”;
}
}
//购买汽车类
plass BuyCar
{
//买车方法
public AuDi buyAuDi()
{
return new AuDi();//匿名对象
}
public BaoMa buyBaoMa()
{
return new BaoMa();//匿名对象
}
public FengTian buyFengTian()
{
return new FengTian();//匿名对象
}
}

//执行类
class Demo
{
public static void main(String[] args)
{
//购买一辆奥迪汽车
new BuyCar().buyAuDi();//使用匿名内部类调用购买方法
}
}


例子可能不是很恰当。但也能说明匿名对象的使用,上例两次利用匿名对象,购买一辆汽车,执行一次,产生一个要购买的汽车也是一次,都可以用匿名对象来完成操作。

 3  成员变量、局部变量 

成员变量:

成员变量是定义在成位置上的变量,成员就是组成类的直接成分,成分里面的成分就不是成员。例如:类中的一个方法,就是成员方法,属于成员,方法中的参数就不是成员,因为参数属于方法所有,是组成方法的成分。同样的,一个变量直接定义在类体中,就属于成员变量,定义在类中的方法中的变量是属于方法体的,不直接属于类体。成员变量包括:基本数据类型变量和引用数据类型变量。

成员变量特点:

成员变量跟随对象(这里不算静态)而加载进堆内存,这些变量都有默认值。基本数据类型中,byteshortintlong默认值为0floatdouble默认值为0.0char类型默认值为\u0000boolean型默认值为false

对于应用数据类型,默认值为null,空。

局部变量:

局部变量定义在局部位置,上面说的定义在方法中的变量就是局部变量。不是类的直接组成部分,依附于方法体存在。定义在方法中的知识其中一种,还有定义在代码块和静态代码块中等。

局部变量特点:

局部变量没有默认值,必须手动进行赋值操作。存在于栈内存中。使用完后就会被释放。

 4  面向对象三特性简谈 

面向对象有三大特征:封装继承和多态。

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

|--将变换隔离

|--便于使用

|--提高重用性

|--提高安全性

私有化是封装的体现之一,private关键字:用于修饰成员变量和成员函数,只能在本类中访问,是java语言的最低权限,将成员变量私有化,对外提供set,get方法进行访问.

例如:

class Person
{
//定义属性
private String name;
private int age;
Person(String name,int age)
{
this.name=name;
this.age=age;
}
//这里简写成一个方法,设置姓名和年龄
public void set(String name,int age)
{
this.name=name;
this.age=age;
}
//获取姓名
public String getName()
{
return this.name;
}
//获取年龄
public int getAge()
{
return this.age;
}
}


将属性私有化,进行封装,提供set和get方法访问。这样可以避免改动一处而需要其他很多地方改动的麻烦,避免了牵一发动全身的弊端。

继承:继承的出现,才产生了面向对象的另一特征多态的出现。

继承的定义:

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可.多个类可以称为子类,单独这个类称为父类或者超类.子类可以直接访问父类中的非私有的属性和行为.继承的出现提高了代码的复用性,让类与类之间产生了关系,提供了多态的前提。子类和父类之间应具所属(is a)关系,不应只为了获取其他类的某种功能而去继承。

继承格式:

Class Fu

{

}

Class Zi extends Fu

{

}

使用关键字extends,让两类产生继承关系。

继承特点:

|--继承是代码重用的一种方式。是实现多态的前提条件。

|--使用继承前提:需要把已存在类中的缺省行为融合进自己的新类中时才需要继承已 存在的类。

|--抽取子类共性得到父类,所以应先有子类,才能有共性,随后又父类。

|--子类父类局部  同名变量 同样遵循就近原则。

|--只能单继承。java语言不支持多继承

|-- 一个类,只能继承一个类,不能继承多个类

|-- 单继承,单线继承

|-- 一个父类可以有多个子类,每个子类只能有一个父类

|--java中,支持多层继承

|-- 正是因为支持多层继承,出现了继承体系

|-- 学习一个体系的时候,学习最高父类

|-- 使用的时候,建立最子类的对象

|-- 最高父类,是这个体系中最共性的内容

|-- 最子类,拥有所有的共性内容和自己的独有内容

|--使用继承情况

|-- 研究某种事物,是不是另一个事物中的一种,使用继承关系

|-- 如果一个事物,继承了另一个事物,发现,子事物中不应该具有父事物中的 内容,该继承无效。

|-- 反之,子事物,应该具备父事物中的内容,继承有效

方法覆盖:

类之间存在继承关系时,父类中的非私有方法会继承给子类,如果子类中与同名的方法,那么该方法就会覆盖掉父类的方法,如果想使用父类中的方法,可以用关键字super 进行调用。

例子:


class Fu
{
public void emthod()
{
System.out.println(“父类方法”);
}
}
Class Zi extends Fu
{
//子类方法覆盖父类方法
Public void method()
{
System.out.println(“子类方法”);
}
super.method();//调用父类的方法
}


函数的覆盖应注意的:父类的私有方法无法覆盖,方法名和参数列表必须相同

继承覆盖的问题:Overload和Override的区别?

  答:Overload是重载,Override是覆盖

     Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同.

     Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现.

子类实例化:

子类创建时,子类的构造方法必须要访问父类的构造方法,默认的是访问父类空参数构造方法;如果父类的构造方法没有空参数的,那么就需要在子类中手动的指定子类构造方法中访问的是父类哪个构造方法。必须通过this或者super语句指定要访问的构造函数。

例子:

class Fu
{
//构造方法1
Fu(int num)
{
num=9;
}
//构造方法2
Fu(int num1,int num2)
{
num1=1;
num2=2;
}
}
class Zi
{
//子类构造方法1
Zi(int num)
{
Super(num);//调用的是父类的构造方法1
}
//子类构造方法2
Zi(int num1,int num2)
{
super(num1,num2);//调用的是父类构造方法2
}
//子类构造方法3
Zi(int num,String str)
{
this(num);//调用子类构造方法1
str=”你好”;
}
//Zi(){ super();}//这是个错误的构造方法父类没有空参数构造方法,而
//该子类构造方法需要自动调用父类的空参数构造方法super(),找不到对应的
//父类构造方法,出错。
}


注意点:

super()和 this()语句必须放在构造方法的第一句,所以两个语句不能同时出现在同一个构造方法内。

原因:构造方法的作用是进行初始化,如果把super()和this()放在了后面,而前面的语句恰巧用到需要被初始化后才能用的数据,就会产生错误,所以必须要先初始化再去使用,即先进内存的不能访问后进内存的,所以super()和this()语句要放在第一句的位置。

this和super:

super与this的区别:

|--this可以区分本类对象中的成员变量和函数内的局部变量,而super可以区 分本类对象中的成员变量和父类对象中的成员变量.

|--访问原则:在函数中就近访问;调用本类对象的成员变量使用this,调用父 类中的成员变量使用super.

|--this表示本类对象,可以有对象的性质,有地址值,代表的就是当前对象, 可以打印出地址值。而super则不能,不能代表父类的对象使用,也就没有 像对象的地址值存在。不能打印出地址值。

子父类加载顺序:

|-- 父类字节码文件进方法区

|-- 父类静态内容,进方法区静态区

|-- 子类字节码文件进方法区

|-- 子类静态内容,进方法区静态区

|-- 建立子类对象,运行顺序

|-- 父类静态代码块

|-- 子类静态代码块

|-- 子类构造方法 super()

|-- 父类构造方法

|-- 父类构造代码块

|-- 父类构造方法

|-- 子类构造代码块

|-- 子类构造方法

多态:多态的前提是存在继承关系。

说到多态,不得不涉及到的就是抽象类和接口,在多态中抽象类和接口的使用是非常灵活多见的。

抽象类:

    Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类.

抽象类的特点:

a.抽象类和抽象方法必须用abstract关键字来修饰

b.抽象方法只有方法声明,没有方法体,定义在抽象类中

  修饰符 abstract   返回值类型    函数名(参数列表) ;//后面没有{方法 体},经常不注意就会加上{}

c.抽象类不可以被实例化,也就是不可以用new创建对象,是因为抽象类是 从 具体事务中抽取的,本身不是具体的,没有对应的实例。而且抽象类即使创 建了对象,调用抽象方法也没有意义

d.抽象类可以通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后 才可以创建对象,否则该子类也是抽象类

注意:抽象类中可以有构造函数,也可以不定义抽象方法(图形化界面运用中的类),abstract不能和final,private,static同时出现。

抽象类需要被子类继承选择性的实现其中的方法,才能够发挥其作用。单纯的一个抽象类是没有意义的,需要与其他类产生继承关系。因其不能被实例化。

接口

    接口的出现将“多继承”以另一种形式表现出来,即多实现,接口内部只有成员变量和成员方法,且格式是固定的子类必须全部覆盖接口中的所有抽象方法.

    格式: interface  {}

       成员常量:public static final int PI=3.14;

       成员变量:public abstract int x=5;

    接口的特点:

    a.接口是对外暴露的规则。

    b.接口是程序的功能扩展。

    c.接口可以用来多实现。

    d.类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。

    e.接口与接口之间可以有继承关系。

   接口与抽象类的区别:

    A:抽象类只能被单继承;接口可以被多实现。

    B:抽象类中的成员:

          成员变量:可以是常量,也可以是变量。

          成员方法:可以是抽象的,也可以是非抽象的。

          构造方法:虽然不可以创建对象,但是可以给子类实例化用。

       接口中的成员:

          成员变量:只能是常量。默认修饰符 public static final

          成员方法:只能是抽象的。默认修饰符 public abstract

    C:抽象类中定义的是体系结构中的共性的内容。

       接口中定义的是对象的扩展功能。

    D:抽象类被继承表示的是:"is a"的关系。xx是yy中的一种。

       接口被实现表示的是: "like a"的关系。xx像yy中的一种。

       接口与接口之间的关系;接口与接口之间是继承关系;接口可以实现多继承

接口注意点:

注意:

(1)

接口1: void show()

接口2:  int show()

类1 implements 接口1,接口2

{

实现接口方法:会出错误,实现重载应遵循 重载重写特性,返回值不一些样

实现方法名一样会出错,若接口方法中有参数列表,则可以用重载重写的方法

进行区分。

}

(2)

接口中的字段存储在接口的静态存储区域中,而不属于该接口。

(3)

|--若没有实现所有的方法,则该类也应是一个接口,接口 继承 接口用关键字  extends,接口可以多继承。

|--类实现接口用 implements,也可以多实现。


类与类、类与接口、接口与接口

(1)类与类 继承关系:单继承 extends

|--普通类继承:可重写覆盖父类方法 和 拥有父类非私有成员变量

|--抽象类继承:继承抽象类必须重写覆盖父类的抽象方法,抽象类可没有抽象方 法。若不全重写,子类也是抽象类。(强制性重写)

(2)类与接口实现关系:(多)实现 implements

|--单实现:需要实现接口中的所有方法,不然,此类也要成为接口

|--多实现:当实现的接口中有同名的方法,参数列表一样,但返回值不同,那么 实现时会出错,应用参数列表不同来区分,同理于方法重载,再重写。

(3)接口与接口:(多)继承 extends

接口与抽象的区别:

顺便说说区别

.区别一  抽象类 体现继承关系:一个类只能单继承

   接 口  体现实现关系:一个类可以多实现

.区别二  抽象类 是继承:is a关系,在定义该体系的共性内容

   接 口  接口是实现:like a关系,定义额外功能

.区别三  抽象类 中可以定义非抽象方法,供子类直接使用

   接 口  的方法都是抽象,其中的成员都有固定修饰符

接口 没有构造方法,类继承了接口后,子类的构造方法中的super()不是接口的。


接口与抽象类

(1)继承 和 实现

|--单继承,多实现

|--继承后可以直接使用父类中的非私有成员

(2)方法

|--抽象类中可以有抽象方法,也可以没有抽象方法,也就是可以提供实现好的方 法。也是抽象类唯一的优点。

好处:在抽象类中提供一个实现好的方法,子类就都具有了,可以不用也 可以用,而对于接口,接口中添加了一个方法后,则所有子类都需要去实 现该方法,不然就错误。

|--接口中全部是抽象方法

(3)子类方法的覆盖

|--抽象类中的抽象方法需要被覆盖,非抽象方法覆盖也可以,也可以直接继承过 来用。

|--接口中的方法需要全部重写,否则 子类也要是接口。

(4)子类接收到的内容

|--抽象类,定义的是体系中的共性内容,也是为子类提供共有的扩展

|--接口,定义的是扩展功能

(5)面向接口编程 可提高代码的重用性 和 程序的扩展性,降低了耦合性。这是接口的 大优势;抽象类,虽有接口的一些特性,但由于单继承而使用效率相对降低很 多。

(6)接口是定义混合工具的离线工具。


言归多态:

多态是指父类或者接口的引用指向自己的子类对象。即一种事物存在的多种形态。

|-- 程序中的体验形式: 父类或者接口引用,指向自己的子类对象

|-- 优势:代码少了,扩展性强了,提高后的可维护性

|-- 前提:必须有继承或者是实现接口

|-- 弊端:只能调用子父类中的公共方法,不能调用子类中的特有方法

多态的转型:

|-- 向上转型:使用多态时,会将子类类型提升为父类类型,自动类型提升,

转型后可以调用子父类*有的功能。

|-- 向下转型:调用子类特有的功能时,需要将已经提升为父类类型的子类对象,转成 子类类型 属于强制类型转换。  

|--特点:手动做。

|--格式 : 目标类型  变量名 = (目标类型)提升为父类类型的引用

|-- 转型判断:instanceof 比较运算符,比较的是引用类型 判断一个引用类型,是不是属于这个 类类型。

多态中的细节:

A、非静态成员变量的特点

a、编译时期特点

|-- 引用型变量所在的类中,没定义这个变量编译失败

|-- 引用型变量所在的类中,定义可这个变量编译成功

b、运行时期特点

|--  运行时期,参考引用型变量所属的类中的结果

B、非静态成员方法的特点

a、编译时期特点

|-- 编译时期,参考引用型变量所在的类中,有没有定义这个方法,有编译成功, 没有编译失败。

b、运行时期特点

|-- 运行时期,虚拟机动态的绑定到了子类对象中,运行的是子类中重写的方 法。

C、静态成员变量特点

a、编译时期特点

|-- 参考引用型变量所在的类中,是否定义了静态变量,定义了编译成功,没 定义编译失败.

b、运行时期特点

|-- 参考引用型变量所以在类中的结果

D、静态成员方法特点

a、编译时期特点

|-- 参考引用型变量所在类中,是否定义了这个静态方法,没有定义编译失败, 定义了就编译成功

b、运行时期特点

|-- 参考引用型变量所在类中的静态方法

总结:

   成员变量

|-- 不论是静态成员变量还是非静态成员变量

编译时期参考等号左边

运行时期参考等号左边

   成员方法

|-- 静态成员方法

编译时期参考等号左边

运行时期参考等号左边

|-- 非静态成员方法

编译时期参考等号左边

运行时期参考等号右边   虚拟机将动态绑定到子类对象中

简单的记:

     非静态成员方法,编译看左边,运行看右边

                 其他情况,编译运行都看左边

博客学习总结:通过记录本篇博客,有把面向对象的核心内容复习了一遍,收获不算小,有些细小的知识容易忘记,知识点会比较多,总结起来可能会费点力,把零碎的知识点糅合到一起;总结下来会感觉这些知识点是相互结合的,一环扣一环,正式因为紧密,可能不容易把知识点划分成明显的区域,像多态,本事面向对象的一种特性,细说依赖需要说到接口,说到接口又可以联系到接口的使用,联系到修饰符的使用等,正式因为联系紧密,一些知识点才能更好地理解。


本篇博文结束!




                                                                                                   @感谢老师的辛苦批阅