Java SE《基础篇》——(六)面向对象

上章节回顾

  • 一维数组创建与使用
  • 二维数组创建与使用
  • 数组排序与查找算法

面向对象介绍

面向过程与面向对象

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

看大家怎么说
1. 来自知乎的【大魔头-诺铁】这么说:
面向对象是把世界描绘成N个有主动性的"活物"之间的交互
面向过程是有一个"上帝之手"通过制定一系列规则来操纵一堆"死物"

2. 来自知乎的【叶晓斌】这么说:
面向过程是一件事"该怎么做"
面向对象是一件事"该让谁来做",然后那个"谁"就是对象,他要怎么做是他自己的事,反正最后一群对象合力能把事做好就行了。
3. 我这么说:
面向过程是把一件事分成几个步骤,创建几个方法,每个方法处理一步操作。每个方法按顺序被调用完成一件事。
面向对象类似把一件事交给几个"人",每个"人"做自己会做的那部分,大家配合把事情完成。(这里我们只面向"人",不关心他们具体怎么做——在这里"人"就是所谓的对象)

大家理解什么是面向对象了吗?
如果没有理解,那很好。我们一起来学习本章面向对象,来慢慢感受面向对象的力量。

类和对象

Java是面向对象的程序设计语言,类是面向对象的重要内容,我们可以把类当成一种自定义数据类型,可以使用类来定义变量,这种类型的变量称为引用型变量。也就是说,所有类是引用数据类型

概念

面向对象的程序设计中有两个重要概念:类和对象。其中类是某一类对象的抽象。对象才是一个具体的实体。如:有一条狗叫”花花”,那么这条真实存在的狗才是对象,而”狗”是类,代表一类”花花”这样的实体。

抽象的重要性

抽象是继承和多态的基础。在抽象的过程中,我们获得是事物本质的东西和事物独特的东西,这样我们很自然的认识到抽象的事物和具体的事物之间是一种继承的关系,而他们之间的不同性可以通过多态来表现出来。

定义类

语法格式
[权限控制符] class 类名 {
//零个到多个构造器 * 构造器定义 * 注意:构造器无返回值类型
[权限控制符] 类名(参数列表){};
//零个到多个属性 * 属性定义
[权限控制符] [static | final] 属性类型 属性名;
//零个到多个方法 * 方法定义
[权限控制符] [abstract | final] [static] 返回值类型 方法名(参数表){};
}

提示:定义类的时候,不是必须显式写构造方法、属性、方法

【示例】:定义一个学生类
属性:学号、姓名、年龄、班级
方法:学习、唱歌

对象的创建和使用

对象创建语法格式
类名 对象名 = new 类名();
示例
Student s = new Student();

操作对象的属性和方法
语法格式
使用类的属性: 对象名.属性
实用类的方法: 对象名.方法名()

以上面学生类Student为例:
Student s = new Student();
s.age = 18; //为属性age赋值为18
s.sing(); //调用sing方法

构造方法

构造方法是一种特殊的方法,它是一个与类同名且没有返回值类型的方法。对象的创建就是通过构造方法来完成,其功能主要是完成对象的初始化。当类实例化一个对象时会自动调用构造方法。构造方法和其他方法一样也可以重载。

特殊性
1. 构造方法作用:构造出来一个类的实例,对构造出来的类的对象初始化。
2. 构造方法的名字必须与定义他的类名完全相同,没有返回类型,甚至连void也没有。
3. 系统会默认为类定义一个无参构造方法,即默认构造方法。若显式定义了构造方法,则系统不会提供默认无参构造函数。

示例

  1. 为上例学生类定义添加构造方法,用来初始化姓名属性。
  2. 再添加一个构造方法,用来同时初始化姓名、年龄两个属性。

思考:我们可以定义两个名称一样的属性么?不可以。那么两个方法的名称一样呢?这里的两个构造方法名称相同了,但是没有报错,为什么呢?这就是方法重载。

方法重载

特点
1. 方法名一定要相同
2. 参数列表不同——参数类型、参数个数
3. 与方法的访问控制符和返回值无关

示例

1. 定义一个无参构造方法
2. 定义一个带一个参数构造方法,初始化姓名属性。
3. 定义一个带一个参数构造方法,初始化年龄属性。
4. 定义一个带两个参数构造方法,初始化姓名、年龄属性。
对象的引用与this

引用
Student s = new Student(“张三”);
上面的对象名s即为对象的引用,而 new Student(“张三”)才是张三对象。

this
总要有个事物来代表类的当前对象,就像C++中的this指针一样,Java中的this关键字就是代表当前对象的引用。

图例
这里写图片描述

它有三个主要的作用:
1、在构造方法中调用其他构造方法
比如:有一个Student类,有三个构造函数,某一个构造函数中调用另外构造函数,就要用到this(),而不可以直接使用Student()。
2、返回当前对象的引用
3、区分成员变量名和参数名

static

static关键字的用途
在《Java编程思想》P86页有这样一段话:
“static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。”

这段话可以看出static关键字的基本作用:方便在没有创建对象的情况下来进行调用(方法/变量)
很显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。

static方法
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说:
1. static是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。
2. 在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
3. 虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。

static变量
static变量也称作静态变量,静态变量和非静态变量的区别是:
1. 静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
2. 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

JVM内存分析——案例

内存分配图

这里写图片描述

JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)
堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身。
3.一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。

栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中。
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
4.由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。

静态区/方法区:
1.方法区又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
3.全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。

举例

Sample test1=new Sample(“测试1”);

这里写图片描述

案例

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

封装

概念

什么是封装
封装是把对象的所有组成部分组合在一起,封装使用访问控制符将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。
作用
适当的封装可以让代码更容易理解和维护,也加强了代码的安全性。

访问控制符

访问控制符用来控制父类、子类间,不同包中的类间数据和方法的访问权限。包括private、protected、public和默认四中类型。其访问权限如下:

访问控制符同一类中同一包中同一子类中其他
private
default
protected
public


【示例】
演示四种访问类型的访问权限?

继承

概念

继承是从已有的类中派生出新的类,新的类能吸收已有类的属性和行为,并能扩展新的属性和行为。

Java继承特点

  • Java是单继承的,不支持多继承。这样使得Java的继承关系很简单,一个类只能有一个父类,易于管理程序。同时一个类可以实现多个接口,从而克服单继承的缺点。
  • 继承关系是传递的
  • private修饰的成员变量或方法是不能被继承的

解决什么问题
提高了代码的效率,避免了代码重写。

图例
这里写图片描述

语法格式

[修饰符] class 子类 extends 父类 {
    //类体
}

【示例】
实现一个继承,定义学生类为父类,定义一个大学生类作为子类。

super

this是指当前对象引用,super是指当前对象的父类对象的引用。由于继承子类可以访问父类的成员,所以this除了可以访问自己的成员还可以访问父类的成员,但是为了明确的指明访问的是父类的成员,则要用关键字super来指定。

这里写图片描述

【示例】
在大学生类中使用super调用父类的属性和方法。

final

final表示不可改变的含义

  • 采用final修饰的类不能被继承
  • 采用final修饰的方法不能被重写
  • 采用final修饰的变量不能被修改
  • final修饰的变量必须显式初始化 如果修饰的引用,那么这个引用只能指向一个对象,该引用不能再次赋值,但对象本身内部可以修改
  • 构造方法不能被final修饰
方法重写

在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写

方法重写的特性
1.发生方法重写的两个方法返回值、方法名、参数列表必须完全一致(子类重写父类的方法)
2.子类抛出的异常下不能超过父类相应方法抛出的异常(子类异常不能大于父类异常)
3.子类方法的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)

【示例】
父亲类上班开车,儿子类上班骑自行车。

父子对象间转换和instanceof

父类和子类拥有公共的属性和方法,那么能否通过父类来访问子类中的继承的公共的属性和方法呢?我们在上面了>解到,如果在子类内部肯定可以,而且通过this和super关>键字可以轻松的使用。
那么如果在类的外部怎么实现这个应用需求呢?Java提供了类对象间的转换来满足这个需求。我们回顾一下之前的>基本数据类型强制转换,父类和子类之间的转换与其类似。

隐式转换:小类转大类,也叫为向上转
强制转换:大类转小类,也为向下转,需要有 “()”作为强制转换。
如:Student student = new DaXueSheng();//隐式转换
DaXueShen daXueShen= (DaXueSheng)student;//强制转换

【总结】
* 子类对象可以被视为其父类的一个对象,如一个大学生(子类)肯定是一个学生(父类)。
* 父类对象不能被声明为某一个子类的对象,如一个学生不能完全表示一个大学生。
* 父类引用可以指向子类对象,但不能访问子类对象新增的属性和方法。

instanceof运算符
Java提供了instanceof关键字,使我们在强制转换之前,判断前一个对象是否后一个对象的实例,是否可以成功的转换,以增强代码的健壮性。
【示例】
演示父子类转换的特性

多态

概念

龙生九子各不相同
意为虽然是亲兄弟,但彼此之间的品性、爱好仍大有不同。

多态性
多态指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)

示例
通过动物类引用调用它的子类——鸟类,猫类,狗类的call()方法,实现多态

必要条件:继承、重写、向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重写,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要父类的引用指向子类的对象。

体现方式
  • 使用父类作为方法形参实现多态。
  • 使用父类作为方法返回值实现多态 。
好处

把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。

//不使用多态,本来想拥有一条狗,后来想拥有一只猫,就需要改代码了
public class Person{
    //Dog d;
    Cat c;
    //public void setAnimal(Dog d){
    //  this.d = d;
    //}
    public void setAnimal(Cat c){
        this.c = c;
    }
    public static void main(String[] args) {
        Person p = new Person();
        //p.setAnimal(new Dog());
        p.setAnimal(new Cat());
    }
}

//使用多态,本来想拥有一条狗,后来想拥有一只猫,只需重新设置
public class Person{
    Animal a;
    public void setAnimal(Animal a){
        this.a = a;
    }
    public static void main(String[] args) {
        Person p = new Person();
        //p.setAnimal(new Dog());
        p.setAnimal(new Cat());
    }
}

//动物类及其子类
class Animal{
}
class Dog extends Animal{
}
class Cat extends Animal{
}

抽象类与接口

抽象类概念

抽象类往往用来表示设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
比如:动物,它只是一个抽象的概念,并没有一个 “东西”叫做 “动物”。所以,它并不能代表一个实体,这种情况下,我们就适合把它定义成抽象类。

语法格式

修饰符 abstract class 类名(){
    //……
    abstract 方法名();
}


abstract修饰:它修饰类就是抽象类,修饰方法就是抽象方法

  • 含有抽象方法的类,必须是抽象类;抽象类必须被继承,抽象方法必须得重写
  • 抽象方法只需声明,不需写具体实现
  • 抽象类不能被实例化,即不能被new操作 abstract不能与final并列修饰同一类
  • abstract不能与private,static,final并列修饰同一方法 abstract方法必须位于abstract类中

【示例】
动物类的call()方法是抽象的,它的子类,鸟,狗,猫具体实现抽象方法。

接口概念

Java接口是一些方法声明的集合,一个接口只有方法的声明没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为。

作用

  • Java是一种单继承的语言
  • 实现多态

语法格式

定义接口

[访问控制符] interface <接口名>{
    类型标识符final 符号常量名n = 常数;
    返回值类型  方法名([参数列表]);
    …
}

实现接口

[访问控制符] class 类名 [implements <interface>[,<interface>]*],]
{
    //类体,必须实现接口中的方法
}

特性
我们可以把接口看作是抽象类的一种特殊情况,在接口中只能定义抽象的方法和常量

  • 接口不可实例化,可结合多态进行使用(接口 对象=new 对象())
  • 接口里的成员属性全部是以 public(公开)、static(静态)、final(最终) 修饰符修饰
  • 接口里的成员方法全部是以 public(公开)、abstract(抽象) 修饰符修饰
  • 接口里不能包含普通方法
  • 子类继承接口必须实现接口里的所有成员方法,除非子类也是抽象类

【示例】
定义动物接口,包含一个call()抽象方法。定义具体实现类-鸟,狗,猫类。

抽象类与接口的对比
普通类抽象类接口
代表一类实体,可以被实例化代表一类实体,但不能被实例化不能代表一类实体,只能代表功能或是属性
单继承单继承多实现
已经很具体,没有再抽象的需要在多个普通类中有共用的方法或属性又有相同方法的不同实现方式单纯的规范,单纯的功能或属性需要独立体现出来

举个例子

  • 有四个方法:吃饭,穿衣,跑步,开豪车;
  • 吃饭,穿衣,跑步这三件事情,普通人都做得到,可以说是 “共性”的行为,只是具体行为实现不同;开豪车不是普通人可以做到的,比较 “独特”。
  • 吃饭,穿衣,跑步三个行为适合放在抽象类,开豪车适合放在接口。

Object类讲解

Object类是所有Java类的祖先,每个类都使用Object作为超类。

  • 所有对象(包括数组)都实现这个类的方法。
  • 在不明确给出超类的情况下,Java会自动把Object作为要定义类的超类。
  • 可以使用类型为Object的变量指向任意类型的对象。

这里写图片描述

重点讲解:toString()、equals()、hashCode()三个方法。
:toString() 方法实现了什么功能?
:toString() 方法将根据调用它的对象返回其对象的字符串形式。
:当 toString() 方法没有被覆盖的时候,返回的字符串通常是什么样子的?
:当 toString() 没有被覆盖的时候,返回的字符串格式是 类名@哈希值,哈希值是十六进制的。举例说,假设有一个 Employee 类,toString() 方法返回的结果可能是 Empoyee@1c7b0f4d。

:euqals()函数是用来做什么的?
:用来检查一个对象与调用这个equals()的对象是否相等。
:使用Object类的equals()方法可以用来做什么比较?
:调用它的对象和传入的对象的引用是否相等。也就是说,默认的equals()进行的是引用比较。如果两个引用是相同的,equals()函数返回true;否则,返回false。

:hashCode()方法是用来做什么的?
:hashCode()方法返回给调用者此对象的哈希码(其值由一个hash函数计算得来)。这个方法通常用在基于hash的集合类中,像HashMap,HashSet和Hashtable。
: 在类中覆盖equals()的时候,为什么要同时覆盖hashCode()?
: 在覆盖equals()的时候同时覆盖hashCode()可以保证对象的功能兼容于hash集合。这是一个好习惯,即使这些对象不会被存储在hash集合中。

类与类之间的关系

请访问天亮教育论坛查看!

23种设计模式

工厂模式单例模式代理模式观察者模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值