Bootstrap

Java——面向对象

目录

一、面向对象

1.1什么是面向对象 

1.2面向对象与面向过程

二、类和对象

2.1类的定义和使用

2.2类的实例化

2.3this引用

2.4对象的构造

2.5static成员

2.6代码块

三、面向对象的三大特性

3.1封装

3.2继承

3.3多态

四、内部类

五,抽象类

5.1抽象类概念

5.2抽象类语法

 5.3抽象类的特性

5.4抽象类的作用


一、面向对象

1.1什么是面向对象 

        Java是一门纯面向对象的语言,在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。

        面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。Java中有很多对象来自标准库,还有一部分由自己定义。从根本上说只要对象能够满足要求,就不必关心其功能的具体实现过程。在OOP中不必关心对象的具体实现,只要其能够满足用户的需求即可。

        以面向对象的方式进行处理,就不需要关注工作执行的过程,通过对象之间的交互便可以完成一件事情。

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

1.2面向对象与面向过程

        面向过程和面歌对象都是对软件分析、设计、和开发的一种思想,它指导着人们以不同的方式去分析、设计和开发软件。

        面向过程适合简单、不需要协作的事务,重点关注执行的过程,面向过程时,我们需要思考怎么按步骤实现,并将步骤对应成方法,一步一步完成这个任务。例如当我们把大象装进冰箱,我们首先需要打开冰箱,然后把大象装进冰箱,最后关上冰箱,这样便可以将大象装进冰箱了。

        但是当我们思考比较复杂的设计任务时,就会发现面向过程的思想比较复杂,此时面向对象的思想就应运而生了。面向对象(OOP)更契合人的思维模式。我们用面向对象的思维去造车,天然就会从车由什么组成,这时我们会发现车由轮胎,发动机,座椅,玻璃等对象组成,这样我们就需将这些零件组装起来就行,整个过程便是由这些对象之间的交互完成的,而不需要去关注那些对象之中的过程。因此面向对象是从整体上去分析整个系统。

        

        面向对象和面向过程并不是一门语言,而是解决问题的一种思维。其中面向过程是一种执行者思维,而面向对象是一种设计者思维,面向对象离不开面向过程。

        面向过程适合解决简单的问题;而面向对象适合解决困难的需要协作的问题


二、类和对象

2.1类的定义和使用

        类( class) 是构造对象的模板或蓝图。我们可以用它对一个实体进行描述,主要描述该实体具有的属性及功能。其中属性被称为成员属性或成员变量,功能被称为成员方法,这个实体就是一个对象。

例如:

        对象:手机

        属性:品牌、颜色、内存、尺寸……

        功能:看视频、听音乐、打游戏……

class MobilePhone{
//成员属性
    String brand;//品牌
    String colour;//颜色
    double internalMemory;//内存
    double size;//尺寸
//成员方法
    public void watchVideo(){
        //看视频
    }

    public void listenMusic(){
        //听音乐
    }

    public void playGame(){
        //打游戏
    }
}

        class为定义类的关键字,MobilePhone为类的名字应采用大驼峰定义。

        成员属性/字段:定义在类的里边方法的外边

        成员方法/行为

注意事项

        类名注意采用大驼峰定义

        成员前写法统一为public,

        此处写的方法不带statiic

        一般一个文件当中只定义一个类

        main方法所在的类一般使用public修饰 注意Eclipse默认会在public修饰的类中找main方法

        public修饰的类必须与文件名相同

2.2类的实例化

2.2.1、什么是实例化

        定义一个类就相当于在计算机中定义了一种新的类型,与int,double类似,只不过int和double是由Java语言自己带的内置类型,而类是由用户自己自定义的一个新的类型,有了这些自定义的类型之后,就可以使用这些类来定义实例(对象),由类构造(construct) 对象的过程称为创建类的实例(instance )。

        在Java中可以通过new关键字可以创建一个对象的实例,使用.来访问对象中的属性和方法。通过一个类可以实例化多个对象。

class MobilePhone{
    String brand;//品牌
    String colour;//颜色
    public void printPhone(){
        System.out.println("这是一台"+colour+ "的"+brand+"手机");
    }
}

public class Test {
    public static void main(String[] args) {
        MobilePhone mobilePhone1 = new MobilePhone();//实例化对象1
        mobilePhone1.brand = "华为";
        mobilePhone1.colour = "白色";
        mobilePhone1.printPhone();
        MobilePhone mobilePhone2 = new MobilePhone();//实例化对象2
        mobilePhone2.brand = "苹果";
        mobilePhone2.colour = "黑色";
        mobilePhone2.printPhone();
    }
}

2.2.2、类和对象的说明

        类只是一个模型一样的东西,用来对一个实体进行描述限定了类有那些成员。 类只是一种自定义的类型,可以用来定义变量。

        一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类的成员变量。

        类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。


2.3this引用

2.3.1、概念

        在Java中this关键字用于表示当前对象的引用。它可以用于访问和操作当前对象的属性和方法在方法内部,可以使用this关键字来显式地引用当前对象。例如:

class Person {
    private String name;
    
    public Person(String name) {
        this.name = name; // 使用this关键字访问当前对象的name属性
    }
    
    public void sayHello() {
        System.out.println("Hello, my name is " + this.name); // 使用this关键字访问当前对象的name属性
    }
}
 

        在上面的代码中,构造函数和sayHello方法都使用了this关键字来访问当前对象的属性name。这样做可以明确地指明要操作哪个对象的属性或方法。

2.3.2、用法

  • 引用当前对象的实例变量
class MobilePhone{
    String brand;//品牌
    String colour;//颜色

    public MobilePhone(String brand, String colour) {
        this.brand = brand;
        this.colour = colour;
    }
}

在上面的代码中,this.brand和this.colour分别引用了当前对象的brand和colour实例变量

  • 调用当前对象的构造函数
class MobilePhone{
    String brand;//品牌
    String colour;//颜色

    public MobilePhone() {
        this("华为","黑色")
    }

    public MobilePhone(String brand, String colour) {
        this.brand = brand;
        this.colour = colour;
    }
}

上述代码中,this("华为","黑色")调用了当前代码中的另一个构造函数;

  • 引用当前对象的方法
class MobilePhone{
    String brand;//品牌
    String colour;//颜色

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColour() {
        return colour;
    }

    public void setColour(String colour) {
        this.colour = colour;
    }
}

        在上面的代码中,this.brand和this.colour分别引用了当前对象的brand和colour实例变量,并通过return 返回了brand和colour的实例变量。

2.3.3、使用场景

  • 解决局部变量和实例变量的命名冲突:如果在一个方法中有一个局部变量和实例变量使用相同的名称,那么使用this关键字可以明确地指出实例变量。

class MobilePhone{
    String brand;//品牌
    String colour;//颜色

    public MobilePhone(String brand, String colour) {
        this.brand = brand;
        this.colour = colour;
    }
}
  • 在构造方法中调用另一个构造方法:如果一个类有多个构造方法,其中一个构造方法可以调用另一个构造方法,可以使用this关键字。例

class MobilePhone{
    String brand;//品牌
    String colour;//颜色

    public MobilePhone() {
        this("华为","黑色")
    }

    public MobilePhone(String brand, String colour) {
        this.brand = brand;
        this.colour = colour;
    }
}
  • 解决多个对象调用相同方法:一个类实例化了多个对象,这些对象都在调用同一个方法,this作为这个方法的第一个隐藏参数,使其知道调用的是哪个对象的方法
class Date {
    public int year;
    public int month;
    public int day;
    public void setDay(int year, int month, int day){
        this.year = year;
        this.month = month;
        this.day = day;
    }
}
public class Test {
    public static void main(String[] args) {
        Date d1 = new Date();
        Date d2 = new Date();
        d1.setDay(2023, 9, 26);
        d2.setDay(2023,9,27);
    }
}

2.4对象的构造

构造方法(构造器)

2.4.1概念

        构造方法(构造器)是一个特殊的成员方法,名字必须要与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。构造方法的作用是对对象的成员进行初始化,并不负责给对象开辟空间。

class Date {
    public int year;
    public int month;
    public int day;

    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
    public void printDate(){
        System.out.println(this.year+"-"+this.month+"-"+this.day);
    }
}
public class Test {
    public static void main(String[] args) {
        Date d1 = new Date(2023,9,27);
        d1.printDate();
    }
}

2.4.2特性

1、构造方法的名字要与类名相同;

2、没有返回值;

3、创建对象时编译器自动调用,并且在对象的生命周期内只调用一次;

4、构造方法可以重载;

class Date {
    public int year;
    public int month;
    public int day;

    public Date(int year, int month) {
        System.out.println("带两个参数的构造方法");
        this.year = year;
        this.month = month;
    }

    public Date(int year, int month, int day) {
        System.out.println("带三个参数的构造方法");
        this.year = year;
        this.month = month;
        this.day = day;
    }
    public void printDate(){
        System.out.println(this.year+"-"+this.month+"-"+this.day);
    }
}
public class Test {
    public static void main(String[] args) {
        Date d1 = new Date(2023,9,27);
        Date d2 = new Date(2023,9);
        d1.printDate();
        d2.printDate();
    }
}

5、用户若没有定义任何构造方法,编译器会默认生成一个不带参数的构造方法,一旦用户定义,编译器便不会再生成构造方法;

6、构造方法中可以通过this调用其他构造方法,this()必须为构造方法的第一条语句。

class Date {
    public int year;
    public int month;
    public int day;

    public Date() {
        this(1900,1,1);
    }

    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
    public void printDate(){
        System.out.println(this.year+"-"+this.month+"-"+this.day);
    }
}

2.5static成员

2.5.1static修饰成员变量

class Student{
    String name;//姓名
    String gender;//性别
    String classes;//班级

    public Student(String name, String gender) {
        this.name = name;
        this.gender = gender;
    }
}

        我们对于上述学生类实例化出三个对象。那么当这三个同学是同一个班级时我们就需要给每个学生对象存储一份班级的成员变量,这个操作相当麻烦。


public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("张三","男");
        Student student2 = new Student("李四","男");
        Student student3 = new Student("王五","女");
        student1.classes = "1班";
        student2.classes = "1班";
        student3.classes = "1班";
    }
}

        因此我们可以使用static来修饰成员变量。在Java中被static修饰的成员,被称之为静态成员,也可以称为类成员,它并不属于某个具体的对象而是所有的对象所共享的。这样就可以对学生的班级只存储一份,让所有学生来共享。

        对于static修饰后的成员变量我们可以直接对其初始化

class Student{
    String name;
    String gender;
    static String classes = "1班";

    public Student(String name, String gender) {
        this.name = name;
        this.gender = gender;
    }

    public void print(){
        System.out.println("姓名:" + this.name+"性别:" + this.gender + "班级:" + this.classes);
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("张三","男");
        Student student2 = new Student("李四","男");
        Student student3 = new Student("王五","女");
        student1.print();
        student2.print();
        student3.print();
    }
}

当然也可以使用类名.来对其初始化

Student.classes = "1班";

静态成员变量特性

  1. 不属于某个具体的对象,是类的属性,是所有对象所共享的,并不存储在某个对象空间中;
  2. 既可以通过对象访问,也可以通过类名访问;
  3. 静态成员变量存储在方法区中;
  4. 生命周期伴随类的一生:随类的加载而创建,类的卸载而销毁。

2.5.2static修饰成员方法

        在Java中被static修饰的成员方法称为成员方法,是类的方法不是某个对象所特有的,静态成员一般通过静态方法来访问。

静态方法不属于某个具体的对象,是类方法。可以通过对象调用,但一般通过类名.静态方法名()的方式调用。在静态方法中不能访问非静态成员变量,也不能访问任何非静态方法,因为非静态方法中有this参数,在静态方法中调用时候无法传递this引用。

2.5.3static成员变量初始化

1.就地初始化:在定义时直接给初始值

class Student{
    String name;
    String gender;
    static String classes = "1班";

}

2.静态代码块初始化

2.6代码块

使用{}定义的一段代码称为代码块,在Java中根据代码块定义的位置,及关键字,可分为以下四类:

  • 局部代码块:定义在方法中的代码块
public class Test {
    public static void main(String[] args) {
        //直接使用{}定义的代码块称为局部代码块,也叫普通代码块
        {
            System.out.println("这是一个局部代码块");
        }
    }
}
  • 构造代码块/实例代码块:定义在类中代码块不加修饰符,一般用于初始化实例成员变量
public class Date {
    int year;
    int month;
    int day;
    {
        this.year = 1900;
        this.month = 1;
        this.day = 1;
        System.out.println("这是一个实例代码块");
    }
}
  • 静态代码块:使用static定义的代码块称为静态代码块,一般用于初始化静态成员变量
public class Student{

    String name;
    String gender;
    static String classes;
    static {
        classes = "1班";
        System.out.println("这是一个静态代码块");
    }

}
  • 同步代码块:用于标识着一个方法或者一个代码块是同步处理的。

注意事项:

  • 静态代码块不管生成多少个对象,都只会执行一次
  • 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
  • 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行
  • 实例代码块只有在创建对象时才会执行。
class Demo{

    String name;
    String gender;
    static String classes;

    static {
        System.out.println("这是一个静态代码块1");
    }
    static {
        System.out.println("这是一个静态代码块2");
    }
    {
        System.out.println("这是一个实例代码块1");
    }

}

public class Test {
    public static void main(String[] args) {
        Demo demo1 = new Demo();
        Demo demo2 = new Demo();
    }
}

三、面向对象的三大特性

3.1封装

3.1.1封装的概念

        面向对象具有三大特征:封装,继承,多态。在类和对象阶段主要研究的是封装特性。封装就是套壳屏蔽细节

        封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互。也就是对类内部的实现细节进行了隐藏(封装)对外只提供一些公开的接口,供其他用户进行访问。

        封装可以隐藏类的实现细节,从而达到安全性

3.1.2访问限定符

        Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符;

访问范围

private

default

protected

public

同一个包中

的同一类

同一个包

的不同类

不同包中

的子类

不同包的

非子类

说明:

        private:只能在当前类中访问

        default:在同一个包中的类可以访问,是默认权限

        protected:主要在继承中使用,可以被子类访问

        public:任何地方的类都可以使用

访问权限除了可以限定类中的成员的可见性,也可以控制类的可见性。

3.1.3封装扩展之包

包的概念:

        为了更好的管理类,把多个类收集在一起称为一组称之为软件包。

        在java中引入包,包是对类、接口等的封装机制的体现,是一种对类或者接口等很好的组织方式,在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。

包的导入

        在Java中可以使用import语句导入包中的类

import java.util.Date;
public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getTime());
    }
}

在上述代码中我们通过import java.util.Date导入了Date类;

我们若是想访问包中的其他类可以使用import java.util.*即可。

import java.util.*;
public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getTime());
    }
}

        但是这样的写法会出现一些问题:util和sql中都有一个Date这样的类,若是同时导入两个包会出现冲突的问题,导致编译出错。在这种情况下我们就需要使用完整的类名来导入Java.util中的Date类了。

import java.util.*;
import java.sql.*;
public class Test {
    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();
        System.out.println(date.getTime());
    }
}

包的自定义

        基本规则

  • 在文件的最上方加上一个package语句指定该代码在哪个包中
package year23.sep;

import java.util.Date;

public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getDate());
    }
}
  • 包名需要尽量指定成唯一的名字
  • 包名要和路径相匹配例如创建year23.sep那么就会有一个对应的路径year23/sep来存储代码
  • 如果一个类没有package语句,则该类就会被放进一个默认包中。

Java中常见的包

  1. java.lang:系统常用的基础类,会自动导入
  2. java.lang.reflect:java反射编程包
  3. java.net:网络编程开发包
  4. java.sql:用于数据库开发的支持包
  5. java.util:是Java提供的工具程序包,
  6. java.io:I/O编程开发包。

3.2继承

        继承(inheritance )是面向对象的另外一个基本概念,利用继承,可以基于已存在的类构造一个新类。继承已存在的类就是复用这些类的方法和属性。在此基础上还可以添加一些新的方法和属性以满足新的需求。通过继承,可以达到对重复代码复用,减少代码量。

public class Cat {
    String name;
    int age;
    float weight;

    public void eat(){
        System.out.println(name + "正在吃饭");
    }

    public void sleep()
    {
        System.out.println(name + "正在睡觉");
    }

    void mew(){
        System.out.println(name + "喵喵喵");
    }
}


public class Dog {
    String name;
    int age;
    float weight;

    public void eat() {
        System.out.println(name + "正在吃饭");
    }

    public void sleep() {
        System.out.println(name + "正在睡觉");
    }

    void Bark() {
        System.out.println(name + "汪汪汪");
    }
}

        通过观察上述两个类,可以发现代码中存在大量重复,对于这些重复的代码我们可以将其抽取出来,创建一个新的类来实现代码的复用。

public class Animal {
    String name;
    int age;
    float weight;

    public void eat() {
        System.out.println(name + "正在吃饭");
    }

    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
}

public class Cat extends Animal{
    void mew() {
        System.out.println(name + "喵喵喵");
    }
    public void catchMouse(){
        System.out.println(name + "正在抓老鼠");
    }
}

public class Dog extends Animal{
    void Bark() {
        System.out.println(name + "汪汪汪");
    }
    public void lookDoor(){
        System.out.println(name + "在看门");
    }
}

        继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。

继承的语法:

punlic class 子类 extends 父类{

}

子类会将父类中的成员变量或者方法继承到子类中,并且子类在继承父类之后,必须添加自己特有的成员,体现与基类的不同,否则就没有必要继承了。

3.2.1父类成员访问

1、子类中访问父类成员变量

public class Animal {
    String name;
    int age;
    float weight;

    public void eat() {
        System.out.println(name + "正在吃饭");
    }

    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
}

public class Dog extends Animal{
    public Dog(){
        this.name = "旺财";
        this.age = 2;
        this.weight = 10.5f;
    }

    public void Dogs(){
        System.out.println(age + "岁的" + name + "重" + weight + "公斤");
    }
}

        在子类中可以直接使用父类的成员变量,当子类中存在与父类相同的成员变量时优先访问自己的成员变量,成员变量访问遵循就近原则,自己有就优先访问自己的,如果没则在父类中找,如果父类中也没有就会编译报错。

2、子类中访问父类成员方法

        当成员方法没有同名时在子类方法中或者通过子类对象访问方法时,会优先访问自己的,如果自己没有就会到父类中找,父类中要是同样没有,就会编译报错。

3.2.2super关键字

        在编写代码时,由于设计不好,或者场景需求,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,直接访问是无法做到的,在Java中提供了super关键字,可以在子类方法中访问父类成员。

class SuperClass{
    int a = 1;
    public void methob1(){
        System.out.println("父类中的mothob1()方法");
    }
}

class SubClass extends SuperClass{
    int a = 2;

    @Override
    public void methob1() {
        System.out.println("子类中的mothob1()方法");
    }
    {
        System.out.println(a);
        System.out.println(super.a);
        methob1();
        super.methob1();
    }
}

super关键字只能在非静态方法中使用,在子类中访问父类的成员变量和方法。

super与this:

  • 相同点:

都是java中的关键子;

只能在非静态方法中使用,用于访问非静态成员方法和属性;

在构造方法中调用时必须是构造方法的第一条语句,因此不能同时存在

  • 不同点:

this是当前对象的引用,而super则是相当于子类对象中从父类继承下来的部分成员的引用。

在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类的继承下来的方法和属性。

在构造方法中,this()用于调用本类的构造方法,super()用于调用父类的构造方法,两种调用不能同时存在,因为这两种调用都必须四构造方法的第一条语句。

构造方法中一定会存在super()的调用,哪怕没有写,编译器也会自行添加,而this()则不会。

3.2.3子类的构造方法

子类对象构造时,需要先调用父类构造方法,然后执行子类构造方法

class SuperClass{
    public SuperClass(){
        System.out.println("父类的构造方法");
    }
}

class SubClass extends SuperClass{
    public SubClass(){
        System.out.println("子类的构造方法");
    }
}

public class Test1 {
    public static void main(String[] args) {
        SubClass subClass = new SubClass();
    }
}

        在子类构造方法中虽没有写任何关于父类构造的方法,但是在构造子类方法中会默认调用父类的构造方法super();

        子类对象的成员是由两部分构成的,一部分为父类继承下来的,另一部分时子类新增的部分,所以在构造子类对象时,会先调用父类的构造方法,将从父类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。

        在子类构造方法中第一行默认有隐含的super()调用,会调用父类的构造方法,若父类构造方法中带有参数,则需用户为子类显示定义构造方法,并在子类中选取合适的父类构造方法调用

3.2.4继承关系下的代码块执行顺序

class SuperClass{
    public SuperClass(){
        System.out.println("父类的构造方法");
    }
    {
        System.out.println("父类的实例代码块");
    }
    static {
        System.out.println("父类的静态代码块");
    }
}

class SubClass extends SuperClass{
    public SubClass(){
        System.out.println("子类的构造方法");
    }
    {
        System.out.println("子类的实例代码块");
    }
    static {
        System.out.println("子类的静态代码块");
    }
}

public class Test1 {
    public static void main(String[] args) {
        SubClass subClass1 = new SubClass();
        System.out.println("-----------------");
        SubClass subClass2 = new SubClass();
    }
}

  1. 静态代码块优先执行,且只执行一次,在类加载阶段执行。其中父类的静态代码块会优先于子类静态代码块执行
  2. 当有对象创建时,会先后执行实例代码块和构造代码块

3.2.5继承方式

在java中不支持多继承

3.2.6final关键字

        有时候,可能希望阻止别人利用某个类来定义子类,在定义类时可以用final修饰这个类来阻止别人定义这个类的子类,被final修饰的类被称为final类表示此类不能被继承。

final public class 类名{

}

当然final也可以修饰成员方法,用于确保它们不会在子类中改变语义。也可以用于修饰成员变量,被final修饰成员变量表示常量。

3.3多态

3.3.1多态的概念

        多态通俗来讲,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

        以动物为例,同样都是吃,狼吃肉,羊吃草这便是多态,

        总的来说就是同一件事,发生在不同对象身上就会产生不同的结果

3.3.2多态的实现条件

        在Java中要实现多态,就必须在继承体系下,且子类必须对父类中的方法进行重写,并且可以通过父类的引用调用重写的方法

        多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法

3.3.3重写

        重写(Override)是指的是子类对父类中已有的方法重新实现。在重写过程中,子类需要按照和父类相同的方法签名(即方法名、参数列表)来定义自己的方法体,可以在方法体中重新定制适合子类的具体实现。

        重写的前提是父类方法必须是非静态、非私有、非final修饰和非构造的方法。因为静态方法是与类关联而不是对象,私有方法不能被子类访问,而final修饰的方法被称为密封方法是不可重写的。

        重写的好处在于子类可以根据自身的特定需求来定义特定的行为,即子类能够根据需要实现父类的方法。这样可以提供更灵活和多样化的功能。同时,通过重写,子类可以修改和优化父类的行为,实现代码的复用和扩展。

public class Animal {

    public void eat() {
        System.out.println(name + "正在吃饭");
    }

}

public class Dog extends Animal{

    @Override
    public void eat() {
        System.out.println(name + "正在吃狗粮");
    }
}

        子类在重写父类方法时,必须与父类方法的返回值类型,方法名,参数列表完全一致。子类重写的方法访问权限不能比父类被重写的方法访问权限更低。重写后的方法可以使用@Overrid注解来显示指定,可以帮助我们进行合法性检验。

重写和重载的区别

1、重写的参数列表一定不能修改,而重载的参数列表必须修改

2、重写的返回类型不能修改,除非可以构成父子类关系,重载的返回类型可以修改

3、重写的访问权限不能低于父类的访问权限,而重载可以修改访问限定符

4、重载是一个类的多态性表现,而重写是子类与父类的一种多态性表现

静态绑定(Static Binding):也称为前期绑定(早绑定),在编译时就确定要调用的方法。当我们使用对象调用一个方法时,编译器会根据引用变量的类型来确定要调用的方法,而不是根据实际运行时对象的类型。这种绑定发生在编译阶段。静态绑定适用于静态方法和私有方法等无法被继承和覆盖的方法。典型代表方法重载。

动态绑定(Dynamic Binding):也称为后期绑定(晚绑定),在运行时根据实际对象的类型来确定要调用的方法。当我们使用父类引用指向子类对象,并通过该引用调用一个被子类重写的方法时,会根据实际运行时对象的类型来决定要调用的方法,而不是根据引用变量的类型。这种绑定发生在运行阶段,因此也被称为晚期绑定。动态绑定实现了多态的特性,允许在运行时根据对象的实际类型选择具体的方法实现。

3.3.4向上转型和向下转型

1、向上转型

        向上转型,JAVA中的一种调用方式。向上转型是对父类的对象的方法的扩充,即父类的对象可访问子类从父类中继承来的和子类重写父类的方法。向上转型后,子类不能使用原来子类中特有的字段,和方法

语法格式:父类类型 对象名 = new 子类对象

public class Animal {
    String name;
    int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "正在吃饭");
    }
}

public class Dog extends Animal{
    public Dog(String name,int age){
        super(name,age);
    }

    @Override
    //重写父类的eat()方法
    public void eat() {
        System.out.println(name + "正在吃狗粮");
    }

    public void lookDoor(){
        System.out.println(name + "在看门");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal dog = new Dog("旺财",2);//直接传参,使用子类对象赋值给父类对象
        dog.eat();
        dog.lookDoor()//编译报错。不能调用子类特有的方法。
    }
}

向上转型的优点:让代码实现更简单灵活

向上转型的缺点:不能调用子类特有的方法

2、向下转型

        将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转型。

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog("旺财",2);
        //向上转型
        Animal animal1 = dog;
        if(animal1 instanceof Dog){//判断是否可以安全转换
            dog = (Dog)animal1;
            dog.lookDoor();
        }
    }
}

        向下转型用的比较少且不安全,在运行时父类指向了子类A,却要强制还原成子类B,此时无法正常还原,运行时会抛出异常ClassCastException。为了提高向下转型的安全性Java中引入了instanceof,若表式为true,则可以安全转换,否则不能转换。

3.3.5多态的优缺点

  • 优点
  1. 可以降低代码的圈复杂度避免使用大量的if-else.提高代码的可读性。
  2. 增强了代码的可扩展性,降低代码的修改成本
  • 缺点

代码的运行效率降

四、内部类

        当一个事务的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构由只为外部事务提供服务,那么此时可以考虑使用内部类。内部类可以访问外部类的所有成员,包括私有成员,从而方便地为外部事务提供服务。

        内部类是Java中一种特殊的类定义方式,它可以被定义在其他类或者方法的内部。内部类也是封装的一种体现,可以被用来实现一个类的私有成员或者私有方法。内部类只有在外部类中是可见的,对外是不可见的,从而实现了隐藏和封装的效果。

        总的来说,内部类在Java中是一种实现封装和提供服务的机制,可以将一个事务的内部结构完整地描述出来,同时也增强了代码的可读性和可维护性。

        在Java中,内部类生成的字节码会是外部类名$内部类名.class;在java中一个类对应一个字节码文件

内部类的类型

1、实例内部类:也被称为非静态内部类。

class OutClass{
    int a = 1;
    static int b = 2;
    String e = "外部类";
    //实例内部类
    class InClass{
        int c = 3;
        static final int d = 4;//常量
        String e = "实例内部类";

        public void print(){
            System.out.println(a);
            System.out.println(b);
            System.out.println(c);
            System.out.println(d);
            System.out.println(e);
            System.out.println(OutClass.this.e);
        }

    }

}

public class Test {
    public static void main(String[] args) {
        //1、创建外部类对象
        OutClass outClass = new OutClass();
        //2、创建内部类对象
        OutClass.InClass inClass = outClass.new InClass();
        inClass.print();
    }
}

注意事项:

  • 外部类中的任何成员都可以在实例内部类方法中直接访问
  • 实例内部类所处位置与外部类成员位置相同,因此也受public,private等访问限定符约束
  • 在实例内部类方法中访问同名成员时,优先访问自己的,如果要访问外部类同名成员。需要使用外部类名.this.同名成员来访问
  • 实例内部类对象必须在先有外部类对象才能创建
  • 实例内部类的非静态方法中包含了一个指向外部类对象的引用
  • 外部类中不能直接访问实例内部类的成员,如果要访问必须先要创建内部类对象。
  • 实例内部类中不能有静态成员变量,如果非要定义静态成员那么需要使用static final 来修饰

2、静态内部类:被static修饰的内部成员类就是静态内部类

class OutClass{
    int a = 1;
    static int b = 2;
    String e = "外部类";
    //静态内部类
    static class InClass{
        int c = 3;
        static int d = 4;
        String e = "静态内部类";

        public void print(){
            OutClass outClass = new OutClass();
            System.out.println(outClass.a);
            System.out.println(b);
            System.out.println(c);
            System.out.println(d);
            System.out.println(e);
            System.out.println(outClass.e);
        }

    }

}

public class Test {
    public static void main(String[] args) {
        OutClass.InClass inClass = new OutClass.InClass();
        inClass.print();
    }
}

注意事项:

  • 在静态内部类中只能访问外部类中的静态成员,如果要访问外部类的非静态成员,需要通过外部类的对象的引用才能访问。
  • 创建静态内部类对象时,不需要先创建外部类对象。

3、局部内部类:定义在外部类的方法体或者{}中,该种内部类只能在其定义的方法体内部使用使用。不能被public,static等修饰符修饰。

4、匿名内部类:

匿名对象只能使用一次,每次使用都新生成了一个对象

五,抽象类

5.1抽象类概念

        在面向对象的概念中所有对象都是通过类来描绘的,但是并不是所有类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类,抽象类无法初始化

        矩形,三角形,圆形都是图形,因此和Shape类的关系应该都是继承关系,但是Shape并不是具体的图形因此内部的draw()方法是没有办法具体实现的。主要绘制图形的都是由Shape的各种子类的draw完成的,像这种没有实际工作的方法,我们可以将其设计成一个抽象方法(abstract method),包含抽象方法的类我们称为抽象类(abstract class)

        抽象类代表着没有人可以创建出该类的实例,它除了被继承以外,是没有用途,没有值,没有目的的。

5.2抽象类语法

        在Java中如果一个类被abstract修饰则被称为抽象类,抽象类中被abstract修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。抽象类也是类内部可以包含普通方法和属性构造方法。

public abstract class Shape {
    double a;

    abstract public void draw();
    abstract public void colour();
}

 5.3抽象类的特性

1、使用abstract修饰的类就是抽象类

2、抽象类当中可以包含普通类所能包含的成员

3、抽象类和普通类不同的是可以包含抽象方法

4、抽象方法是使用abstract修饰的这个方法没有具体实现

5、抽象类不能被实例化

public class Test{
    public static void main(String[] args) {
        Shape shape = new Shape();
    }
}

6、抽象类存在最大的意义就是为了继承;

7、如果一个普通类继承了抽象类,必须重写抽象类中的方法

public abstract class Shape {
    String colour;
    abstract public void draw();
}
public class Rect extends Shape {

    @Override
    public void draw() {
        System.out.println("矩形");
    }

}

8、如果一个抽象类B继承了一个抽象类A,此时在B中不需要重写A中的抽象方法,但当抽象类B再次被一个普通类继承,就需要重写

public abstract class Animal {

    abstract public void eat();

}

public abstract class Feline extends Animal {

}

public class Cat extends Feline{

    @Override
    public void eat() {
        System.out.println("吃猫粮");
    }

}

5.4抽象类的作用

        抽象类本身不能被实例化,要想使用只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法,使用抽象类就相当于多了一层编译器的校验。

        使用抽象类的场景就如上面的代码,实际工作不由父类完成,而应由子类完成,那么此时不小心误用父类的话,会因为父类是抽象类就会在实例化的时候提示错误,让我们尽早发现错误,充分利用编译器的校验,在实际开发时非常有意义的

;