【万字总结】Java面向对象的学习

文章目录

面向对象

一、初识面向对象

1. 思想

  • 面向过程思想

    • 步骤清晰简单,第一步做什么,第二步做什么…
    • 面对过程适合处理一些较为简单的问题
  • 面对对象思想

    • 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
    • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
  • 对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

2. 什么是面向对象

  • 面向对象编程(Object-Oriented Programming, OOP)

  • 面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据

  • 抽象

  • 三大特性:封装、继承、多态

  • 从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。

  • 从代码运行角度考虑是先有类后有对象。类是对象的模板。

3. 什么是类

  • :是一类具有相同特性的事物的抽象描述,是一组相关属性行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。

  • 属性:就是该事物的状态信息。对应成员变量

  • 行为:就是该事物能够做什么。对应成员方法

4. 类与对象的关系

  • 类是对一类事物的描述,是抽象的
  • 对象是一类事物的实例,是具体的
  • 类是对象的模板,对象是类的实体

二、对象的创建和使用

1. 创建与初始化对象

  • 使用new关键字创建对象
  • 语法:
类名 对象名 = new 类名(); //实例化一个对象
  • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
  • 对象中存储的是对象地址。每个对象都有一个自己独立的区域(用于存储自身的属性值)。

2. 使用对象

2.1 对象属性的访问
  • 赋值:对象名.属性名 = 值;
  • 取值:对象名.属性名;

三、类的五大成员

1. 成员变量

1.1 分类
  • 实例变量:变量属于对象

  • 类变量: 变量属于类

1.2 实例变量的声明
[修饰符] class 类名{
    [修饰符] 数据类型 属性名;       //属性有默认值
    [修饰符] 数据类型 属性名 =;  //属性有初始值 
}
1.3 实例变量的使用
  • 前提:必须要有对象

  • 语法:

直接使用          //在本类的实例方法中
对象名.成员变量名   //在其他类的方法中
1.4 成员变量的特点
  • 成员变量有默认值(具体和数据类型相关)
数据类型默认值
基本类型浮点数(float,double)0.0
字符(char)‘\u0000’
布尔(boolean)false
整数(byte,short,int,long)0
引用类型数组,类,接口null
  • 实例变量的值是每个对象独立的
1.5 成员变量和局部变量的区别

1)位置不同:

成员变量:直接在类体中

局部变量:某个局部(方法内,if内,循环内…)

2)作用范围不同:

成员变量:本类以及其他类都能使用(要符合访问修饰符)

局部变量:只能在当前大括号内使用

3)初始化值不同:

成员变量:有默认值

局部变量:没有初始化不允许使用

4)内存中位置不同

成员变量:在堆内存

局部变量:在栈内存

5)生命周期不同:

成员变量:对象被回收时,销毁

局部变量:当前大括号运行完毕,销毁

1.6 成员变量类型
  • 可以是任意类型(基本数据类型、引用数据类型)

2. 成员方法

  • 类:一组具有相同特征(属性),相同行为(方法)的对象的集合,抽象出来的概念
  • 属性:通过变量去存储数据
  • 方法:一段可以运行的代码(实现某些需求)
2.1 方法的概念

方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。

把一个功能封装为方法,可以实现代码的重用,从而减少代码量。

2.2 方法的原则

方法的使用原则:

(1)必须先声明后使用。

类,变量,方法等都要先声明后使用

(2)不调用不执行,调用一次执行一次。调用一次,在栈中压入一个方法栈。

2.3 成员方法的分类

1)实例方法:属于对象的,必须有对象才可以

2)类方法:属于类

2.4 方法的语法
[修饰符] 返回值类型 方法名([形参列表]){
    方法体
}

1)修饰符:可有可无

2)返回值类型

3)方法名:自定义名字(符合变量名的命名规则和规范)

4)形参列表

  • 形参:在定义方法时方法名后面括号中的变量名称称为形式参数(简称形参),即形参出现在方法定义中。
  • 实参:调用者方法中调用另一个方法时,方法名后面括号中的参数称为实际参数(简称实参),即实参出现在调用者方法中。

5)方法体

方法的声明位置是类体中,方法中不能嵌套方法

2.5 方法的形式

1)无参无返回值

[public] void 方法名(){方法体}

无参类型中 return; //默认存在,写不写都有,在方法的末尾

2)有参无返回值

[public] void 方法名(参数列表){方法体}

参数列表可以是任意类型(基本数据类型,引用数据类型)

参数列表中必须给相应的值(数据类型,个数,顺序)

3)无参有返回值

[public] 返回值类型 方法名(){方法体}

返回值类型要指定且只能指定一个(可以是任意类型)

如果是多个相同类型的数据,可以使用数组

如果是多个不同类型的数据,可以使用集合

有返回值的方法,必须要执行到 return 数据; 这个代码

接收返回值:数据类型 变量名 = 对象.方法();

4)有参有返回值

[public] 返回值类型 方法名(参数列表){方法体}

返回值类型和参数类型没有任何关系

2.6 实例方法的调用

1)在本类内:

直接调用 语法:[this.]本类的方法();

this是当前对象,是当前方法的调用者,可以省略。

2)在其他类中:

必须通过对象去调用

对象名.方法名(参数);

参数可以是任意类型,如果想传一个未被定义声明的数组,(new int[]{1,2,3})

  • 总结:

(1)调用时,需要通过方法名来识别调用哪个方法

(2)调用时,需要传“实参”,实参的个数、类型、顺序顺序要与形参列表一一对应

​ 如果方法没有形参,就不需要也不能传实参。

(3)调用时,如果方法有返回值,可以接受或处理返回值结果。

​ 如果方法的返回值类型是void,不需要也不能接收和处理返回值结果。

2.7 可变参数

​ 在JDK1.5之后,如果我们定义一个方法时,此时某个形参的类型可以确定,但是形参的个数不确定,那么我们可以使用可变参数。

  • 语法:
修饰符 返回值类型 方法名([非可变参数部分的形参列表,] 参数类型...形参名){ 方法体 }
  • 注意:

(1)可变参数就当成数组使用即可(本质就是数组)

(2)可变参数最多只能有一个,且必须是参数列表的最后一个

(3)可变参数的实参都必须符合数据类型

2.8 方法重载
  • 方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。

  • 方法的要求:在同一个类中,不允许出现两个完全相同的方法

  • 重载的要求:

    (1)同一个类中,方法名可以相同

    (2)参数列表必须不同(个数、顺序、类型,任何一个不同即可)

    (3)和返回值、访问修饰符无关

  • 重载方法调用的时候,先找完全符合的,找不到完全符合的找兼容的,如果有多个兼容的则报错。

例如:实参为int类型,但是形参中没有int类型但有double类型的,则找double类型的。

  • 注意:同类型的数组和可变参数是相同的,无法重载。
2.9 参数的传递机制
  • 形参:在方法定义位置,指定的形式参数

  • 实参:在调用者方法中,指定的实际参数

  • 传递机制:实参给形参赋值

    • 基本数据类型,栈内存中存储的是值,在方法传参的时候,就是值传递。

    • 引用数据类型,栈内存存储的是地址,在方法传参的时候,就是地址传递(引用),多个变量指向同一个空间(堆内存、常量池)。形参地址的改变不会影响实参,但形参地址值里面的数据改变会影响实参。

      • 注意:String、Integer等特殊类型除外

在方法中,如果 形参 = 新new对象,则和实参无关,因为形参指向了新对象,将一个新的地址值赋给了形参,与实参无关系了。

String对象不可变,一旦修改就会产生新对象。

2.10 递归
  • 递归:指在当前方法内调用自己的这种现象。

  • 递归分类:

    ​ 1)直接递归:自己调用自己

    ​ 2)间接递归:例如 A–>B–>C–>A

  • 注意:

    • 递归一定要有条件限定,保证递归能停止下来,否则就会栈内存溢出。

    • java.lang.StackOverflowError :栈内存溢出错误

  • 出现宕机:①优化代码;②增加机器

2.11 对象数组

对象数组的用法和普通数组一样

对象数组的默认值是null

对象数组中的元素存的是各个对象的地址

对象类型[] 变量名 = new 对象类型[对象数组的大小];

3.构造器(Constructor)

3.1 概念
  • 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。
3.2 作用
  • 要创建一个类的实例对象,必须调用一个对象的构造器,来完成类的实例初始化过程。实例初始化过程就是为实例变量赋初始值的过程。

  • 当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。

3.3 构造器语法
[修饰符] 构造器名([参数列表]) { 代码块 }
3.4 构造器特点
  • 没有返回值(void也没有)
  • 方法名必须和类名一致
3.5 构造器调用时机
  • 在实例化对象时调用
  • 实例化一次调用一次
3.6 注意
  • 任何一个类都默认存在一个无参构造器
  • 构造器可以重载
  • 一旦定义了有参构造,无参就必须显示定义
3.7 this关键字
  • 含义:当前对象(不是当前类)

  • this.内容(属性、方法)

    • 区分局部变量和成员变量重名的问题
    • 适用于所有方法(包括构造器)
    • 在Java的封装类中,允许局部变量和成员变量重名
      • 因为成员变量和局部变量不在同一内存(成员变量在堆,局部变量在栈)
      • 如果成员变量和局部变量重名,默认识别的是局部变量,成员变量用this.属性名
  • this()

    • 应用在构造器
    • 能够调用本类其他构造器
    • 注意:this()必须在构造器的首行
3.8 标准JavaBean

  JavaBean 是 Java语言编写类的一种标准规范。符合JavaBean 的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,成员变量私有化,并提供用来操作成员变量的setget 方法。

3.9 包
  • 语法:全小写,级别之间通过.进行分割
  • 作用:
    • 避免类的重名
    • 分类组织管理众多的类,例如:
      • java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread等,提供常用功能(不需要导包)
      • java.net----包含执行与网络相关的操作的类和接口。
      • java.io ----包含能提供多种输入/输出功能的类。
      • java.util----包含一些实用工具类,如集合框架类、日期时间、数组工具类Arrays,文本扫描仪Scanner,随机值产生工具Random。
      • java.text----包含了一些java格式化相关的类
      • java.sql和javax.sql----包含了java进行JDBC数据库编程的相关类/接口
      • java.awt和java.swing----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
    • 可以控制某些类型或成员的可见范围
  • 使用方法:使用其他包下的内容时,需要导包(idea中自动导包)
3.10 static – 静态的
  • stati用途:
    • 用于属性,为了资源共享
    • 用于方法,为了类名调用方便
    • 用于初始化块,为了初始化类时加载一段代码
    • 用于内部类,为了使内部类实例化时不依赖于外部类对象

1)成员变量

  • 成员变量

    • 实例变量:属于对象的
    • 类变量:属于类的 在成员变量前添加修饰符static
  • 实例变量的使用

    • 对象.属性(符合权限访问修饰符范围)
    • 类名.属性(符合权限访问修饰符范围)(推荐)
  • 类加载顺序:

    1)先加载所有的静态资源

    2)再加载所有的普通资源

  • 特点:

    • 静态的属性,空间开辟在方法区内,并且只有一份,因为类只加载一次
    • 所有对象操作该属性时,操作的都是方法区的这一个空间,达到共享的目的

2)成员方法

  • 成员方法
    • 实例方法:属于对象
    • 类方法:属于类的 在成员方法前添加修饰符static
    • 使用方法
      • 对象.方法
      • 类名.方法(推荐)(不依赖于对象,工具类方法就设置为static)
  • 加载顺序:先加载静态方法,再加载普通方法
  • 注意:
    • 静态资源内只能直接使用静态资源,不能直接使用非静态资源
      • 因为static没有this关键字,静态的先加载
    • 非静态资源内都可以直接使用

3)初始化块

  • 用于修饰初始化块时,该初始化块在类加载的时候自动执行

  • 特点:在类加载的时候只执行一次

4)内部类

  • 可以使成员内部类的对象实例化时,不依赖于外部类的对象
3.11 静态导入

例如:import static java.lang.Math.*

3.12 一些工具类介绍

1)数组工具类java.util.Arrays(每一个方法都有各种重载形式,以下只列出int[]类型的,其他类型的数组类推)

  • static int binarySearch(int[] a, int key) :要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数

  • static int[] copyOf(int[] original, int newLength) :根据original原数组复制一个长度为newLength的新数组,并返回新数组**(常用)**

  • static int[] copyOfRange(int[] original, int from, int to) :复制original原数组的[from,to)构成新数组,并返回新数组

  • static boolean equals(int[] a, int[] a2) :比较两个数组的长度、元素是否完全相同

  • static void fill(int[] a, int val) :用val填充整个a数组

  • static void fill(int[] a, int fromIndex, int toIndex, int val):将a数组[fromIndex,toIndex)部分填充为val

  • static void sort(int[] a) :将a数组按照从小到大进行排序**(常用)**

  • static void sort(int[] a, int fromIndex, int toIndex) :将a数组的[fromIndex, toIndex)部分按照升序排列

  • static String toString(int[] a) :把a数组的元素,拼接为一个字符串,形式为:[元素1,元素2,元素3。。。](常用)

2)系统类System

  • static long currentTimeMillis() :返回当前系统时间距离1970-1-1 0:0:0的毫秒值(注意返回值是long)

  • static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

    从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。常用于数组的插入和删除

  • static void exit(int status) :退出当前系统(一般退出status为0)

  • tatic void gc() :运行垃圾回收器。

统一用法:

System.类名(参数列表)

3)数学类Math

java.lang.Math类包含用于执行基本数学运算的方法。其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。

  • public static double abs(double a) :返回 double 值的绝对值。

  • public static double ceil(double a) :返回大于等于参数的最小的整数。

  • public static double floor(double a) :返回小于等于参数最大的整数。

  • public static long round(double a) :返回最接近参数的 long。(相当于四舍五入方法)

  • public static double pow(double a,double b):返回a的b幂次方法

  • public static double sqrt(double a):返回a的平方根

  • public static double random():返回[0,1)的随机值

  • public static final double PI:返回圆周率

  • public static double max(double x, double y):返回x,y中的最大值

  • public static double min(double x, double y):返回x,y中的最小值

统一用法:

Math.类名(参数列表)

4.初始化块

4.1 分类

1)实例化初始化块

  • 每次实例化对象自动执行的一个代码块

  • 语法:

{}

  • 功能:对类中多个构造器中相同内容的复用性提高
  • 特点:在构造器之前执行
    • 如果有继承:实例化子类的对象,会先执行父类构造器,再执行子类构造器
    • 如果存在初始化块:父类初始化块->父类构造器->子类初始化块->子类构造器

2)类初始化块

  • 在类加载(只加载一次)的时候自动执行的一个代码块(只执行一次)
  • 语法
static{}
  • 特点:在类加载的时候只执行一次

  • 用途:该类一旦被使用,就执行一段代码

5.内部类

5.1 分类
  • 成员内部类

  • 静态成员内部类

  • 局部(方法)内部类

  • 匿名内部类

5.2 匿名内部类
  • 语法:
new 类、抽象类、接口(){
    重写方法...
}
  • 解析:大括号相当于是类的子类、接口的实现类的大括号

  • 优缺点:

    • 优点:简单一些(省略了创建类)
    • 缺点:复用性很差
  • JDK1.8 Lambda表达式对部分内部类简化

5.3 成员内部类
  • 位置:类体中(和其他成员在相同位置)
  • class文件:外部类$内部类.class
  • 语法:
[修饰符] class 类名{}
  • 成员内部类的对象实例化,依赖于外部类的对象

  • 成员:五大成员都可以,但不可以有静态资源

  • 修饰符:四个访问修饰符都可以,abstract和final都可以单独修饰

  • 使用:

    • 内部类使用外部类的资源:直接访问
    • 外部类使用内部类的资源:需要实例化内部类对象
    • 外部其他类(测试类)使用内部类:需要实例化内部类的对象
      • 首先需要外部类对象Outher outher = nwe Outher();
      • 再实例化内部类对象Outher.Inner inner = outher.new Inner();
      • 或者一步到位Outher.Inner inner = new Outher().new Inner();
  • 冲突问题解决:

    • 外部类和内部类的属性或者方法重名
      • this.内容:this是当前对象
      • Outher.this.内容:外部类.this是外部类的对象
5.4 静态成员内部类
  • 位置:类体中(和其他成员在相同位置)
  • class文件:外部类$内部类.class
  • 语法:
[修饰符] static class 类名{}
  • 成员内部类的对象实例化,不依赖于外部类的对象

  • 成员:五大成员都可以,可以有静态资源

  • 修饰符:四个访问修饰符都可以,abstract和final都可以单独修饰

  • 使用:

    • 内部类使用外部类的资源:静态资源可以直接使用,非静态的实例化对象使用
    • 外部类使用内部类的资源:静态资源可以使用类名调用,非静态的实例化对象使用
    • 外部其他类(测试类)使用内部类:
      • 不依赖于外部类的对象,直接实例化对象即可
      • Outher.Inner2 inner2 = new Outher.Inner2();
    • 冲突问题
      • 外部类的静态资源和内部类的普通资源重复时,静态资源可以通过类名调用
5.5 方法(局部)内部类
  • 位置:在某个局部(方法内)
  • class文件:外部类$1内部类.class
  • 语法:
[修饰符] class 类名{}  //修饰符不能使用访问修饰符
  • 修饰符:abstract、final可以单独修饰,访问修饰符不可以使用

  • 成员:五大成员都可以,但不包括静态资源

  • 使用:

    • 内部类使用外部类的资源:直接使用
    • 外部类使用内部类的资源:当前局部可以通过实例化对象的形式,其他地方无法访问
    • 外部其他类(测试类)使用内部类:访问不到
  • 冲突问题:

    • this:当前对象
    • Outher.this:外部类对象
  • 如果当前局部存在局部变量,并且该变量在内部类中使用的话,默认会变为常量

四、面向对象三大特性

  • 三大特性:封装、继承、多态
    • 封装:安全、方便
    • 继承:代码的复用性和拓展
    • 多态:功能更灵活,可以使用父类类型的变量可以统一管理它的子类对象

1. 封装

1.1 概述

​ 封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

1.2 对属性的封装
  • 封装原则

    1)对需要封装的属性设置访问权限(私有的)

    2)对外界提供两个方法(设置、获取)

  • 属性封装的目的

    • 隐藏类的实现细节
    • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。
    • 可以进行数据检查,从而有利于保证对象信息的完整性。
    • 便于修改,提高代码的可维护性。
  • 实现步骤

    1)使用private修饰成员变量

    private 数据类型 变量名;
    

    代码如下:

    public class Student {
      private String name;
    }
    

    2)提供 getXxx方法 / setXxx 方法,可以访问私有化的成员变量,代码如下:

    public class Student {
      private String name;
      public void setName(String n) {
        name = n;
      }
      public String getName() {
        return name;
      }
    }
    
1.3 权限(访问)修饰符
  • 设置元素(类、成员变量、成员方法)的访问范围。

在这里插入图片描述

类的权限修饰符:只能用public和缺省的

成员变量和成员方法:四个修饰符都可以用

2.继承

2.1 继承的理解

继承就是子类继承父类的属性行为,使得子类对象具有与父类相同的属性、相同的行为。

2.2 继承的好处
  • 提高代码复用性
  • 提高代码拓展性
2.3 继承的格式

通过 extends 关键字:

[修饰符] class 子类 extends 父类 { ... }
2.4 继承中的成员变量

1)父类中所有成员变量都会被子类继承下去

  • 如果父类中的成员变量是私有的,那么子类是不可以直接访问的。

2)成员变量不重名

  • 访问成员变量没有影响

3)成员变量重名

  • this.成员变量默认为就近原则(当前对象的引用)
  • super.成员变量访问父类的成员变量(父类空间的引用)
2.5 继承中的成员方法

1)成员方法不重名

  • 调用成员方法没有影响

2)成员方法重名——重写

  • 如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。

  • 目的:子类根据需求对父类的方法重新编写。

  • 重写后依旧想用父类的方法:

    • 本类:通过super.方法名调用父类的方法
    • 其他类:没有直接的办法,要通过本类的super

3)重写的条件

  • 必须是父子类
  • 方法名必须相同
  • 形参列表必须相同(个数、类型、顺序)
  • 和返回值有关
    • 基本数据类型和void:返回值类型必须一致
    • 引用数据类型:子类的要小于等于父类的
  • 和权限修饰符有关
    • 子类的要大于等于父类的权限

4)不允许被重写

  • 静态方法(静态方法属于类)
  • 私有等在子类中不可见的方法不能被重写
  • final方法不能被重写

5)继承的应用(难度很大)

  • this调用属性

    • 类型(看在哪个类)
  • this调用方法

    • 实际对象(看new的是谁)
2.6 继承中的构造器
  • 由于构造器的名称必须和类名一致,所以子类是无法继承父类构造器的

  • 构造器的作用是初始化成员变量的

  • 类的加载顺序

    • 先加载父类再加载子类
    • 实例化子类对象的时候,父类空间优于子类对象的产生(先执行父类构造器,再执行子类构造器
    • 具体:
      • 1.父类静态属性、静态代码块
      • 2.子类静态属性、静态代码块
      • 3.父类非静态属性、代码块
      • 4.父类构造方法
      • 5.子类非静态属性、代码块
      • 6.子类构造方法
  • 每一个构造器的首行都默认存在一行super() 默认调用父类无参

  • 要求:

    • super()如果显示的创建,也必须在首行
    • 父类必须存在无参构造器,否则默认情况会报错
  • 当父类的构造器调用子类的方法时,属于对父类空间进行初始化,子类对象未被加载,子类中的属性不会被初始化(子类中的属性此时都为默认值)

2.7 继承中的单继承

  一个类中只能又一个直接父类,但是一个类可以有多个子类

  • 继承具有局限性
  • 任何类都是有父类的,如果没有extend,则默认父类为Object
2.8 super

  父类空间优先于子类对象产生

  • super:指的是子类对象中的父类空间
  • this:指的是子类对象
  • 用法:
    • super.内容:调用父类的属性或方法
    • this.内容:调用自己的属性或方法
    • 共同的:找不到都会往上一级一级的找
    • super([实参列表])调用父类的构造器(对父类空间进行初始化)(只能从直接父类找)
    • this()调用本类的构造器
  • 特点:都在构造器的第一行,不能同时出现

注意:super和this都不能出现在静态方法和静态代码块中,因为super和this都是存在与对象中的

2.9 final

final:最终的,不可更改的

  • 修饰类

    • 该类不可以被继承
    • String类就是final修饰的
  • 修饰变量

    • 该变量变为常量
    • final修饰成员变量必须要赋值(初始化)
    • final修饰局部变量,可以不初始化,但无法使用
      • 常量名:建议使用全大写,多个单词使用_隔开
  • 修饰方法

    • 该方法无法重写

3.多态

3.1 定义

同一行为,具有多个不同的表现形式

3.2 前提

1)父子类

2)必须有重写

3)父类的引用指向子类对象

3.3 多态的语法

父类的引用指向子类对象

  • 语法
父类类型 变量名 = new 子类对象;
变量名.方法名();

例:

Person per = new Emplpyee();
per.eat();//名义上调用的是Person类中的eat方法,实际上是Employee的
3.4 多态的注意事项
  • 使用不了子类独有的内容
    • 因为名义是父类类型的,就只能调用父类中存在的,如果子类有重写调用子类的方法,没有重写就是父类的方法
    • 总结:多态只能调用父类中的内容
  • 成员变量
    • 子类独有的访问不到,如果有重名属性,调用属性看类型
3.5 多态的用途

1)多态参数

实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。

2)多态数组(对象数组的升级)

3.6 父子类之间的类型转换

1)向上转型(自动转换,小–>大)

多态本身就是子类类型向父类类型向上转换的过程,这个过程是默认的。

父类类型 变量名 = new 子类类型();

2)向下转型(强制转换,大–>小)

父类类型向子类类型向下转换的过程,这个过程是强制的。

因为多态调用不到子类独有的内容,所以需要向下转型。

  • 语法
子类类型 变量名 = (子类类型)父类变量名;
  • 向下转型时可能会出现类型转换异常(ClassCastException),例如:
Animal a = new Cat();
Dog d = (Dog)a;
  • 所以强转之前要做判断,使用instanceof关键字
Employee emp = new Employee;
boolean flag = employee instanceof Employee;  //instanceof判断前面的实际对象是否属于后面的类型,falg == ture
boolean flag = employee instanceof Person;  //flag == ture
  • 注意
Person per = new Employee;
boolean flag = person instanceof Employee;  //flag == ture
3.7 native关键字

native–本地的原生的

追踪源码时会看见,代码是由c语言实现

native只能修饰方法,可以被重写

3.8 修饰符的使用问题

1)不能和abstract一起使用的修饰符?

  • final:不能一起修饰方法和类
  • static:不能一起修饰方法
  • native:不能一起修饰方法
  • private:不能一起修饰方法

2)static和final一起使用:

  • 修饰方法:可以,因为都不能被重写
  • 修饰成员变量:可以,表示静态常量
  • 修饰局部变量:不可以,static不能修饰局部变量
  • 修饰代码块:不可以,final不能修改代码块
  • 修饰内部类:可以一起修饰成员内部类,不能一起修饰局部内部类(后面讲)
3.9 Object–根父类

默认是所有类的父类,该类中所有方法都可以被任意对象继承使用。

1)构造器Object();

2)方法

  • toString

    • 默认情况下,toString()返回的是“对象的运行时类型 @ 对象的hashCode值的十六进制形式"

    • 直接用System.out.println(对象),默认会自动调用这个对象的toString()

    • 源码:

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • getClass:由C语言编写

  • 功能:返回对象的运行时类型

    • 源码:
    public static void main(String[] args) {
    		Object obj = new String();
    		System.out.println(obj.getClass());//运行时类型
    	}
    
  • int hashCode()

    • 功能:将当前对象,通过哈希算法,得到一个int值
    • 两对相同的对象(地址相同):经过相同的hash算法,得到的int值相同
    • 两个不同的对象,经过相同的hash算法,得到的int值可能相同,大概率不相同
  • finalize()

    • java中存在的垃圾回收机制(自动的机制)
    • 没有引用的对象会被认为是垃圾,将不定时回收
    • 垃圾对象被回收时默认调用该对象的finalize方法(此方法并不是回收的代码)
    • System.gc();:通知垃圾回收机制来回收垃圾(不会立刻来回收垃圾对象)
  • equals==区别:

    • ==:判断两个对象的地址是否一致
    • equals:判断两个对象值是否一致
    • 注:
    String str1 = new String;
    String str2 = new String;
    Systom.out.println("str1.equals(str2)");  //false
    
    • (Object obj)中的equals源码:
    public boolean equals(Object obj){
        return (this == obj);
    }
    

    和 == 没有区别

    • String类中的equals方法源码:
    public boolean equals(Object anObject) {
        	//先判断地址是否一致
            if (this == anObject) {
                return true;
            }
        	//判断anObject是否是String类型
            if (anObject instanceof String) {
                String anotherString = (String)anObject; //向下转型
                int n = value.length;
                //判断长度是否一致
                if (n == anotherString.value.length) {
                    //v1是this的字符串内容
                    char v1[] = value;
                    //v2是参数字符串的内容
                    char v2[] = anotherString.value;
                    int i = 0;
                    while (n-- != 0) {
                        if (v1[i] != v2[i])
                            return false;
                        i++;
                    }
                    return true;
                }
            }
            return false;
        }
    

    String中的equals对比的是内容

五、抽象类和接口

1.抽象类

包含抽象方法的类必须是抽象类

1.1 定义
  • 抽象方法 : 没有方法体的方法。
  • 抽象类:被abstract所修饰的抽的类。
  • 语法:

    • 抽象类语法
    【权限修饰符】 abstract class 类名{
    
    }
    【权限修饰符】 abstract class 类名 extends 父类{
        
    }
    
    • 抽象方法语法:
    【其他修饰符】 abstract 返回值类型  方法名(【形参列表】);
    

    注意:抽象方法没有方法体

1.2 抽象的使用(特点)

1)抽象方法存在的类,必须是抽象类

2)子类在继承抽象类时,必须实现父类所有的抽象方法

3)抽象类中可以有0-n个抽象方法或普通方法

4)如果子类不能实现抽象类中的抽象方法,那么子类也必须变为抽象类

5)抽象类不能实例化对象

6)抽象类中有构造器,初始化子类对象的父类空间时加载此构造器

1.3 抽象类应用

一般作为模板工具类

2.接口

2.1 概述
  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。has-a
    • 例如:你想要会飞,那么必须拥有fly()方法
    • 例如:你想要连接USB,那么必须拥有read()/in()和write()/out()等
    • 例如:你的数据库产品想要被Java连接,那么你需要实现Connection, Statement等
  • 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
2.2 接口语法
[修饰符] interface 接口名{ //修饰符只能是public或者没有修饰符
    //接口的成员列表:
    // 静态常量
    // 抽象方法
    // 默认方法
    // 静态方法
    // 私有方法
}
  • JDK8之前:

    • 接口中所有的成员变量,默认都是共有的静态常量(不允许修改)
    • 接口的所有的成员方法,默认都是所有的抽象方法
    • 接口不能实例化对象,因为没有构造器
    • 接口中没有初始化块
    • 接口只能作为父级使用
    • 类可以实现多个接口,但是类需要实现接口中所有的抽象方法
    • 接口可以继承多个接口
  • JDK8新增:(都可以有方法体)

    • 默认方法(default)(可以认为是接口中的普通方法)

      static void staticMethod(){}
      

      使用方式:实现类的对象.defaultMethod();,默认方法可以被继承类重写

    • 静态方法

      default void defaultMethod(){}
      

      使用方式:接口名.staticMethod();

2.3 特点
  • 接口不能实例化对象,但是接口支持多态
  • 只能作为父类(父接口)
    • 实现类实现父接口
  • 实现类实现父接口,并实现父接口中所有的抽象方法
[public] class 实现类 implements B {
    // 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
  	// 重写接口中默认方法【可选】
}
[public] class 实现类 extends 父类 implements 接口{ //父类必须在接口之前
    // 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
  	// 重写接口中默认方法【可选】
}
  • 类只能有一个父类,可以有多个父接口
2.4 关系
  • 类与类之间:单继承
  • 类与接口之间:多实现
  • 接口和接口之间:多继承
2.5 冲突
  • 实现类是可以实现接口,同时也可以继承父类

    • 当父接口和父类中有相同的方法,默认调用父类的
  • 实现类是可以实现多个接口的

    • 若两个及以上父接口有相同的默认方法,则实现类必须重写该方法

      ①自己真正的重写

      ②在重写中选择父接口中的一个、几个或都选,语法:父接口.super.方法

3. 经典接口

3.1 java.lang.Comparable(自然排序)

对比两个对象的大小

  • 实现抽象方法——int compareTo(Object obj)

  • 功能:this和obj对比大小

  • 返回值:

    • 正数——this大
    • 负数——this小
    • 0——相同
  • sort排序会自动默认调用compareTo方法

  • 接口的源码

package java.lang;

public interface Comparable{
    int compareTo(Object obj);
}
  • 具体步骤:
    • 第一步:哪个类的对象要比较大小,哪个类就实现java.lang.Comparable接口,并重写方法
      • 方法体就是你要如何比较当前对象和指定的另一个对象的大小
    • 第二步:对象比较大小时,通过对象调用compareTo方法,根据方法的返回值决定谁大谁小。
  • 举例:
public class Student implements Comparable{
    ......
    public int compareTo(Object o) {
        if (this==o){
            return 0;
        }
        if (o instanceof Student){
            Student s = (Student) o;
			return this.getId() - o.getId();
        }
        return 0;
    }
}
3.2 java.util.Comparator(定制排序)

前提:你没有权力修改某个类的代码,无法进行自然排序,如第三方的产品(只提供class文件)

  • 实现方法——int compare(Object o1,Object o2)
  • 当定制排序和自然排序都存在时,以定制排序为主
  • 举例:
class MyComparator implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        if (o1 == o2){
            return 0;
        }
        if (o1 instanceof Employee && o2 instanceof Employee){
            Employee emp1 = (Employee) o1;
            Employee emp2 = (Employee) o2;
            return emp1.getId() - emp2.getId();
        }
        return 0;
    }
}

如果排序时比较的是double类型的,return时可以使用 Double.compare(emp1.getSalary(),stu2.getSalary()) 从低到高排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值